Add comprehensive i18n localization for KMM and iOS
KMM (Android/Shared): - Add strings.xml with 200+ localized strings - Add translation files for es, fr, de, pt languages - Update all screens to use stringResource() for i18n - Add Accept-Language header to API client for all platforms iOS: - Add L10n.swift helper with type-safe string accessors - Add Localizable.xcstrings with translations for all 5 languages - Update all SwiftUI views to use L10n.* for localized strings - Localize Auth, Residence, Task, Contractor, Document, and Profile views Supported languages: English, Spanish, French, German, Portuguese 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
@@ -2,13 +2,19 @@ package com.example.casera.network
|
||||
|
||||
import io.ktor.client.*
|
||||
import io.ktor.client.engine.okhttp.*
|
||||
import io.ktor.client.plugins.*
|
||||
import io.ktor.client.plugins.contentnegotiation.*
|
||||
import io.ktor.client.plugins.logging.*
|
||||
import io.ktor.serialization.kotlinx.json.*
|
||||
import kotlinx.serialization.json.Json
|
||||
import java.util.Locale
|
||||
|
||||
actual fun getLocalhostAddress(): String = "10.0.2.2"
|
||||
|
||||
actual fun getDeviceLanguage(): String {
|
||||
return Locale.getDefault().language
|
||||
}
|
||||
|
||||
actual fun createHttpClient(): HttpClient {
|
||||
return HttpClient(OkHttp) {
|
||||
install(ContentNegotiation) {
|
||||
@@ -23,5 +29,9 @@ actual fun createHttpClient(): HttpClient {
|
||||
logger = Logger.DEFAULT
|
||||
level = LogLevel.ALL
|
||||
}
|
||||
|
||||
install(DefaultRequest) {
|
||||
headers.append("Accept-Language", getDeviceLanguage())
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
473
composeApp/src/commonMain/composeResources/values-de/strings.xml
Normal file
473
composeApp/src/commonMain/composeResources/values-de/strings.xml
Normal file
@@ -0,0 +1,473 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<resources>
|
||||
<!-- App Name -->
|
||||
<string name="app_name">Casera</string>
|
||||
<string name="app_tagline">Verwalten Sie Ihre Immobilien einfach</string>
|
||||
|
||||
<!-- Auth - Login -->
|
||||
<string name="auth_login_title">Casera</string>
|
||||
<string name="auth_login_subtitle">Verwalten Sie Ihre Immobilien einfach</string>
|
||||
<string name="auth_login_username_label">Benutzername oder E-Mail</string>
|
||||
<string name="auth_login_password_label">Passwort</string>
|
||||
<string name="auth_login_button">Anmelden</string>
|
||||
<string name="auth_forgot_password">Passwort vergessen?</string>
|
||||
<string name="auth_no_account">Kein Konto? Registrieren</string>
|
||||
<string name="auth_login_failed">Anmeldung fehlgeschlagen</string>
|
||||
<string name="auth_show_password">Passwort anzeigen</string>
|
||||
<string name="auth_hide_password">Passwort verbergen</string>
|
||||
|
||||
<!-- Auth - Register -->
|
||||
<string name="auth_register_title">Konto erstellen</string>
|
||||
<string name="auth_register_subtitle">Registrieren Sie sich, um zu beginnen</string>
|
||||
<string name="auth_register_first_name">Vorname</string>
|
||||
<string name="auth_register_last_name">Nachname</string>
|
||||
<string name="auth_register_email">E-Mail</string>
|
||||
<string name="auth_register_username">Benutzername</string>
|
||||
<string name="auth_register_password">Passwort</string>
|
||||
<string name="auth_register_confirm_password">Passwort bestatigen</string>
|
||||
<string name="auth_register_button">Konto erstellen</string>
|
||||
<string name="auth_have_account">Bereits ein Konto? Anmelden</string>
|
||||
<string name="auth_passwords_dont_match">Passworter stimmen nicht uberein</string>
|
||||
|
||||
<!-- Auth - Verify Email -->
|
||||
<string name="auth_verify_title">E-Mail bestatigen</string>
|
||||
<string name="auth_verify_subtitle">Geben Sie den 6-stelligen Code ein</string>
|
||||
<string name="auth_verify_code_label">Bestatigungscode</string>
|
||||
<string name="auth_verify_button">Bestatigen</string>
|
||||
<string name="auth_verify_resend">Code erneut senden</string>
|
||||
<string name="auth_verify_success">E-Mail erfolgreich bestatigt</string>
|
||||
|
||||
<!-- Auth - Forgot Password -->
|
||||
<string name="auth_forgot_title">Passwort vergessen</string>
|
||||
<string name="auth_forgot_subtitle">Geben Sie Ihre E-Mail ein, um einen Code zu erhalten</string>
|
||||
<string name="auth_forgot_email_label">E-Mail</string>
|
||||
<string name="auth_forgot_button">Code senden</string>
|
||||
<string name="auth_forgot_success">Wenn ein Konto existiert, wurde ein Code gesendet</string>
|
||||
|
||||
<!-- Auth - Reset Password -->
|
||||
<string name="auth_reset_title">Passwort zurucksetzen</string>
|
||||
<string name="auth_reset_subtitle">Geben Sie Ihr neues Passwort ein</string>
|
||||
<string name="auth_reset_code_label">Code</string>
|
||||
<string name="auth_reset_new_password">Neues Passwort</string>
|
||||
<string name="auth_reset_confirm_password">Neues Passwort bestatigen</string>
|
||||
<string name="auth_reset_button">Passwort zurucksetzen</string>
|
||||
<string name="auth_reset_success">Passwort erfolgreich zuruckgesetzt</string>
|
||||
|
||||
<!-- Properties / Residences -->
|
||||
<string name="properties_title">Meine Immobilien</string>
|
||||
<string name="properties_empty_title">Keine Immobilien</string>
|
||||
<string name="properties_empty_subtitle">Fugen Sie Ihre erste Immobilie hinzu!</string>
|
||||
<string name="properties_add_button">Immobilie hinzufugen</string>
|
||||
<string name="properties_add_title">Immobilie hinzufugen</string>
|
||||
<string name="properties_edit_title">Immobilie bearbeiten</string>
|
||||
<string name="properties_name_label">Name der Immobilie</string>
|
||||
<string name="properties_address_label">Adresse</string>
|
||||
<string name="properties_type_label">Immobilientyp</string>
|
||||
<string name="properties_notes_label">Notizen</string>
|
||||
<string name="properties_delete_confirm">Mochten Sie diese Immobilie wirklich loschen?</string>
|
||||
<string name="properties_deleted">Immobilie geloscht</string>
|
||||
<string name="properties_share_title">Immobilie teilen</string>
|
||||
<string name="properties_share_code">Freigabecode</string>
|
||||
<string name="properties_share_expires">Lauft ab in %1$s</string>
|
||||
<string name="properties_join_title">Immobilie beitreten</string>
|
||||
<string name="properties_join_code_label">Freigabecode eingeben</string>
|
||||
<string name="properties_join_button">Beitreten</string>
|
||||
<string name="properties_users_title">Immobilienmitglieder</string>
|
||||
<string name="properties_owner">Eigentumer</string>
|
||||
<string name="properties_member">Mitglied</string>
|
||||
<string name="properties_remove_user">Benutzer entfernen</string>
|
||||
<string name="properties_details">Immobiliendetails</string>
|
||||
<string name="properties_form_name_required">Immobilienname *</string>
|
||||
<string name="properties_form_name_error">Name ist erforderlich</string>
|
||||
<string name="properties_form_required">Erforderlich</string>
|
||||
<string name="properties_form_street">Straßenadresse</string>
|
||||
<string name="properties_form_apartment">Wohnung/Einheit #</string>
|
||||
<string name="properties_form_city">Stadt</string>
|
||||
<string name="properties_form_state">Bundesland/Kanton</string>
|
||||
<string name="properties_form_postal">Postleitzahl</string>
|
||||
<string name="properties_form_country">Land</string>
|
||||
<string name="properties_form_optional">Optionale Details</string>
|
||||
<string name="properties_form_sqft">Quadratmeter</string>
|
||||
<string name="properties_form_lot_size">Grundstücksgröße (Hektar)</string>
|
||||
<string name="properties_form_description">Beschreibung</string>
|
||||
<string name="properties_form_primary">Hauptwohnsitz</string>
|
||||
<string name="properties_form_create">Immobilie Erstellen</string>
|
||||
<string name="properties_form_update">Immobilie Aktualisieren</string>
|
||||
<string name="properties_loading">Lade Immobilie...</string>
|
||||
<string name="properties_failed_to_load">Fehler beim Laden der Immobilie</string>
|
||||
<string name="properties_failed_to_delete">Fehler beim Löschen der Immobilie</string>
|
||||
<string name="properties_failed_to_generate_report">Fehler beim Generieren des Berichts</string>
|
||||
<string name="properties_generate_report">Bericht Generieren</string>
|
||||
<string name="properties_generate_report_confirm">Dies wird einen Wartungsbericht für diese Immobilie generieren und per E-Mail senden. Möchten Sie fortfahren?</string>
|
||||
<string name="properties_generate">Generieren</string>
|
||||
<string name="properties_manage_users">Benutzer Verwalten</string>
|
||||
<string name="properties_edit_residence">Immobilie Bearbeiten</string>
|
||||
<string name="properties_delete_residence">Immobilie Löschen</string>
|
||||
<string name="properties_delete_name_confirm">Sind Sie sicher, dass Sie %1$s löschen möchten? Diese Aktion kann nicht rückgängig gemacht werden.</string>
|
||||
<string name="properties_address_section">Adresse</string>
|
||||
<string name="properties_unit">Einheit: %1$s</string>
|
||||
<string name="properties_property_details_section">Immobiliendetails</string>
|
||||
<string name="properties_bedrooms">Schlafzimmer</string>
|
||||
<string name="properties_bathrooms">Badezimmer</string>
|
||||
<string name="properties_square_footage">Quadratmeter</string>
|
||||
<string name="properties_lot_size">Grundstücksgröße</string>
|
||||
<string name="properties_year_built">Baujahr</string>
|
||||
<string name="properties_sq_ft">%1$s m²</string>
|
||||
<string name="properties_acres">%1$s Hektar</string>
|
||||
<string name="properties_description_section">Beschreibung</string>
|
||||
<string name="properties_purchase_info">Kaufinformationen</string>
|
||||
<string name="properties_purchase_date">Kaufdatum</string>
|
||||
<string name="properties_purchase_price">Kaufpreis</string>
|
||||
<string name="properties_add_task">Aufgabe Hinzufügen</string>
|
||||
<string name="properties_no_tasks">Noch keine Aufgaben</string>
|
||||
<string name="properties_add_task_start">Fügen Sie eine Aufgabe hinzu, um zu beginnen</string>
|
||||
<string name="properties_error_loading_tasks">Fehler beim Laden der Aufgaben: %1$s</string>
|
||||
<string name="properties_no_contractors">Noch keine Dienstleister</string>
|
||||
<string name="properties_add_contractors_hint">Fügen Sie Dienstleister über den Dienstleister-Tab hinzu</string>
|
||||
<string name="properties_error_loading_contractors">Fehler beim Laden der Dienstleister: %1$s</string>
|
||||
<string name="properties_cancel_task">Aufgabe Abbrechen</string>
|
||||
<string name="properties_cancel_task_confirm">Sind Sie sicher, dass Sie \"%1$s\" abbrechen möchten? Diese Aktion kann nicht rückgängig gemacht werden.</string>
|
||||
<string name="properties_archive_task">Aufgabe Archivieren</string>
|
||||
<string name="properties_archive_task_confirm">Sind Sie sicher, dass Sie \"%1$s\" archivieren möchten? Sie können sie später aus dem Archiv wiederherstellen.</string>
|
||||
<string name="properties_dismiss">Ablehnen</string>
|
||||
|
||||
<!-- Tasks -->
|
||||
<string name="tasks_title">Aufgaben</string>
|
||||
<string name="tasks_empty_title">Keine Aufgaben</string>
|
||||
<string name="tasks_empty_subtitle">Fugen Sie Ihre erste Wartungsaufgabe hinzu</string>
|
||||
<string name="tasks_add_button">Aufgabe hinzufugen</string>
|
||||
<string name="tasks_add_title">Aufgabe hinzufugen</string>
|
||||
<string name="tasks_edit_title">Aufgabe bearbeiten</string>
|
||||
<string name="tasks_title_label">Aufgabentitel</string>
|
||||
<string name="tasks_description_label">Beschreibung</string>
|
||||
<string name="tasks_category_label">Kategorie</string>
|
||||
<string name="tasks_priority_label">Prioritat</string>
|
||||
<string name="tasks_frequency_label">Haufigkeit</string>
|
||||
<string name="tasks_due_date_label">Falligkeitsdatum</string>
|
||||
<string name="tasks_estimated_cost_label">Geschatzte Kosten</string>
|
||||
<string name="tasks_contractor_label">Dienstleister</string>
|
||||
<string name="tasks_notes_label">Notizen</string>
|
||||
<string name="tasks_delete_confirm">Mochten Sie diese Aufgabe wirklich loschen?</string>
|
||||
<string name="tasks_deleted">Aufgabe geloscht</string>
|
||||
<string name="tasks_details">Aufgabendetails</string>
|
||||
<string name="tasks_title_required">Titel *</string>
|
||||
<string name="tasks_title_error">Titel ist erforderlich</string>
|
||||
<string name="tasks_category_required">Kategorie *</string>
|
||||
<string name="tasks_frequency_required">Häufigkeit *</string>
|
||||
<string name="tasks_priority_required">Priorität *</string>
|
||||
<string name="tasks_status_label">Status *</string>
|
||||
<string name="tasks_due_date_required">Fälligkeitsdatum (JJJJ-MM-TT) *</string>
|
||||
<string name="tasks_due_date_error">Fälligkeitsdatum ist erforderlich</string>
|
||||
<string name="tasks_due_date_placeholder">2025-01-31</string>
|
||||
<string name="tasks_update">Aufgabe Aktualisieren</string>
|
||||
<string name="tasks_failed_to_update">Fehler beim Aktualisieren der Aufgabe</string>
|
||||
|
||||
<!-- Task Columns / Kanban -->
|
||||
<string name="tasks_column_overdue">Uberfallig</string>
|
||||
<string name="tasks_column_due_soon">Bald fallig</string>
|
||||
<string name="tasks_column_upcoming">Bevorstehend</string>
|
||||
<string name="tasks_column_in_progress">In Bearbeitung</string>
|
||||
<string name="tasks_column_completed">Abgeschlossen</string>
|
||||
<string name="tasks_column_cancelled">Storniert</string>
|
||||
|
||||
<!-- Task Actions -->
|
||||
<string name="tasks_mark_complete">Abschliessen</string>
|
||||
<string name="tasks_mark_in_progress">Starten</string>
|
||||
<string name="tasks_cancel">Stornieren</string>
|
||||
<string name="tasks_uncancel">Wiederherstellen</string>
|
||||
<string name="tasks_archive">Archivieren</string>
|
||||
<string name="tasks_unarchive">Dearchivieren</string>
|
||||
<string name="tasks_completed_message">Aufgabe als abgeschlossen markiert</string>
|
||||
<string name="tasks_in_progress_message">Aufgabe als in Bearbeitung markiert</string>
|
||||
<string name="tasks_cancelled_message">Aufgabe storniert</string>
|
||||
|
||||
<!-- Task Completions -->
|
||||
<string name="completions_title">Aufgabenabschlusse</string>
|
||||
<string name="completions_add_title">Aufgabe abschliessen</string>
|
||||
<string name="completions_notes_label">Notizen</string>
|
||||
<string name="completions_actual_cost_label">Tatsachliche Kosten</string>
|
||||
<string name="completions_date_label">Abschlussdatum</string>
|
||||
<string name="completions_photos_label">Fotos</string>
|
||||
<string name="completions_add_photo">Foto hinzufugen</string>
|
||||
<string name="completions_delete_confirm">Diesen Eintrag loschen?</string>
|
||||
|
||||
<!-- Contractors -->
|
||||
<string name="contractors_title">Dienstleister</string>
|
||||
<string name="contractors_empty_title">Keine Dienstleister</string>
|
||||
<string name="contractors_empty_subtitle">Fugen Sie vertrauenswurdige Dienstleister hinzu</string>
|
||||
<string name="contractors_empty_subtitle_first">Fugen Sie Ihren ersten Dienstleister hinzu</string>
|
||||
<string name="contractors_no_results">Keine Dienstleister gefunden</string>
|
||||
<string name="contractors_add_button">Dienstleister hinzufugen</string>
|
||||
<string name="contractors_add_title">Dienstleister hinzufugen</string>
|
||||
<string name="contractors_edit_title">Dienstleister bearbeiten</string>
|
||||
<string name="contractors_name_label">Name</string>
|
||||
<string name="contractors_company_label">Firma</string>
|
||||
<string name="contractors_phone_label">Telefon</string>
|
||||
<string name="contractors_email_label">E-Mail</string>
|
||||
<string name="contractors_specialty_label">Fachgebiet</string>
|
||||
<string name="contractors_notes_label">Notizen</string>
|
||||
<string name="contractors_favorite">Favorit</string>
|
||||
<string name="contractors_favorites">Favoriten</string>
|
||||
<string name="contractors_filter_favorites">Favoriten filtern</string>
|
||||
<string name="contractors_filter_specialty">Nach Fachgebiet filtern</string>
|
||||
<string name="contractors_all_specialties">Alle Fachgebiete</string>
|
||||
<string name="contractors_search">Dienstleister suchen...</string>
|
||||
<string name="contractors_clear_search">Suche loschen</string>
|
||||
<string name="contractors_add_to_favorites">Zu Favoriten hinzufugen</string>
|
||||
<string name="contractors_remove_from_favorites">Aus Favoriten entfernen</string>
|
||||
<string name="contractors_view_details">Details anzeigen</string>
|
||||
<string name="contractors_tasks_count">%1$d Aufgaben</string>
|
||||
<string name="contractors_delete_confirm">Mochten Sie diesen Dienstleister wirklich loschen?</string>
|
||||
<string name="contractors_deleted">Dienstleister geloscht</string>
|
||||
<string name="contractors_call">Anrufen</string>
|
||||
<string name="contractors_send_email">E-Mail senden</string>
|
||||
<string name="contractors_failed_to_load">Fehler beim Laden der Dienstleister</string>
|
||||
<string name="contractors_failed_to_delete">Fehler beim Loschen des Dienstleisters</string>
|
||||
<string name="contractors_failed_to_update_favorite">Fehler beim Aktualisieren des Favoriten</string>
|
||||
<string name="contractors_upgrade_required">Upgrade erforderlich</string>
|
||||
<string name="contractors_upgrade_message">Sie haben die maximale Anzahl von Dienstleistern fur Ihren aktuellen Plan erreicht. Upgraden Sie auf Pro fur unbegrenzte Dienstleister.</string>
|
||||
<string name="contractors_details">Dienstleisterdetails</string>
|
||||
<string name="contractors_toggle_favorite">Favorit umschalten</string>
|
||||
<string name="contractors_website">Webseite</string>
|
||||
<string name="contractors_directions">Wegbeschreibung</string>
|
||||
<string name="contractors_contact_info">Kontaktinformationen</string>
|
||||
<string name="contractors_no_contact_info">Keine Kontaktinformationen verfügbar</string>
|
||||
<string name="contractors_address">Adresse</string>
|
||||
<string name="contractors_location">Standort</string>
|
||||
<string name="contractors_associated_property">Zugehörige Immobilie</string>
|
||||
<string name="contractors_property">Immobilie</string>
|
||||
<string name="contractors_notes">Notizen</string>
|
||||
<string name="contractors_statistics">Statistiken</string>
|
||||
<string name="contractors_tasks_completed">Aufgaben\nAbgeschlossen</string>
|
||||
<string name="contractors_average_rating">Durchschnittliche\nBewertung</string>
|
||||
<string name="contractors_info">Informationen</string>
|
||||
<string name="contractors_added_by">Hinzugefügt Von</string>
|
||||
<string name="contractors_member_since">Mitglied Seit</string>
|
||||
<string name="contractors_delete">Dienstleister Löschen</string>
|
||||
<string name="contractors_delete_warning">Sind Sie sicher, dass Sie diesen Dienstleister löschen möchten? Diese Aktion kann nicht rückgängig gemacht werden.</string>
|
||||
<string name="contractors_completed_tasks">%1$d abgeschlossene Aufgaben</string>
|
||||
|
||||
<!-- Documents -->
|
||||
<string name="documents_title">Dokumente</string>
|
||||
<string name="documents_and_warranties">Dokumente und Garantien</string>
|
||||
<string name="documents_empty_title">Keine Dokumente</string>
|
||||
<string name="documents_empty_subtitle">Speichern Sie wichtige Dokumente und Garantien</string>
|
||||
<string name="documents_add_button">Dokument hinzufugen</string>
|
||||
<string name="documents_add_title">Dokument hinzufugen</string>
|
||||
<string name="documents_edit_title">Dokument bearbeiten</string>
|
||||
<string name="documents_title_label">Titel</string>
|
||||
<string name="documents_description_label">Beschreibung</string>
|
||||
<string name="documents_type_label">Dokumenttyp</string>
|
||||
<string name="documents_vendor_label">Anbieter</string>
|
||||
<string name="documents_serial_label">Seriennummer</string>
|
||||
<string name="documents_model_label">Modellnummer</string>
|
||||
<string name="documents_purchase_date_label">Kaufdatum</string>
|
||||
<string name="documents_purchase_price_label">Kaufpreis</string>
|
||||
<string name="documents_expiry_date_label">Ablaufdatum</string>
|
||||
<string name="documents_delete_confirm">Mochten Sie dieses Dokument wirklich loschen?</string>
|
||||
<string name="documents_deleted">Dokument geloscht</string>
|
||||
<string name="documents_warranties_title">Garantien</string>
|
||||
<string name="documents_warranties_tab">Garantien</string>
|
||||
<string name="documents_documents_tab">Dokumente</string>
|
||||
<string name="documents_upload">Datei hochladen</string>
|
||||
<string name="documents_view">Dokument anzeigen</string>
|
||||
<string name="documents_all_categories">Alle Kategorien</string>
|
||||
<string name="documents_all_types">Alle Typen</string>
|
||||
<string name="documents_filter_active">Aktive filtern</string>
|
||||
<string name="documents_filters">Filter</string>
|
||||
<string name="documents_upgrade_required">Upgrade erforderlich</string>
|
||||
<string name="documents_upgrade_message">Sie haben die maximale Anzahl von Dokumenten fur Ihren aktuellen Plan erreicht. Upgraden Sie auf Pro fur unbegrenzte Dokumente.</string>
|
||||
<string name="documents_details">Dokumentdetails</string>
|
||||
<string name="documents_status">Status</string>
|
||||
<string name="documents_inactive">Inaktiv</string>
|
||||
<string name="documents_expired">Abgelaufen</string>
|
||||
<string name="documents_expiring_soon">Läuft bald ab</string>
|
||||
<string name="documents_active">Aktiv</string>
|
||||
<string name="documents_days_remaining">Verbleibende Tage</string>
|
||||
<string name="documents_basic_info">Grundinformationen</string>
|
||||
<string name="documents_item_details">Artikeldetails</string>
|
||||
<string name="documents_item_name">Artikelname</string>
|
||||
<string name="documents_model_number">Modellnummer</string>
|
||||
<string name="documents_serial_number">Seriennummer</string>
|
||||
<string name="documents_provider">Anbieter</string>
|
||||
<string name="documents_provider_contact">Anbieterkontakt</string>
|
||||
<string name="documents_claim_info">Anspruchsinformationen</string>
|
||||
<string name="documents_claim_phone">Anspruchstelefon</string>
|
||||
<string name="documents_claim_email">Anspruchs-E-Mail</string>
|
||||
<string name="documents_claim_website">Anspruchs-Webseite</string>
|
||||
<string name="documents_important_dates">Wichtige Daten</string>
|
||||
<string name="documents_purchase_date">Kaufdatum</string>
|
||||
<string name="documents_start_date">Startdatum</string>
|
||||
<string name="documents_end_date">Enddatum</string>
|
||||
<string name="documents_associations">Zuordnungen</string>
|
||||
<string name="documents_residence">Immobilie</string>
|
||||
<string name="documents_contractor">Dienstleister</string>
|
||||
<string name="documents_contractor_phone">Dienstleistertelefon</string>
|
||||
<string name="documents_additional_info">Zusätzliche Informationen</string>
|
||||
<string name="documents_tags">Tags</string>
|
||||
<string name="documents_notes">Notizen</string>
|
||||
<string name="documents_images">Bilder (%1$d)</string>
|
||||
<string name="documents_attached_file">Angehängte Datei</string>
|
||||
<string name="documents_file_type">Dateityp</string>
|
||||
<string name="documents_file_size">Dateigröße</string>
|
||||
<string name="documents_download_file">Datei Herunterladen</string>
|
||||
<string name="documents_metadata">Metadaten</string>
|
||||
<string name="documents_uploaded_by">Hochgeladen Von</string>
|
||||
<string name="documents_created">Erstellt</string>
|
||||
<string name="documents_updated">Aktualisiert</string>
|
||||
<string name="documents_delete">Dokument Löschen</string>
|
||||
<string name="documents_delete_warning">Sind Sie sicher, dass Sie dieses Dokument löschen möchten? Diese Aktion kann nicht rückgängig gemacht werden.</string>
|
||||
<string name="documents_failed_to_load">Fehler beim Laden des Dokuments</string>
|
||||
<string name="documents_failed_to_delete">Fehler beim Löschen des Dokuments</string>
|
||||
<string name="documents_image_viewer">Dokumentbilder</string>
|
||||
<string name="documents_image_of">Bild %1$d von %2$d</string>
|
||||
<string name="documents_error_loading_image">Fehler beim Laden des Bildes</string>
|
||||
<string name="documents_failed_to_load_image">Fehler beim Laden des Bildes</string>
|
||||
<string name="documents_previous">Zurück</string>
|
||||
<string name="documents_next">Weiter</string>
|
||||
|
||||
<!-- Profile -->
|
||||
<string name="profile_title">Profil</string>
|
||||
<string name="profile_edit_title">Profil bearbeiten</string>
|
||||
<string name="profile_update_title">Profil aktualisieren</string>
|
||||
<string name="profile_first_name">Vorname</string>
|
||||
<string name="profile_last_name">Nachname</string>
|
||||
<string name="profile_email">E-Mail</string>
|
||||
<string name="profile_phone">Telefon</string>
|
||||
<string name="profile_save">Anderungen speichern</string>
|
||||
<string name="profile_logout">Abmelden</string>
|
||||
<string name="profile_logout_confirm">Mochten Sie sich wirklich abmelden?</string>
|
||||
<string name="profile_settings">Einstellungen</string>
|
||||
<string name="profile_notifications">Benachrichtigungen</string>
|
||||
<string name="profile_notifications_subtitle">Benachrichtigungseinstellungen verwalten</string>
|
||||
<string name="profile_theme">Design</string>
|
||||
<string name="profile_appearance">Erscheinungsbild</string>
|
||||
<string name="profile_change_theme">Design andern</string>
|
||||
<string name="profile_information">Profilinformationen</string>
|
||||
<string name="profile_update_failed">Profil-Aktualisierung fehlgeschlagen</string>
|
||||
<string name="profile_load_failed">Fehler beim Laden der Benutzerdaten</string>
|
||||
<string name="profile_not_authenticated">Nicht authentifiziert</string>
|
||||
<string name="profile_update_coming_soon">Profilaktualisierung demnachst verfugbar</string>
|
||||
<string name="profile_email_required">E-Mail ist erforderlich</string>
|
||||
<string name="profile_update_success">Profil erfolgreich aktualisiert</string>
|
||||
<string name="profile_subscription">Abonnement</string>
|
||||
<string name="profile_pro_plan">Pro-Plan</string>
|
||||
<string name="profile_free_plan">Kostenloser Plan</string>
|
||||
<string name="profile_active_until">Aktiv bis %1$s</string>
|
||||
<string name="profile_limited_features">Eingeschrankte Funktionen</string>
|
||||
<string name="profile_upgrade_to_pro">Auf Pro upgraden</string>
|
||||
<string name="profile_manage_subscription">Verwalten Sie Ihr Abonnement im Google Play Store</string>
|
||||
|
||||
<!-- Settings -->
|
||||
<string name="settings_title">Einstellungen</string>
|
||||
<string name="settings_notifications">Benachrichtigungseinstellungen</string>
|
||||
<string name="settings_theme">App-Design</string>
|
||||
<string name="settings_language">Sprache</string>
|
||||
<string name="settings_about">Uber</string>
|
||||
<string name="settings_privacy">Datenschutzrichtlinie</string>
|
||||
<string name="settings_terms">Nutzungsbedingungen</string>
|
||||
|
||||
<!-- Notifications -->
|
||||
<string name="notifications_title">Benachrichtigungen</string>
|
||||
<string name="notifications_empty">Keine Benachrichtigungen</string>
|
||||
<string name="notifications_task_reminders">Aufgabenerinnerungen</string>
|
||||
<string name="notifications_task_completed">Aufgabe abgeschlossen</string>
|
||||
<string name="notifications_property_shared">Immobilie geteilt</string>
|
||||
<string name="notifications_enabled">Aktiviert</string>
|
||||
<string name="notifications_disabled">Deaktiviert</string>
|
||||
<string name="notifications_preferences">Benachrichtigungseinstellungen</string>
|
||||
<string name="notifications_choose">Wählen Sie, welche Benachrichtigungen Sie erhalten möchten</string>
|
||||
<string name="notifications_task_section">Aufgabenbenachrichtigungen</string>
|
||||
<string name="notifications_other_section">Andere Benachrichtigungen</string>
|
||||
<string name="notifications_task_due_soon">Aufgabe Bald Fällig</string>
|
||||
<string name="notifications_task_due_soon_desc">Erinnerungen für anstehende Aufgaben</string>
|
||||
<string name="notifications_task_overdue">Aufgabe Überfällig</string>
|
||||
<string name="notifications_task_overdue_desc">Warnungen für überfällige Aufgaben</string>
|
||||
<string name="notifications_task_completed_desc">Wenn jemand eine Aufgabe abschließt</string>
|
||||
<string name="notifications_task_assigned">Aufgabe Zugewiesen</string>
|
||||
<string name="notifications_task_assigned_desc">Wenn Ihnen eine Aufgabe zugewiesen wird</string>
|
||||
<string name="notifications_property_shared_desc">Wenn jemand eine Immobilie mit Ihnen teilt</string>
|
||||
<string name="notifications_warranty_expiring">Garantie Läuft Ab</string>
|
||||
<string name="notifications_warranty_expiring_desc">Erinnerungen für ablaufende Garantien</string>
|
||||
|
||||
<!-- Common -->
|
||||
<string name="common_save">Speichern</string>
|
||||
<string name="common_cancel">Abbrechen</string>
|
||||
<string name="common_delete">Loschen</string>
|
||||
<string name="common_edit">Bearbeiten</string>
|
||||
<string name="common_done">Fertig</string>
|
||||
<string name="common_close">Schliessen</string>
|
||||
<string name="common_back">Zuruck</string>
|
||||
<string name="common_next">Weiter</string>
|
||||
<string name="common_submit">Absenden</string>
|
||||
<string name="common_confirm">Bestatigen</string>
|
||||
<string name="common_loading">Wird geladen...</string>
|
||||
<string name="common_error">Fehler</string>
|
||||
<string name="common_retry">Erneut versuchen</string>
|
||||
<string name="common_success">Erfolg</string>
|
||||
<string name="common_search">Suchen</string>
|
||||
<string name="common_filter">Filtern</string>
|
||||
<string name="common_sort">Sortieren</string>
|
||||
<string name="common_refresh">Aktualisieren</string>
|
||||
<string name="common_select">Auswahlen</string>
|
||||
<string name="common_none">Keine</string>
|
||||
<string name="common_all">Alle</string>
|
||||
<string name="common_yes">Ja</string>
|
||||
<string name="common_no">Nein</string>
|
||||
<string name="common_ok">OK</string>
|
||||
|
||||
<!-- Errors -->
|
||||
<string name="error_generic">Etwas ist schiefgelaufen. Bitte versuchen Sie es erneut.</string>
|
||||
<string name="error_network">Netzwerkfehler. Uberprufen Sie Ihre Verbindung.</string>
|
||||
<string name="error_timeout">Zeituberschreitung. Bitte versuchen Sie es erneut.</string>
|
||||
<string name="error_server">Serverfehler. Bitte versuchen Sie es spater erneut.</string>
|
||||
<string name="error_unauthorized">Sitzung abgelaufen. Bitte melden Sie sich erneut an.</string>
|
||||
<string name="error_not_found">Nicht gefunden</string>
|
||||
<string name="error_invalid_input">Bitte uberprufen Sie Ihre Eingabe</string>
|
||||
<string name="error_required_field">Dieses Feld ist erforderlich</string>
|
||||
|
||||
<!-- Accessibility -->
|
||||
<string name="a11y_back">Zuruck</string>
|
||||
<string name="a11y_close">Schliessen</string>
|
||||
<string name="a11y_menu">Menu offnen</string>
|
||||
<string name="a11y_add">Neues Element hinzufugen</string>
|
||||
<string name="a11y_edit">Element bearbeiten</string>
|
||||
<string name="a11y_delete">Element loschen</string>
|
||||
<string name="a11y_refresh">Inhalt aktualisieren</string>
|
||||
<string name="a11y_search">Suchen</string>
|
||||
<string name="a11y_filter">Filteroptionen</string>
|
||||
<string name="a11y_loading">Inhalt wird geladen</string>
|
||||
|
||||
<!-- Date/Time -->
|
||||
<string name="date_today">Heute</string>
|
||||
<string name="date_yesterday">Gestern</string>
|
||||
<string name="date_tomorrow">Morgen</string>
|
||||
<string name="date_days_ago">vor %1$d Tagen</string>
|
||||
<string name="date_in_days">in %1$d Tagen</string>
|
||||
|
||||
<!-- Home Screen -->
|
||||
<string name="home_welcome">Willkommen zuruck</string>
|
||||
<string name="home_manage_properties">Verwalten Sie Ihre Immobilien</string>
|
||||
<string name="home_failed_to_load">Laden fehlgeschlagen</string>
|
||||
<string name="home_logout">Abmelden</string>
|
||||
<string name="home_properties">Immobilien</string>
|
||||
<string name="home_tasks">Aufgaben</string>
|
||||
<string name="home_view_all_properties">Alle Immobilien Anzeigen</string>
|
||||
<string name="home_view_all_tasks">Alle Aufgaben Anzeigen</string>
|
||||
<string name="home_overview">Ubersicht</string>
|
||||
<string name="home_property_stats">Ihre Immobilienstatistiken</string>
|
||||
<string name="home_total_tasks">Aufgaben Gesamt</string>
|
||||
<string name="home_pending">Ausstehend</string>
|
||||
<string name="home_manage_residences">Verwalten Sie Ihre Wohnungen</string>
|
||||
<string name="home_view_manage_tasks">Aufgaben anzeigen und verwalten</string>
|
||||
|
||||
<!-- Subscription -->
|
||||
<string name="subscription_title">Abonnement</string>
|
||||
<string name="subscription_free">Kostenlos</string>
|
||||
<string name="subscription_premium">Premium</string>
|
||||
<string name="subscription_upgrade">Upgrade</string>
|
||||
<string name="subscription_restore">Kaufe wiederherstellen</string>
|
||||
<string name="subscription_features">Premium-Funktionen</string>
|
||||
<string name="subscription_limit_properties">Sie haben das Immobilienlimit Ihres Plans erreicht</string>
|
||||
<string name="subscription_limit_tasks">Sie haben das Aufgabenlimit Ihres Plans erreicht</string>
|
||||
</resources>
|
||||
473
composeApp/src/commonMain/composeResources/values-es/strings.xml
Normal file
473
composeApp/src/commonMain/composeResources/values-es/strings.xml
Normal file
@@ -0,0 +1,473 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<resources>
|
||||
<!-- App Name -->
|
||||
<string name="app_name">Casera</string>
|
||||
<string name="app_tagline">Administra tus propiedades con facilidad</string>
|
||||
|
||||
<!-- Auth - Login -->
|
||||
<string name="auth_login_title">Casera</string>
|
||||
<string name="auth_login_subtitle">Administra tus propiedades con facilidad</string>
|
||||
<string name="auth_login_username_label">Usuario o Correo</string>
|
||||
<string name="auth_login_password_label">Contrasena</string>
|
||||
<string name="auth_login_button">Iniciar Sesion</string>
|
||||
<string name="auth_forgot_password">Olvidaste tu Contrasena?</string>
|
||||
<string name="auth_no_account">No tienes cuenta? Registrate</string>
|
||||
<string name="auth_login_failed">Error de Inicio de Sesion</string>
|
||||
<string name="auth_show_password">Mostrar contrasena</string>
|
||||
<string name="auth_hide_password">Ocultar contrasena</string>
|
||||
|
||||
<!-- Auth - Register -->
|
||||
<string name="auth_register_title">Crear Cuenta</string>
|
||||
<string name="auth_register_subtitle">Registrate para comenzar</string>
|
||||
<string name="auth_register_first_name">Nombre</string>
|
||||
<string name="auth_register_last_name">Apellido</string>
|
||||
<string name="auth_register_email">Correo Electronico</string>
|
||||
<string name="auth_register_username">Usuario</string>
|
||||
<string name="auth_register_password">Contrasena</string>
|
||||
<string name="auth_register_confirm_password">Confirmar Contrasena</string>
|
||||
<string name="auth_register_button">Crear Cuenta</string>
|
||||
<string name="auth_have_account">Ya tienes cuenta? Inicia Sesion</string>
|
||||
<string name="auth_passwords_dont_match">Las contrasenas no coinciden</string>
|
||||
|
||||
<!-- Auth - Verify Email -->
|
||||
<string name="auth_verify_title">Verificar Correo</string>
|
||||
<string name="auth_verify_subtitle">Ingresa el codigo de 6 digitos enviado a tu correo</string>
|
||||
<string name="auth_verify_code_label">Codigo de Verificacion</string>
|
||||
<string name="auth_verify_button">Verificar</string>
|
||||
<string name="auth_verify_resend">Reenviar Codigo</string>
|
||||
<string name="auth_verify_success">Correo verificado correctamente</string>
|
||||
|
||||
<!-- Auth - Forgot Password -->
|
||||
<string name="auth_forgot_title">Olvidaste tu Contrasena</string>
|
||||
<string name="auth_forgot_subtitle">Ingresa tu correo para recibir un codigo</string>
|
||||
<string name="auth_forgot_email_label">Correo Electronico</string>
|
||||
<string name="auth_forgot_button">Enviar Codigo</string>
|
||||
<string name="auth_forgot_success">Si existe una cuenta, se envio un codigo</string>
|
||||
|
||||
<!-- Auth - Reset Password -->
|
||||
<string name="auth_reset_title">Restablecer Contrasena</string>
|
||||
<string name="auth_reset_subtitle">Ingresa tu nueva contrasena</string>
|
||||
<string name="auth_reset_code_label">Codigo</string>
|
||||
<string name="auth_reset_new_password">Nueva Contrasena</string>
|
||||
<string name="auth_reset_confirm_password">Confirmar Nueva Contrasena</string>
|
||||
<string name="auth_reset_button">Restablecer Contrasena</string>
|
||||
<string name="auth_reset_success">Contrasena restablecida correctamente</string>
|
||||
|
||||
<!-- Properties / Residences -->
|
||||
<string name="properties_title">Mis Propiedades</string>
|
||||
<string name="properties_empty_title">Sin propiedades</string>
|
||||
<string name="properties_empty_subtitle">Agrega tu primera propiedad para comenzar!</string>
|
||||
<string name="properties_add_button">Agregar Propiedad</string>
|
||||
<string name="properties_add_title">Agregar Propiedad</string>
|
||||
<string name="properties_edit_title">Editar Propiedad</string>
|
||||
<string name="properties_name_label">Nombre de la Propiedad</string>
|
||||
<string name="properties_address_label">Direccion</string>
|
||||
<string name="properties_type_label">Tipo de Propiedad</string>
|
||||
<string name="properties_notes_label">Notas</string>
|
||||
<string name="properties_delete_confirm">Seguro que quieres eliminar esta propiedad?</string>
|
||||
<string name="properties_deleted">Propiedad eliminada</string>
|
||||
<string name="properties_share_title">Compartir Propiedad</string>
|
||||
<string name="properties_share_code">Codigo para Compartir</string>
|
||||
<string name="properties_share_expires">Expira en %1$s</string>
|
||||
<string name="properties_join_title">Unirse a Propiedad</string>
|
||||
<string name="properties_join_code_label">Ingresa el Codigo</string>
|
||||
<string name="properties_join_button">Unirse</string>
|
||||
<string name="properties_users_title">Miembros de la Propiedad</string>
|
||||
<string name="properties_owner">Propietario</string>
|
||||
<string name="properties_member">Miembro</string>
|
||||
<string name="properties_remove_user">Eliminar Usuario</string>
|
||||
<string name="properties_details">Detalles de la Propiedad</string>
|
||||
<string name="properties_form_name_required">Nombre de la Propiedad *</string>
|
||||
<string name="properties_form_name_error">El nombre es requerido</string>
|
||||
<string name="properties_form_required">Requerido</string>
|
||||
<string name="properties_form_street">Direccion</string>
|
||||
<string name="properties_form_apartment">Apartamento/Unidad #</string>
|
||||
<string name="properties_form_city">Ciudad</string>
|
||||
<string name="properties_form_state">Estado/Provincia</string>
|
||||
<string name="properties_form_postal">Codigo Postal</string>
|
||||
<string name="properties_form_country">Pais</string>
|
||||
<string name="properties_form_optional">Detalles Opcionales</string>
|
||||
<string name="properties_form_sqft">Metros Cuadrados</string>
|
||||
<string name="properties_form_lot_size">Tamano del Lote (acres)</string>
|
||||
<string name="properties_form_description">Descripcion</string>
|
||||
<string name="properties_form_primary">Residencia Principal</string>
|
||||
<string name="properties_form_create">Crear Residencia</string>
|
||||
<string name="properties_form_update">Actualizar Residencia</string>
|
||||
<string name="properties_loading">Cargando residencia...</string>
|
||||
<string name="properties_failed_to_load">Error al Cargar Propiedad</string>
|
||||
<string name="properties_failed_to_delete">Error al Eliminar Propiedad</string>
|
||||
<string name="properties_failed_to_generate_report">Error al Generar Reporte</string>
|
||||
<string name="properties_generate_report">Generar Reporte</string>
|
||||
<string name="properties_generate_report_confirm">Esto generara y enviara por correo un reporte de mantenimiento para esta propiedad. Deseas continuar?</string>
|
||||
<string name="properties_generate">Generar</string>
|
||||
<string name="properties_manage_users">Administrar Usuarios</string>
|
||||
<string name="properties_edit_residence">Editar Residencia</string>
|
||||
<string name="properties_delete_residence">Eliminar Residencia</string>
|
||||
<string name="properties_delete_name_confirm">Seguro que deseas eliminar %1$s? Esta accion no se puede deshacer.</string>
|
||||
<string name="properties_address_section">Direccion</string>
|
||||
<string name="properties_unit">Unidad: %1$s</string>
|
||||
<string name="properties_property_details_section">Detalles de la Propiedad</string>
|
||||
<string name="properties_bedrooms">Habitaciones</string>
|
||||
<string name="properties_bathrooms">Banos</string>
|
||||
<string name="properties_square_footage">Metros Cuadrados</string>
|
||||
<string name="properties_lot_size">Tamano del Lote</string>
|
||||
<string name="properties_year_built">Ano de Construccion</string>
|
||||
<string name="properties_sq_ft">%1$s m2</string>
|
||||
<string name="properties_acres">%1$s acres</string>
|
||||
<string name="properties_description_section">Descripcion</string>
|
||||
<string name="properties_purchase_info">Informacion de Compra</string>
|
||||
<string name="properties_purchase_date">Fecha de Compra</string>
|
||||
<string name="properties_purchase_price">Precio de Compra</string>
|
||||
<string name="properties_add_task">Agregar Tarea</string>
|
||||
<string name="properties_no_tasks">Sin tareas aun</string>
|
||||
<string name="properties_add_task_start">Agrega una tarea para comenzar</string>
|
||||
<string name="properties_error_loading_tasks">Error al cargar tareas: %1$s</string>
|
||||
<string name="properties_no_contractors">Sin contratistas aun</string>
|
||||
<string name="properties_add_contractors_hint">Agrega contratistas desde la pestana de Contratistas</string>
|
||||
<string name="properties_error_loading_contractors">Error al cargar contratistas: %1$s</string>
|
||||
<string name="properties_cancel_task">Cancelar Tarea</string>
|
||||
<string name="properties_cancel_task_confirm">Seguro que deseas cancelar \"%1$s\"? Esta accion no se puede deshacer.</string>
|
||||
<string name="properties_archive_task">Archivar Tarea</string>
|
||||
<string name="properties_archive_task_confirm">Seguro que deseas archivar \"%1$s\"? Puedes desarchivarla despues desde tareas archivadas.</string>
|
||||
<string name="properties_dismiss">Descartar</string>
|
||||
|
||||
<!-- Tasks -->
|
||||
<string name="tasks_title">Tareas</string>
|
||||
<string name="tasks_empty_title">Sin tareas</string>
|
||||
<string name="tasks_empty_subtitle">Agrega tu primera tarea de mantenimiento</string>
|
||||
<string name="tasks_add_button">Agregar Tarea</string>
|
||||
<string name="tasks_add_title">Agregar Tarea</string>
|
||||
<string name="tasks_edit_title">Editar Tarea</string>
|
||||
<string name="tasks_title_label">Titulo de la Tarea</string>
|
||||
<string name="tasks_description_label">Descripcion</string>
|
||||
<string name="tasks_category_label">Categoria</string>
|
||||
<string name="tasks_priority_label">Prioridad</string>
|
||||
<string name="tasks_frequency_label">Frecuencia</string>
|
||||
<string name="tasks_due_date_label">Fecha de Vencimiento</string>
|
||||
<string name="tasks_estimated_cost_label">Costo Estimado</string>
|
||||
<string name="tasks_contractor_label">Contratista</string>
|
||||
<string name="tasks_notes_label">Notas</string>
|
||||
<string name="tasks_delete_confirm">Seguro que quieres eliminar esta tarea?</string>
|
||||
<string name="tasks_deleted">Tarea eliminada</string>
|
||||
<string name="tasks_details">Detalles de la Tarea</string>
|
||||
<string name="tasks_title_required">Titulo *</string>
|
||||
<string name="tasks_title_error">El titulo es requerido</string>
|
||||
<string name="tasks_category_required">Categoria *</string>
|
||||
<string name="tasks_frequency_required">Frecuencia *</string>
|
||||
<string name="tasks_priority_required">Prioridad *</string>
|
||||
<string name="tasks_status_label">Estado *</string>
|
||||
<string name="tasks_due_date_required">Fecha de Vencimiento (AAAA-MM-DD) *</string>
|
||||
<string name="tasks_due_date_error">La fecha de vencimiento es requerida</string>
|
||||
<string name="tasks_due_date_placeholder">2025-01-31</string>
|
||||
<string name="tasks_update">Actualizar Tarea</string>
|
||||
<string name="tasks_failed_to_update">Error al Actualizar Tarea</string>
|
||||
|
||||
<!-- Task Columns / Kanban -->
|
||||
<string name="tasks_column_overdue">Vencidas</string>
|
||||
<string name="tasks_column_due_soon">Por Vencer</string>
|
||||
<string name="tasks_column_upcoming">Proximas</string>
|
||||
<string name="tasks_column_in_progress">En Progreso</string>
|
||||
<string name="tasks_column_completed">Completadas</string>
|
||||
<string name="tasks_column_cancelled">Canceladas</string>
|
||||
|
||||
<!-- Task Actions -->
|
||||
<string name="tasks_mark_complete">Completar</string>
|
||||
<string name="tasks_mark_in_progress">Iniciar</string>
|
||||
<string name="tasks_cancel">Cancelar</string>
|
||||
<string name="tasks_uncancel">Restaurar</string>
|
||||
<string name="tasks_archive">Archivar</string>
|
||||
<string name="tasks_unarchive">Desarchivar</string>
|
||||
<string name="tasks_completed_message">Tarea marcada como completada</string>
|
||||
<string name="tasks_in_progress_message">Tarea marcada como en progreso</string>
|
||||
<string name="tasks_cancelled_message">Tarea cancelada</string>
|
||||
|
||||
<!-- Task Completions -->
|
||||
<string name="completions_title">Registros de Tareas</string>
|
||||
<string name="completions_add_title">Completar Tarea</string>
|
||||
<string name="completions_notes_label">Notas</string>
|
||||
<string name="completions_actual_cost_label">Costo Real</string>
|
||||
<string name="completions_date_label">Fecha de Finalizacion</string>
|
||||
<string name="completions_photos_label">Fotos</string>
|
||||
<string name="completions_add_photo">Agregar Foto</string>
|
||||
<string name="completions_delete_confirm">Eliminar este registro?</string>
|
||||
|
||||
<!-- Contractors -->
|
||||
<string name="contractors_title">Contratistas</string>
|
||||
<string name="contractors_empty_title">Sin contratistas</string>
|
||||
<string name="contractors_empty_subtitle">Agrega contratistas de confianza</string>
|
||||
<string name="contractors_empty_subtitle_first">Agrega tu primer contratista para empezar</string>
|
||||
<string name="contractors_no_results">No se encontraron contratistas</string>
|
||||
<string name="contractors_add_button">Agregar Contratista</string>
|
||||
<string name="contractors_add_title">Agregar Contratista</string>
|
||||
<string name="contractors_edit_title">Editar Contratista</string>
|
||||
<string name="contractors_name_label">Nombre</string>
|
||||
<string name="contractors_company_label">Empresa</string>
|
||||
<string name="contractors_phone_label">Telefono</string>
|
||||
<string name="contractors_email_label">Correo</string>
|
||||
<string name="contractors_specialty_label">Especialidad</string>
|
||||
<string name="contractors_notes_label">Notas</string>
|
||||
<string name="contractors_favorite">Favorito</string>
|
||||
<string name="contractors_favorites">Favoritos</string>
|
||||
<string name="contractors_filter_favorites">Filtrar favoritos</string>
|
||||
<string name="contractors_filter_specialty">Filtrar por especialidad</string>
|
||||
<string name="contractors_all_specialties">Todas las Especialidades</string>
|
||||
<string name="contractors_search">Buscar contratistas...</string>
|
||||
<string name="contractors_clear_search">Limpiar busqueda</string>
|
||||
<string name="contractors_add_to_favorites">Agregar a favoritos</string>
|
||||
<string name="contractors_remove_from_favorites">Quitar de favoritos</string>
|
||||
<string name="contractors_view_details">Ver detalles</string>
|
||||
<string name="contractors_tasks_count">%1$d tareas</string>
|
||||
<string name="contractors_delete_confirm">Seguro que quieres eliminar este contratista?</string>
|
||||
<string name="contractors_deleted">Contratista eliminado</string>
|
||||
<string name="contractors_call">Llamar</string>
|
||||
<string name="contractors_send_email">Enviar Correo</string>
|
||||
<string name="contractors_failed_to_load">Error al Cargar Contratistas</string>
|
||||
<string name="contractors_failed_to_delete">Error al Eliminar Contratista</string>
|
||||
<string name="contractors_failed_to_update_favorite">Error al Actualizar Favorito</string>
|
||||
<string name="contractors_upgrade_required">Actualizacion Requerida</string>
|
||||
<string name="contractors_upgrade_message">Has alcanzado el maximo de contratistas para tu plan actual. Actualiza a Pro para contratistas ilimitados.</string>
|
||||
<string name="contractors_details">Detalles del Contratista</string>
|
||||
<string name="contractors_toggle_favorite">Cambiar favorito</string>
|
||||
<string name="contractors_website">Sitio Web</string>
|
||||
<string name="contractors_directions">Direcciones</string>
|
||||
<string name="contractors_contact_info">Informacion de Contacto</string>
|
||||
<string name="contractors_no_contact_info">No hay informacion de contacto disponible</string>
|
||||
<string name="contractors_address">Direccion</string>
|
||||
<string name="contractors_location">Ubicacion</string>
|
||||
<string name="contractors_associated_property">Propiedad Asociada</string>
|
||||
<string name="contractors_property">Propiedad</string>
|
||||
<string name="contractors_notes">Notas</string>
|
||||
<string name="contractors_statistics">Estadisticas</string>
|
||||
<string name="contractors_tasks_completed">Tareas\nCompletadas</string>
|
||||
<string name="contractors_average_rating">Calificacion\nPromedio</string>
|
||||
<string name="contractors_info">Informacion</string>
|
||||
<string name="contractors_added_by">Agregado Por</string>
|
||||
<string name="contractors_member_since">Miembro Desde</string>
|
||||
<string name="contractors_delete">Eliminar Contratista</string>
|
||||
<string name="contractors_delete_warning">Seguro que deseas eliminar este contratista? Esta accion no se puede deshacer.</string>
|
||||
<string name="contractors_completed_tasks">%1$d tareas completadas</string>
|
||||
|
||||
<!-- Documents -->
|
||||
<string name="documents_title">Documentos</string>
|
||||
<string name="documents_and_warranties">Documentos y Garantias</string>
|
||||
<string name="documents_empty_title">Sin documentos</string>
|
||||
<string name="documents_empty_subtitle">Guarda documentos importantes y garantias</string>
|
||||
<string name="documents_add_button">Agregar Documento</string>
|
||||
<string name="documents_add_title">Agregar Documento</string>
|
||||
<string name="documents_edit_title">Editar Documento</string>
|
||||
<string name="documents_title_label">Titulo</string>
|
||||
<string name="documents_description_label">Descripcion</string>
|
||||
<string name="documents_type_label">Tipo de Documento</string>
|
||||
<string name="documents_vendor_label">Proveedor</string>
|
||||
<string name="documents_serial_label">Numero de Serie</string>
|
||||
<string name="documents_model_label">Numero de Modelo</string>
|
||||
<string name="documents_purchase_date_label">Fecha de Compra</string>
|
||||
<string name="documents_purchase_price_label">Precio de Compra</string>
|
||||
<string name="documents_expiry_date_label">Fecha de Vencimiento</string>
|
||||
<string name="documents_delete_confirm">Seguro que quieres eliminar este documento?</string>
|
||||
<string name="documents_deleted">Documento eliminado</string>
|
||||
<string name="documents_warranties_title">Garantias</string>
|
||||
<string name="documents_warranties_tab">Garantias</string>
|
||||
<string name="documents_documents_tab">Documentos</string>
|
||||
<string name="documents_upload">Subir Archivo</string>
|
||||
<string name="documents_view">Ver Documento</string>
|
||||
<string name="documents_all_categories">Todas las Categorias</string>
|
||||
<string name="documents_all_types">Todos los Tipos</string>
|
||||
<string name="documents_filter_active">Filtrar activos</string>
|
||||
<string name="documents_filters">Filtros</string>
|
||||
<string name="documents_upgrade_required">Actualizacion Requerida</string>
|
||||
<string name="documents_upgrade_message">Has alcanzado el maximo de documentos para tu plan actual. Actualiza a Pro para documentos ilimitados.</string>
|
||||
<string name="documents_details">Detalles del Documento</string>
|
||||
<string name="documents_status">Estado</string>
|
||||
<string name="documents_inactive">Inactivo</string>
|
||||
<string name="documents_expired">Expirado</string>
|
||||
<string name="documents_expiring_soon">Expira pronto</string>
|
||||
<string name="documents_active">Activo</string>
|
||||
<string name="documents_days_remaining">Dias Restantes</string>
|
||||
<string name="documents_basic_info">Informacion Basica</string>
|
||||
<string name="documents_item_details">Detalles del Articulo</string>
|
||||
<string name="documents_item_name">Nombre del Articulo</string>
|
||||
<string name="documents_model_number">Numero de Modelo</string>
|
||||
<string name="documents_serial_number">Numero de Serie</string>
|
||||
<string name="documents_provider">Proveedor</string>
|
||||
<string name="documents_provider_contact">Contacto del Proveedor</string>
|
||||
<string name="documents_claim_info">Informacion de Reclamo</string>
|
||||
<string name="documents_claim_phone">Telefono de Reclamo</string>
|
||||
<string name="documents_claim_email">Correo de Reclamo</string>
|
||||
<string name="documents_claim_website">Sitio Web de Reclamo</string>
|
||||
<string name="documents_important_dates">Fechas Importantes</string>
|
||||
<string name="documents_purchase_date">Fecha de Compra</string>
|
||||
<string name="documents_start_date">Fecha de Inicio</string>
|
||||
<string name="documents_end_date">Fecha de Fin</string>
|
||||
<string name="documents_associations">Asociaciones</string>
|
||||
<string name="documents_residence">Residencia</string>
|
||||
<string name="documents_contractor">Contratista</string>
|
||||
<string name="documents_contractor_phone">Telefono del Contratista</string>
|
||||
<string name="documents_additional_info">Informacion Adicional</string>
|
||||
<string name="documents_tags">Etiquetas</string>
|
||||
<string name="documents_notes">Notas</string>
|
||||
<string name="documents_images">Imagenes (%1$d)</string>
|
||||
<string name="documents_attached_file">Archivo Adjunto</string>
|
||||
<string name="documents_file_type">Tipo de Archivo</string>
|
||||
<string name="documents_file_size">Tamano del Archivo</string>
|
||||
<string name="documents_download_file">Descargar Archivo</string>
|
||||
<string name="documents_metadata">Metadatos</string>
|
||||
<string name="documents_uploaded_by">Subido Por</string>
|
||||
<string name="documents_created">Creado</string>
|
||||
<string name="documents_updated">Actualizado</string>
|
||||
<string name="documents_delete">Eliminar Documento</string>
|
||||
<string name="documents_delete_warning">Seguro que deseas eliminar este documento? Esta accion no se puede deshacer.</string>
|
||||
<string name="documents_failed_to_load">Error al Cargar Documento</string>
|
||||
<string name="documents_failed_to_delete">Error al Eliminar Documento</string>
|
||||
<string name="documents_image_viewer">Imagenes del Documento</string>
|
||||
<string name="documents_image_of">Imagen %1$d de %2$d</string>
|
||||
<string name="documents_error_loading_image">Error al cargar imagen</string>
|
||||
<string name="documents_failed_to_load_image">Error al cargar imagen</string>
|
||||
<string name="documents_previous">Anterior</string>
|
||||
<string name="documents_next">Siguiente</string>
|
||||
|
||||
<!-- Profile -->
|
||||
<string name="profile_title">Perfil</string>
|
||||
<string name="profile_edit_title">Editar Perfil</string>
|
||||
<string name="profile_update_title">Actualiza Tu Perfil</string>
|
||||
<string name="profile_first_name">Nombre</string>
|
||||
<string name="profile_last_name">Apellido</string>
|
||||
<string name="profile_email">Correo Electronico</string>
|
||||
<string name="profile_phone">Telefono</string>
|
||||
<string name="profile_save">Guardar Cambios</string>
|
||||
<string name="profile_logout">Cerrar Sesion</string>
|
||||
<string name="profile_logout_confirm">Seguro que quieres cerrar sesion?</string>
|
||||
<string name="profile_settings">Configuracion</string>
|
||||
<string name="profile_notifications">Notificaciones</string>
|
||||
<string name="profile_notifications_subtitle">Administrar preferencias de notificaciones</string>
|
||||
<string name="profile_theme">Tema</string>
|
||||
<string name="profile_appearance">Apariencia</string>
|
||||
<string name="profile_change_theme">Cambiar tema</string>
|
||||
<string name="profile_information">Informacion del Perfil</string>
|
||||
<string name="profile_update_failed">Error al Actualizar Perfil</string>
|
||||
<string name="profile_load_failed">Error al cargar datos del usuario</string>
|
||||
<string name="profile_not_authenticated">No autenticado</string>
|
||||
<string name="profile_update_coming_soon">Actualizacion de perfil proximamente</string>
|
||||
<string name="profile_email_required">El correo es requerido</string>
|
||||
<string name="profile_update_success">Perfil actualizado exitosamente</string>
|
||||
<string name="profile_subscription">Suscripcion</string>
|
||||
<string name="profile_pro_plan">Plan Pro</string>
|
||||
<string name="profile_free_plan">Plan Gratis</string>
|
||||
<string name="profile_active_until">Activo hasta %1$s</string>
|
||||
<string name="profile_limited_features">Funciones limitadas</string>
|
||||
<string name="profile_upgrade_to_pro">Actualizar a Pro</string>
|
||||
<string name="profile_manage_subscription">Administra tu suscripcion en Google Play Store</string>
|
||||
|
||||
<!-- Settings -->
|
||||
<string name="settings_title">Configuracion</string>
|
||||
<string name="settings_notifications">Preferencias de Notificaciones</string>
|
||||
<string name="settings_theme">Tema de la App</string>
|
||||
<string name="settings_language">Idioma</string>
|
||||
<string name="settings_about">Acerca de</string>
|
||||
<string name="settings_privacy">Politica de Privacidad</string>
|
||||
<string name="settings_terms">Terminos de Servicio</string>
|
||||
|
||||
<!-- Notifications -->
|
||||
<string name="notifications_title">Notificaciones</string>
|
||||
<string name="notifications_empty">Sin notificaciones</string>
|
||||
<string name="notifications_task_reminders">Recordatorios de Tareas</string>
|
||||
<string name="notifications_task_completed">Tarea Completada</string>
|
||||
<string name="notifications_property_shared">Propiedad Compartida</string>
|
||||
<string name="notifications_enabled">Activado</string>
|
||||
<string name="notifications_disabled">Desactivado</string>
|
||||
<string name="notifications_preferences">Preferencias de Notificaciones</string>
|
||||
<string name="notifications_choose">Elige que notificaciones deseas recibir</string>
|
||||
<string name="notifications_task_section">Notificaciones de Tareas</string>
|
||||
<string name="notifications_other_section">Otras Notificaciones</string>
|
||||
<string name="notifications_task_due_soon">Tarea Por Vencer</string>
|
||||
<string name="notifications_task_due_soon_desc">Recordatorios de tareas proximas</string>
|
||||
<string name="notifications_task_overdue">Tarea Vencida</string>
|
||||
<string name="notifications_task_overdue_desc">Alertas de tareas vencidas</string>
|
||||
<string name="notifications_task_completed_desc">Cuando alguien completa una tarea</string>
|
||||
<string name="notifications_task_assigned">Tarea Asignada</string>
|
||||
<string name="notifications_task_assigned_desc">Cuando te asignan una tarea</string>
|
||||
<string name="notifications_property_shared_desc">Cuando alguien comparte una propiedad contigo</string>
|
||||
<string name="notifications_warranty_expiring">Garantia Por Vencer</string>
|
||||
<string name="notifications_warranty_expiring_desc">Recordatorios de garantias por vencer</string>
|
||||
|
||||
<!-- Common -->
|
||||
<string name="common_save">Guardar</string>
|
||||
<string name="common_cancel">Cancelar</string>
|
||||
<string name="common_delete">Eliminar</string>
|
||||
<string name="common_edit">Editar</string>
|
||||
<string name="common_done">Listo</string>
|
||||
<string name="common_close">Cerrar</string>
|
||||
<string name="common_back">Atras</string>
|
||||
<string name="common_next">Siguiente</string>
|
||||
<string name="common_submit">Enviar</string>
|
||||
<string name="common_confirm">Confirmar</string>
|
||||
<string name="common_loading">Cargando...</string>
|
||||
<string name="common_error">Error</string>
|
||||
<string name="common_retry">Reintentar</string>
|
||||
<string name="common_success">Exito</string>
|
||||
<string name="common_search">Buscar</string>
|
||||
<string name="common_filter">Filtrar</string>
|
||||
<string name="common_sort">Ordenar</string>
|
||||
<string name="common_refresh">Actualizar</string>
|
||||
<string name="common_select">Seleccionar</string>
|
||||
<string name="common_none">Ninguno</string>
|
||||
<string name="common_all">Todo</string>
|
||||
<string name="common_yes">Si</string>
|
||||
<string name="common_no">No</string>
|
||||
<string name="common_ok">OK</string>
|
||||
|
||||
<!-- Errors -->
|
||||
<string name="error_generic">Algo salio mal. Por favor intenta de nuevo.</string>
|
||||
<string name="error_network">Error de red. Verifica tu conexion.</string>
|
||||
<string name="error_timeout">Tiempo de espera agotado. Intenta de nuevo.</string>
|
||||
<string name="error_server">Error del servidor. Intenta mas tarde.</string>
|
||||
<string name="error_unauthorized">Sesion expirada. Inicia sesion de nuevo.</string>
|
||||
<string name="error_not_found">No encontrado</string>
|
||||
<string name="error_invalid_input">Verifica los datos ingresados</string>
|
||||
<string name="error_required_field">Este campo es requerido</string>
|
||||
|
||||
<!-- Accessibility -->
|
||||
<string name="a11y_back">Volver</string>
|
||||
<string name="a11y_close">Cerrar</string>
|
||||
<string name="a11y_menu">Abrir menu</string>
|
||||
<string name="a11y_add">Agregar nuevo</string>
|
||||
<string name="a11y_edit">Editar</string>
|
||||
<string name="a11y_delete">Eliminar</string>
|
||||
<string name="a11y_refresh">Actualizar contenido</string>
|
||||
<string name="a11y_search">Buscar</string>
|
||||
<string name="a11y_filter">Opciones de filtro</string>
|
||||
<string name="a11y_loading">Cargando contenido</string>
|
||||
|
||||
<!-- Date/Time -->
|
||||
<string name="date_today">Hoy</string>
|
||||
<string name="date_yesterday">Ayer</string>
|
||||
<string name="date_tomorrow">Manana</string>
|
||||
<string name="date_days_ago">hace %1$d dias</string>
|
||||
<string name="date_in_days">en %1$d dias</string>
|
||||
|
||||
<!-- Home Screen -->
|
||||
<string name="home_welcome">Bienvenido</string>
|
||||
<string name="home_manage_properties">Administra tus propiedades</string>
|
||||
<string name="home_failed_to_load">Error al Cargar Resumen</string>
|
||||
<string name="home_logout">Cerrar Sesion</string>
|
||||
<string name="home_properties">Propiedades</string>
|
||||
<string name="home_tasks">Tareas</string>
|
||||
<string name="home_view_all_properties">Ver Todas las Propiedades</string>
|
||||
<string name="home_view_all_tasks">Ver Todas las Tareas</string>
|
||||
<string name="home_overview">Resumen</string>
|
||||
<string name="home_property_stats">Estadisticas de propiedades</string>
|
||||
<string name="home_total_tasks">Total de Tareas</string>
|
||||
<string name="home_pending">Pendientes</string>
|
||||
<string name="home_manage_residences">Administra tus residencias</string>
|
||||
<string name="home_view_manage_tasks">Ver y administrar tareas</string>
|
||||
|
||||
<!-- Subscription -->
|
||||
<string name="subscription_title">Suscripcion</string>
|
||||
<string name="subscription_free">Gratis</string>
|
||||
<string name="subscription_premium">Premium</string>
|
||||
<string name="subscription_upgrade">Mejorar</string>
|
||||
<string name="subscription_restore">Restaurar Compras</string>
|
||||
<string name="subscription_features">Funciones Premium</string>
|
||||
<string name="subscription_limit_properties">Has alcanzado el limite de propiedades de tu plan</string>
|
||||
<string name="subscription_limit_tasks">Has alcanzado el limite de tareas de tu plan</string>
|
||||
</resources>
|
||||
473
composeApp/src/commonMain/composeResources/values-fr/strings.xml
Normal file
473
composeApp/src/commonMain/composeResources/values-fr/strings.xml
Normal file
@@ -0,0 +1,473 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<resources>
|
||||
<!-- App Name -->
|
||||
<string name="app_name">Casera</string>
|
||||
<string name="app_tagline">Gerez vos proprietes facilement</string>
|
||||
|
||||
<!-- Auth - Login -->
|
||||
<string name="auth_login_title">Casera</string>
|
||||
<string name="auth_login_subtitle">Gerez vos proprietes facilement</string>
|
||||
<string name="auth_login_username_label">Nom d\'utilisateur ou Email</string>
|
||||
<string name="auth_login_password_label">Mot de passe</string>
|
||||
<string name="auth_login_button">Se connecter</string>
|
||||
<string name="auth_forgot_password">Mot de passe oublie ?</string>
|
||||
<string name="auth_no_account">Pas de compte ? S\'inscrire</string>
|
||||
<string name="auth_login_failed">Echec de connexion</string>
|
||||
<string name="auth_show_password">Afficher le mot de passe</string>
|
||||
<string name="auth_hide_password">Masquer le mot de passe</string>
|
||||
|
||||
<!-- Auth - Register -->
|
||||
<string name="auth_register_title">Creer un compte</string>
|
||||
<string name="auth_register_subtitle">Inscrivez-vous pour commencer</string>
|
||||
<string name="auth_register_first_name">Prenom</string>
|
||||
<string name="auth_register_last_name">Nom</string>
|
||||
<string name="auth_register_email">Email</string>
|
||||
<string name="auth_register_username">Nom d\'utilisateur</string>
|
||||
<string name="auth_register_password">Mot de passe</string>
|
||||
<string name="auth_register_confirm_password">Confirmer le mot de passe</string>
|
||||
<string name="auth_register_button">Creer un compte</string>
|
||||
<string name="auth_have_account">Deja un compte ? Se connecter</string>
|
||||
<string name="auth_passwords_dont_match">Les mots de passe ne correspondent pas</string>
|
||||
|
||||
<!-- Auth - Verify Email -->
|
||||
<string name="auth_verify_title">Verifier l\'email</string>
|
||||
<string name="auth_verify_subtitle">Entrez le code a 6 chiffres envoye a votre email</string>
|
||||
<string name="auth_verify_code_label">Code de verification</string>
|
||||
<string name="auth_verify_button">Verifier</string>
|
||||
<string name="auth_verify_resend">Renvoyer le code</string>
|
||||
<string name="auth_verify_success">Email verifie avec succes</string>
|
||||
|
||||
<!-- Auth - Forgot Password -->
|
||||
<string name="auth_forgot_title">Mot de passe oublie</string>
|
||||
<string name="auth_forgot_subtitle">Entrez votre email pour recevoir un code</string>
|
||||
<string name="auth_forgot_email_label">Email</string>
|
||||
<string name="auth_forgot_button">Envoyer le code</string>
|
||||
<string name="auth_forgot_success">Si un compte existe, un code a ete envoye</string>
|
||||
|
||||
<!-- Auth - Reset Password -->
|
||||
<string name="auth_reset_title">Reinitialiser le mot de passe</string>
|
||||
<string name="auth_reset_subtitle">Entrez votre nouveau mot de passe</string>
|
||||
<string name="auth_reset_code_label">Code</string>
|
||||
<string name="auth_reset_new_password">Nouveau mot de passe</string>
|
||||
<string name="auth_reset_confirm_password">Confirmer le nouveau mot de passe</string>
|
||||
<string name="auth_reset_button">Reinitialiser</string>
|
||||
<string name="auth_reset_success">Mot de passe reinitialise avec succes</string>
|
||||
|
||||
<!-- Properties / Residences -->
|
||||
<string name="properties_title">Mes Proprietes</string>
|
||||
<string name="properties_empty_title">Aucune propriete</string>
|
||||
<string name="properties_empty_subtitle">Ajoutez votre premiere propriete pour commencer !</string>
|
||||
<string name="properties_add_button">Ajouter une propriete</string>
|
||||
<string name="properties_add_title">Ajouter une propriete</string>
|
||||
<string name="properties_edit_title">Modifier la propriete</string>
|
||||
<string name="properties_name_label">Nom de la propriete</string>
|
||||
<string name="properties_address_label">Adresse</string>
|
||||
<string name="properties_type_label">Type de propriete</string>
|
||||
<string name="properties_notes_label">Notes</string>
|
||||
<string name="properties_delete_confirm">Etes-vous sur de vouloir supprimer cette propriete ?</string>
|
||||
<string name="properties_deleted">Propriete supprimee</string>
|
||||
<string name="properties_share_title">Partager la propriete</string>
|
||||
<string name="properties_share_code">Code de partage</string>
|
||||
<string name="properties_share_expires">Expire dans %1$s</string>
|
||||
<string name="properties_join_title">Rejoindre une propriete</string>
|
||||
<string name="properties_join_code_label">Entrez le code de partage</string>
|
||||
<string name="properties_join_button">Rejoindre</string>
|
||||
<string name="properties_users_title">Membres de la propriete</string>
|
||||
<string name="properties_owner">Proprietaire</string>
|
||||
<string name="properties_member">Membre</string>
|
||||
<string name="properties_remove_user">Supprimer l\'utilisateur</string>
|
||||
<string name="properties_details">Détails de la Propriété</string>
|
||||
<string name="properties_form_name_required">Nom de la Propriété *</string>
|
||||
<string name="properties_form_name_error">Le nom est requis</string>
|
||||
<string name="properties_form_required">Requis</string>
|
||||
<string name="properties_form_street">Adresse</string>
|
||||
<string name="properties_form_apartment">Appartement/Unité #</string>
|
||||
<string name="properties_form_city">Ville</string>
|
||||
<string name="properties_form_state">État/Province</string>
|
||||
<string name="properties_form_postal">Code Postal</string>
|
||||
<string name="properties_form_country">Pays</string>
|
||||
<string name="properties_form_optional">Détails Optionnels</string>
|
||||
<string name="properties_form_sqft">Mètres Carrés</string>
|
||||
<string name="properties_form_lot_size">Taille du Terrain (acres)</string>
|
||||
<string name="properties_form_description">Description</string>
|
||||
<string name="properties_form_primary">Résidence Principale</string>
|
||||
<string name="properties_form_create">Créer la Résidence</string>
|
||||
<string name="properties_form_update">Mettre à Jour la Résidence</string>
|
||||
<string name="properties_loading">Chargement de la résidence...</string>
|
||||
<string name="properties_failed_to_load">Échec du Chargement de la Propriété</string>
|
||||
<string name="properties_failed_to_delete">Échec de la Suppression de la Propriété</string>
|
||||
<string name="properties_failed_to_generate_report">Échec de la Génération du Rapport</string>
|
||||
<string name="properties_generate_report">Générer le Rapport</string>
|
||||
<string name="properties_generate_report_confirm">Ceci va générer et envoyer par email un rapport de maintenance pour cette propriété. Voulez-vous continuer?</string>
|
||||
<string name="properties_generate">Générer</string>
|
||||
<string name="properties_manage_users">Gérer les Utilisateurs</string>
|
||||
<string name="properties_edit_residence">Modifier la Résidence</string>
|
||||
<string name="properties_delete_residence">Supprimer la Résidence</string>
|
||||
<string name="properties_delete_name_confirm">Êtes-vous sûr de vouloir supprimer %1$s? Cette action est irréversible.</string>
|
||||
<string name="properties_address_section">Adresse</string>
|
||||
<string name="properties_unit">Unité: %1$s</string>
|
||||
<string name="properties_property_details_section">Détails de la Propriété</string>
|
||||
<string name="properties_bedrooms">Chambres</string>
|
||||
<string name="properties_bathrooms">Salles de Bain</string>
|
||||
<string name="properties_square_footage">Mètres Carrés</string>
|
||||
<string name="properties_lot_size">Taille du Terrain</string>
|
||||
<string name="properties_year_built">Année de Construction</string>
|
||||
<string name="properties_sq_ft">%1$s m²</string>
|
||||
<string name="properties_acres">%1$s acres</string>
|
||||
<string name="properties_description_section">Description</string>
|
||||
<string name="properties_purchase_info">Informations d\'Achat</string>
|
||||
<string name="properties_purchase_date">Date d\'Achat</string>
|
||||
<string name="properties_purchase_price">Prix d\'Achat</string>
|
||||
<string name="properties_add_task">Ajouter une Tâche</string>
|
||||
<string name="properties_no_tasks">Pas encore de tâches</string>
|
||||
<string name="properties_add_task_start">Ajoutez une tâche pour commencer</string>
|
||||
<string name="properties_error_loading_tasks">Erreur de chargement des tâches: %1$s</string>
|
||||
<string name="properties_no_contractors">Pas encore de prestataires</string>
|
||||
<string name="properties_add_contractors_hint">Ajoutez des prestataires depuis l\'onglet Prestataires</string>
|
||||
<string name="properties_error_loading_contractors">Erreur de chargement des prestataires: %1$s</string>
|
||||
<string name="properties_cancel_task">Annuler la Tâche</string>
|
||||
<string name="properties_cancel_task_confirm">Êtes-vous sûr de vouloir annuler \"%1$s\"? Cette action est irréversible.</string>
|
||||
<string name="properties_archive_task">Archiver la Tâche</string>
|
||||
<string name="properties_archive_task_confirm">Êtes-vous sûr de vouloir archiver \"%1$s\"? Vous pourrez la désarchiver plus tard.</string>
|
||||
<string name="properties_dismiss">Ignorer</string>
|
||||
|
||||
<!-- Tasks -->
|
||||
<string name="tasks_title">Taches</string>
|
||||
<string name="tasks_empty_title">Aucune tache</string>
|
||||
<string name="tasks_empty_subtitle">Ajoutez votre premiere tache de maintenance</string>
|
||||
<string name="tasks_add_button">Ajouter une tache</string>
|
||||
<string name="tasks_add_title">Ajouter une tache</string>
|
||||
<string name="tasks_edit_title">Modifier la tache</string>
|
||||
<string name="tasks_title_label">Titre de la tache</string>
|
||||
<string name="tasks_description_label">Description</string>
|
||||
<string name="tasks_category_label">Categorie</string>
|
||||
<string name="tasks_priority_label">Priorite</string>
|
||||
<string name="tasks_frequency_label">Frequence</string>
|
||||
<string name="tasks_due_date_label">Date d\'echeance</string>
|
||||
<string name="tasks_estimated_cost_label">Cout estime</string>
|
||||
<string name="tasks_contractor_label">Prestataire</string>
|
||||
<string name="tasks_notes_label">Notes</string>
|
||||
<string name="tasks_delete_confirm">Etes-vous sur de vouloir supprimer cette tache ?</string>
|
||||
<string name="tasks_deleted">Tache supprimee</string>
|
||||
<string name="tasks_details">Détails de la Tâche</string>
|
||||
<string name="tasks_title_required">Titre *</string>
|
||||
<string name="tasks_title_error">Le titre est requis</string>
|
||||
<string name="tasks_category_required">Catégorie *</string>
|
||||
<string name="tasks_frequency_required">Fréquence *</string>
|
||||
<string name="tasks_priority_required">Priorité *</string>
|
||||
<string name="tasks_status_label">Statut *</string>
|
||||
<string name="tasks_due_date_required">Date d\'Échéance (AAAA-MM-JJ) *</string>
|
||||
<string name="tasks_due_date_error">La date d\'échéance est requise</string>
|
||||
<string name="tasks_due_date_placeholder">2025-01-31</string>
|
||||
<string name="tasks_update">Mettre à Jour la Tâche</string>
|
||||
<string name="tasks_failed_to_update">Échec de la Mise à Jour de la Tâche</string>
|
||||
|
||||
<!-- Task Columns / Kanban -->
|
||||
<string name="tasks_column_overdue">En retard</string>
|
||||
<string name="tasks_column_due_soon">Bientot dues</string>
|
||||
<string name="tasks_column_upcoming">A venir</string>
|
||||
<string name="tasks_column_in_progress">En cours</string>
|
||||
<string name="tasks_column_completed">Terminees</string>
|
||||
<string name="tasks_column_cancelled">Annulees</string>
|
||||
|
||||
<!-- Task Actions -->
|
||||
<string name="tasks_mark_complete">Terminer</string>
|
||||
<string name="tasks_mark_in_progress">Demarrer</string>
|
||||
<string name="tasks_cancel">Annuler</string>
|
||||
<string name="tasks_uncancel">Restaurer</string>
|
||||
<string name="tasks_archive">Archiver</string>
|
||||
<string name="tasks_unarchive">Desarchiver</string>
|
||||
<string name="tasks_completed_message">Tache marquee comme terminee</string>
|
||||
<string name="tasks_in_progress_message">Tache marquee comme en cours</string>
|
||||
<string name="tasks_cancelled_message">Tache annulee</string>
|
||||
|
||||
<!-- Task Completions -->
|
||||
<string name="completions_title">Historique des taches</string>
|
||||
<string name="completions_add_title">Terminer la tache</string>
|
||||
<string name="completions_notes_label">Notes</string>
|
||||
<string name="completions_actual_cost_label">Cout reel</string>
|
||||
<string name="completions_date_label">Date de completion</string>
|
||||
<string name="completions_photos_label">Photos</string>
|
||||
<string name="completions_add_photo">Ajouter une photo</string>
|
||||
<string name="completions_delete_confirm">Supprimer cet enregistrement ?</string>
|
||||
|
||||
<!-- Contractors -->
|
||||
<string name="contractors_title">Prestataires</string>
|
||||
<string name="contractors_empty_title">Aucun prestataire</string>
|
||||
<string name="contractors_empty_subtitle">Ajoutez des prestataires de confiance</string>
|
||||
<string name="contractors_empty_subtitle_first">Ajoutez votre premier prestataire pour commencer</string>
|
||||
<string name="contractors_no_results">Aucun prestataire trouve</string>
|
||||
<string name="contractors_add_button">Ajouter un prestataire</string>
|
||||
<string name="contractors_add_title">Ajouter un prestataire</string>
|
||||
<string name="contractors_edit_title">Modifier le prestataire</string>
|
||||
<string name="contractors_name_label">Nom</string>
|
||||
<string name="contractors_company_label">Entreprise</string>
|
||||
<string name="contractors_phone_label">Telephone</string>
|
||||
<string name="contractors_email_label">Email</string>
|
||||
<string name="contractors_specialty_label">Specialite</string>
|
||||
<string name="contractors_notes_label">Notes</string>
|
||||
<string name="contractors_favorite">Favori</string>
|
||||
<string name="contractors_favorites">Favoris</string>
|
||||
<string name="contractors_filter_favorites">Filtrer les favoris</string>
|
||||
<string name="contractors_filter_specialty">Filtrer par specialite</string>
|
||||
<string name="contractors_all_specialties">Toutes les specialites</string>
|
||||
<string name="contractors_search">Rechercher des prestataires...</string>
|
||||
<string name="contractors_clear_search">Effacer la recherche</string>
|
||||
<string name="contractors_add_to_favorites">Ajouter aux favoris</string>
|
||||
<string name="contractors_remove_from_favorites">Retirer des favoris</string>
|
||||
<string name="contractors_view_details">Voir les details</string>
|
||||
<string name="contractors_tasks_count">%1$d taches</string>
|
||||
<string name="contractors_delete_confirm">Etes-vous sur de vouloir supprimer ce prestataire ?</string>
|
||||
<string name="contractors_deleted">Prestataire supprime</string>
|
||||
<string name="contractors_call">Appeler</string>
|
||||
<string name="contractors_send_email">Envoyer un email</string>
|
||||
<string name="contractors_failed_to_load">Echec du chargement des prestataires</string>
|
||||
<string name="contractors_failed_to_delete">Echec de la suppression du prestataire</string>
|
||||
<string name="contractors_failed_to_update_favorite">Echec de la mise a jour du favori</string>
|
||||
<string name="contractors_upgrade_required">Mise a niveau requise</string>
|
||||
<string name="contractors_upgrade_message">Vous avez atteint le nombre maximum de prestataires pour votre forfait actuel. Passez a Pro pour des prestataires illimites.</string>
|
||||
<string name="contractors_details">Détails du Prestataire</string>
|
||||
<string name="contractors_toggle_favorite">Basculer favori</string>
|
||||
<string name="contractors_website">Site Web</string>
|
||||
<string name="contractors_directions">Itinéraire</string>
|
||||
<string name="contractors_contact_info">Informations de Contact</string>
|
||||
<string name="contractors_no_contact_info">Aucune information de contact disponible</string>
|
||||
<string name="contractors_address">Adresse</string>
|
||||
<string name="contractors_location">Emplacement</string>
|
||||
<string name="contractors_associated_property">Propriété Associée</string>
|
||||
<string name="contractors_property">Propriété</string>
|
||||
<string name="contractors_notes">Notes</string>
|
||||
<string name="contractors_statistics">Statistiques</string>
|
||||
<string name="contractors_tasks_completed">Tâches\nTerminées</string>
|
||||
<string name="contractors_average_rating">Note\nMoyenne</string>
|
||||
<string name="contractors_info">Informations</string>
|
||||
<string name="contractors_added_by">Ajouté Par</string>
|
||||
<string name="contractors_member_since">Membre Depuis</string>
|
||||
<string name="contractors_delete">Supprimer le Prestataire</string>
|
||||
<string name="contractors_delete_warning">Êtes-vous sûr de vouloir supprimer ce prestataire? Cette action est irréversible.</string>
|
||||
<string name="contractors_completed_tasks">%1$d tâches terminées</string>
|
||||
|
||||
<!-- Documents -->
|
||||
<string name="documents_title">Documents</string>
|
||||
<string name="documents_and_warranties">Documents et Garanties</string>
|
||||
<string name="documents_empty_title">Aucun document</string>
|
||||
<string name="documents_empty_subtitle">Stockez vos documents importants et garanties</string>
|
||||
<string name="documents_add_button">Ajouter un document</string>
|
||||
<string name="documents_add_title">Ajouter un document</string>
|
||||
<string name="documents_edit_title">Modifier le document</string>
|
||||
<string name="documents_title_label">Titre</string>
|
||||
<string name="documents_description_label">Description</string>
|
||||
<string name="documents_type_label">Type de document</string>
|
||||
<string name="documents_vendor_label">Fournisseur</string>
|
||||
<string name="documents_serial_label">Numero de serie</string>
|
||||
<string name="documents_model_label">Numero de modele</string>
|
||||
<string name="documents_purchase_date_label">Date d\'achat</string>
|
||||
<string name="documents_purchase_price_label">Prix d\'achat</string>
|
||||
<string name="documents_expiry_date_label">Date d\'expiration</string>
|
||||
<string name="documents_delete_confirm">Etes-vous sur de vouloir supprimer ce document ?</string>
|
||||
<string name="documents_deleted">Document supprime</string>
|
||||
<string name="documents_warranties_title">Garanties</string>
|
||||
<string name="documents_warranties_tab">Garanties</string>
|
||||
<string name="documents_documents_tab">Documents</string>
|
||||
<string name="documents_upload">Telecharger un fichier</string>
|
||||
<string name="documents_view">Voir le document</string>
|
||||
<string name="documents_all_categories">Toutes les categories</string>
|
||||
<string name="documents_all_types">Tous les types</string>
|
||||
<string name="documents_filter_active">Filtrer les actifs</string>
|
||||
<string name="documents_filters">Filtres</string>
|
||||
<string name="documents_upgrade_required">Mise a niveau requise</string>
|
||||
<string name="documents_upgrade_message">Vous avez atteint le nombre maximum de documents pour votre forfait actuel. Passez a Pro pour des documents illimites.</string>
|
||||
<string name="documents_details">Détails du Document</string>
|
||||
<string name="documents_status">Statut</string>
|
||||
<string name="documents_inactive">Inactif</string>
|
||||
<string name="documents_expired">Expiré</string>
|
||||
<string name="documents_expiring_soon">Expire bientôt</string>
|
||||
<string name="documents_active">Actif</string>
|
||||
<string name="documents_days_remaining">Jours Restants</string>
|
||||
<string name="documents_basic_info">Informations de Base</string>
|
||||
<string name="documents_item_details">Détails de l\'Article</string>
|
||||
<string name="documents_item_name">Nom de l\'Article</string>
|
||||
<string name="documents_model_number">Numéro de Modèle</string>
|
||||
<string name="documents_serial_number">Numéro de Série</string>
|
||||
<string name="documents_provider">Fournisseur</string>
|
||||
<string name="documents_provider_contact">Contact du Fournisseur</string>
|
||||
<string name="documents_claim_info">Informations de Réclamation</string>
|
||||
<string name="documents_claim_phone">Téléphone de Réclamation</string>
|
||||
<string name="documents_claim_email">Email de Réclamation</string>
|
||||
<string name="documents_claim_website">Site Web de Réclamation</string>
|
||||
<string name="documents_important_dates">Dates Importantes</string>
|
||||
<string name="documents_purchase_date">Date d\'Achat</string>
|
||||
<string name="documents_start_date">Date de Début</string>
|
||||
<string name="documents_end_date">Date de Fin</string>
|
||||
<string name="documents_associations">Associations</string>
|
||||
<string name="documents_residence">Résidence</string>
|
||||
<string name="documents_contractor">Prestataire</string>
|
||||
<string name="documents_contractor_phone">Téléphone du Prestataire</string>
|
||||
<string name="documents_additional_info">Informations Supplémentaires</string>
|
||||
<string name="documents_tags">Tags</string>
|
||||
<string name="documents_notes">Notes</string>
|
||||
<string name="documents_images">Images (%1$d)</string>
|
||||
<string name="documents_attached_file">Fichier Joint</string>
|
||||
<string name="documents_file_type">Type de Fichier</string>
|
||||
<string name="documents_file_size">Taille du Fichier</string>
|
||||
<string name="documents_download_file">Télécharger le Fichier</string>
|
||||
<string name="documents_metadata">Métadonnées</string>
|
||||
<string name="documents_uploaded_by">Téléchargé Par</string>
|
||||
<string name="documents_created">Créé</string>
|
||||
<string name="documents_updated">Mis à Jour</string>
|
||||
<string name="documents_delete">Supprimer le Document</string>
|
||||
<string name="documents_delete_warning">Êtes-vous sûr de vouloir supprimer ce document? Cette action est irréversible.</string>
|
||||
<string name="documents_failed_to_load">Échec du Chargement du Document</string>
|
||||
<string name="documents_failed_to_delete">Échec de la Suppression du Document</string>
|
||||
<string name="documents_image_viewer">Images du Document</string>
|
||||
<string name="documents_image_of">Image %1$d sur %2$d</string>
|
||||
<string name="documents_error_loading_image">Erreur de chargement de l\'image</string>
|
||||
<string name="documents_failed_to_load_image">Échec du chargement de l\'image</string>
|
||||
<string name="documents_previous">Précédent</string>
|
||||
<string name="documents_next">Suivant</string>
|
||||
|
||||
<!-- Profile -->
|
||||
<string name="profile_title">Profil</string>
|
||||
<string name="profile_edit_title">Modifier le profil</string>
|
||||
<string name="profile_update_title">Mettre a jour votre profil</string>
|
||||
<string name="profile_first_name">Prenom</string>
|
||||
<string name="profile_last_name">Nom</string>
|
||||
<string name="profile_email">Email</string>
|
||||
<string name="profile_phone">Telephone</string>
|
||||
<string name="profile_save">Enregistrer</string>
|
||||
<string name="profile_logout">Se deconnecter</string>
|
||||
<string name="profile_logout_confirm">Etes-vous sur de vouloir vous deconnecter ?</string>
|
||||
<string name="profile_settings">Parametres</string>
|
||||
<string name="profile_notifications">Notifications</string>
|
||||
<string name="profile_notifications_subtitle">Gerer les preferences de notifications</string>
|
||||
<string name="profile_theme">Theme</string>
|
||||
<string name="profile_appearance">Apparence</string>
|
||||
<string name="profile_change_theme">Changer le theme</string>
|
||||
<string name="profile_information">Informations du profil</string>
|
||||
<string name="profile_update_failed">Echec de la mise a jour du profil</string>
|
||||
<string name="profile_load_failed">Echec du chargement des donnees utilisateur</string>
|
||||
<string name="profile_not_authenticated">Non authentifie</string>
|
||||
<string name="profile_update_coming_soon">Mise a jour du profil bientot disponible</string>
|
||||
<string name="profile_email_required">L\'email est requis</string>
|
||||
<string name="profile_update_success">Profil mis a jour avec succes</string>
|
||||
<string name="profile_subscription">Abonnement</string>
|
||||
<string name="profile_pro_plan">Forfait Pro</string>
|
||||
<string name="profile_free_plan">Forfait Gratuit</string>
|
||||
<string name="profile_active_until">Actif jusqu\'au %1$s</string>
|
||||
<string name="profile_limited_features">Fonctionnalites limitees</string>
|
||||
<string name="profile_upgrade_to_pro">Passer a Pro</string>
|
||||
<string name="profile_manage_subscription">Gerez votre abonnement dans Google Play Store</string>
|
||||
|
||||
<!-- Settings -->
|
||||
<string name="settings_title">Parametres</string>
|
||||
<string name="settings_notifications">Preferences de notifications</string>
|
||||
<string name="settings_theme">Theme de l\'application</string>
|
||||
<string name="settings_language">Langue</string>
|
||||
<string name="settings_about">A propos</string>
|
||||
<string name="settings_privacy">Politique de confidentialite</string>
|
||||
<string name="settings_terms">Conditions d\'utilisation</string>
|
||||
|
||||
<!-- Notifications -->
|
||||
<string name="notifications_title">Notifications</string>
|
||||
<string name="notifications_empty">Aucune notification</string>
|
||||
<string name="notifications_task_reminders">Rappels de taches</string>
|
||||
<string name="notifications_task_completed">Tache terminee</string>
|
||||
<string name="notifications_property_shared">Propriete partagee</string>
|
||||
<string name="notifications_enabled">Active</string>
|
||||
<string name="notifications_disabled">Desactive</string>
|
||||
<string name="notifications_preferences">Préférences de Notifications</string>
|
||||
<string name="notifications_choose">Choisissez les notifications que vous souhaitez recevoir</string>
|
||||
<string name="notifications_task_section">Notifications de Tâches</string>
|
||||
<string name="notifications_other_section">Autres Notifications</string>
|
||||
<string name="notifications_task_due_soon">Tâche Bientôt Due</string>
|
||||
<string name="notifications_task_due_soon_desc">Rappels pour les tâches à venir</string>
|
||||
<string name="notifications_task_overdue">Tâche en Retard</string>
|
||||
<string name="notifications_task_overdue_desc">Alertes pour les tâches en retard</string>
|
||||
<string name="notifications_task_completed_desc">Quand quelqu\'un termine une tâche</string>
|
||||
<string name="notifications_task_assigned">Tâche Assignée</string>
|
||||
<string name="notifications_task_assigned_desc">Quand une tâche vous est assignée</string>
|
||||
<string name="notifications_property_shared_desc">Quand quelqu\'un partage une propriété avec vous</string>
|
||||
<string name="notifications_warranty_expiring">Garantie Expirante</string>
|
||||
<string name="notifications_warranty_expiring_desc">Rappels pour les garanties qui expirent</string>
|
||||
|
||||
<!-- Common -->
|
||||
<string name="common_save">Enregistrer</string>
|
||||
<string name="common_cancel">Annuler</string>
|
||||
<string name="common_delete">Supprimer</string>
|
||||
<string name="common_edit">Modifier</string>
|
||||
<string name="common_done">Termine</string>
|
||||
<string name="common_close">Fermer</string>
|
||||
<string name="common_back">Retour</string>
|
||||
<string name="common_next">Suivant</string>
|
||||
<string name="common_submit">Soumettre</string>
|
||||
<string name="common_confirm">Confirmer</string>
|
||||
<string name="common_loading">Chargement...</string>
|
||||
<string name="common_error">Erreur</string>
|
||||
<string name="common_retry">Reessayer</string>
|
||||
<string name="common_success">Succes</string>
|
||||
<string name="common_search">Rechercher</string>
|
||||
<string name="common_filter">Filtrer</string>
|
||||
<string name="common_sort">Trier</string>
|
||||
<string name="common_refresh">Actualiser</string>
|
||||
<string name="common_select">Selectionner</string>
|
||||
<string name="common_none">Aucun</string>
|
||||
<string name="common_all">Tous</string>
|
||||
<string name="common_yes">Oui</string>
|
||||
<string name="common_no">Non</string>
|
||||
<string name="common_ok">OK</string>
|
||||
|
||||
<!-- Errors -->
|
||||
<string name="error_generic">Une erreur s\'est produite. Veuillez reessayer.</string>
|
||||
<string name="error_network">Erreur reseau. Verifiez votre connexion.</string>
|
||||
<string name="error_timeout">Delai d\'attente depasse. Veuillez reessayer.</string>
|
||||
<string name="error_server">Erreur serveur. Veuillez reessayer plus tard.</string>
|
||||
<string name="error_unauthorized">Session expiree. Veuillez vous reconnecter.</string>
|
||||
<string name="error_not_found">Non trouve</string>
|
||||
<string name="error_invalid_input">Veuillez verifier vos donnees</string>
|
||||
<string name="error_required_field">Ce champ est requis</string>
|
||||
|
||||
<!-- Accessibility -->
|
||||
<string name="a11y_back">Retour</string>
|
||||
<string name="a11y_close">Fermer</string>
|
||||
<string name="a11y_menu">Ouvrir le menu</string>
|
||||
<string name="a11y_add">Ajouter un nouvel element</string>
|
||||
<string name="a11y_edit">Modifier l\'element</string>
|
||||
<string name="a11y_delete">Supprimer l\'element</string>
|
||||
<string name="a11y_refresh">Actualiser le contenu</string>
|
||||
<string name="a11y_search">Rechercher</string>
|
||||
<string name="a11y_filter">Options de filtre</string>
|
||||
<string name="a11y_loading">Chargement du contenu</string>
|
||||
|
||||
<!-- Date/Time -->
|
||||
<string name="date_today">Aujourd\'hui</string>
|
||||
<string name="date_yesterday">Hier</string>
|
||||
<string name="date_tomorrow">Demain</string>
|
||||
<string name="date_days_ago">il y a %1$d jours</string>
|
||||
<string name="date_in_days">dans %1$d jours</string>
|
||||
|
||||
<!-- Home Screen -->
|
||||
<string name="home_welcome">Bon retour</string>
|
||||
<string name="home_manage_properties">Gerez vos proprietes</string>
|
||||
<string name="home_failed_to_load">Echec du Chargement</string>
|
||||
<string name="home_logout">Deconnexion</string>
|
||||
<string name="home_properties">Proprietes</string>
|
||||
<string name="home_tasks">Taches</string>
|
||||
<string name="home_view_all_properties">Voir Toutes les Proprietes</string>
|
||||
<string name="home_view_all_tasks">Voir Toutes les Taches</string>
|
||||
<string name="home_overview">Apercu</string>
|
||||
<string name="home_property_stats">Statistiques de vos proprietes</string>
|
||||
<string name="home_total_tasks">Total des Taches</string>
|
||||
<string name="home_pending">En Attente</string>
|
||||
<string name="home_manage_residences">Gerez vos residences</string>
|
||||
<string name="home_view_manage_tasks">Voir et gerer les taches</string>
|
||||
|
||||
<!-- Subscription -->
|
||||
<string name="subscription_title">Abonnement</string>
|
||||
<string name="subscription_free">Gratuit</string>
|
||||
<string name="subscription_premium">Premium</string>
|
||||
<string name="subscription_upgrade">Ameliorer</string>
|
||||
<string name="subscription_restore">Restaurer les achats</string>
|
||||
<string name="subscription_features">Fonctionnalites Premium</string>
|
||||
<string name="subscription_limit_properties">Vous avez atteint la limite de proprietes de votre forfait</string>
|
||||
<string name="subscription_limit_tasks">Vous avez atteint la limite de taches de votre forfait</string>
|
||||
</resources>
|
||||
473
composeApp/src/commonMain/composeResources/values-pt/strings.xml
Normal file
473
composeApp/src/commonMain/composeResources/values-pt/strings.xml
Normal file
@@ -0,0 +1,473 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<resources>
|
||||
<!-- App Name -->
|
||||
<string name="app_name">Casera</string>
|
||||
<string name="app_tagline">Gerencie suas propriedades com facilidade</string>
|
||||
|
||||
<!-- Auth - Login -->
|
||||
<string name="auth_login_title">Casera</string>
|
||||
<string name="auth_login_subtitle">Gerencie suas propriedades com facilidade</string>
|
||||
<string name="auth_login_username_label">Usuario ou Email</string>
|
||||
<string name="auth_login_password_label">Senha</string>
|
||||
<string name="auth_login_button">Entrar</string>
|
||||
<string name="auth_forgot_password">Esqueceu a Senha?</string>
|
||||
<string name="auth_no_account">Nao tem conta? Cadastre-se</string>
|
||||
<string name="auth_login_failed">Falha no Login</string>
|
||||
<string name="auth_show_password">Mostrar senha</string>
|
||||
<string name="auth_hide_password">Ocultar senha</string>
|
||||
|
||||
<!-- Auth - Register -->
|
||||
<string name="auth_register_title">Criar Conta</string>
|
||||
<string name="auth_register_subtitle">Cadastre-se para comecar</string>
|
||||
<string name="auth_register_first_name">Nome</string>
|
||||
<string name="auth_register_last_name">Sobrenome</string>
|
||||
<string name="auth_register_email">Email</string>
|
||||
<string name="auth_register_username">Usuario</string>
|
||||
<string name="auth_register_password">Senha</string>
|
||||
<string name="auth_register_confirm_password">Confirmar Senha</string>
|
||||
<string name="auth_register_button">Criar Conta</string>
|
||||
<string name="auth_have_account">Ja tem conta? Entrar</string>
|
||||
<string name="auth_passwords_dont_match">As senhas nao coincidem</string>
|
||||
|
||||
<!-- Auth - Verify Email -->
|
||||
<string name="auth_verify_title">Verificar Email</string>
|
||||
<string name="auth_verify_subtitle">Digite o codigo de 6 digitos enviado para seu email</string>
|
||||
<string name="auth_verify_code_label">Codigo de Verificacao</string>
|
||||
<string name="auth_verify_button">Verificar</string>
|
||||
<string name="auth_verify_resend">Reenviar Codigo</string>
|
||||
<string name="auth_verify_success">Email verificado com sucesso</string>
|
||||
|
||||
<!-- Auth - Forgot Password -->
|
||||
<string name="auth_forgot_title">Esqueceu a Senha</string>
|
||||
<string name="auth_forgot_subtitle">Digite seu email para receber um codigo</string>
|
||||
<string name="auth_forgot_email_label">Email</string>
|
||||
<string name="auth_forgot_button">Enviar Codigo</string>
|
||||
<string name="auth_forgot_success">Se uma conta existir, um codigo foi enviado</string>
|
||||
|
||||
<!-- Auth - Reset Password -->
|
||||
<string name="auth_reset_title">Redefinir Senha</string>
|
||||
<string name="auth_reset_subtitle">Digite sua nova senha</string>
|
||||
<string name="auth_reset_code_label">Codigo</string>
|
||||
<string name="auth_reset_new_password">Nova Senha</string>
|
||||
<string name="auth_reset_confirm_password">Confirmar Nova Senha</string>
|
||||
<string name="auth_reset_button">Redefinir Senha</string>
|
||||
<string name="auth_reset_success">Senha redefinida com sucesso</string>
|
||||
|
||||
<!-- Properties / Residences -->
|
||||
<string name="properties_title">Minhas Propriedades</string>
|
||||
<string name="properties_empty_title">Sem propriedades</string>
|
||||
<string name="properties_empty_subtitle">Adicione sua primeira propriedade para comecar!</string>
|
||||
<string name="properties_add_button">Adicionar Propriedade</string>
|
||||
<string name="properties_add_title">Adicionar Propriedade</string>
|
||||
<string name="properties_edit_title">Editar Propriedade</string>
|
||||
<string name="properties_name_label">Nome da Propriedade</string>
|
||||
<string name="properties_address_label">Endereco</string>
|
||||
<string name="properties_type_label">Tipo de Propriedade</string>
|
||||
<string name="properties_notes_label">Notas</string>
|
||||
<string name="properties_delete_confirm">Tem certeza que deseja excluir esta propriedade?</string>
|
||||
<string name="properties_deleted">Propriedade excluida</string>
|
||||
<string name="properties_share_title">Compartilhar Propriedade</string>
|
||||
<string name="properties_share_code">Codigo de Compartilhamento</string>
|
||||
<string name="properties_share_expires">Expira em %1$s</string>
|
||||
<string name="properties_join_title">Entrar em Propriedade</string>
|
||||
<string name="properties_join_code_label">Digite o Codigo</string>
|
||||
<string name="properties_join_button">Entrar</string>
|
||||
<string name="properties_users_title">Membros da Propriedade</string>
|
||||
<string name="properties_owner">Proprietario</string>
|
||||
<string name="properties_member">Membro</string>
|
||||
<string name="properties_remove_user">Remover Usuario</string>
|
||||
<string name="properties_details">Detalhes da Propriedade</string>
|
||||
<string name="properties_form_name_required">Nome da Propriedade *</string>
|
||||
<string name="properties_form_name_error">Nome é obrigatório</string>
|
||||
<string name="properties_form_required">Obrigatório</string>
|
||||
<string name="properties_form_street">Endereço</string>
|
||||
<string name="properties_form_apartment">Apartamento/Unidade #</string>
|
||||
<string name="properties_form_city">Cidade</string>
|
||||
<string name="properties_form_state">Estado/Província</string>
|
||||
<string name="properties_form_postal">Código Postal</string>
|
||||
<string name="properties_form_country">País</string>
|
||||
<string name="properties_form_optional">Detalhes Opcionais</string>
|
||||
<string name="properties_form_sqft">Metros Quadrados</string>
|
||||
<string name="properties_form_lot_size">Tamanho do Lote (acres)</string>
|
||||
<string name="properties_form_description">Descrição</string>
|
||||
<string name="properties_form_primary">Residência Principal</string>
|
||||
<string name="properties_form_create">Criar Residência</string>
|
||||
<string name="properties_form_update">Atualizar Residência</string>
|
||||
<string name="properties_loading">Carregando residência...</string>
|
||||
<string name="properties_failed_to_load">Falha ao Carregar Propriedade</string>
|
||||
<string name="properties_failed_to_delete">Falha ao Excluir Propriedade</string>
|
||||
<string name="properties_failed_to_generate_report">Falha ao Gerar Relatório</string>
|
||||
<string name="properties_generate_report">Gerar Relatório</string>
|
||||
<string name="properties_generate_report_confirm">Isso irá gerar e enviar por e-mail um relatório de manutenção para esta propriedade. Deseja continuar?</string>
|
||||
<string name="properties_generate">Gerar</string>
|
||||
<string name="properties_manage_users">Gerenciar Usuários</string>
|
||||
<string name="properties_edit_residence">Editar Residência</string>
|
||||
<string name="properties_delete_residence">Excluir Residência</string>
|
||||
<string name="properties_delete_name_confirm">Tem certeza que deseja excluir %1$s? Esta ação não pode ser desfeita.</string>
|
||||
<string name="properties_address_section">Endereço</string>
|
||||
<string name="properties_unit">Unidade: %1$s</string>
|
||||
<string name="properties_property_details_section">Detalhes da Propriedade</string>
|
||||
<string name="properties_bedrooms">Quartos</string>
|
||||
<string name="properties_bathrooms">Banheiros</string>
|
||||
<string name="properties_square_footage">Metros Quadrados</string>
|
||||
<string name="properties_lot_size">Tamanho do Lote</string>
|
||||
<string name="properties_year_built">Ano de Construção</string>
|
||||
<string name="properties_sq_ft">%1$s m²</string>
|
||||
<string name="properties_acres">%1$s acres</string>
|
||||
<string name="properties_description_section">Descrição</string>
|
||||
<string name="properties_purchase_info">Informações de Compra</string>
|
||||
<string name="properties_purchase_date">Data de Compra</string>
|
||||
<string name="properties_purchase_price">Preço de Compra</string>
|
||||
<string name="properties_add_task">Adicionar Tarefa</string>
|
||||
<string name="properties_no_tasks">Sem tarefas ainda</string>
|
||||
<string name="properties_add_task_start">Adicione uma tarefa para começar</string>
|
||||
<string name="properties_error_loading_tasks">Erro ao carregar tarefas: %1$s</string>
|
||||
<string name="properties_no_contractors">Sem prestadores ainda</string>
|
||||
<string name="properties_add_contractors_hint">Adicione prestadores na aba Prestadores</string>
|
||||
<string name="properties_error_loading_contractors">Erro ao carregar prestadores: %1$s</string>
|
||||
<string name="properties_cancel_task">Cancelar Tarefa</string>
|
||||
<string name="properties_cancel_task_confirm">Tem certeza que deseja cancelar \"%1$s\"? Esta ação não pode ser desfeita.</string>
|
||||
<string name="properties_archive_task">Arquivar Tarefa</string>
|
||||
<string name="properties_archive_task_confirm">Tem certeza que deseja arquivar \"%1$s\"? Você pode desarquivar depois.</string>
|
||||
<string name="properties_dismiss">Dispensar</string>
|
||||
|
||||
<!-- Tasks -->
|
||||
<string name="tasks_title">Tarefas</string>
|
||||
<string name="tasks_empty_title">Sem tarefas</string>
|
||||
<string name="tasks_empty_subtitle">Adicione sua primeira tarefa de manutencao</string>
|
||||
<string name="tasks_add_button">Adicionar Tarefa</string>
|
||||
<string name="tasks_add_title">Adicionar Tarefa</string>
|
||||
<string name="tasks_edit_title">Editar Tarefa</string>
|
||||
<string name="tasks_title_label">Titulo da Tarefa</string>
|
||||
<string name="tasks_description_label">Descricao</string>
|
||||
<string name="tasks_category_label">Categoria</string>
|
||||
<string name="tasks_priority_label">Prioridade</string>
|
||||
<string name="tasks_frequency_label">Frequencia</string>
|
||||
<string name="tasks_due_date_label">Data de Vencimento</string>
|
||||
<string name="tasks_estimated_cost_label">Custo Estimado</string>
|
||||
<string name="tasks_contractor_label">Prestador</string>
|
||||
<string name="tasks_notes_label">Notas</string>
|
||||
<string name="tasks_delete_confirm">Tem certeza que deseja excluir esta tarefa?</string>
|
||||
<string name="tasks_deleted">Tarefa excluida</string>
|
||||
<string name="tasks_details">Detalhes da Tarefa</string>
|
||||
<string name="tasks_title_required">Título *</string>
|
||||
<string name="tasks_title_error">Título é obrigatório</string>
|
||||
<string name="tasks_category_required">Categoria *</string>
|
||||
<string name="tasks_frequency_required">Frequência *</string>
|
||||
<string name="tasks_priority_required">Prioridade *</string>
|
||||
<string name="tasks_status_label">Status *</string>
|
||||
<string name="tasks_due_date_required">Data de Vencimento (AAAA-MM-DD) *</string>
|
||||
<string name="tasks_due_date_error">Data de vencimento é obrigatória</string>
|
||||
<string name="tasks_due_date_placeholder">2025-01-31</string>
|
||||
<string name="tasks_update">Atualizar Tarefa</string>
|
||||
<string name="tasks_failed_to_update">Falha ao Atualizar Tarefa</string>
|
||||
|
||||
<!-- Task Columns / Kanban -->
|
||||
<string name="tasks_column_overdue">Atrasadas</string>
|
||||
<string name="tasks_column_due_soon">Vencendo</string>
|
||||
<string name="tasks_column_upcoming">Proximas</string>
|
||||
<string name="tasks_column_in_progress">Em Progresso</string>
|
||||
<string name="tasks_column_completed">Concluidas</string>
|
||||
<string name="tasks_column_cancelled">Canceladas</string>
|
||||
|
||||
<!-- Task Actions -->
|
||||
<string name="tasks_mark_complete">Concluir</string>
|
||||
<string name="tasks_mark_in_progress">Iniciar</string>
|
||||
<string name="tasks_cancel">Cancelar</string>
|
||||
<string name="tasks_uncancel">Restaurar</string>
|
||||
<string name="tasks_archive">Arquivar</string>
|
||||
<string name="tasks_unarchive">Desarquivar</string>
|
||||
<string name="tasks_completed_message">Tarefa marcada como concluida</string>
|
||||
<string name="tasks_in_progress_message">Tarefa marcada como em progresso</string>
|
||||
<string name="tasks_cancelled_message">Tarefa cancelada</string>
|
||||
|
||||
<!-- Task Completions -->
|
||||
<string name="completions_title">Registros de Tarefas</string>
|
||||
<string name="completions_add_title">Concluir Tarefa</string>
|
||||
<string name="completions_notes_label">Notas</string>
|
||||
<string name="completions_actual_cost_label">Custo Real</string>
|
||||
<string name="completions_date_label">Data de Conclusao</string>
|
||||
<string name="completions_photos_label">Fotos</string>
|
||||
<string name="completions_add_photo">Adicionar Foto</string>
|
||||
<string name="completions_delete_confirm">Excluir este registro?</string>
|
||||
|
||||
<!-- Contractors -->
|
||||
<string name="contractors_title">Prestadores</string>
|
||||
<string name="contractors_empty_title">Sem prestadores</string>
|
||||
<string name="contractors_empty_subtitle">Adicione prestadores de confianca</string>
|
||||
<string name="contractors_empty_subtitle_first">Adicione seu primeiro prestador para comecar</string>
|
||||
<string name="contractors_no_results">Nenhum prestador encontrado</string>
|
||||
<string name="contractors_add_button">Adicionar Prestador</string>
|
||||
<string name="contractors_add_title">Adicionar Prestador</string>
|
||||
<string name="contractors_edit_title">Editar Prestador</string>
|
||||
<string name="contractors_name_label">Nome</string>
|
||||
<string name="contractors_company_label">Empresa</string>
|
||||
<string name="contractors_phone_label">Telefone</string>
|
||||
<string name="contractors_email_label">Email</string>
|
||||
<string name="contractors_specialty_label">Especialidade</string>
|
||||
<string name="contractors_notes_label">Notas</string>
|
||||
<string name="contractors_favorite">Favorito</string>
|
||||
<string name="contractors_favorites">Favoritos</string>
|
||||
<string name="contractors_filter_favorites">Filtrar favoritos</string>
|
||||
<string name="contractors_filter_specialty">Filtrar por especialidade</string>
|
||||
<string name="contractors_all_specialties">Todas as Especialidades</string>
|
||||
<string name="contractors_search">Buscar prestadores...</string>
|
||||
<string name="contractors_clear_search">Limpar busca</string>
|
||||
<string name="contractors_add_to_favorites">Adicionar aos favoritos</string>
|
||||
<string name="contractors_remove_from_favorites">Remover dos favoritos</string>
|
||||
<string name="contractors_view_details">Ver detalhes</string>
|
||||
<string name="contractors_tasks_count">%1$d tarefas</string>
|
||||
<string name="contractors_delete_confirm">Tem certeza que deseja excluir este prestador?</string>
|
||||
<string name="contractors_deleted">Prestador excluido</string>
|
||||
<string name="contractors_call">Ligar</string>
|
||||
<string name="contractors_send_email">Enviar Email</string>
|
||||
<string name="contractors_failed_to_load">Falha ao Carregar Prestadores</string>
|
||||
<string name="contractors_failed_to_delete">Falha ao Excluir Prestador</string>
|
||||
<string name="contractors_failed_to_update_favorite">Falha ao Atualizar Favorito</string>
|
||||
<string name="contractors_upgrade_required">Atualizacao Necessaria</string>
|
||||
<string name="contractors_upgrade_message">Voce atingiu o numero maximo de prestadores para seu plano atual. Atualize para Pro para prestadores ilimitados.</string>
|
||||
<string name="contractors_details">Detalhes do Prestador</string>
|
||||
<string name="contractors_toggle_favorite">Alternar favorito</string>
|
||||
<string name="contractors_website">Site</string>
|
||||
<string name="contractors_directions">Direções</string>
|
||||
<string name="contractors_contact_info">Informações de Contato</string>
|
||||
<string name="contractors_no_contact_info">Nenhuma informação de contato disponível</string>
|
||||
<string name="contractors_address">Endereço</string>
|
||||
<string name="contractors_location">Localização</string>
|
||||
<string name="contractors_associated_property">Propriedade Associada</string>
|
||||
<string name="contractors_property">Propriedade</string>
|
||||
<string name="contractors_notes">Notas</string>
|
||||
<string name="contractors_statistics">Estatísticas</string>
|
||||
<string name="contractors_tasks_completed">Tarefas\nConcluídas</string>
|
||||
<string name="contractors_average_rating">Avaliação\nMédia</string>
|
||||
<string name="contractors_info">Informações</string>
|
||||
<string name="contractors_added_by">Adicionado Por</string>
|
||||
<string name="contractors_member_since">Membro Desde</string>
|
||||
<string name="contractors_delete">Excluir Prestador</string>
|
||||
<string name="contractors_delete_warning">Tem certeza que deseja excluir este prestador? Esta ação não pode ser desfeita.</string>
|
||||
<string name="contractors_completed_tasks">%1$d tarefas concluídas</string>
|
||||
|
||||
<!-- Documents -->
|
||||
<string name="documents_title">Documentos</string>
|
||||
<string name="documents_and_warranties">Documentos e Garantias</string>
|
||||
<string name="documents_empty_title">Sem documentos</string>
|
||||
<string name="documents_empty_subtitle">Guarde documentos importantes e garantias</string>
|
||||
<string name="documents_add_button">Adicionar Documento</string>
|
||||
<string name="documents_add_title">Adicionar Documento</string>
|
||||
<string name="documents_edit_title">Editar Documento</string>
|
||||
<string name="documents_title_label">Titulo</string>
|
||||
<string name="documents_description_label">Descricao</string>
|
||||
<string name="documents_type_label">Tipo de Documento</string>
|
||||
<string name="documents_vendor_label">Fornecedor</string>
|
||||
<string name="documents_serial_label">Numero de Serie</string>
|
||||
<string name="documents_model_label">Numero do Modelo</string>
|
||||
<string name="documents_purchase_date_label">Data da Compra</string>
|
||||
<string name="documents_purchase_price_label">Preco de Compra</string>
|
||||
<string name="documents_expiry_date_label">Data de Validade</string>
|
||||
<string name="documents_delete_confirm">Tem certeza que deseja excluir este documento?</string>
|
||||
<string name="documents_deleted">Documento excluido</string>
|
||||
<string name="documents_warranties_title">Garantias</string>
|
||||
<string name="documents_warranties_tab">Garantias</string>
|
||||
<string name="documents_documents_tab">Documentos</string>
|
||||
<string name="documents_upload">Enviar Arquivo</string>
|
||||
<string name="documents_view">Ver Documento</string>
|
||||
<string name="documents_all_categories">Todas as Categorias</string>
|
||||
<string name="documents_all_types">Todos os Tipos</string>
|
||||
<string name="documents_filter_active">Filtrar ativos</string>
|
||||
<string name="documents_filters">Filtros</string>
|
||||
<string name="documents_upgrade_required">Atualizacao Necessaria</string>
|
||||
<string name="documents_upgrade_message">Voce atingiu o numero maximo de documentos para seu plano atual. Atualize para Pro para documentos ilimitados.</string>
|
||||
<string name="documents_details">Detalhes do Documento</string>
|
||||
<string name="documents_status">Status</string>
|
||||
<string name="documents_inactive">Inativo</string>
|
||||
<string name="documents_expired">Expirado</string>
|
||||
<string name="documents_expiring_soon">Expirando em breve</string>
|
||||
<string name="documents_active">Ativo</string>
|
||||
<string name="documents_days_remaining">Dias Restantes</string>
|
||||
<string name="documents_basic_info">Informações Básicas</string>
|
||||
<string name="documents_item_details">Detalhes do Item</string>
|
||||
<string name="documents_item_name">Nome do Item</string>
|
||||
<string name="documents_model_number">Número do Modelo</string>
|
||||
<string name="documents_serial_number">Número de Série</string>
|
||||
<string name="documents_provider">Fornecedor</string>
|
||||
<string name="documents_provider_contact">Contato do Fornecedor</string>
|
||||
<string name="documents_claim_info">Informações de Reclamação</string>
|
||||
<string name="documents_claim_phone">Telefone de Reclamação</string>
|
||||
<string name="documents_claim_email">E-mail de Reclamação</string>
|
||||
<string name="documents_claim_website">Site de Reclamação</string>
|
||||
<string name="documents_important_dates">Datas Importantes</string>
|
||||
<string name="documents_purchase_date">Data de Compra</string>
|
||||
<string name="documents_start_date">Data de Início</string>
|
||||
<string name="documents_end_date">Data de Término</string>
|
||||
<string name="documents_associations">Associações</string>
|
||||
<string name="documents_residence">Residência</string>
|
||||
<string name="documents_contractor">Prestador</string>
|
||||
<string name="documents_contractor_phone">Telefone do Prestador</string>
|
||||
<string name="documents_additional_info">Informações Adicionais</string>
|
||||
<string name="documents_tags">Tags</string>
|
||||
<string name="documents_notes">Notas</string>
|
||||
<string name="documents_images">Imagens (%1$d)</string>
|
||||
<string name="documents_attached_file">Arquivo Anexado</string>
|
||||
<string name="documents_file_type">Tipo de Arquivo</string>
|
||||
<string name="documents_file_size">Tamanho do Arquivo</string>
|
||||
<string name="documents_download_file">Baixar Arquivo</string>
|
||||
<string name="documents_metadata">Metadados</string>
|
||||
<string name="documents_uploaded_by">Enviado Por</string>
|
||||
<string name="documents_created">Criado</string>
|
||||
<string name="documents_updated">Atualizado</string>
|
||||
<string name="documents_delete">Excluir Documento</string>
|
||||
<string name="documents_delete_warning">Tem certeza que deseja excluir este documento? Esta ação não pode ser desfeita.</string>
|
||||
<string name="documents_failed_to_load">Falha ao Carregar Documento</string>
|
||||
<string name="documents_failed_to_delete">Falha ao Excluir Documento</string>
|
||||
<string name="documents_image_viewer">Imagens do Documento</string>
|
||||
<string name="documents_image_of">Imagem %1$d de %2$d</string>
|
||||
<string name="documents_error_loading_image">Erro ao carregar imagem</string>
|
||||
<string name="documents_failed_to_load_image">Falha ao carregar imagem</string>
|
||||
<string name="documents_previous">Anterior</string>
|
||||
<string name="documents_next">Próximo</string>
|
||||
|
||||
<!-- Profile -->
|
||||
<string name="profile_title">Perfil</string>
|
||||
<string name="profile_edit_title">Editar Perfil</string>
|
||||
<string name="profile_update_title">Atualizar Seu Perfil</string>
|
||||
<string name="profile_first_name">Nome</string>
|
||||
<string name="profile_last_name">Sobrenome</string>
|
||||
<string name="profile_email">Email</string>
|
||||
<string name="profile_phone">Telefone</string>
|
||||
<string name="profile_save">Salvar Alteracoes</string>
|
||||
<string name="profile_logout">Sair</string>
|
||||
<string name="profile_logout_confirm">Tem certeza que deseja sair?</string>
|
||||
<string name="profile_settings">Configuracoes</string>
|
||||
<string name="profile_notifications">Notificacoes</string>
|
||||
<string name="profile_notifications_subtitle">Gerenciar preferencias de notificacoes</string>
|
||||
<string name="profile_theme">Tema</string>
|
||||
<string name="profile_appearance">Aparencia</string>
|
||||
<string name="profile_change_theme">Alterar tema</string>
|
||||
<string name="profile_information">Informacoes do Perfil</string>
|
||||
<string name="profile_update_failed">Falha ao Atualizar Perfil</string>
|
||||
<string name="profile_load_failed">Falha ao carregar dados do usuario</string>
|
||||
<string name="profile_not_authenticated">Nao autenticado</string>
|
||||
<string name="profile_update_coming_soon">Atualizacao de perfil em breve</string>
|
||||
<string name="profile_email_required">Email e obrigatorio</string>
|
||||
<string name="profile_update_success">Perfil atualizado com sucesso</string>
|
||||
<string name="profile_subscription">Assinatura</string>
|
||||
<string name="profile_pro_plan">Plano Pro</string>
|
||||
<string name="profile_free_plan">Plano Gratuito</string>
|
||||
<string name="profile_active_until">Ativo ate %1$s</string>
|
||||
<string name="profile_limited_features">Recursos limitados</string>
|
||||
<string name="profile_upgrade_to_pro">Atualizar para Pro</string>
|
||||
<string name="profile_manage_subscription">Gerencie sua assinatura na Google Play Store</string>
|
||||
|
||||
<!-- Settings -->
|
||||
<string name="settings_title">Configuracoes</string>
|
||||
<string name="settings_notifications">Preferencias de Notificacoes</string>
|
||||
<string name="settings_theme">Tema do App</string>
|
||||
<string name="settings_language">Idioma</string>
|
||||
<string name="settings_about">Sobre</string>
|
||||
<string name="settings_privacy">Politica de Privacidade</string>
|
||||
<string name="settings_terms">Termos de Servico</string>
|
||||
|
||||
<!-- Notifications -->
|
||||
<string name="notifications_title">Notificacoes</string>
|
||||
<string name="notifications_empty">Sem notificacoes</string>
|
||||
<string name="notifications_task_reminders">Lembretes de Tarefas</string>
|
||||
<string name="notifications_task_completed">Tarefa Concluida</string>
|
||||
<string name="notifications_property_shared">Propriedade Compartilhada</string>
|
||||
<string name="notifications_enabled">Ativado</string>
|
||||
<string name="notifications_disabled">Desativado</string>
|
||||
<string name="notifications_preferences">Preferências de Notificações</string>
|
||||
<string name="notifications_choose">Escolha quais notificações você deseja receber</string>
|
||||
<string name="notifications_task_section">Notificações de Tarefas</string>
|
||||
<string name="notifications_other_section">Outras Notificações</string>
|
||||
<string name="notifications_task_due_soon">Tarefa Próxima ao Vencimento</string>
|
||||
<string name="notifications_task_due_soon_desc">Lembretes para tarefas próximas</string>
|
||||
<string name="notifications_task_overdue">Tarefa Vencida</string>
|
||||
<string name="notifications_task_overdue_desc">Alertas para tarefas vencidas</string>
|
||||
<string name="notifications_task_completed_desc">Quando alguém conclui uma tarefa</string>
|
||||
<string name="notifications_task_assigned">Tarefa Atribuída</string>
|
||||
<string name="notifications_task_assigned_desc">Quando uma tarefa é atribuída a você</string>
|
||||
<string name="notifications_property_shared_desc">Quando alguém compartilha uma propriedade com você</string>
|
||||
<string name="notifications_warranty_expiring">Garantia Expirando</string>
|
||||
<string name="notifications_warranty_expiring_desc">Lembretes para garantias que estão expirando</string>
|
||||
|
||||
<!-- Common -->
|
||||
<string name="common_save">Salvar</string>
|
||||
<string name="common_cancel">Cancelar</string>
|
||||
<string name="common_delete">Excluir</string>
|
||||
<string name="common_edit">Editar</string>
|
||||
<string name="common_done">Pronto</string>
|
||||
<string name="common_close">Fechar</string>
|
||||
<string name="common_back">Voltar</string>
|
||||
<string name="common_next">Proximo</string>
|
||||
<string name="common_submit">Enviar</string>
|
||||
<string name="common_confirm">Confirmar</string>
|
||||
<string name="common_loading">Carregando...</string>
|
||||
<string name="common_error">Erro</string>
|
||||
<string name="common_retry">Tentar Novamente</string>
|
||||
<string name="common_success">Sucesso</string>
|
||||
<string name="common_search">Buscar</string>
|
||||
<string name="common_filter">Filtrar</string>
|
||||
<string name="common_sort">Ordenar</string>
|
||||
<string name="common_refresh">Atualizar</string>
|
||||
<string name="common_select">Selecionar</string>
|
||||
<string name="common_none">Nenhum</string>
|
||||
<string name="common_all">Todos</string>
|
||||
<string name="common_yes">Sim</string>
|
||||
<string name="common_no">Nao</string>
|
||||
<string name="common_ok">OK</string>
|
||||
|
||||
<!-- Errors -->
|
||||
<string name="error_generic">Algo deu errado. Por favor, tente novamente.</string>
|
||||
<string name="error_network">Erro de rede. Verifique sua conexao.</string>
|
||||
<string name="error_timeout">Tempo esgotado. Tente novamente.</string>
|
||||
<string name="error_server">Erro do servidor. Tente mais tarde.</string>
|
||||
<string name="error_unauthorized">Sessao expirada. Entre novamente.</string>
|
||||
<string name="error_not_found">Nao encontrado</string>
|
||||
<string name="error_invalid_input">Verifique os dados informados</string>
|
||||
<string name="error_required_field">Este campo e obrigatorio</string>
|
||||
|
||||
<!-- Accessibility -->
|
||||
<string name="a11y_back">Voltar</string>
|
||||
<string name="a11y_close">Fechar</string>
|
||||
<string name="a11y_menu">Abrir menu</string>
|
||||
<string name="a11y_add">Adicionar novo</string>
|
||||
<string name="a11y_edit">Editar</string>
|
||||
<string name="a11y_delete">Excluir</string>
|
||||
<string name="a11y_refresh">Atualizar conteudo</string>
|
||||
<string name="a11y_search">Buscar</string>
|
||||
<string name="a11y_filter">Opcoes de filtro</string>
|
||||
<string name="a11y_loading">Carregando conteudo</string>
|
||||
|
||||
<!-- Date/Time -->
|
||||
<string name="date_today">Hoje</string>
|
||||
<string name="date_yesterday">Ontem</string>
|
||||
<string name="date_tomorrow">Amanha</string>
|
||||
<string name="date_days_ago">ha %1$d dias</string>
|
||||
<string name="date_in_days">em %1$d dias</string>
|
||||
|
||||
<!-- Home Screen -->
|
||||
<string name="home_welcome">Bem-vindo de volta</string>
|
||||
<string name="home_manage_properties">Gerencie suas propriedades</string>
|
||||
<string name="home_failed_to_load">Falha ao Carregar Resumo</string>
|
||||
<string name="home_logout">Sair</string>
|
||||
<string name="home_properties">Propriedades</string>
|
||||
<string name="home_tasks">Tarefas</string>
|
||||
<string name="home_view_all_properties">Ver Todas as Propriedades</string>
|
||||
<string name="home_view_all_tasks">Ver Todas as Tarefas</string>
|
||||
<string name="home_overview">Resumo</string>
|
||||
<string name="home_property_stats">Estatisticas das suas propriedades</string>
|
||||
<string name="home_total_tasks">Total de Tarefas</string>
|
||||
<string name="home_pending">Pendentes</string>
|
||||
<string name="home_manage_residences">Gerencie suas residencias</string>
|
||||
<string name="home_view_manage_tasks">Ver e gerenciar tarefas</string>
|
||||
|
||||
<!-- Subscription -->
|
||||
<string name="subscription_title">Assinatura</string>
|
||||
<string name="subscription_free">Gratis</string>
|
||||
<string name="subscription_premium">Premium</string>
|
||||
<string name="subscription_upgrade">Melhorar</string>
|
||||
<string name="subscription_restore">Restaurar Compras</string>
|
||||
<string name="subscription_features">Recursos Premium</string>
|
||||
<string name="subscription_limit_properties">Voce atingiu o limite de propriedades do seu plano</string>
|
||||
<string name="subscription_limit_tasks">Voce atingiu o limite de tarefas do seu plano</string>
|
||||
</resources>
|
||||
473
composeApp/src/commonMain/composeResources/values/strings.xml
Normal file
473
composeApp/src/commonMain/composeResources/values/strings.xml
Normal file
@@ -0,0 +1,473 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<resources>
|
||||
<!-- App Name -->
|
||||
<string name="app_name">Casera</string>
|
||||
<string name="app_tagline">Manage your properties with ease</string>
|
||||
|
||||
<!-- Auth - Login -->
|
||||
<string name="auth_login_title">Casera</string>
|
||||
<string name="auth_login_subtitle">Manage your properties with ease</string>
|
||||
<string name="auth_login_username_label">Username or Email</string>
|
||||
<string name="auth_login_password_label">Password</string>
|
||||
<string name="auth_login_button">Sign In</string>
|
||||
<string name="auth_forgot_password">Forgot Password?</string>
|
||||
<string name="auth_no_account">Don\'t have an account? Register</string>
|
||||
<string name="auth_login_failed">Login Failed</string>
|
||||
<string name="auth_show_password">Show password</string>
|
||||
<string name="auth_hide_password">Hide password</string>
|
||||
|
||||
<!-- Auth - Register -->
|
||||
<string name="auth_register_title">Create Account</string>
|
||||
<string name="auth_register_subtitle">Sign up to get started</string>
|
||||
<string name="auth_register_first_name">First Name</string>
|
||||
<string name="auth_register_last_name">Last Name</string>
|
||||
<string name="auth_register_email">Email</string>
|
||||
<string name="auth_register_username">Username</string>
|
||||
<string name="auth_register_password">Password</string>
|
||||
<string name="auth_register_confirm_password">Confirm Password</string>
|
||||
<string name="auth_register_button">Create Account</string>
|
||||
<string name="auth_have_account">Already have an account? Sign In</string>
|
||||
<string name="auth_passwords_dont_match">Passwords don\'t match</string>
|
||||
|
||||
<!-- Auth - Verify Email -->
|
||||
<string name="auth_verify_title">Verify Email</string>
|
||||
<string name="auth_verify_subtitle">Enter the 6-digit code sent to your email</string>
|
||||
<string name="auth_verify_code_label">Verification Code</string>
|
||||
<string name="auth_verify_button">Verify</string>
|
||||
<string name="auth_verify_resend">Resend Code</string>
|
||||
<string name="auth_verify_success">Email verified successfully</string>
|
||||
|
||||
<!-- Auth - Forgot Password -->
|
||||
<string name="auth_forgot_title">Forgot Password</string>
|
||||
<string name="auth_forgot_subtitle">Enter your email to receive a reset code</string>
|
||||
<string name="auth_forgot_email_label">Email</string>
|
||||
<string name="auth_forgot_button">Send Reset Code</string>
|
||||
<string name="auth_forgot_success">If an account exists, a reset code has been sent</string>
|
||||
|
||||
<!-- Auth - Reset Password -->
|
||||
<string name="auth_reset_title">Reset Password</string>
|
||||
<string name="auth_reset_subtitle">Enter your new password</string>
|
||||
<string name="auth_reset_code_label">Reset Code</string>
|
||||
<string name="auth_reset_new_password">New Password</string>
|
||||
<string name="auth_reset_confirm_password">Confirm New Password</string>
|
||||
<string name="auth_reset_button">Reset Password</string>
|
||||
<string name="auth_reset_success">Password reset successfully</string>
|
||||
|
||||
<!-- Properties / Residences -->
|
||||
<string name="properties_title">My Properties</string>
|
||||
<string name="properties_empty_title">No properties yet</string>
|
||||
<string name="properties_empty_subtitle">Add your first property to get started!</string>
|
||||
<string name="properties_add_button">Add Property</string>
|
||||
<string name="properties_add_title">Add Property</string>
|
||||
<string name="properties_edit_title">Edit Property</string>
|
||||
<string name="properties_name_label">Property Name</string>
|
||||
<string name="properties_address_label">Address</string>
|
||||
<string name="properties_type_label">Property Type</string>
|
||||
<string name="properties_notes_label">Notes</string>
|
||||
<string name="properties_delete_confirm">Are you sure you want to delete this property?</string>
|
||||
<string name="properties_deleted">Property deleted</string>
|
||||
<string name="properties_share_title">Share Property</string>
|
||||
<string name="properties_share_code">Share Code</string>
|
||||
<string name="properties_share_expires">Expires in %1$s</string>
|
||||
<string name="properties_join_title">Join Property</string>
|
||||
<string name="properties_join_code_label">Enter Share Code</string>
|
||||
<string name="properties_join_button">Join</string>
|
||||
<string name="properties_users_title">Property Members</string>
|
||||
<string name="properties_owner">Owner</string>
|
||||
<string name="properties_member">Member</string>
|
||||
<string name="properties_remove_user">Remove User</string>
|
||||
<string name="properties_details">Property Details</string>
|
||||
<string name="properties_form_name_required">Property Name *</string>
|
||||
<string name="properties_form_name_error">Name is required</string>
|
||||
<string name="properties_form_required">Required</string>
|
||||
<string name="properties_form_street">Street Address</string>
|
||||
<string name="properties_form_apartment">Apartment/Unit #</string>
|
||||
<string name="properties_form_city">City</string>
|
||||
<string name="properties_form_state">State/Province</string>
|
||||
<string name="properties_form_postal">Postal Code</string>
|
||||
<string name="properties_form_country">Country</string>
|
||||
<string name="properties_form_optional">Optional Details</string>
|
||||
<string name="properties_form_sqft">Square Footage</string>
|
||||
<string name="properties_form_lot_size">Lot Size (acres)</string>
|
||||
<string name="properties_form_description">Description</string>
|
||||
<string name="properties_form_primary">Primary Residence</string>
|
||||
<string name="properties_form_create">Create Residence</string>
|
||||
<string name="properties_form_update">Update Residence</string>
|
||||
<string name="properties_loading">Loading residence...</string>
|
||||
<string name="properties_failed_to_load">Failed to Load Property</string>
|
||||
<string name="properties_failed_to_delete">Failed to Delete Property</string>
|
||||
<string name="properties_failed_to_generate_report">Failed to Generate Report</string>
|
||||
<string name="properties_generate_report">Generate Report</string>
|
||||
<string name="properties_generate_report_confirm">This will generate and email a maintenance report for this property. Do you want to continue?</string>
|
||||
<string name="properties_generate">Generate</string>
|
||||
<string name="properties_manage_users">Manage Users</string>
|
||||
<string name="properties_edit_residence">Edit Residence</string>
|
||||
<string name="properties_delete_residence">Delete Residence</string>
|
||||
<string name="properties_delete_name_confirm">Are you sure you want to delete %1$s? This action cannot be undone.</string>
|
||||
<string name="properties_address_section">Address</string>
|
||||
<string name="properties_unit">Unit: %1$s</string>
|
||||
<string name="properties_property_details_section">Property Details</string>
|
||||
<string name="properties_bedrooms">Bedrooms</string>
|
||||
<string name="properties_bathrooms">Bathrooms</string>
|
||||
<string name="properties_square_footage">Square Footage</string>
|
||||
<string name="properties_lot_size">Lot Size</string>
|
||||
<string name="properties_year_built">Year Built</string>
|
||||
<string name="properties_sq_ft">%1$s sq ft</string>
|
||||
<string name="properties_acres">%1$s acres</string>
|
||||
<string name="properties_description_section">Description</string>
|
||||
<string name="properties_purchase_info">Purchase Information</string>
|
||||
<string name="properties_purchase_date">Purchase Date</string>
|
||||
<string name="properties_purchase_price">Purchase Price</string>
|
||||
<string name="properties_add_task">Add Task</string>
|
||||
<string name="properties_no_tasks">No tasks yet</string>
|
||||
<string name="properties_add_task_start">Add a task to get started</string>
|
||||
<string name="properties_error_loading_tasks">Error loading tasks: %1$s</string>
|
||||
<string name="properties_no_contractors">No contractors yet</string>
|
||||
<string name="properties_add_contractors_hint">Add contractors from the Contractors tab</string>
|
||||
<string name="properties_error_loading_contractors">Error loading contractors: %1$s</string>
|
||||
<string name="properties_cancel_task">Cancel Task</string>
|
||||
<string name="properties_cancel_task_confirm">Are you sure you want to cancel \"%1$s\"? This action cannot be undone.</string>
|
||||
<string name="properties_archive_task">Archive Task</string>
|
||||
<string name="properties_archive_task_confirm">Are you sure you want to archive \"%1$s\"? You can unarchive it later from archived tasks.</string>
|
||||
<string name="properties_dismiss">Dismiss</string>
|
||||
|
||||
<!-- Tasks -->
|
||||
<string name="tasks_title">Tasks</string>
|
||||
<string name="tasks_empty_title">No tasks yet</string>
|
||||
<string name="tasks_empty_subtitle">Add your first maintenance task</string>
|
||||
<string name="tasks_add_button">Add Task</string>
|
||||
<string name="tasks_add_title">Add Task</string>
|
||||
<string name="tasks_edit_title">Edit Task</string>
|
||||
<string name="tasks_title_label">Task Title</string>
|
||||
<string name="tasks_description_label">Description</string>
|
||||
<string name="tasks_category_label">Category</string>
|
||||
<string name="tasks_priority_label">Priority</string>
|
||||
<string name="tasks_frequency_label">Frequency</string>
|
||||
<string name="tasks_due_date_label">Due Date</string>
|
||||
<string name="tasks_estimated_cost_label">Estimated Cost</string>
|
||||
<string name="tasks_contractor_label">Contractor</string>
|
||||
<string name="tasks_notes_label">Notes</string>
|
||||
<string name="tasks_delete_confirm">Are you sure you want to delete this task?</string>
|
||||
<string name="tasks_deleted">Task deleted</string>
|
||||
<string name="tasks_details">Task Details</string>
|
||||
<string name="tasks_title_required">Title *</string>
|
||||
<string name="tasks_title_error">Title is required</string>
|
||||
<string name="tasks_category_required">Category *</string>
|
||||
<string name="tasks_frequency_required">Frequency *</string>
|
||||
<string name="tasks_priority_required">Priority *</string>
|
||||
<string name="tasks_status_label">Status *</string>
|
||||
<string name="tasks_due_date_required">Due Date (YYYY-MM-DD) *</string>
|
||||
<string name="tasks_due_date_error">Due date is required</string>
|
||||
<string name="tasks_due_date_placeholder">2025-01-31</string>
|
||||
<string name="tasks_update">Update Task</string>
|
||||
<string name="tasks_failed_to_update">Failed to Update Task</string>
|
||||
|
||||
<!-- Task Columns / Kanban -->
|
||||
<string name="tasks_column_overdue">Overdue</string>
|
||||
<string name="tasks_column_due_soon">Due Soon</string>
|
||||
<string name="tasks_column_upcoming">Upcoming</string>
|
||||
<string name="tasks_column_in_progress">In Progress</string>
|
||||
<string name="tasks_column_completed">Completed</string>
|
||||
<string name="tasks_column_cancelled">Cancelled</string>
|
||||
|
||||
<!-- Task Actions -->
|
||||
<string name="tasks_mark_complete">Complete</string>
|
||||
<string name="tasks_mark_in_progress">Start</string>
|
||||
<string name="tasks_cancel">Cancel</string>
|
||||
<string name="tasks_uncancel">Restore</string>
|
||||
<string name="tasks_archive">Archive</string>
|
||||
<string name="tasks_unarchive">Unarchive</string>
|
||||
<string name="tasks_completed_message">Task marked as completed</string>
|
||||
<string name="tasks_in_progress_message">Task marked as in progress</string>
|
||||
<string name="tasks_cancelled_message">Task cancelled</string>
|
||||
|
||||
<!-- Task Completions -->
|
||||
<string name="completions_title">Task Completions</string>
|
||||
<string name="completions_add_title">Complete Task</string>
|
||||
<string name="completions_notes_label">Notes</string>
|
||||
<string name="completions_actual_cost_label">Actual Cost</string>
|
||||
<string name="completions_date_label">Completion Date</string>
|
||||
<string name="completions_photos_label">Photos</string>
|
||||
<string name="completions_add_photo">Add Photo</string>
|
||||
<string name="completions_delete_confirm">Delete this completion record?</string>
|
||||
|
||||
<!-- Contractors -->
|
||||
<string name="contractors_title">Contractors</string>
|
||||
<string name="contractors_empty_title">No contractors yet</string>
|
||||
<string name="contractors_empty_subtitle">Add trusted contractors for your property</string>
|
||||
<string name="contractors_empty_subtitle_first">Add your first contractor to get started</string>
|
||||
<string name="contractors_no_results">No contractors found</string>
|
||||
<string name="contractors_add_button">Add Contractor</string>
|
||||
<string name="contractors_add_title">Add Contractor</string>
|
||||
<string name="contractors_edit_title">Edit Contractor</string>
|
||||
<string name="contractors_name_label">Name</string>
|
||||
<string name="contractors_company_label">Company</string>
|
||||
<string name="contractors_phone_label">Phone</string>
|
||||
<string name="contractors_email_label">Email</string>
|
||||
<string name="contractors_specialty_label">Specialty</string>
|
||||
<string name="contractors_notes_label">Notes</string>
|
||||
<string name="contractors_favorite">Favorite</string>
|
||||
<string name="contractors_favorites">Favorites</string>
|
||||
<string name="contractors_filter_favorites">Filter favorites</string>
|
||||
<string name="contractors_filter_specialty">Filter by specialty</string>
|
||||
<string name="contractors_all_specialties">All Specialties</string>
|
||||
<string name="contractors_search">Search contractors...</string>
|
||||
<string name="contractors_clear_search">Clear search</string>
|
||||
<string name="contractors_add_to_favorites">Add to favorites</string>
|
||||
<string name="contractors_remove_from_favorites">Remove from favorites</string>
|
||||
<string name="contractors_view_details">View details</string>
|
||||
<string name="contractors_tasks_count">%1$d tasks</string>
|
||||
<string name="contractors_delete_confirm">Are you sure you want to delete this contractor?</string>
|
||||
<string name="contractors_deleted">Contractor deleted</string>
|
||||
<string name="contractors_call">Call</string>
|
||||
<string name="contractors_send_email">Email</string>
|
||||
<string name="contractors_failed_to_load">Failed to Load Contractors</string>
|
||||
<string name="contractors_failed_to_delete">Failed to Delete Contractor</string>
|
||||
<string name="contractors_failed_to_update_favorite">Failed to Update Favorite</string>
|
||||
<string name="contractors_upgrade_required">Upgrade Required</string>
|
||||
<string name="contractors_upgrade_message">You\'ve reached the maximum number of contractors for your current plan. Upgrade to Pro for unlimited contractors.</string>
|
||||
<string name="contractors_details">Contractor Details</string>
|
||||
<string name="contractors_toggle_favorite">Toggle favorite</string>
|
||||
<string name="contractors_website">Website</string>
|
||||
<string name="contractors_directions">Directions</string>
|
||||
<string name="contractors_contact_info">Contact Information</string>
|
||||
<string name="contractors_no_contact_info">No contact information available</string>
|
||||
<string name="contractors_address">Address</string>
|
||||
<string name="contractors_location">Location</string>
|
||||
<string name="contractors_associated_property">Associated Property</string>
|
||||
<string name="contractors_property">Property</string>
|
||||
<string name="contractors_notes">Notes</string>
|
||||
<string name="contractors_statistics">Statistics</string>
|
||||
<string name="contractors_tasks_completed">Tasks\nCompleted</string>
|
||||
<string name="contractors_average_rating">Average\nRating</string>
|
||||
<string name="contractors_info">Info</string>
|
||||
<string name="contractors_added_by">Added By</string>
|
||||
<string name="contractors_member_since">Member Since</string>
|
||||
<string name="contractors_delete">Delete Contractor</string>
|
||||
<string name="contractors_delete_warning">Are you sure you want to delete this contractor? This action cannot be undone.</string>
|
||||
<string name="contractors_completed_tasks">%1$d completed tasks</string>
|
||||
|
||||
<!-- Documents -->
|
||||
<string name="documents_title">Documents</string>
|
||||
<string name="documents_and_warranties">Documents & Warranties</string>
|
||||
<string name="documents_empty_title">No documents yet</string>
|
||||
<string name="documents_empty_subtitle">Store important documents and warranties</string>
|
||||
<string name="documents_add_button">Add Document</string>
|
||||
<string name="documents_add_title">Add Document</string>
|
||||
<string name="documents_edit_title">Edit Document</string>
|
||||
<string name="documents_title_label">Title</string>
|
||||
<string name="documents_description_label">Description</string>
|
||||
<string name="documents_type_label">Document Type</string>
|
||||
<string name="documents_vendor_label">Vendor</string>
|
||||
<string name="documents_serial_label">Serial Number</string>
|
||||
<string name="documents_model_label">Model Number</string>
|
||||
<string name="documents_purchase_date_label">Purchase Date</string>
|
||||
<string name="documents_purchase_price_label">Purchase Price</string>
|
||||
<string name="documents_expiry_date_label">Expiry Date</string>
|
||||
<string name="documents_delete_confirm">Are you sure you want to delete this document?</string>
|
||||
<string name="documents_deleted">Document deleted</string>
|
||||
<string name="documents_warranties_title">Warranties</string>
|
||||
<string name="documents_warranties_tab">Warranties</string>
|
||||
<string name="documents_documents_tab">Documents</string>
|
||||
<string name="documents_upload">Upload File</string>
|
||||
<string name="documents_view">View Document</string>
|
||||
<string name="documents_all_categories">All Categories</string>
|
||||
<string name="documents_all_types">All Types</string>
|
||||
<string name="documents_filter_active">Filter active</string>
|
||||
<string name="documents_filters">Filters</string>
|
||||
<string name="documents_upgrade_required">Upgrade Required</string>
|
||||
<string name="documents_upgrade_message">You\'ve reached the maximum number of documents for your current plan. Upgrade to Pro for unlimited documents.</string>
|
||||
<string name="documents_details">Document Details</string>
|
||||
<string name="documents_status">Status</string>
|
||||
<string name="documents_inactive">Inactive</string>
|
||||
<string name="documents_expired">Expired</string>
|
||||
<string name="documents_expiring_soon">Expiring soon</string>
|
||||
<string name="documents_active">Active</string>
|
||||
<string name="documents_days_remaining">Days Remaining</string>
|
||||
<string name="documents_basic_info">Basic Information</string>
|
||||
<string name="documents_item_details">Item Details</string>
|
||||
<string name="documents_item_name">Item Name</string>
|
||||
<string name="documents_model_number">Model Number</string>
|
||||
<string name="documents_serial_number">Serial Number</string>
|
||||
<string name="documents_provider">Provider</string>
|
||||
<string name="documents_provider_contact">Provider Contact</string>
|
||||
<string name="documents_claim_info">Claim Information</string>
|
||||
<string name="documents_claim_phone">Claim Phone</string>
|
||||
<string name="documents_claim_email">Claim Email</string>
|
||||
<string name="documents_claim_website">Claim Website</string>
|
||||
<string name="documents_important_dates">Important Dates</string>
|
||||
<string name="documents_purchase_date">Purchase Date</string>
|
||||
<string name="documents_start_date">Start Date</string>
|
||||
<string name="documents_end_date">End Date</string>
|
||||
<string name="documents_associations">Associations</string>
|
||||
<string name="documents_residence">Residence</string>
|
||||
<string name="documents_contractor">Contractor</string>
|
||||
<string name="documents_contractor_phone">Contractor Phone</string>
|
||||
<string name="documents_additional_info">Additional Information</string>
|
||||
<string name="documents_tags">Tags</string>
|
||||
<string name="documents_notes">Notes</string>
|
||||
<string name="documents_images">Images (%1$d)</string>
|
||||
<string name="documents_attached_file">Attached File</string>
|
||||
<string name="documents_file_type">File Type</string>
|
||||
<string name="documents_file_size">File Size</string>
|
||||
<string name="documents_download_file">Download File</string>
|
||||
<string name="documents_metadata">Metadata</string>
|
||||
<string name="documents_uploaded_by">Uploaded By</string>
|
||||
<string name="documents_created">Created</string>
|
||||
<string name="documents_updated">Updated</string>
|
||||
<string name="documents_delete">Delete Document</string>
|
||||
<string name="documents_delete_warning">Are you sure you want to delete this document? This action cannot be undone.</string>
|
||||
<string name="documents_failed_to_load">Failed to Load Document</string>
|
||||
<string name="documents_failed_to_delete">Failed to Delete Document</string>
|
||||
<string name="documents_image_viewer">Document Images</string>
|
||||
<string name="documents_image_of">Image %1$d of %2$d</string>
|
||||
<string name="documents_error_loading_image">Error loading image</string>
|
||||
<string name="documents_failed_to_load_image">Failed to load image</string>
|
||||
<string name="documents_previous">Previous</string>
|
||||
<string name="documents_next">Next</string>
|
||||
|
||||
<!-- Profile -->
|
||||
<string name="profile_title">Profile</string>
|
||||
<string name="profile_edit_title">Edit Profile</string>
|
||||
<string name="profile_update_title">Update Your Profile</string>
|
||||
<string name="profile_first_name">First Name</string>
|
||||
<string name="profile_last_name">Last Name</string>
|
||||
<string name="profile_email">Email</string>
|
||||
<string name="profile_phone">Phone</string>
|
||||
<string name="profile_save">Save Changes</string>
|
||||
<string name="profile_logout">Log Out</string>
|
||||
<string name="profile_logout_confirm">Are you sure you want to log out?</string>
|
||||
<string name="profile_settings">Settings</string>
|
||||
<string name="profile_notifications">Notifications</string>
|
||||
<string name="profile_notifications_subtitle">Manage notification preferences</string>
|
||||
<string name="profile_theme">Theme</string>
|
||||
<string name="profile_appearance">Appearance</string>
|
||||
<string name="profile_change_theme">Change theme</string>
|
||||
<string name="profile_information">Profile Information</string>
|
||||
<string name="profile_update_failed">Failed to Update Profile</string>
|
||||
<string name="profile_load_failed">Failed to load user data</string>
|
||||
<string name="profile_not_authenticated">Not authenticated</string>
|
||||
<string name="profile_update_coming_soon">Profile update coming soon</string>
|
||||
<string name="profile_email_required">Email is required</string>
|
||||
<string name="profile_update_success">Profile updated successfully</string>
|
||||
<string name="profile_subscription">Subscription</string>
|
||||
<string name="profile_pro_plan">Pro Plan</string>
|
||||
<string name="profile_free_plan">Free Plan</string>
|
||||
<string name="profile_active_until">Active until %1$s</string>
|
||||
<string name="profile_limited_features">Limited features</string>
|
||||
<string name="profile_upgrade_to_pro">Upgrade to Pro</string>
|
||||
<string name="profile_manage_subscription">Manage your subscription in the Google Play Store</string>
|
||||
|
||||
<!-- Settings -->
|
||||
<string name="settings_title">Settings</string>
|
||||
<string name="settings_notifications">Notification Preferences</string>
|
||||
<string name="settings_theme">App Theme</string>
|
||||
<string name="settings_language">Language</string>
|
||||
<string name="settings_about">About</string>
|
||||
<string name="settings_privacy">Privacy Policy</string>
|
||||
<string name="settings_terms">Terms of Service</string>
|
||||
|
||||
<!-- Notifications -->
|
||||
<string name="notifications_title">Notifications</string>
|
||||
<string name="notifications_empty">No notifications</string>
|
||||
<string name="notifications_task_reminders">Task Reminders</string>
|
||||
<string name="notifications_task_completed">Task Completed</string>
|
||||
<string name="notifications_property_shared">Property Shared</string>
|
||||
<string name="notifications_enabled">Enabled</string>
|
||||
<string name="notifications_disabled">Disabled</string>
|
||||
<string name="notifications_preferences">Notification Preferences</string>
|
||||
<string name="notifications_choose">Choose which notifications you\'d like to receive</string>
|
||||
<string name="notifications_task_section">Task Notifications</string>
|
||||
<string name="notifications_other_section">Other Notifications</string>
|
||||
<string name="notifications_task_due_soon">Task Due Soon</string>
|
||||
<string name="notifications_task_due_soon_desc">Reminders for upcoming tasks</string>
|
||||
<string name="notifications_task_overdue">Task Overdue</string>
|
||||
<string name="notifications_task_overdue_desc">Alerts for overdue tasks</string>
|
||||
<string name="notifications_task_completed_desc">When someone completes a task</string>
|
||||
<string name="notifications_task_assigned">Task Assigned</string>
|
||||
<string name="notifications_task_assigned_desc">When a task is assigned to you</string>
|
||||
<string name="notifications_property_shared_desc">When someone shares a property with you</string>
|
||||
<string name="notifications_warranty_expiring">Warranty Expiring</string>
|
||||
<string name="notifications_warranty_expiring_desc">Reminders for expiring warranties</string>
|
||||
|
||||
<!-- Common -->
|
||||
<string name="common_save">Save</string>
|
||||
<string name="common_cancel">Cancel</string>
|
||||
<string name="common_delete">Delete</string>
|
||||
<string name="common_edit">Edit</string>
|
||||
<string name="common_done">Done</string>
|
||||
<string name="common_close">Close</string>
|
||||
<string name="common_back">Back</string>
|
||||
<string name="common_next">Next</string>
|
||||
<string name="common_submit">Submit</string>
|
||||
<string name="common_confirm">Confirm</string>
|
||||
<string name="common_loading">Loading...</string>
|
||||
<string name="common_error">Error</string>
|
||||
<string name="common_retry">Retry</string>
|
||||
<string name="common_success">Success</string>
|
||||
<string name="common_search">Search</string>
|
||||
<string name="common_filter">Filter</string>
|
||||
<string name="common_sort">Sort</string>
|
||||
<string name="common_refresh">Refresh</string>
|
||||
<string name="common_select">Select</string>
|
||||
<string name="common_none">None</string>
|
||||
<string name="common_all">All</string>
|
||||
<string name="common_yes">Yes</string>
|
||||
<string name="common_no">No</string>
|
||||
<string name="common_ok">OK</string>
|
||||
|
||||
<!-- Errors -->
|
||||
<string name="error_generic">Something went wrong. Please try again.</string>
|
||||
<string name="error_network">Network error. Please check your connection.</string>
|
||||
<string name="error_timeout">Request timed out. Please try again.</string>
|
||||
<string name="error_server">Server error. Please try again later.</string>
|
||||
<string name="error_unauthorized">Session expired. Please log in again.</string>
|
||||
<string name="error_not_found">Not found</string>
|
||||
<string name="error_invalid_input">Please check your input</string>
|
||||
<string name="error_required_field">This field is required</string>
|
||||
|
||||
<!-- Accessibility -->
|
||||
<string name="a11y_back">Go back</string>
|
||||
<string name="a11y_close">Close</string>
|
||||
<string name="a11y_menu">Open menu</string>
|
||||
<string name="a11y_add">Add new item</string>
|
||||
<string name="a11y_edit">Edit item</string>
|
||||
<string name="a11y_delete">Delete item</string>
|
||||
<string name="a11y_refresh">Refresh content</string>
|
||||
<string name="a11y_search">Search</string>
|
||||
<string name="a11y_filter">Filter options</string>
|
||||
<string name="a11y_loading">Loading content</string>
|
||||
|
||||
<!-- Date/Time -->
|
||||
<string name="date_today">Today</string>
|
||||
<string name="date_yesterday">Yesterday</string>
|
||||
<string name="date_tomorrow">Tomorrow</string>
|
||||
<string name="date_days_ago">%1$d days ago</string>
|
||||
<string name="date_in_days">in %1$d days</string>
|
||||
|
||||
<!-- Home Screen -->
|
||||
<string name="home_welcome">Welcome back</string>
|
||||
<string name="home_manage_properties">Manage your properties</string>
|
||||
<string name="home_failed_to_load">Failed to Load Summary</string>
|
||||
<string name="home_logout">Logout</string>
|
||||
<string name="home_properties">Properties</string>
|
||||
<string name="home_tasks">Tasks</string>
|
||||
<string name="home_view_all_properties">View All Properties</string>
|
||||
<string name="home_view_all_tasks">View All Tasks</string>
|
||||
<string name="home_overview">Overview</string>
|
||||
<string name="home_property_stats">Your property stats</string>
|
||||
<string name="home_total_tasks">Total Tasks</string>
|
||||
<string name="home_pending">Pending</string>
|
||||
<string name="home_manage_residences">Manage your residences</string>
|
||||
<string name="home_view_manage_tasks">View and manage tasks</string>
|
||||
|
||||
<!-- Subscription -->
|
||||
<string name="subscription_title">Subscription</string>
|
||||
<string name="subscription_free">Free</string>
|
||||
<string name="subscription_premium">Premium</string>
|
||||
<string name="subscription_upgrade">Upgrade</string>
|
||||
<string name="subscription_restore">Restore Purchases</string>
|
||||
<string name="subscription_features">Premium Features</string>
|
||||
<string name="subscription_limit_properties">You\'ve reached the property limit for your plan</string>
|
||||
<string name="subscription_limit_tasks">You\'ve reached the task limit for your plan</string>
|
||||
</resources>
|
||||
@@ -9,6 +9,13 @@ import kotlinx.serialization.json.Json
|
||||
expect fun getLocalhostAddress(): String
|
||||
expect fun createHttpClient(): HttpClient
|
||||
|
||||
/**
|
||||
* Get the device's preferred language code (e.g., "en", "es", "fr").
|
||||
* This is used to set the Accept-Language header for API requests
|
||||
* so the server can return localized error messages and content.
|
||||
*/
|
||||
expect fun getDeviceLanguage(): String
|
||||
|
||||
object ApiClient {
|
||||
val httpClient = createHttpClient()
|
||||
|
||||
|
||||
@@ -28,6 +28,8 @@ import com.example.casera.ui.components.HandleErrors
|
||||
import com.example.casera.util.DateUtils
|
||||
import com.example.casera.viewmodel.ContractorViewModel
|
||||
import com.example.casera.network.ApiResult
|
||||
import casera.composeapp.generated.resources.*
|
||||
import org.jetbrains.compose.resources.stringResource
|
||||
|
||||
@OptIn(ExperimentalMaterial3Api::class)
|
||||
@Composable
|
||||
@@ -50,13 +52,13 @@ fun ContractorDetailScreen(
|
||||
// Handle errors for delete contractor
|
||||
deleteState.HandleErrors(
|
||||
onRetry = { viewModel.deleteContractor(contractorId) },
|
||||
errorTitle = "Failed to Delete Contractor"
|
||||
errorTitle = stringResource(Res.string.contractors_failed_to_delete)
|
||||
)
|
||||
|
||||
// Handle errors for toggle favorite
|
||||
toggleFavoriteState.HandleErrors(
|
||||
onRetry = { viewModel.toggleFavorite(contractorId) },
|
||||
errorTitle = "Failed to Update Favorite"
|
||||
errorTitle = stringResource(Res.string.contractors_failed_to_update_favorite)
|
||||
)
|
||||
|
||||
LaunchedEffect(deleteState) {
|
||||
@@ -76,10 +78,10 @@ fun ContractorDetailScreen(
|
||||
Scaffold(
|
||||
topBar = {
|
||||
TopAppBar(
|
||||
title = { Text("Contractor Details", fontWeight = FontWeight.Bold) },
|
||||
title = { Text(stringResource(Res.string.contractors_details), fontWeight = FontWeight.Bold) },
|
||||
navigationIcon = {
|
||||
IconButton(onClick = onNavigateBack) {
|
||||
Icon(Icons.Default.ArrowBack, "Back")
|
||||
Icon(Icons.Default.ArrowBack, stringResource(Res.string.common_back))
|
||||
}
|
||||
},
|
||||
actions = {
|
||||
@@ -88,15 +90,15 @@ fun ContractorDetailScreen(
|
||||
IconButton(onClick = { viewModel.toggleFavorite(contractorId) }) {
|
||||
Icon(
|
||||
if (state.data.isFavorite) Icons.Default.Star else Icons.Default.StarOutline,
|
||||
"Toggle favorite",
|
||||
stringResource(Res.string.contractors_toggle_favorite),
|
||||
tint = if (state.data.isFavorite) Color(0xFFF59E0B) else LocalContentColor.current
|
||||
)
|
||||
}
|
||||
IconButton(onClick = { showEditDialog = true }) {
|
||||
Icon(Icons.Default.Edit, "Edit")
|
||||
Icon(Icons.Default.Edit, stringResource(Res.string.common_edit))
|
||||
}
|
||||
IconButton(onClick = { showDeleteConfirmation = true }) {
|
||||
Icon(Icons.Default.Delete, "Delete", tint = Color(0xFFEF4444))
|
||||
Icon(Icons.Default.Delete, stringResource(Res.string.common_delete), tint = Color(0xFFEF4444))
|
||||
}
|
||||
}
|
||||
else -> {}
|
||||
@@ -120,7 +122,7 @@ fun ContractorDetailScreen(
|
||||
ApiResultHandler(
|
||||
state = contractorState,
|
||||
onRetry = { viewModel.loadContractorDetail(contractorId) },
|
||||
errorTitle = "Failed to Load Contractor",
|
||||
errorTitle = stringResource(Res.string.contractors_failed_to_load),
|
||||
loadingContent = {
|
||||
CircularProgressIndicator(color = MaterialTheme.colorScheme.primary)
|
||||
}
|
||||
@@ -235,7 +237,7 @@ fun ContractorDetailScreen(
|
||||
if (contractor.taskCount > 0) {
|
||||
Spacer(modifier = Modifier.height(4.dp))
|
||||
Text(
|
||||
text = "${contractor.taskCount} completed tasks",
|
||||
text = stringResource(Res.string.contractors_completed_tasks, contractor.taskCount),
|
||||
style = MaterialTheme.typography.bodySmall,
|
||||
color = MaterialTheme.colorScheme.onSurfaceVariant
|
||||
)
|
||||
@@ -254,7 +256,7 @@ fun ContractorDetailScreen(
|
||||
contractor.phone?.let { phone ->
|
||||
QuickActionButton(
|
||||
icon = Icons.Default.Phone,
|
||||
label = "Call",
|
||||
label = stringResource(Res.string.contractors_call),
|
||||
color = MaterialTheme.colorScheme.primary,
|
||||
modifier = Modifier.weight(1f),
|
||||
onClick = {
|
||||
@@ -268,7 +270,7 @@ fun ContractorDetailScreen(
|
||||
contractor.email?.let { email ->
|
||||
QuickActionButton(
|
||||
icon = Icons.Default.Email,
|
||||
label = "Email",
|
||||
label = stringResource(Res.string.contractors_send_email),
|
||||
color = MaterialTheme.colorScheme.secondary,
|
||||
modifier = Modifier.weight(1f),
|
||||
onClick = {
|
||||
@@ -282,7 +284,7 @@ fun ContractorDetailScreen(
|
||||
contractor.website?.let { website ->
|
||||
QuickActionButton(
|
||||
icon = Icons.Default.Language,
|
||||
label = "Website",
|
||||
label = stringResource(Res.string.contractors_website),
|
||||
color = Color(0xFFF59E0B),
|
||||
modifier = Modifier.weight(1f),
|
||||
onClick = {
|
||||
@@ -297,7 +299,7 @@ fun ContractorDetailScreen(
|
||||
if (contractor.streetAddress != null || contractor.city != null) {
|
||||
QuickActionButton(
|
||||
icon = Icons.Default.Map,
|
||||
label = "Directions",
|
||||
label = stringResource(Res.string.contractors_directions),
|
||||
color = Color(0xFFEF4444),
|
||||
modifier = Modifier.weight(1f),
|
||||
onClick = {
|
||||
@@ -319,11 +321,11 @@ fun ContractorDetailScreen(
|
||||
|
||||
// Contact Information
|
||||
item {
|
||||
DetailSection(title = "Contact Information") {
|
||||
DetailSection(title = stringResource(Res.string.contractors_contact_info)) {
|
||||
contractor.phone?.let { phone ->
|
||||
ClickableDetailRow(
|
||||
icon = Icons.Default.Phone,
|
||||
label = "Phone",
|
||||
label = stringResource(Res.string.contractors_phone_label),
|
||||
value = phone,
|
||||
iconTint = MaterialTheme.colorScheme.primary,
|
||||
onClick = {
|
||||
@@ -337,7 +339,7 @@ fun ContractorDetailScreen(
|
||||
contractor.email?.let { email ->
|
||||
ClickableDetailRow(
|
||||
icon = Icons.Default.Email,
|
||||
label = "Email",
|
||||
label = stringResource(Res.string.contractors_email_label),
|
||||
value = email,
|
||||
iconTint = MaterialTheme.colorScheme.secondary,
|
||||
onClick = {
|
||||
@@ -351,7 +353,7 @@ fun ContractorDetailScreen(
|
||||
contractor.website?.let { website ->
|
||||
ClickableDetailRow(
|
||||
icon = Icons.Default.Language,
|
||||
label = "Website",
|
||||
label = stringResource(Res.string.contractors_website),
|
||||
value = website,
|
||||
iconTint = Color(0xFFF59E0B),
|
||||
onClick = {
|
||||
@@ -365,7 +367,7 @@ fun ContractorDetailScreen(
|
||||
|
||||
if (contractor.phone == null && contractor.email == null && contractor.website == null) {
|
||||
Text(
|
||||
text = "No contact information available",
|
||||
text = stringResource(Res.string.contractors_no_contact_info),
|
||||
style = MaterialTheme.typography.bodyMedium,
|
||||
color = MaterialTheme.colorScheme.onSurfaceVariant,
|
||||
modifier = Modifier.padding(16.dp)
|
||||
@@ -377,7 +379,7 @@ fun ContractorDetailScreen(
|
||||
// Address
|
||||
if (contractor.streetAddress != null || contractor.city != null) {
|
||||
item {
|
||||
DetailSection(title = "Address") {
|
||||
DetailSection(title = stringResource(Res.string.contractors_address)) {
|
||||
val fullAddress = buildString {
|
||||
contractor.streetAddress?.let { append(it) }
|
||||
if (contractor.city != null || contractor.stateProvince != null || contractor.postalCode != null) {
|
||||
@@ -397,7 +399,7 @@ fun ContractorDetailScreen(
|
||||
if (fullAddress.isNotBlank()) {
|
||||
ClickableDetailRow(
|
||||
icon = Icons.Default.LocationOn,
|
||||
label = "Location",
|
||||
label = stringResource(Res.string.contractors_location),
|
||||
value = fullAddress,
|
||||
iconTint = Color(0xFFEF4444),
|
||||
onClick = {
|
||||
@@ -423,10 +425,10 @@ fun ContractorDetailScreen(
|
||||
?: "Property #$resId"
|
||||
|
||||
item {
|
||||
DetailSection(title = "Associated Property") {
|
||||
DetailSection(title = stringResource(Res.string.contractors_associated_property)) {
|
||||
DetailRow(
|
||||
icon = Icons.Default.Home,
|
||||
label = "Property",
|
||||
label = stringResource(Res.string.contractors_property),
|
||||
value = residenceName,
|
||||
iconTint = MaterialTheme.colorScheme.primary
|
||||
)
|
||||
@@ -437,7 +439,7 @@ fun ContractorDetailScreen(
|
||||
// Notes
|
||||
if (!contractor.notes.isNullOrBlank()) {
|
||||
item {
|
||||
DetailSection(title = "Notes") {
|
||||
DetailSection(title = stringResource(Res.string.contractors_notes)) {
|
||||
Row(
|
||||
modifier = Modifier.padding(horizontal = 16.dp, vertical = 12.dp),
|
||||
horizontalArrangement = Arrangement.spacedBy(12.dp)
|
||||
@@ -460,7 +462,7 @@ fun ContractorDetailScreen(
|
||||
|
||||
// Statistics
|
||||
item {
|
||||
DetailSection(title = "Statistics") {
|
||||
DetailSection(title = stringResource(Res.string.contractors_statistics)) {
|
||||
Row(
|
||||
modifier = Modifier
|
||||
.fillMaxWidth()
|
||||
@@ -470,7 +472,7 @@ fun ContractorDetailScreen(
|
||||
StatCard(
|
||||
icon = Icons.Default.CheckCircle,
|
||||
value = contractor.taskCount.toString(),
|
||||
label = "Tasks\nCompleted",
|
||||
label = stringResource(Res.string.contractors_tasks_completed),
|
||||
color = MaterialTheme.colorScheme.primary
|
||||
)
|
||||
|
||||
@@ -478,7 +480,7 @@ fun ContractorDetailScreen(
|
||||
StatCard(
|
||||
icon = Icons.Default.Star,
|
||||
value = ((contractor.rating * 10).toInt() / 10.0).toString(),
|
||||
label = "Average\nRating",
|
||||
label = stringResource(Res.string.contractors_average_rating),
|
||||
color = Color(0xFFF59E0B)
|
||||
)
|
||||
}
|
||||
@@ -488,11 +490,11 @@ fun ContractorDetailScreen(
|
||||
|
||||
// Metadata
|
||||
item {
|
||||
DetailSection(title = "Info") {
|
||||
DetailSection(title = stringResource(Res.string.contractors_info)) {
|
||||
contractor.createdBy?.let { createdBy ->
|
||||
DetailRow(
|
||||
icon = Icons.Default.PersonAdd,
|
||||
label = "Added By",
|
||||
label = stringResource(Res.string.contractors_added_by),
|
||||
value = createdBy.username,
|
||||
iconTint = MaterialTheme.colorScheme.onSurfaceVariant
|
||||
)
|
||||
@@ -501,7 +503,7 @@ fun ContractorDetailScreen(
|
||||
|
||||
DetailRow(
|
||||
icon = Icons.Default.CalendarMonth,
|
||||
label = "Member Since",
|
||||
label = stringResource(Res.string.contractors_member_since),
|
||||
value = DateUtils.formatDateMedium(contractor.createdAt),
|
||||
iconTint = MaterialTheme.colorScheme.onSurfaceVariant
|
||||
)
|
||||
@@ -527,8 +529,8 @@ fun ContractorDetailScreen(
|
||||
AlertDialog(
|
||||
onDismissRequest = { showDeleteConfirmation = false },
|
||||
icon = { Icon(Icons.Default.Warning, null, tint = Color(0xFFEF4444)) },
|
||||
title = { Text("Delete Contractor") },
|
||||
text = { Text("Are you sure you want to delete this contractor? This action cannot be undone.") },
|
||||
title = { Text(stringResource(Res.string.contractors_delete)) },
|
||||
text = { Text(stringResource(Res.string.contractors_delete_warning)) },
|
||||
confirmButton = {
|
||||
Button(
|
||||
onClick = {
|
||||
@@ -537,12 +539,12 @@ fun ContractorDetailScreen(
|
||||
},
|
||||
colors = ButtonDefaults.buttonColors(containerColor = Color(0xFFEF4444))
|
||||
) {
|
||||
Text("Delete")
|
||||
Text(stringResource(Res.string.common_delete))
|
||||
}
|
||||
},
|
||||
dismissButton = {
|
||||
TextButton(onClick = { showDeleteConfirmation = false }) {
|
||||
Text("Cancel")
|
||||
Text(stringResource(Res.string.common_cancel))
|
||||
}
|
||||
},
|
||||
containerColor = Color.White,
|
||||
|
||||
@@ -29,6 +29,8 @@ import com.example.casera.network.ApiResult
|
||||
import com.example.casera.repository.LookupsRepository
|
||||
import com.example.casera.ui.subscription.UpgradeFeatureScreen
|
||||
import com.example.casera.utils.SubscriptionHelper
|
||||
import casera.composeapp.generated.resources.*
|
||||
import org.jetbrains.compose.resources.stringResource
|
||||
|
||||
@OptIn(ExperimentalMaterial3Api::class)
|
||||
@Composable
|
||||
@@ -83,13 +85,13 @@ fun ContractorsScreen(
|
||||
// Handle errors for delete contractor
|
||||
deleteState.HandleErrors(
|
||||
onRetry = { /* Handled in UI */ },
|
||||
errorTitle = "Failed to Delete Contractor"
|
||||
errorTitle = stringResource(Res.string.contractors_failed_to_delete)
|
||||
)
|
||||
|
||||
// Handle errors for toggle favorite
|
||||
toggleFavoriteState.HandleErrors(
|
||||
onRetry = { /* Handled in UI */ },
|
||||
errorTitle = "Failed to Update Favorite"
|
||||
errorTitle = stringResource(Res.string.contractors_failed_to_update_favorite)
|
||||
)
|
||||
|
||||
LaunchedEffect(deleteState) {
|
||||
@@ -109,13 +111,13 @@ fun ContractorsScreen(
|
||||
Scaffold(
|
||||
topBar = {
|
||||
TopAppBar(
|
||||
title = { Text("Contractors", fontWeight = FontWeight.Bold) },
|
||||
title = { Text(stringResource(Res.string.contractors_title), fontWeight = FontWeight.Bold) },
|
||||
actions = {
|
||||
// Favorites filter toggle
|
||||
IconButton(onClick = { showFavoritesOnly = !showFavoritesOnly }) {
|
||||
Icon(
|
||||
if (showFavoritesOnly) Icons.Default.Star else Icons.Default.StarOutline,
|
||||
"Filter favorites",
|
||||
stringResource(Res.string.contractors_filter_favorites),
|
||||
tint = if (showFavoritesOnly) MaterialTheme.colorScheme.tertiary else LocalContentColor.current
|
||||
)
|
||||
}
|
||||
@@ -125,7 +127,7 @@ fun ContractorsScreen(
|
||||
IconButton(onClick = { showFiltersMenu = true }) {
|
||||
Icon(
|
||||
Icons.Default.FilterList,
|
||||
"Filter by specialty",
|
||||
stringResource(Res.string.contractors_filter_specialty),
|
||||
tint = if (selectedFilter != null) MaterialTheme.colorScheme.primary else LocalContentColor.current
|
||||
)
|
||||
}
|
||||
@@ -135,7 +137,7 @@ fun ContractorsScreen(
|
||||
onDismissRequest = { showFiltersMenu = false }
|
||||
) {
|
||||
DropdownMenuItem(
|
||||
text = { Text("All Specialties") },
|
||||
text = { Text(stringResource(Res.string.contractors_all_specialties)) },
|
||||
onClick = {
|
||||
selectedFilter = null
|
||||
showFiltersMenu = false
|
||||
@@ -186,7 +188,7 @@ fun ContractorsScreen(
|
||||
containerColor = MaterialTheme.colorScheme.primary,
|
||||
contentColor = MaterialTheme.colorScheme.onPrimary
|
||||
) {
|
||||
Icon(Icons.Default.Add, "Add contractor")
|
||||
Icon(Icons.Default.Add, stringResource(Res.string.contractors_add_button))
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -219,12 +221,12 @@ fun ContractorsScreen(
|
||||
modifier = Modifier
|
||||
.fillMaxWidth()
|
||||
.padding(horizontal = 16.dp, vertical = 8.dp),
|
||||
placeholder = { Text("Search contractors...") },
|
||||
leadingIcon = { Icon(Icons.Default.Search, "Search") },
|
||||
placeholder = { Text(stringResource(Res.string.contractors_search)) },
|
||||
leadingIcon = { Icon(Icons.Default.Search, stringResource(Res.string.common_search)) },
|
||||
trailingIcon = {
|
||||
if (searchQuery.isNotEmpty()) {
|
||||
IconButton(onClick = { searchQuery = "" }) {
|
||||
Icon(Icons.Default.Close, "Clear search")
|
||||
Icon(Icons.Default.Close, stringResource(Res.string.contractors_clear_search))
|
||||
}
|
||||
}
|
||||
},
|
||||
@@ -250,7 +252,7 @@ fun ContractorsScreen(
|
||||
FilterChip(
|
||||
selected = true,
|
||||
onClick = { showFavoritesOnly = false },
|
||||
label = { Text("Favorites") },
|
||||
label = { Text(stringResource(Res.string.contractors_favorites)) },
|
||||
leadingIcon = { Icon(Icons.Default.Star, null, modifier = Modifier.size(16.dp)) }
|
||||
)
|
||||
}
|
||||
@@ -270,7 +272,7 @@ fun ContractorsScreen(
|
||||
onRetry = {
|
||||
viewModel.loadContractors()
|
||||
},
|
||||
errorTitle = "Failed to Load Contractors",
|
||||
errorTitle = stringResource(Res.string.contractors_failed_to_load),
|
||||
loadingContent = {
|
||||
if (!isRefreshing) {
|
||||
CircularProgressIndicator(color = MaterialTheme.colorScheme.primary)
|
||||
@@ -296,14 +298,14 @@ fun ContractorsScreen(
|
||||
)
|
||||
Text(
|
||||
if (searchQuery.isNotEmpty() || selectedFilter != null || showFavoritesOnly)
|
||||
"No contractors found"
|
||||
stringResource(Res.string.contractors_no_results)
|
||||
else
|
||||
"No contractors yet",
|
||||
stringResource(Res.string.contractors_empty_title),
|
||||
color = MaterialTheme.colorScheme.onSurfaceVariant
|
||||
)
|
||||
if (searchQuery.isEmpty() && selectedFilter == null && !showFavoritesOnly) {
|
||||
Text(
|
||||
"Add your first contractor to get started",
|
||||
stringResource(Res.string.contractors_empty_subtitle_first),
|
||||
color = MaterialTheme.colorScheme.onSurfaceVariant,
|
||||
style = MaterialTheme.typography.bodySmall
|
||||
)
|
||||
@@ -353,13 +355,13 @@ fun ContractorsScreen(
|
||||
if (showUpgradeDialog) {
|
||||
AlertDialog(
|
||||
onDismissRequest = { showUpgradeDialog = false },
|
||||
title = { Text("Upgrade Required") },
|
||||
title = { Text(stringResource(Res.string.contractors_upgrade_required)) },
|
||||
text = {
|
||||
Text("You've reached the maximum number of contractors for your current plan. Upgrade to Pro for unlimited contractors.")
|
||||
Text(stringResource(Res.string.contractors_upgrade_message))
|
||||
},
|
||||
confirmButton = {
|
||||
TextButton(onClick = { showUpgradeDialog = false }) {
|
||||
Text("OK")
|
||||
Text(stringResource(Res.string.common_ok))
|
||||
}
|
||||
}
|
||||
)
|
||||
@@ -422,7 +424,7 @@ fun ContractorCard(
|
||||
Spacer(modifier = Modifier.width(4.dp))
|
||||
Icon(
|
||||
Icons.Default.Star,
|
||||
contentDescription = "Favorite",
|
||||
contentDescription = stringResource(Res.string.contractors_favorite),
|
||||
modifier = Modifier.size(16.dp),
|
||||
tint = MaterialTheme.colorScheme.tertiary
|
||||
)
|
||||
@@ -490,7 +492,7 @@ fun ContractorCard(
|
||||
)
|
||||
Spacer(modifier = Modifier.width(4.dp))
|
||||
Text(
|
||||
text = "${contractor.taskCount} tasks",
|
||||
text = stringResource(Res.string.contractors_tasks_count, contractor.taskCount),
|
||||
style = MaterialTheme.typography.bodySmall,
|
||||
color = MaterialTheme.colorScheme.onSurfaceVariant
|
||||
)
|
||||
@@ -505,7 +507,7 @@ fun ContractorCard(
|
||||
) {
|
||||
Icon(
|
||||
if (contractor.isFavorite) Icons.Default.Star else Icons.Default.StarOutline,
|
||||
contentDescription = if (contractor.isFavorite) "Remove from favorites" else "Add to favorites",
|
||||
contentDescription = if (contractor.isFavorite) stringResource(Res.string.contractors_remove_from_favorites) else stringResource(Res.string.contractors_add_to_favorites),
|
||||
tint = if (contractor.isFavorite) MaterialTheme.colorScheme.tertiary else MaterialTheme.colorScheme.onSurfaceVariant
|
||||
)
|
||||
}
|
||||
@@ -513,7 +515,7 @@ fun ContractorCard(
|
||||
// Arrow icon
|
||||
Icon(
|
||||
Icons.Default.ChevronRight,
|
||||
contentDescription = "View details",
|
||||
contentDescription = stringResource(Res.string.contractors_view_details),
|
||||
tint = MaterialTheme.colorScheme.onSurfaceVariant
|
||||
)
|
||||
}
|
||||
|
||||
@@ -35,6 +35,8 @@ import coil3.compose.SubcomposeAsyncImage
|
||||
import coil3.compose.SubcomposeAsyncImageContent
|
||||
import coil3.compose.AsyncImagePainter
|
||||
import com.example.casera.util.DateUtils
|
||||
import casera.composeapp.generated.resources.*
|
||||
import org.jetbrains.compose.resources.stringResource
|
||||
|
||||
@OptIn(ExperimentalMaterial3Api::class)
|
||||
@Composable
|
||||
@@ -57,7 +59,7 @@ fun DocumentDetailScreen(
|
||||
// Handle errors for document deletion
|
||||
deleteState.HandleErrors(
|
||||
onRetry = { documentViewModel.deleteDocument(documentId) },
|
||||
errorTitle = "Failed to Delete Document"
|
||||
errorTitle = stringResource(Res.string.documents_failed_to_delete)
|
||||
)
|
||||
|
||||
// Handle successful deletion
|
||||
@@ -71,20 +73,20 @@ fun DocumentDetailScreen(
|
||||
Scaffold(
|
||||
topBar = {
|
||||
TopAppBar(
|
||||
title = { Text("Document Details", fontWeight = FontWeight.Bold) },
|
||||
title = { Text(stringResource(Res.string.documents_details), fontWeight = FontWeight.Bold) },
|
||||
navigationIcon = {
|
||||
IconButton(onClick = onNavigateBack) {
|
||||
Icon(Icons.Default.ArrowBack, "Back")
|
||||
Icon(Icons.Default.ArrowBack, stringResource(Res.string.common_back))
|
||||
}
|
||||
},
|
||||
actions = {
|
||||
when (documentState) {
|
||||
is ApiResult.Success -> {
|
||||
IconButton(onClick = { onNavigateToEdit(documentId) }) {
|
||||
Icon(Icons.Default.Edit, "Edit")
|
||||
Icon(Icons.Default.Edit, stringResource(Res.string.common_edit))
|
||||
}
|
||||
IconButton(onClick = { showDeleteDialog = true }) {
|
||||
Icon(Icons.Default.Delete, "Delete", tint = Color.Red)
|
||||
Icon(Icons.Default.Delete, stringResource(Res.string.common_delete), tint = Color.Red)
|
||||
}
|
||||
}
|
||||
else -> {}
|
||||
@@ -101,7 +103,7 @@ fun DocumentDetailScreen(
|
||||
ApiResultHandler(
|
||||
state = documentState,
|
||||
onRetry = { documentViewModel.loadDocumentDetail(documentId) },
|
||||
errorTitle = "Failed to Load Document"
|
||||
errorTitle = stringResource(Res.string.documents_failed_to_load)
|
||||
) { document ->
|
||||
Column(
|
||||
modifier = Modifier
|
||||
@@ -136,16 +138,16 @@ fun DocumentDetailScreen(
|
||||
) {
|
||||
Column {
|
||||
Text(
|
||||
"Status",
|
||||
stringResource(Res.string.documents_status),
|
||||
style = MaterialTheme.typography.labelMedium,
|
||||
color = Color.Gray
|
||||
)
|
||||
Text(
|
||||
when {
|
||||
!document.isActive -> "Inactive"
|
||||
daysUntilExpiration < 0 -> "Expired"
|
||||
daysUntilExpiration < 30 -> "Expiring soon"
|
||||
else -> "Active"
|
||||
!document.isActive -> stringResource(Res.string.documents_inactive)
|
||||
daysUntilExpiration < 0 -> stringResource(Res.string.documents_expired)
|
||||
daysUntilExpiration < 30 -> stringResource(Res.string.documents_expiring_soon)
|
||||
else -> stringResource(Res.string.documents_active)
|
||||
},
|
||||
style = MaterialTheme.typography.titleLarge,
|
||||
fontWeight = FontWeight.Bold,
|
||||
@@ -155,7 +157,7 @@ fun DocumentDetailScreen(
|
||||
if (document.isActive && daysUntilExpiration >= 0) {
|
||||
Column(horizontalAlignment = Alignment.End) {
|
||||
Text(
|
||||
"Days Remaining",
|
||||
stringResource(Res.string.documents_days_remaining),
|
||||
style = MaterialTheme.typography.labelMedium,
|
||||
color = Color.Gray
|
||||
)
|
||||
@@ -178,19 +180,19 @@ fun DocumentDetailScreen(
|
||||
verticalArrangement = Arrangement.spacedBy(12.dp)
|
||||
) {
|
||||
Text(
|
||||
"Basic Information",
|
||||
stringResource(Res.string.documents_basic_info),
|
||||
style = MaterialTheme.typography.titleMedium,
|
||||
fontWeight = FontWeight.Bold
|
||||
)
|
||||
Divider()
|
||||
|
||||
DetailRow("Title", document.title)
|
||||
DetailRow("Type", DocumentType.fromValue(document.documentType).displayName)
|
||||
DetailRow(stringResource(Res.string.documents_title_label), document.title)
|
||||
DetailRow(stringResource(Res.string.documents_type_label), DocumentType.fromValue(document.documentType).displayName)
|
||||
document.category?.let {
|
||||
DetailRow("Category", DocumentCategory.fromValue(it).displayName)
|
||||
DetailRow(stringResource(Res.string.documents_all_categories), DocumentCategory.fromValue(it).displayName)
|
||||
}
|
||||
document.description?.let {
|
||||
DetailRow("Description", it)
|
||||
DetailRow(stringResource(Res.string.documents_description_label), it)
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -205,17 +207,17 @@ fun DocumentDetailScreen(
|
||||
verticalArrangement = Arrangement.spacedBy(12.dp)
|
||||
) {
|
||||
Text(
|
||||
"Item Details",
|
||||
stringResource(Res.string.documents_item_details),
|
||||
style = MaterialTheme.typography.titleMedium,
|
||||
fontWeight = FontWeight.Bold
|
||||
)
|
||||
Divider()
|
||||
|
||||
document.itemName?.let { DetailRow("Item Name", it) }
|
||||
document.modelNumber?.let { DetailRow("Model Number", it) }
|
||||
document.serialNumber?.let { DetailRow("Serial Number", it) }
|
||||
document.provider?.let { DetailRow("Provider", it) }
|
||||
document.providerContact?.let { DetailRow("Provider Contact", it) }
|
||||
document.itemName?.let { DetailRow(stringResource(Res.string.documents_item_name), it) }
|
||||
document.modelNumber?.let { DetailRow(stringResource(Res.string.documents_model_number), it) }
|
||||
document.serialNumber?.let { DetailRow(stringResource(Res.string.documents_serial_number), it) }
|
||||
document.provider?.let { DetailRow(stringResource(Res.string.documents_provider), it) }
|
||||
document.providerContact?.let { DetailRow(stringResource(Res.string.documents_provider_contact), it) }
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -230,15 +232,15 @@ fun DocumentDetailScreen(
|
||||
verticalArrangement = Arrangement.spacedBy(12.dp)
|
||||
) {
|
||||
Text(
|
||||
"Claim Information",
|
||||
stringResource(Res.string.documents_claim_info),
|
||||
style = MaterialTheme.typography.titleMedium,
|
||||
fontWeight = FontWeight.Bold
|
||||
)
|
||||
Divider()
|
||||
|
||||
document.claimPhone?.let { DetailRow("Claim Phone", it) }
|
||||
document.claimEmail?.let { DetailRow("Claim Email", it) }
|
||||
document.claimWebsite?.let { DetailRow("Claim Website", it) }
|
||||
document.claimPhone?.let { DetailRow(stringResource(Res.string.documents_claim_phone), it) }
|
||||
document.claimEmail?.let { DetailRow(stringResource(Res.string.documents_claim_email), it) }
|
||||
document.claimWebsite?.let { DetailRow(stringResource(Res.string.documents_claim_website), it) }
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -252,15 +254,15 @@ fun DocumentDetailScreen(
|
||||
verticalArrangement = Arrangement.spacedBy(12.dp)
|
||||
) {
|
||||
Text(
|
||||
"Important Dates",
|
||||
stringResource(Res.string.documents_important_dates),
|
||||
style = MaterialTheme.typography.titleMedium,
|
||||
fontWeight = FontWeight.Bold
|
||||
)
|
||||
Divider()
|
||||
|
||||
document.purchaseDate?.let { DetailRow("Purchase Date", DateUtils.formatDateMedium(it)) }
|
||||
document.startDate?.let { DetailRow("Start Date", DateUtils.formatDateMedium(it)) }
|
||||
document.endDate?.let { DetailRow("End Date", DateUtils.formatDateMedium(it)) }
|
||||
document.purchaseDate?.let { DetailRow(stringResource(Res.string.documents_purchase_date), DateUtils.formatDateMedium(it)) }
|
||||
document.startDate?.let { DetailRow(stringResource(Res.string.documents_start_date), DateUtils.formatDateMedium(it)) }
|
||||
document.endDate?.let { DetailRow(stringResource(Res.string.documents_end_date), DateUtils.formatDateMedium(it)) }
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -272,15 +274,15 @@ fun DocumentDetailScreen(
|
||||
verticalArrangement = Arrangement.spacedBy(12.dp)
|
||||
) {
|
||||
Text(
|
||||
"Associations",
|
||||
stringResource(Res.string.documents_associations),
|
||||
style = MaterialTheme.typography.titleMedium,
|
||||
fontWeight = FontWeight.Bold
|
||||
)
|
||||
Divider()
|
||||
|
||||
document.residenceAddress?.let { DetailRow("Residence", it) }
|
||||
document.contractorName?.let { DetailRow("Contractor", it) }
|
||||
document.contractorPhone?.let { DetailRow("Contractor Phone", it) }
|
||||
document.residenceAddress?.let { DetailRow(stringResource(Res.string.documents_residence), it) }
|
||||
document.contractorName?.let { DetailRow(stringResource(Res.string.documents_contractor), it) }
|
||||
document.contractorPhone?.let { DetailRow(stringResource(Res.string.documents_contractor_phone), it) }
|
||||
}
|
||||
}
|
||||
|
||||
@@ -292,14 +294,14 @@ fun DocumentDetailScreen(
|
||||
verticalArrangement = Arrangement.spacedBy(12.dp)
|
||||
) {
|
||||
Text(
|
||||
"Additional Information",
|
||||
stringResource(Res.string.documents_additional_info),
|
||||
style = MaterialTheme.typography.titleMedium,
|
||||
fontWeight = FontWeight.Bold
|
||||
)
|
||||
Divider()
|
||||
|
||||
document.tags?.let { DetailRow("Tags", it) }
|
||||
document.notes?.let { DetailRow("Notes", it) }
|
||||
document.tags?.let { DetailRow(stringResource(Res.string.documents_tags), it) }
|
||||
document.notes?.let { DetailRow(stringResource(Res.string.documents_notes), it) }
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -312,7 +314,7 @@ fun DocumentDetailScreen(
|
||||
verticalArrangement = Arrangement.spacedBy(12.dp)
|
||||
) {
|
||||
Text(
|
||||
"Images (${document.images.size})",
|
||||
stringResource(Res.string.documents_images, document.images.size),
|
||||
style = MaterialTheme.typography.titleMedium,
|
||||
fontWeight = FontWeight.Bold
|
||||
)
|
||||
@@ -369,15 +371,15 @@ fun DocumentDetailScreen(
|
||||
verticalArrangement = Arrangement.spacedBy(12.dp)
|
||||
) {
|
||||
Text(
|
||||
"Attached File",
|
||||
stringResource(Res.string.documents_attached_file),
|
||||
style = MaterialTheme.typography.titleMedium,
|
||||
fontWeight = FontWeight.Bold
|
||||
)
|
||||
Divider()
|
||||
|
||||
document.fileType?.let { DetailRow("File Type", it) }
|
||||
document.fileType?.let { DetailRow(stringResource(Res.string.documents_file_type), it) }
|
||||
document.fileSize?.let {
|
||||
DetailRow("File Size", formatFileSize(it))
|
||||
DetailRow(stringResource(Res.string.documents_file_size), formatFileSize(it))
|
||||
}
|
||||
|
||||
Button(
|
||||
@@ -386,7 +388,7 @@ fun DocumentDetailScreen(
|
||||
) {
|
||||
Icon(Icons.Default.Download, null)
|
||||
Spacer(modifier = Modifier.width(8.dp))
|
||||
Text("Download File")
|
||||
Text(stringResource(Res.string.documents_download_file))
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -399,15 +401,15 @@ fun DocumentDetailScreen(
|
||||
verticalArrangement = Arrangement.spacedBy(12.dp)
|
||||
) {
|
||||
Text(
|
||||
"Metadata",
|
||||
stringResource(Res.string.documents_metadata),
|
||||
style = MaterialTheme.typography.titleMedium,
|
||||
fontWeight = FontWeight.Bold
|
||||
)
|
||||
Divider()
|
||||
|
||||
document.uploadedByUsername?.let { DetailRow("Uploaded By", it) }
|
||||
document.createdAt?.let { DetailRow("Created", DateUtils.formatDateMedium(it)) }
|
||||
document.updatedAt?.let { DetailRow("Updated", DateUtils.formatDateMedium(it)) }
|
||||
document.uploadedByUsername?.let { DetailRow(stringResource(Res.string.documents_uploaded_by), it) }
|
||||
document.createdAt?.let { DetailRow(stringResource(Res.string.documents_created), DateUtils.formatDateMedium(it)) }
|
||||
document.updatedAt?.let { DetailRow(stringResource(Res.string.documents_updated), DateUtils.formatDateMedium(it)) }
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -419,8 +421,8 @@ fun DocumentDetailScreen(
|
||||
if (showDeleteDialog) {
|
||||
AlertDialog(
|
||||
onDismissRequest = { showDeleteDialog = false },
|
||||
title = { Text("Delete Document") },
|
||||
text = { Text("Are you sure you want to delete this document? This action cannot be undone.") },
|
||||
title = { Text(stringResource(Res.string.documents_delete)) },
|
||||
text = { Text(stringResource(Res.string.documents_delete_warning)) },
|
||||
confirmButton = {
|
||||
TextButton(
|
||||
onClick = {
|
||||
@@ -428,12 +430,12 @@ fun DocumentDetailScreen(
|
||||
showDeleteDialog = false
|
||||
}
|
||||
) {
|
||||
Text("Delete", color = Color.Red)
|
||||
Text(stringResource(Res.string.common_delete), color = Color.Red)
|
||||
}
|
||||
},
|
||||
dismissButton = {
|
||||
TextButton(onClick = { showDeleteDialog = false }) {
|
||||
Text("Cancel")
|
||||
Text(stringResource(Res.string.common_cancel))
|
||||
}
|
||||
}
|
||||
)
|
||||
|
||||
@@ -16,6 +16,8 @@ import com.example.casera.ui.subscription.UpgradeFeatureScreen
|
||||
import com.example.casera.utils.SubscriptionHelper
|
||||
import com.example.casera.viewmodel.DocumentViewModel
|
||||
import com.example.casera.models.*
|
||||
import casera.composeapp.generated.resources.*
|
||||
import org.jetbrains.compose.resources.stringResource
|
||||
|
||||
enum class DocumentTab {
|
||||
WARRANTIES, DOCUMENTS
|
||||
@@ -73,10 +75,10 @@ fun DocumentsScreen(
|
||||
topBar = {
|
||||
Column {
|
||||
TopAppBar(
|
||||
title = { Text("Documents & Warranties", fontWeight = FontWeight.Bold) },
|
||||
title = { Text(stringResource(Res.string.documents_and_warranties), fontWeight = FontWeight.Bold) },
|
||||
navigationIcon = {
|
||||
IconButton(onClick = onNavigateBack) {
|
||||
Icon(Icons.Default.ArrowBack, "Back")
|
||||
Icon(Icons.Default.ArrowBack, stringResource(Res.string.common_back))
|
||||
}
|
||||
},
|
||||
actions = {
|
||||
@@ -85,7 +87,7 @@ fun DocumentsScreen(
|
||||
IconButton(onClick = { showActiveOnly = !showActiveOnly }) {
|
||||
Icon(
|
||||
if (showActiveOnly) Icons.Default.CheckCircle else Icons.Default.CheckCircleOutline,
|
||||
"Filter active",
|
||||
stringResource(Res.string.documents_filter_active),
|
||||
tint = if (showActiveOnly) MaterialTheme.colorScheme.secondary else LocalContentColor.current
|
||||
)
|
||||
}
|
||||
@@ -96,7 +98,7 @@ fun DocumentsScreen(
|
||||
IconButton(onClick = { showFiltersMenu = true }) {
|
||||
Icon(
|
||||
Icons.Default.FilterList,
|
||||
"Filters",
|
||||
stringResource(Res.string.documents_filters),
|
||||
tint = if (selectedCategory != null || selectedDocType != null)
|
||||
MaterialTheme.colorScheme.primary else LocalContentColor.current
|
||||
)
|
||||
@@ -108,7 +110,7 @@ fun DocumentsScreen(
|
||||
) {
|
||||
if (selectedTab == DocumentTab.WARRANTIES) {
|
||||
DropdownMenuItem(
|
||||
text = { Text("All Categories") },
|
||||
text = { Text(stringResource(Res.string.documents_all_categories)) },
|
||||
onClick = {
|
||||
selectedCategory = null
|
||||
showFiltersMenu = false
|
||||
@@ -126,7 +128,7 @@ fun DocumentsScreen(
|
||||
}
|
||||
} else {
|
||||
DropdownMenuItem(
|
||||
text = { Text("All Types") },
|
||||
text = { Text(stringResource(Res.string.documents_all_types)) },
|
||||
onClick = {
|
||||
selectedDocType = null
|
||||
showFiltersMenu = false
|
||||
@@ -153,13 +155,13 @@ fun DocumentsScreen(
|
||||
Tab(
|
||||
selected = selectedTab == DocumentTab.WARRANTIES,
|
||||
onClick = { selectedTab = DocumentTab.WARRANTIES },
|
||||
text = { Text("Warranties") },
|
||||
text = { Text(stringResource(Res.string.documents_warranties_tab)) },
|
||||
icon = { Icon(Icons.Default.VerifiedUser, null) }
|
||||
)
|
||||
Tab(
|
||||
selected = selectedTab == DocumentTab.DOCUMENTS,
|
||||
onClick = { selectedTab = DocumentTab.DOCUMENTS },
|
||||
text = { Text("Documents") },
|
||||
text = { Text(stringResource(Res.string.documents_documents_tab)) },
|
||||
icon = { Icon(Icons.Default.Description, null) }
|
||||
)
|
||||
}
|
||||
@@ -183,7 +185,7 @@ fun DocumentsScreen(
|
||||
},
|
||||
containerColor = MaterialTheme.colorScheme.primary
|
||||
) {
|
||||
Icon(Icons.Default.Add, "Add")
|
||||
Icon(Icons.Default.Add, stringResource(Res.string.documents_add_button))
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -243,13 +245,13 @@ fun DocumentsScreen(
|
||||
if (showUpgradeDialog) {
|
||||
AlertDialog(
|
||||
onDismissRequest = { showUpgradeDialog = false },
|
||||
title = { Text("Upgrade Required") },
|
||||
title = { Text(stringResource(Res.string.documents_upgrade_required)) },
|
||||
text = {
|
||||
Text("You've reached the maximum number of documents for your current plan. Upgrade to Pro for unlimited documents.")
|
||||
Text(stringResource(Res.string.documents_upgrade_message))
|
||||
},
|
||||
confirmButton = {
|
||||
TextButton(onClick = { showUpgradeDialog = false }) {
|
||||
Text("OK")
|
||||
Text(stringResource(Res.string.common_ok))
|
||||
}
|
||||
}
|
||||
)
|
||||
|
||||
@@ -17,6 +17,8 @@ import com.example.casera.viewmodel.ResidenceViewModel
|
||||
import com.example.casera.repository.LookupsRepository
|
||||
import com.example.casera.models.*
|
||||
import com.example.casera.network.ApiResult
|
||||
import casera.composeapp.generated.resources.*
|
||||
import org.jetbrains.compose.resources.stringResource
|
||||
|
||||
@OptIn(ExperimentalMaterial3Api::class)
|
||||
@Composable
|
||||
@@ -53,7 +55,7 @@ fun EditTaskScreen(
|
||||
// Handle errors for task update
|
||||
updateTaskState.HandleErrors(
|
||||
onRetry = { /* Retry handled in UI */ },
|
||||
errorTitle = "Failed to Update Task"
|
||||
errorTitle = stringResource(Res.string.tasks_failed_to_update)
|
||||
)
|
||||
|
||||
// Handle update state changes
|
||||
@@ -67,18 +69,21 @@ fun EditTaskScreen(
|
||||
}
|
||||
}
|
||||
|
||||
val titleRequiredError = stringResource(Res.string.tasks_title_error)
|
||||
val dueDateRequiredError = stringResource(Res.string.tasks_due_date_error)
|
||||
|
||||
fun validateForm(): Boolean {
|
||||
var isValid = true
|
||||
|
||||
if (title.isBlank()) {
|
||||
titleError = "Title is required"
|
||||
titleError = titleRequiredError
|
||||
isValid = false
|
||||
} else {
|
||||
titleError = ""
|
||||
}
|
||||
|
||||
if (dueDate.isNullOrBlank()) {
|
||||
dueDateError = "Due date is required"
|
||||
dueDateError = dueDateRequiredError
|
||||
isValid = false
|
||||
} else {
|
||||
dueDateError = ""
|
||||
@@ -90,10 +95,10 @@ fun EditTaskScreen(
|
||||
Scaffold(
|
||||
topBar = {
|
||||
TopAppBar(
|
||||
title = { Text("Edit Task") },
|
||||
title = { Text(stringResource(Res.string.tasks_edit_title)) },
|
||||
navigationIcon = {
|
||||
IconButton(onClick = onNavigateBack) {
|
||||
Icon(Icons.Default.ArrowBack, contentDescription = "Back")
|
||||
Icon(Icons.Default.ArrowBack, contentDescription = stringResource(Res.string.common_back))
|
||||
}
|
||||
}
|
||||
)
|
||||
@@ -109,7 +114,7 @@ fun EditTaskScreen(
|
||||
) {
|
||||
// Required fields section
|
||||
Text(
|
||||
text = "Task Details",
|
||||
text = stringResource(Res.string.tasks_details),
|
||||
style = MaterialTheme.typography.titleMedium,
|
||||
color = MaterialTheme.colorScheme.primary
|
||||
)
|
||||
@@ -117,7 +122,7 @@ fun EditTaskScreen(
|
||||
OutlinedTextField(
|
||||
value = title,
|
||||
onValueChange = { title = it },
|
||||
label = { Text("Title *") },
|
||||
label = { Text(stringResource(Res.string.tasks_title_required)) },
|
||||
modifier = Modifier.fillMaxWidth(),
|
||||
isError = titleError.isNotEmpty(),
|
||||
supportingText = if (titleError.isNotEmpty()) {
|
||||
@@ -128,7 +133,7 @@ fun EditTaskScreen(
|
||||
OutlinedTextField(
|
||||
value = description,
|
||||
onValueChange = { description = it },
|
||||
label = { Text("Description") },
|
||||
label = { Text(stringResource(Res.string.tasks_description_label)) },
|
||||
modifier = Modifier.fillMaxWidth(),
|
||||
minLines = 3,
|
||||
maxLines = 5
|
||||
@@ -143,7 +148,7 @@ fun EditTaskScreen(
|
||||
value = selectedCategory?.name?.replaceFirstChar { it.uppercase() } ?: "",
|
||||
onValueChange = {},
|
||||
readOnly = true,
|
||||
label = { Text("Category *") },
|
||||
label = { Text(stringResource(Res.string.tasks_category_required)) },
|
||||
trailingIcon = { ExposedDropdownMenuDefaults.TrailingIcon(expanded = categoryExpanded) },
|
||||
modifier = Modifier
|
||||
.fillMaxWidth()
|
||||
@@ -175,7 +180,7 @@ fun EditTaskScreen(
|
||||
value = selectedFrequency?.name?.replaceFirstChar { it.uppercase() } ?: "",
|
||||
onValueChange = {},
|
||||
readOnly = true,
|
||||
label = { Text("Frequency *") },
|
||||
label = { Text(stringResource(Res.string.tasks_frequency_required)) },
|
||||
trailingIcon = { ExposedDropdownMenuDefaults.TrailingIcon(expanded = frequencyExpanded) },
|
||||
modifier = Modifier
|
||||
.fillMaxWidth()
|
||||
@@ -207,7 +212,7 @@ fun EditTaskScreen(
|
||||
value = selectedPriority?.name?.replaceFirstChar { it.uppercase() } ?: "",
|
||||
onValueChange = {},
|
||||
readOnly = true,
|
||||
label = { Text("Priority *") },
|
||||
label = { Text(stringResource(Res.string.tasks_priority_required)) },
|
||||
trailingIcon = { ExposedDropdownMenuDefaults.TrailingIcon(expanded = priorityExpanded) },
|
||||
modifier = Modifier
|
||||
.fillMaxWidth()
|
||||
@@ -239,7 +244,7 @@ fun EditTaskScreen(
|
||||
value = selectedStatus?.name?.replaceFirstChar { it.uppercase() } ?: "",
|
||||
onValueChange = {},
|
||||
readOnly = true,
|
||||
label = { Text("Status *") },
|
||||
label = { Text(stringResource(Res.string.tasks_status_label)) },
|
||||
trailingIcon = { ExposedDropdownMenuDefaults.TrailingIcon(expanded = statusExpanded) },
|
||||
modifier = Modifier
|
||||
.fillMaxWidth()
|
||||
@@ -265,19 +270,19 @@ fun EditTaskScreen(
|
||||
OutlinedTextField(
|
||||
value = dueDate,
|
||||
onValueChange = { dueDate = it },
|
||||
label = { Text("Due Date (YYYY-MM-DD) *") },
|
||||
label = { Text(stringResource(Res.string.tasks_due_date_required)) },
|
||||
modifier = Modifier.fillMaxWidth(),
|
||||
isError = dueDateError.isNotEmpty(),
|
||||
supportingText = if (dueDateError.isNotEmpty()) {
|
||||
{ Text(dueDateError) }
|
||||
} else null,
|
||||
placeholder = { Text("2025-01-31") }
|
||||
placeholder = { Text(stringResource(Res.string.tasks_due_date_placeholder)) }
|
||||
)
|
||||
|
||||
OutlinedTextField(
|
||||
value = estimatedCost,
|
||||
onValueChange = { estimatedCost = it },
|
||||
label = { Text("Estimated Cost") },
|
||||
label = { Text(stringResource(Res.string.tasks_estimated_cost_label)) },
|
||||
keyboardOptions = KeyboardOptions(keyboardType = KeyboardType.Decimal),
|
||||
modifier = Modifier.fillMaxWidth(),
|
||||
prefix = { Text("$") }
|
||||
@@ -325,7 +330,7 @@ fun EditTaskScreen(
|
||||
color = MaterialTheme.colorScheme.onPrimary
|
||||
)
|
||||
} else {
|
||||
Text("Update Task")
|
||||
Text(stringResource(Res.string.tasks_update))
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -18,6 +18,8 @@ import com.example.casera.ui.components.auth.AuthHeader
|
||||
import com.example.casera.ui.components.common.ErrorCard
|
||||
import com.example.casera.viewmodel.PasswordResetViewModel
|
||||
import com.example.casera.network.ApiResult
|
||||
import casera.composeapp.generated.resources.*
|
||||
import org.jetbrains.compose.resources.stringResource
|
||||
|
||||
@OptIn(ExperimentalMaterial3Api::class)
|
||||
@Composable
|
||||
@@ -60,10 +62,10 @@ fun ForgotPasswordScreen(
|
||||
Scaffold(
|
||||
topBar = {
|
||||
TopAppBar(
|
||||
title = { Text("Forgot Password") },
|
||||
title = { Text(stringResource(Res.string.auth_forgot_title)) },
|
||||
navigationIcon = {
|
||||
IconButton(onClick = onNavigateBack) {
|
||||
Icon(Icons.Default.ArrowBack, contentDescription = "Back")
|
||||
Icon(Icons.Default.ArrowBack, contentDescription = stringResource(Res.string.common_back))
|
||||
}
|
||||
},
|
||||
colors = TopAppBarDefaults.topAppBarColors(
|
||||
@@ -98,8 +100,8 @@ fun ForgotPasswordScreen(
|
||||
) {
|
||||
AuthHeader(
|
||||
icon = Icons.Default.Key,
|
||||
title = "Forgot Password?",
|
||||
subtitle = "Enter your email address and we'll send you a code to reset your password"
|
||||
title = stringResource(Res.string.auth_forgot_title),
|
||||
subtitle = stringResource(Res.string.auth_forgot_subtitle)
|
||||
)
|
||||
|
||||
Spacer(modifier = Modifier.height(8.dp))
|
||||
@@ -110,7 +112,7 @@ fun ForgotPasswordScreen(
|
||||
email = it
|
||||
viewModel.resetForgotPasswordState()
|
||||
},
|
||||
label = { Text("Email Address") },
|
||||
label = { Text(stringResource(Res.string.auth_forgot_email_label)) },
|
||||
leadingIcon = {
|
||||
Icon(Icons.Default.Email, contentDescription = null)
|
||||
},
|
||||
@@ -178,7 +180,7 @@ fun ForgotPasswordScreen(
|
||||
Icon(Icons.Default.Send, contentDescription = null)
|
||||
Spacer(modifier = Modifier.width(8.dp))
|
||||
Text(
|
||||
"Send Reset Code",
|
||||
stringResource(Res.string.auth_forgot_button),
|
||||
style = MaterialTheme.typography.titleMedium,
|
||||
fontWeight = FontWeight.SemiBold
|
||||
)
|
||||
|
||||
@@ -19,6 +19,8 @@ import com.example.casera.ui.components.HandleErrors
|
||||
import com.example.casera.ui.theme.AppRadius
|
||||
import com.example.casera.viewmodel.ResidenceViewModel
|
||||
import com.example.casera.network.ApiResult
|
||||
import casera.composeapp.generated.resources.*
|
||||
import org.jetbrains.compose.resources.stringResource
|
||||
|
||||
@OptIn(ExperimentalMaterial3Api::class)
|
||||
@Composable
|
||||
@@ -37,13 +39,13 @@ fun HomeScreen(
|
||||
// Handle errors for loading summary
|
||||
summaryState.HandleErrors(
|
||||
onRetry = { viewModel.loadMyResidences() },
|
||||
errorTitle = "Failed to Load Summary"
|
||||
errorTitle = stringResource(Res.string.home_failed_to_load)
|
||||
)
|
||||
|
||||
Scaffold(
|
||||
topBar = {
|
||||
TopAppBar(
|
||||
title = { Text("myCrib", style = MaterialTheme.typography.headlineSmall) },
|
||||
title = { Text(stringResource(Res.string.app_name), style = MaterialTheme.typography.headlineSmall) },
|
||||
colors = TopAppBarDefaults.topAppBarColors(
|
||||
containerColor = MaterialTheme.colorScheme.background
|
||||
),
|
||||
@@ -51,7 +53,7 @@ fun HomeScreen(
|
||||
IconButton(onClick = onLogout) {
|
||||
Icon(
|
||||
Icons.Default.ExitToApp,
|
||||
contentDescription = "Logout",
|
||||
contentDescription = stringResource(Res.string.home_logout),
|
||||
tint = MaterialTheme.colorScheme.primary
|
||||
)
|
||||
}
|
||||
@@ -72,12 +74,12 @@ fun HomeScreen(
|
||||
modifier = Modifier.padding(vertical = 8.dp)
|
||||
) {
|
||||
Text(
|
||||
text = "Welcome back",
|
||||
text = stringResource(Res.string.home_welcome),
|
||||
style = MaterialTheme.typography.titleLarge,
|
||||
color = MaterialTheme.colorScheme.onSurfaceVariant
|
||||
)
|
||||
Text(
|
||||
text = "Manage your properties",
|
||||
text = stringResource(Res.string.home_manage_properties),
|
||||
style = MaterialTheme.typography.bodyMedium,
|
||||
color = MaterialTheme.colorScheme.onSurfaceVariant
|
||||
)
|
||||
@@ -127,12 +129,12 @@ fun HomeScreen(
|
||||
}
|
||||
Column {
|
||||
Text(
|
||||
text = "Overview",
|
||||
text = stringResource(Res.string.home_overview),
|
||||
style = MaterialTheme.typography.titleMedium,
|
||||
color = MaterialTheme.colorScheme.onSurface
|
||||
)
|
||||
Text(
|
||||
text = "Your property stats",
|
||||
text = stringResource(Res.string.home_property_stats),
|
||||
style = MaterialTheme.typography.bodySmall,
|
||||
color = MaterialTheme.colorScheme.onSurfaceVariant
|
||||
)
|
||||
@@ -147,7 +149,7 @@ fun HomeScreen(
|
||||
) {
|
||||
StatItem(
|
||||
value = "${summary.residences.size}",
|
||||
label = "Properties",
|
||||
label = stringResource(Res.string.home_properties),
|
||||
color = Color(0xFF3B82F6)
|
||||
)
|
||||
Divider(
|
||||
@@ -158,7 +160,7 @@ fun HomeScreen(
|
||||
)
|
||||
StatItem(
|
||||
value = "${summary.summary.totalTasks}",
|
||||
label = "Total Tasks",
|
||||
label = stringResource(Res.string.home_total_tasks),
|
||||
color = Color(0xFF8B5CF6)
|
||||
)
|
||||
Divider(
|
||||
@@ -169,7 +171,7 @@ fun HomeScreen(
|
||||
)
|
||||
StatItem(
|
||||
value = "${summary.summary.totalPending}",
|
||||
label = "Pending",
|
||||
label = stringResource(Res.string.home_pending),
|
||||
color = Color(0xFFF59E0B)
|
||||
)
|
||||
}
|
||||
@@ -197,8 +199,8 @@ fun HomeScreen(
|
||||
|
||||
// Residences Card
|
||||
NavigationCard(
|
||||
title = "Properties",
|
||||
subtitle = "Manage your residences",
|
||||
title = stringResource(Res.string.home_properties),
|
||||
subtitle = stringResource(Res.string.home_manage_residences),
|
||||
icon = Icons.Default.Home,
|
||||
iconColor = Color(0xFF3B82F6),
|
||||
onClick = onNavigateToResidences
|
||||
@@ -206,8 +208,8 @@ fun HomeScreen(
|
||||
|
||||
// Tasks Card
|
||||
NavigationCard(
|
||||
title = "Tasks",
|
||||
subtitle = "View and manage tasks",
|
||||
title = stringResource(Res.string.home_tasks),
|
||||
subtitle = stringResource(Res.string.home_view_manage_tasks),
|
||||
icon = Icons.Default.CheckCircle,
|
||||
iconColor = Color(0xFF10B981),
|
||||
onClick = onNavigateToTasks
|
||||
|
||||
@@ -26,6 +26,8 @@ import com.example.casera.ui.components.auth.AuthHeader
|
||||
import com.example.casera.ui.components.common.ErrorCard
|
||||
import com.example.casera.viewmodel.AuthViewModel
|
||||
import com.example.casera.network.ApiResult
|
||||
import casera.composeapp.generated.resources.*
|
||||
import org.jetbrains.compose.resources.stringResource
|
||||
|
||||
@Composable
|
||||
fun LoginScreen(
|
||||
@@ -42,7 +44,7 @@ fun LoginScreen(
|
||||
// Handle errors for login
|
||||
loginState.HandleErrors(
|
||||
onRetry = { viewModel.login(username, password) },
|
||||
errorTitle = "Login Failed"
|
||||
errorTitle = stringResource(Res.string.auth_login_failed)
|
||||
)
|
||||
|
||||
// Handle login state changes
|
||||
@@ -88,8 +90,8 @@ fun LoginScreen(
|
||||
) {
|
||||
AuthHeader(
|
||||
icon = Icons.Default.Home,
|
||||
title = "myCrib",
|
||||
subtitle = "Manage your properties with ease"
|
||||
title = stringResource(Res.string.app_name),
|
||||
subtitle = stringResource(Res.string.app_tagline)
|
||||
)
|
||||
|
||||
Spacer(modifier = Modifier.height(8.dp))
|
||||
@@ -97,7 +99,7 @@ fun LoginScreen(
|
||||
OutlinedTextField(
|
||||
value = username,
|
||||
onValueChange = { username = it },
|
||||
label = { Text("Username or Email") },
|
||||
label = { Text(stringResource(Res.string.auth_login_username_label)) },
|
||||
leadingIcon = {
|
||||
Icon(Icons.Default.Person, contentDescription = null)
|
||||
},
|
||||
@@ -113,7 +115,7 @@ fun LoginScreen(
|
||||
OutlinedTextField(
|
||||
value = password,
|
||||
onValueChange = { password = it },
|
||||
label = { Text("Password") },
|
||||
label = { Text(stringResource(Res.string.auth_login_password_label)) },
|
||||
leadingIcon = {
|
||||
Icon(Icons.Default.Lock, contentDescription = null)
|
||||
},
|
||||
@@ -121,7 +123,7 @@ fun LoginScreen(
|
||||
IconButton(onClick = { passwordVisible = !passwordVisible }) {
|
||||
Icon(
|
||||
imageVector = if (passwordVisible) Icons.Default.Visibility else Icons.Default.VisibilityOff,
|
||||
contentDescription = if (passwordVisible) "Hide password" else "Show password"
|
||||
contentDescription = if (passwordVisible) stringResource(Res.string.auth_hide_password) else stringResource(Res.string.auth_show_password)
|
||||
)
|
||||
}
|
||||
},
|
||||
@@ -175,7 +177,7 @@ fun LoginScreen(
|
||||
)
|
||||
} else {
|
||||
Text(
|
||||
"Sign In",
|
||||
stringResource(Res.string.auth_login_button),
|
||||
style = MaterialTheme.typography.titleMedium,
|
||||
fontWeight = FontWeight.SemiBold,
|
||||
color = Color.White
|
||||
@@ -189,7 +191,7 @@ fun LoginScreen(
|
||||
modifier = Modifier.fillMaxWidth()
|
||||
) {
|
||||
Text(
|
||||
"Forgot Password?",
|
||||
stringResource(Res.string.auth_forgot_password),
|
||||
style = MaterialTheme.typography.bodyMedium,
|
||||
fontWeight = FontWeight.SemiBold
|
||||
)
|
||||
@@ -200,7 +202,7 @@ fun LoginScreen(
|
||||
modifier = Modifier.fillMaxWidth()
|
||||
) {
|
||||
Text(
|
||||
"Don't have an account? Register",
|
||||
stringResource(Res.string.auth_no_account),
|
||||
style = MaterialTheme.typography.bodyMedium
|
||||
)
|
||||
}
|
||||
|
||||
@@ -15,6 +15,8 @@ import com.example.casera.navigation.*
|
||||
import com.example.casera.repository.LookupsRepository
|
||||
import com.example.casera.models.Residence
|
||||
import com.example.casera.storage.TokenStorage
|
||||
import casera.composeapp.generated.resources.*
|
||||
import org.jetbrains.compose.resources.stringResource
|
||||
|
||||
@Composable
|
||||
fun MainScreen(
|
||||
@@ -35,8 +37,8 @@ fun MainScreen(
|
||||
tonalElevation = 3.dp
|
||||
) {
|
||||
NavigationBarItem(
|
||||
icon = { Icon(Icons.Default.Home, contentDescription = "Residences") },
|
||||
label = { Text("Residences") },
|
||||
icon = { Icon(Icons.Default.Home, contentDescription = stringResource(Res.string.properties_title)) },
|
||||
label = { Text(stringResource(Res.string.properties_title)) },
|
||||
selected = selectedTab == 0,
|
||||
onClick = {
|
||||
selectedTab = 0
|
||||
@@ -53,8 +55,8 @@ fun MainScreen(
|
||||
)
|
||||
)
|
||||
NavigationBarItem(
|
||||
icon = { Icon(Icons.Default.CheckCircle, contentDescription = "Tasks") },
|
||||
label = { Text("Tasks") },
|
||||
icon = { Icon(Icons.Default.CheckCircle, contentDescription = stringResource(Res.string.tasks_title)) },
|
||||
label = { Text(stringResource(Res.string.tasks_title)) },
|
||||
selected = selectedTab == 1,
|
||||
onClick = {
|
||||
selectedTab = 1
|
||||
@@ -71,8 +73,8 @@ fun MainScreen(
|
||||
)
|
||||
)
|
||||
NavigationBarItem(
|
||||
icon = { Icon(Icons.Default.Build, contentDescription = "Contractors") },
|
||||
label = { Text("Contractors") },
|
||||
icon = { Icon(Icons.Default.Build, contentDescription = stringResource(Res.string.contractors_title)) },
|
||||
label = { Text(stringResource(Res.string.contractors_title)) },
|
||||
selected = selectedTab == 2,
|
||||
onClick = {
|
||||
selectedTab = 2
|
||||
@@ -89,8 +91,8 @@ fun MainScreen(
|
||||
)
|
||||
)
|
||||
NavigationBarItem(
|
||||
icon = { Icon(Icons.Default.Description, contentDescription = "Documents") },
|
||||
label = { Text("Documents") },
|
||||
icon = { Icon(Icons.Default.Description, contentDescription = stringResource(Res.string.documents_title)) },
|
||||
label = { Text(stringResource(Res.string.documents_title)) },
|
||||
selected = selectedTab == 3,
|
||||
onClick = {
|
||||
selectedTab = 3
|
||||
|
||||
@@ -17,6 +17,8 @@ import com.example.casera.network.ApiResult
|
||||
import com.example.casera.ui.theme.AppRadius
|
||||
import com.example.casera.ui.theme.AppSpacing
|
||||
import com.example.casera.viewmodel.NotificationPreferencesViewModel
|
||||
import casera.composeapp.generated.resources.*
|
||||
import org.jetbrains.compose.resources.stringResource
|
||||
|
||||
@OptIn(ExperimentalMaterial3Api::class)
|
||||
@Composable
|
||||
@@ -55,10 +57,10 @@ fun NotificationPreferencesScreen(
|
||||
Scaffold(
|
||||
topBar = {
|
||||
TopAppBar(
|
||||
title = { Text("Notifications", fontWeight = FontWeight.SemiBold) },
|
||||
title = { Text(stringResource(Res.string.notifications_title), fontWeight = FontWeight.SemiBold) },
|
||||
navigationIcon = {
|
||||
IconButton(onClick = onNavigateBack) {
|
||||
Icon(Icons.Default.ArrowBack, contentDescription = "Back")
|
||||
Icon(Icons.Default.ArrowBack, contentDescription = stringResource(Res.string.common_back))
|
||||
}
|
||||
},
|
||||
colors = TopAppBarDefaults.topAppBarColors(
|
||||
@@ -98,13 +100,13 @@ fun NotificationPreferencesScreen(
|
||||
)
|
||||
|
||||
Text(
|
||||
"Notification Preferences",
|
||||
stringResource(Res.string.notifications_preferences),
|
||||
style = MaterialTheme.typography.titleLarge,
|
||||
fontWeight = FontWeight.Bold
|
||||
)
|
||||
|
||||
Text(
|
||||
"Choose which notifications you'd like to receive",
|
||||
stringResource(Res.string.notifications_choose),
|
||||
style = MaterialTheme.typography.bodyMedium,
|
||||
color = MaterialTheme.colorScheme.onSurfaceVariant
|
||||
)
|
||||
@@ -157,7 +159,7 @@ fun NotificationPreferencesScreen(
|
||||
onClick = { viewModel.loadPreferences() },
|
||||
modifier = Modifier.fillMaxWidth()
|
||||
) {
|
||||
Text("Retry")
|
||||
Text(stringResource(Res.string.common_retry))
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -166,7 +168,7 @@ fun NotificationPreferencesScreen(
|
||||
is ApiResult.Success, is ApiResult.Idle -> {
|
||||
// Task Notifications Section
|
||||
Text(
|
||||
"Task Notifications",
|
||||
stringResource(Res.string.notifications_task_section),
|
||||
style = MaterialTheme.typography.titleMedium,
|
||||
fontWeight = FontWeight.SemiBold,
|
||||
modifier = Modifier.padding(top = AppSpacing.md)
|
||||
@@ -181,8 +183,8 @@ fun NotificationPreferencesScreen(
|
||||
) {
|
||||
Column {
|
||||
NotificationToggle(
|
||||
title = "Task Due Soon",
|
||||
description = "Reminders for upcoming tasks",
|
||||
title = stringResource(Res.string.notifications_task_due_soon),
|
||||
description = stringResource(Res.string.notifications_task_due_soon_desc),
|
||||
icon = Icons.Default.Schedule,
|
||||
iconTint = MaterialTheme.colorScheme.tertiary,
|
||||
checked = taskDueSoon,
|
||||
@@ -198,8 +200,8 @@ fun NotificationPreferencesScreen(
|
||||
)
|
||||
|
||||
NotificationToggle(
|
||||
title = "Task Overdue",
|
||||
description = "Alerts for overdue tasks",
|
||||
title = stringResource(Res.string.notifications_task_overdue),
|
||||
description = stringResource(Res.string.notifications_task_overdue_desc),
|
||||
icon = Icons.Default.Warning,
|
||||
iconTint = MaterialTheme.colorScheme.error,
|
||||
checked = taskOverdue,
|
||||
@@ -215,8 +217,8 @@ fun NotificationPreferencesScreen(
|
||||
)
|
||||
|
||||
NotificationToggle(
|
||||
title = "Task Completed",
|
||||
description = "When someone completes a task",
|
||||
title = stringResource(Res.string.notifications_task_completed),
|
||||
description = stringResource(Res.string.notifications_task_completed_desc),
|
||||
icon = Icons.Default.CheckCircle,
|
||||
iconTint = MaterialTheme.colorScheme.primary,
|
||||
checked = taskCompleted,
|
||||
@@ -232,8 +234,8 @@ fun NotificationPreferencesScreen(
|
||||
)
|
||||
|
||||
NotificationToggle(
|
||||
title = "Task Assigned",
|
||||
description = "When a task is assigned to you",
|
||||
title = stringResource(Res.string.notifications_task_assigned),
|
||||
description = stringResource(Res.string.notifications_task_assigned_desc),
|
||||
icon = Icons.Default.PersonAdd,
|
||||
iconTint = MaterialTheme.colorScheme.secondary,
|
||||
checked = taskAssigned,
|
||||
@@ -247,7 +249,7 @@ fun NotificationPreferencesScreen(
|
||||
|
||||
// Other Notifications Section
|
||||
Text(
|
||||
"Other Notifications",
|
||||
stringResource(Res.string.notifications_other_section),
|
||||
style = MaterialTheme.typography.titleMedium,
|
||||
fontWeight = FontWeight.SemiBold,
|
||||
modifier = Modifier.padding(top = AppSpacing.md)
|
||||
@@ -262,8 +264,8 @@ fun NotificationPreferencesScreen(
|
||||
) {
|
||||
Column {
|
||||
NotificationToggle(
|
||||
title = "Property Shared",
|
||||
description = "When someone shares a property with you",
|
||||
title = stringResource(Res.string.notifications_property_shared),
|
||||
description = stringResource(Res.string.notifications_property_shared_desc),
|
||||
icon = Icons.Default.Home,
|
||||
iconTint = MaterialTheme.colorScheme.primary,
|
||||
checked = residenceShared,
|
||||
@@ -279,8 +281,8 @@ fun NotificationPreferencesScreen(
|
||||
)
|
||||
|
||||
NotificationToggle(
|
||||
title = "Warranty Expiring",
|
||||
description = "Reminders for expiring warranties",
|
||||
title = stringResource(Res.string.notifications_warranty_expiring),
|
||||
description = stringResource(Res.string.notifications_warranty_expiring_desc),
|
||||
icon = Icons.Default.Description,
|
||||
iconTint = MaterialTheme.colorScheme.tertiary,
|
||||
checked = warrantyExpiring,
|
||||
|
||||
@@ -28,6 +28,8 @@ import com.example.casera.storage.TokenStorage
|
||||
import com.example.casera.cache.SubscriptionCache
|
||||
import com.example.casera.ui.subscription.UpgradePromptDialog
|
||||
import androidx.compose.runtime.getValue
|
||||
import casera.composeapp.generated.resources.*
|
||||
import org.jetbrains.compose.resources.stringResource
|
||||
|
||||
@OptIn(ExperimentalMaterial3Api::class)
|
||||
@Composable
|
||||
@@ -60,7 +62,7 @@ fun ProfileScreen(
|
||||
email = email
|
||||
)
|
||||
},
|
||||
errorTitle = "Failed to Update Profile"
|
||||
errorTitle = stringResource(Res.string.profile_update_failed)
|
||||
)
|
||||
|
||||
// Load current user data
|
||||
@@ -76,12 +78,12 @@ fun ProfileScreen(
|
||||
isLoadingUser = false
|
||||
}
|
||||
else -> {
|
||||
errorMessage = "Failed to load user data"
|
||||
errorMessage = "profile_load_failed"
|
||||
isLoadingUser = false
|
||||
}
|
||||
}
|
||||
} else {
|
||||
errorMessage = "Not authenticated"
|
||||
errorMessage = "profile_not_authenticated"
|
||||
isLoadingUser = false
|
||||
}
|
||||
}
|
||||
@@ -116,15 +118,15 @@ fun ProfileScreen(
|
||||
Scaffold(
|
||||
topBar = {
|
||||
TopAppBar(
|
||||
title = { Text("Profile", fontWeight = FontWeight.SemiBold) },
|
||||
title = { Text(stringResource(Res.string.profile_title), fontWeight = FontWeight.SemiBold) },
|
||||
navigationIcon = {
|
||||
IconButton(onClick = onNavigateBack) {
|
||||
Icon(Icons.Default.ArrowBack, contentDescription = "Back")
|
||||
Icon(Icons.Default.ArrowBack, contentDescription = stringResource(Res.string.common_back))
|
||||
}
|
||||
},
|
||||
actions = {
|
||||
IconButton(onClick = onLogout) {
|
||||
Icon(Icons.Default.Logout, contentDescription = "Logout")
|
||||
Icon(Icons.Default.Logout, contentDescription = stringResource(Res.string.profile_logout))
|
||||
}
|
||||
},
|
||||
colors = TopAppBarDefaults.topAppBarColors(
|
||||
@@ -163,7 +165,7 @@ fun ProfileScreen(
|
||||
)
|
||||
|
||||
Text(
|
||||
"Update Your Profile",
|
||||
stringResource(Res.string.profile_update_title),
|
||||
style = MaterialTheme.typography.headlineSmall,
|
||||
fontWeight = FontWeight.Bold
|
||||
)
|
||||
@@ -191,7 +193,7 @@ fun ProfileScreen(
|
||||
verticalArrangement = Arrangement.spacedBy(AppSpacing.xs)
|
||||
) {
|
||||
Text(
|
||||
text = "Appearance",
|
||||
text = stringResource(Res.string.profile_appearance),
|
||||
style = MaterialTheme.typography.titleMedium,
|
||||
fontWeight = FontWeight.SemiBold
|
||||
)
|
||||
@@ -203,7 +205,7 @@ fun ProfileScreen(
|
||||
}
|
||||
Icon(
|
||||
imageVector = Icons.Default.Palette,
|
||||
contentDescription = "Change theme",
|
||||
contentDescription = stringResource(Res.string.profile_change_theme),
|
||||
tint = MaterialTheme.colorScheme.primary
|
||||
)
|
||||
}
|
||||
@@ -230,19 +232,19 @@ fun ProfileScreen(
|
||||
verticalArrangement = Arrangement.spacedBy(AppSpacing.xs)
|
||||
) {
|
||||
Text(
|
||||
text = "Notifications",
|
||||
text = stringResource(Res.string.profile_notifications),
|
||||
style = MaterialTheme.typography.titleMedium,
|
||||
fontWeight = FontWeight.SemiBold
|
||||
)
|
||||
Text(
|
||||
text = "Manage notification preferences",
|
||||
text = stringResource(Res.string.profile_notifications_subtitle),
|
||||
style = MaterialTheme.typography.bodyMedium,
|
||||
color = MaterialTheme.colorScheme.onSurfaceVariant
|
||||
)
|
||||
}
|
||||
Icon(
|
||||
imageVector = Icons.Default.Notifications,
|
||||
contentDescription = "Notification preferences",
|
||||
contentDescription = stringResource(Res.string.profile_notifications),
|
||||
tint = MaterialTheme.colorScheme.primary
|
||||
)
|
||||
}
|
||||
@@ -279,16 +281,16 @@ fun ProfileScreen(
|
||||
verticalArrangement = Arrangement.spacedBy(AppSpacing.xs)
|
||||
) {
|
||||
Text(
|
||||
text = if (SubscriptionHelper.currentTier == "pro") "Pro Plan" else "Free Plan",
|
||||
text = if (SubscriptionHelper.currentTier == "pro") stringResource(Res.string.profile_pro_plan) else stringResource(Res.string.profile_free_plan),
|
||||
style = MaterialTheme.typography.titleMedium,
|
||||
fontWeight = FontWeight.SemiBold
|
||||
)
|
||||
|
||||
Text(
|
||||
text = if (SubscriptionHelper.currentTier == "pro" && currentSubscription?.expiresAt != null) {
|
||||
"Active until ${currentSubscription?.expiresAt}"
|
||||
stringResource(Res.string.profile_active_until, currentSubscription?.expiresAt ?: "")
|
||||
} else {
|
||||
"Limited features"
|
||||
stringResource(Res.string.profile_limited_features)
|
||||
},
|
||||
style = MaterialTheme.typography.bodySmall,
|
||||
color = MaterialTheme.colorScheme.onSurfaceVariant
|
||||
@@ -309,11 +311,11 @@ fun ProfileScreen(
|
||||
contentDescription = null
|
||||
)
|
||||
Spacer(modifier = Modifier.width(AppSpacing.sm))
|
||||
Text("Upgrade to Pro", fontWeight = FontWeight.SemiBold)
|
||||
Text(stringResource(Res.string.profile_upgrade_to_pro), fontWeight = FontWeight.SemiBold)
|
||||
}
|
||||
} else {
|
||||
Text(
|
||||
text = "Manage your subscription in the Google Play Store",
|
||||
text = stringResource(Res.string.profile_manage_subscription),
|
||||
style = MaterialTheme.typography.bodySmall,
|
||||
color = MaterialTheme.colorScheme.onSurfaceVariant,
|
||||
modifier = Modifier.padding(top = AppSpacing.xs)
|
||||
@@ -326,7 +328,7 @@ fun ProfileScreen(
|
||||
}
|
||||
|
||||
Text(
|
||||
"Profile Information",
|
||||
stringResource(Res.string.profile_information),
|
||||
style = MaterialTheme.typography.titleMedium,
|
||||
fontWeight = FontWeight.SemiBold,
|
||||
modifier = Modifier.align(Alignment.Start)
|
||||
@@ -335,7 +337,7 @@ fun ProfileScreen(
|
||||
OutlinedTextField(
|
||||
value = firstName,
|
||||
onValueChange = { firstName = it },
|
||||
label = { Text("First Name") },
|
||||
label = { Text(stringResource(Res.string.profile_first_name)) },
|
||||
leadingIcon = {
|
||||
Icon(Icons.Default.Person, contentDescription = null)
|
||||
},
|
||||
@@ -347,7 +349,7 @@ fun ProfileScreen(
|
||||
OutlinedTextField(
|
||||
value = lastName,
|
||||
onValueChange = { lastName = it },
|
||||
label = { Text("Last Name") },
|
||||
label = { Text(stringResource(Res.string.profile_last_name)) },
|
||||
leadingIcon = {
|
||||
Icon(Icons.Default.Person, contentDescription = null)
|
||||
},
|
||||
@@ -359,7 +361,7 @@ fun ProfileScreen(
|
||||
OutlinedTextField(
|
||||
value = email,
|
||||
onValueChange = { email = it },
|
||||
label = { Text("Email") },
|
||||
label = { Text(stringResource(Res.string.profile_email)) },
|
||||
leadingIcon = {
|
||||
Icon(Icons.Default.Email, contentDescription = null)
|
||||
},
|
||||
@@ -369,6 +371,13 @@ fun ProfileScreen(
|
||||
)
|
||||
|
||||
if (errorMessage.isNotEmpty()) {
|
||||
val displayError = when (errorMessage) {
|
||||
"profile_load_failed" -> stringResource(Res.string.profile_load_failed)
|
||||
"profile_not_authenticated" -> stringResource(Res.string.profile_not_authenticated)
|
||||
"profile_update_coming_soon" -> stringResource(Res.string.profile_update_coming_soon)
|
||||
"profile_email_required" -> stringResource(Res.string.profile_email_required)
|
||||
else -> errorMessage
|
||||
}
|
||||
Card(
|
||||
modifier = Modifier.fillMaxWidth(),
|
||||
colors = CardDefaults.cardColors(
|
||||
@@ -389,7 +398,7 @@ fun ProfileScreen(
|
||||
tint = MaterialTheme.colorScheme.error
|
||||
)
|
||||
Text(
|
||||
errorMessage,
|
||||
displayError,
|
||||
color = MaterialTheme.colorScheme.error,
|
||||
style = MaterialTheme.typography.bodyMedium,
|
||||
fontWeight = FontWeight.SemiBold
|
||||
@@ -434,9 +443,9 @@ fun ProfileScreen(
|
||||
onClick = {
|
||||
if (email.isNotEmpty()) {
|
||||
// viewModel.updateProfile not available yet
|
||||
errorMessage = "Profile update coming soon"
|
||||
errorMessage = "profile_update_coming_soon"
|
||||
} else {
|
||||
errorMessage = "Email is required"
|
||||
errorMessage = "profile_email_required"
|
||||
}
|
||||
},
|
||||
modifier = Modifier
|
||||
@@ -458,7 +467,7 @@ fun ProfileScreen(
|
||||
) {
|
||||
Icon(Icons.Default.Save, contentDescription = null)
|
||||
Text(
|
||||
"Save Changes",
|
||||
stringResource(Res.string.profile_save),
|
||||
style = MaterialTheme.typography.titleMedium,
|
||||
fontWeight = FontWeight.SemiBold
|
||||
)
|
||||
|
||||
@@ -19,6 +19,8 @@ import com.example.casera.ui.components.auth.AuthHeader
|
||||
import com.example.casera.ui.components.common.ErrorCard
|
||||
import com.example.casera.viewmodel.AuthViewModel
|
||||
import com.example.casera.network.ApiResult
|
||||
import casera.composeapp.generated.resources.*
|
||||
import org.jetbrains.compose.resources.stringResource
|
||||
|
||||
@OptIn(ExperimentalMaterial3Api::class)
|
||||
@Composable
|
||||
@@ -39,7 +41,7 @@ fun RegisterScreen(
|
||||
// Handle errors for registration
|
||||
createState.HandleErrors(
|
||||
onRetry = { viewModel.register(username, email, password) },
|
||||
errorTitle = "Registration Failed"
|
||||
errorTitle = stringResource(Res.string.error_generic)
|
||||
)
|
||||
|
||||
LaunchedEffect(createState) {
|
||||
@@ -55,10 +57,10 @@ fun RegisterScreen(
|
||||
Scaffold(
|
||||
topBar = {
|
||||
TopAppBar(
|
||||
title = { Text("Create Account", fontWeight = FontWeight.SemiBold) },
|
||||
title = { Text(stringResource(Res.string.auth_register_title), fontWeight = FontWeight.SemiBold) },
|
||||
navigationIcon = {
|
||||
IconButton(onClick = onNavigateBack) {
|
||||
Icon(Icons.Default.ArrowBack, contentDescription = "Back")
|
||||
Icon(Icons.Default.ArrowBack, contentDescription = stringResource(Res.string.common_back))
|
||||
}
|
||||
},
|
||||
colors = TopAppBarDefaults.topAppBarColors(
|
||||
@@ -80,8 +82,8 @@ fun RegisterScreen(
|
||||
|
||||
AuthHeader(
|
||||
icon = Icons.Default.PersonAdd,
|
||||
title = "Join myCrib",
|
||||
subtitle = "Start managing your properties today"
|
||||
title = stringResource(Res.string.auth_register_title),
|
||||
subtitle = stringResource(Res.string.auth_register_subtitle)
|
||||
)
|
||||
|
||||
Spacer(modifier = Modifier.height(16.dp))
|
||||
@@ -89,7 +91,7 @@ fun RegisterScreen(
|
||||
OutlinedTextField(
|
||||
value = username,
|
||||
onValueChange = { username = it },
|
||||
label = { Text("Username") },
|
||||
label = { Text(stringResource(Res.string.auth_register_username)) },
|
||||
leadingIcon = {
|
||||
Icon(Icons.Default.Person, contentDescription = null)
|
||||
},
|
||||
@@ -101,7 +103,7 @@ fun RegisterScreen(
|
||||
OutlinedTextField(
|
||||
value = email,
|
||||
onValueChange = { email = it },
|
||||
label = { Text("Email") },
|
||||
label = { Text(stringResource(Res.string.auth_register_email)) },
|
||||
leadingIcon = {
|
||||
Icon(Icons.Default.Email, contentDescription = null)
|
||||
},
|
||||
@@ -113,7 +115,7 @@ fun RegisterScreen(
|
||||
OutlinedTextField(
|
||||
value = password,
|
||||
onValueChange = { password = it },
|
||||
label = { Text("Password") },
|
||||
label = { Text(stringResource(Res.string.auth_register_password)) },
|
||||
leadingIcon = {
|
||||
Icon(Icons.Default.Lock, contentDescription = null)
|
||||
},
|
||||
@@ -126,7 +128,7 @@ fun RegisterScreen(
|
||||
OutlinedTextField(
|
||||
value = confirmPassword,
|
||||
onValueChange = { confirmPassword = it },
|
||||
label = { Text("Confirm Password") },
|
||||
label = { Text(stringResource(Res.string.auth_register_confirm_password)) },
|
||||
leadingIcon = {
|
||||
Icon(Icons.Default.Lock, contentDescription = null)
|
||||
},
|
||||
@@ -140,11 +142,12 @@ fun RegisterScreen(
|
||||
|
||||
Spacer(modifier = Modifier.height(8.dp))
|
||||
|
||||
val passwordsDontMatchMessage = stringResource(Res.string.auth_passwords_dont_match)
|
||||
Button(
|
||||
onClick = {
|
||||
when {
|
||||
password != confirmPassword -> {
|
||||
errorMessage = "Passwords do not match"
|
||||
errorMessage = passwordsDontMatchMessage
|
||||
}
|
||||
else -> {
|
||||
isLoading = true
|
||||
@@ -168,7 +171,7 @@ fun RegisterScreen(
|
||||
)
|
||||
} else {
|
||||
Text(
|
||||
"Create Account",
|
||||
stringResource(Res.string.auth_register_button),
|
||||
style = MaterialTheme.typography.titleMedium,
|
||||
fontWeight = FontWeight.SemiBold
|
||||
)
|
||||
|
||||
@@ -20,6 +20,8 @@ import com.example.casera.ui.components.auth.RequirementItem
|
||||
import com.example.casera.ui.components.common.ErrorCard
|
||||
import com.example.casera.viewmodel.PasswordResetViewModel
|
||||
import com.example.casera.network.ApiResult
|
||||
import casera.composeapp.generated.resources.*
|
||||
import org.jetbrains.compose.resources.stringResource
|
||||
|
||||
@OptIn(ExperimentalMaterial3Api::class)
|
||||
@Composable
|
||||
@@ -59,11 +61,11 @@ fun ResetPasswordScreen(
|
||||
Scaffold(
|
||||
topBar = {
|
||||
TopAppBar(
|
||||
title = { Text("Reset Password") },
|
||||
title = { Text(stringResource(Res.string.auth_reset_title)) },
|
||||
navigationIcon = {
|
||||
onNavigateBack?.let { callback ->
|
||||
IconButton(onClick = callback) {
|
||||
Icon(Icons.Default.ArrowBack, contentDescription = "Back")
|
||||
Icon(Icons.Default.ArrowBack, contentDescription = stringResource(Res.string.common_back))
|
||||
}
|
||||
}
|
||||
},
|
||||
@@ -184,7 +186,7 @@ fun ResetPasswordScreen(
|
||||
newPassword = it
|
||||
viewModel.resetResetPasswordState()
|
||||
},
|
||||
label = { Text("New Password") },
|
||||
label = { Text(stringResource(Res.string.auth_reset_new_password)) },
|
||||
leadingIcon = {
|
||||
Icon(Icons.Default.Lock, contentDescription = null)
|
||||
},
|
||||
@@ -209,7 +211,7 @@ fun ResetPasswordScreen(
|
||||
confirmPassword = it
|
||||
viewModel.resetResetPasswordState()
|
||||
},
|
||||
label = { Text("Confirm Password") },
|
||||
label = { Text(stringResource(Res.string.auth_reset_confirm_password)) },
|
||||
leadingIcon = {
|
||||
Icon(Icons.Default.Lock, contentDescription = null)
|
||||
},
|
||||
@@ -250,7 +252,7 @@ fun ResetPasswordScreen(
|
||||
Icon(Icons.Default.LockReset, contentDescription = null)
|
||||
Spacer(modifier = Modifier.width(8.dp))
|
||||
Text(
|
||||
"Reset Password",
|
||||
stringResource(Res.string.auth_reset_button),
|
||||
style = MaterialTheme.typography.titleMedium,
|
||||
fontWeight = FontWeight.SemiBold
|
||||
)
|
||||
|
||||
@@ -36,6 +36,8 @@ import com.example.casera.ui.subscription.UpgradePromptDialog
|
||||
import com.example.casera.cache.SubscriptionCache
|
||||
import com.example.casera.cache.DataCache
|
||||
import com.example.casera.util.DateUtils
|
||||
import casera.composeapp.generated.resources.*
|
||||
import org.jetbrains.compose.resources.stringResource
|
||||
|
||||
@OptIn(ExperimentalMaterial3Api::class)
|
||||
@Composable
|
||||
@@ -146,7 +148,7 @@ fun ResidenceDetailScreen(
|
||||
// Handle errors for generate report
|
||||
generateReportState.HandleErrors(
|
||||
onRetry = { residenceViewModel.generateTasksReport(residenceId) },
|
||||
errorTitle = "Failed to Generate Report"
|
||||
errorTitle = stringResource(Res.string.properties_failed_to_generate_report)
|
||||
)
|
||||
|
||||
LaunchedEffect(generateReportState) {
|
||||
@@ -164,7 +166,7 @@ fun ResidenceDetailScreen(
|
||||
// Handle errors for delete residence
|
||||
deleteState.HandleErrors(
|
||||
onRetry = { residenceViewModel.deleteResidence(residenceId) },
|
||||
errorTitle = "Failed to Delete Property"
|
||||
errorTitle = stringResource(Res.string.properties_failed_to_delete)
|
||||
)
|
||||
|
||||
// Handle delete residence state
|
||||
@@ -245,8 +247,8 @@ fun ResidenceDetailScreen(
|
||||
if (showReportConfirmation) {
|
||||
AlertDialog(
|
||||
onDismissRequest = { showReportConfirmation = false },
|
||||
title = { Text("Generate Report") },
|
||||
text = { Text("This will generate and email a maintenance report for this property. Do you want to continue?") },
|
||||
title = { Text(stringResource(Res.string.properties_generate_report)) },
|
||||
text = { Text(stringResource(Res.string.properties_generate_report_confirm)) },
|
||||
confirmButton = {
|
||||
Button(
|
||||
onClick = {
|
||||
@@ -254,12 +256,12 @@ fun ResidenceDetailScreen(
|
||||
residenceViewModel.generateTasksReport(residenceId)
|
||||
}
|
||||
) {
|
||||
Text("Generate")
|
||||
Text(stringResource(Res.string.properties_generate))
|
||||
}
|
||||
},
|
||||
dismissButton = {
|
||||
TextButton(onClick = { showReportConfirmation = false }) {
|
||||
Text("Cancel")
|
||||
Text(stringResource(Res.string.common_cancel))
|
||||
}
|
||||
}
|
||||
)
|
||||
@@ -269,8 +271,8 @@ fun ResidenceDetailScreen(
|
||||
val residence = (residenceState as ApiResult.Success<Residence>).data
|
||||
AlertDialog(
|
||||
onDismissRequest = { showDeleteConfirmation = false },
|
||||
title = { Text("Delete Residence") },
|
||||
text = { Text("Are you sure you want to delete ${residence.name}? This action cannot be undone.") },
|
||||
title = { Text(stringResource(Res.string.properties_delete_residence)) },
|
||||
text = { Text(stringResource(Res.string.properties_delete_name_confirm, residence.name)) },
|
||||
confirmButton = {
|
||||
Button(
|
||||
onClick = {
|
||||
@@ -281,12 +283,12 @@ fun ResidenceDetailScreen(
|
||||
containerColor = MaterialTheme.colorScheme.error
|
||||
)
|
||||
) {
|
||||
Text("Delete")
|
||||
Text(stringResource(Res.string.common_delete))
|
||||
}
|
||||
},
|
||||
dismissButton = {
|
||||
TextButton(onClick = { showDeleteConfirmation = false }) {
|
||||
Text("Cancel")
|
||||
Text(stringResource(Res.string.common_cancel))
|
||||
}
|
||||
}
|
||||
)
|
||||
@@ -298,8 +300,8 @@ fun ResidenceDetailScreen(
|
||||
showCancelTaskConfirmation = false
|
||||
taskToCancel = null
|
||||
},
|
||||
title = { Text("Cancel Task") },
|
||||
text = { Text("Are you sure you want to cancel \"${taskToCancel!!.title}\"? This action cannot be undone.") },
|
||||
title = { Text(stringResource(Res.string.properties_cancel_task)) },
|
||||
text = { Text(stringResource(Res.string.properties_cancel_task_confirm, taskToCancel!!.title)) },
|
||||
confirmButton = {
|
||||
Button(
|
||||
onClick = {
|
||||
@@ -311,7 +313,7 @@ fun ResidenceDetailScreen(
|
||||
containerColor = MaterialTheme.colorScheme.error
|
||||
)
|
||||
) {
|
||||
Text("Cancel Task")
|
||||
Text(stringResource(Res.string.properties_cancel_task))
|
||||
}
|
||||
},
|
||||
dismissButton = {
|
||||
@@ -319,7 +321,7 @@ fun ResidenceDetailScreen(
|
||||
showCancelTaskConfirmation = false
|
||||
taskToCancel = null
|
||||
}) {
|
||||
Text("Dismiss")
|
||||
Text(stringResource(Res.string.properties_dismiss))
|
||||
}
|
||||
}
|
||||
)
|
||||
@@ -331,8 +333,8 @@ fun ResidenceDetailScreen(
|
||||
showArchiveTaskConfirmation = false
|
||||
taskToArchive = null
|
||||
},
|
||||
title = { Text("Archive Task") },
|
||||
text = { Text("Are you sure you want to archive \"${taskToArchive!!.title}\"? You can unarchive it later from archived tasks.") },
|
||||
title = { Text(stringResource(Res.string.properties_archive_task)) },
|
||||
text = { Text(stringResource(Res.string.properties_archive_task_confirm, taskToArchive!!.title)) },
|
||||
confirmButton = {
|
||||
Button(
|
||||
onClick = {
|
||||
@@ -348,7 +350,7 @@ fun ResidenceDetailScreen(
|
||||
containerColor = MaterialTheme.colorScheme.error
|
||||
)
|
||||
) {
|
||||
Text("Archive")
|
||||
Text(stringResource(Res.string.tasks_archive))
|
||||
}
|
||||
},
|
||||
dismissButton = {
|
||||
@@ -356,7 +358,7 @@ fun ResidenceDetailScreen(
|
||||
showArchiveTaskConfirmation = false
|
||||
taskToArchive = null
|
||||
}) {
|
||||
Text("Dismiss")
|
||||
Text(stringResource(Res.string.properties_dismiss))
|
||||
}
|
||||
}
|
||||
)
|
||||
@@ -375,10 +377,10 @@ fun ResidenceDetailScreen(
|
||||
snackbarHost = { SnackbarHost(hostState = snackbarHostState) },
|
||||
topBar = {
|
||||
TopAppBar(
|
||||
title = { Text("Property Details", fontWeight = FontWeight.Bold) },
|
||||
title = { Text(stringResource(Res.string.properties_details), fontWeight = FontWeight.Bold) },
|
||||
navigationIcon = {
|
||||
IconButton(onClick = onNavigateBack) {
|
||||
Icon(Icons.Default.ArrowBack, contentDescription = "Back")
|
||||
Icon(Icons.Default.ArrowBack, contentDescription = stringResource(Res.string.common_back))
|
||||
}
|
||||
},
|
||||
actions = {
|
||||
@@ -399,7 +401,7 @@ fun ResidenceDetailScreen(
|
||||
strokeWidth = 2.dp
|
||||
)
|
||||
} else {
|
||||
Icon(Icons.Default.Description, contentDescription = "Generate Report")
|
||||
Icon(Icons.Default.Description, contentDescription = stringResource(Res.string.properties_generate_report))
|
||||
}
|
||||
}
|
||||
|
||||
@@ -408,14 +410,14 @@ fun ResidenceDetailScreen(
|
||||
IconButton(onClick = {
|
||||
showManageUsersDialog = true
|
||||
}) {
|
||||
Icon(Icons.Default.People, contentDescription = "Manage Users")
|
||||
Icon(Icons.Default.People, contentDescription = stringResource(Res.string.properties_manage_users))
|
||||
}
|
||||
}
|
||||
|
||||
IconButton(onClick = {
|
||||
onNavigateToEditResidence(residence)
|
||||
}) {
|
||||
Icon(Icons.Default.Edit, contentDescription = "Edit Residence")
|
||||
Icon(Icons.Default.Edit, contentDescription = stringResource(Res.string.properties_edit_residence))
|
||||
}
|
||||
|
||||
// Delete button - only show for primary owners
|
||||
@@ -425,7 +427,7 @@ fun ResidenceDetailScreen(
|
||||
}) {
|
||||
Icon(
|
||||
Icons.Default.Delete,
|
||||
contentDescription = "Delete Residence",
|
||||
contentDescription = stringResource(Res.string.properties_delete_residence),
|
||||
tint = MaterialTheme.colorScheme.error
|
||||
)
|
||||
}
|
||||
@@ -453,7 +455,7 @@ fun ResidenceDetailScreen(
|
||||
containerColor = MaterialTheme.colorScheme.primary,
|
||||
shape = RoundedCornerShape(16.dp)
|
||||
) {
|
||||
Icon(Icons.Default.Add, contentDescription = "Add Task")
|
||||
Icon(Icons.Default.Add, contentDescription = stringResource(Res.string.properties_add_task))
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -466,7 +468,7 @@ fun ResidenceDetailScreen(
|
||||
}
|
||||
},
|
||||
modifier = Modifier.padding(paddingValues),
|
||||
errorTitle = "Failed to Load Property",
|
||||
errorTitle = stringResource(Res.string.properties_failed_to_load),
|
||||
loadingContent = {
|
||||
Column(
|
||||
horizontalAlignment = Alignment.CenterHorizontally,
|
||||
@@ -474,7 +476,7 @@ fun ResidenceDetailScreen(
|
||||
) {
|
||||
CircularProgressIndicator()
|
||||
Text(
|
||||
text = "Loading residence...",
|
||||
text = stringResource(Res.string.properties_loading),
|
||||
style = MaterialTheme.typography.bodyMedium,
|
||||
color = MaterialTheme.colorScheme.onSurfaceVariant
|
||||
)
|
||||
@@ -528,7 +530,7 @@ fun ResidenceDetailScreen(
|
||||
item {
|
||||
InfoCard(
|
||||
icon = Icons.Default.LocationOn,
|
||||
title = "Address"
|
||||
title = stringResource(Res.string.properties_address_section)
|
||||
) {
|
||||
if (residence.streetAddress != null) {
|
||||
Text(text = residence.streetAddress)
|
||||
@@ -552,7 +554,7 @@ fun ResidenceDetailScreen(
|
||||
item {
|
||||
InfoCard(
|
||||
icon = Icons.Default.Info,
|
||||
title = "Property Details"
|
||||
title = stringResource(Res.string.properties_property_details_section)
|
||||
) {
|
||||
Row(
|
||||
modifier = Modifier.fillMaxWidth(),
|
||||
@@ -584,7 +586,7 @@ fun ResidenceDetailScreen(
|
||||
item {
|
||||
InfoCard(
|
||||
icon = Icons.Default.Description,
|
||||
title = "Description"
|
||||
title = stringResource(Res.string.properties_description_section)
|
||||
) {
|
||||
Text(
|
||||
text = residence.description,
|
||||
@@ -600,7 +602,7 @@ fun ResidenceDetailScreen(
|
||||
item {
|
||||
InfoCard(
|
||||
icon = Icons.Default.AttachMoney,
|
||||
title = "Purchase Information"
|
||||
title = stringResource(Res.string.properties_purchase_info)
|
||||
) {
|
||||
residence.purchaseDate?.let {
|
||||
DetailRow(Icons.Default.Event, "Purchase Date", DateUtils.formatDateMedium(it))
|
||||
@@ -628,7 +630,7 @@ fun ResidenceDetailScreen(
|
||||
)
|
||||
Spacer(modifier = Modifier.width(8.dp))
|
||||
Text(
|
||||
text = "Tasks",
|
||||
text = stringResource(Res.string.tasks_title),
|
||||
style = MaterialTheme.typography.headlineSmall,
|
||||
fontWeight = FontWeight.Bold,
|
||||
color = MaterialTheme.colorScheme.primary
|
||||
@@ -688,12 +690,12 @@ fun ResidenceDetailScreen(
|
||||
)
|
||||
Spacer(modifier = Modifier.height(16.dp))
|
||||
Text(
|
||||
"No tasks yet",
|
||||
stringResource(Res.string.properties_no_tasks),
|
||||
style = MaterialTheme.typography.titleLarge,
|
||||
fontWeight = FontWeight.SemiBold
|
||||
)
|
||||
Text(
|
||||
"Add a task to get started",
|
||||
stringResource(Res.string.properties_add_task_start),
|
||||
style = MaterialTheme.typography.bodyMedium,
|
||||
color = MaterialTheme.colorScheme.onSurfaceVariant
|
||||
)
|
||||
@@ -767,7 +769,7 @@ fun ResidenceDetailScreen(
|
||||
)
|
||||
Spacer(modifier = Modifier.width(8.dp))
|
||||
Text(
|
||||
text = "Contractors",
|
||||
text = stringResource(Res.string.contractors_title),
|
||||
style = MaterialTheme.typography.headlineSmall,
|
||||
fontWeight = FontWeight.Bold,
|
||||
color = MaterialTheme.colorScheme.primary
|
||||
@@ -826,12 +828,12 @@ fun ResidenceDetailScreen(
|
||||
)
|
||||
Spacer(modifier = Modifier.height(12.dp))
|
||||
Text(
|
||||
"No contractors yet",
|
||||
stringResource(Res.string.properties_no_contractors),
|
||||
style = MaterialTheme.typography.titleMedium,
|
||||
fontWeight = FontWeight.SemiBold
|
||||
)
|
||||
Text(
|
||||
"Add contractors from the Contractors tab",
|
||||
stringResource(Res.string.properties_add_contractors_hint),
|
||||
style = MaterialTheme.typography.bodySmall,
|
||||
color = MaterialTheme.colorScheme.onSurfaceVariant
|
||||
)
|
||||
|
||||
@@ -18,6 +18,8 @@ import com.example.casera.models.Residence
|
||||
import com.example.casera.models.ResidenceCreateRequest
|
||||
import com.example.casera.models.ResidenceType
|
||||
import com.example.casera.network.ApiResult
|
||||
import casera.composeapp.generated.resources.*
|
||||
import org.jetbrains.compose.resources.stringResource
|
||||
|
||||
@OptIn(ExperimentalMaterial3Api::class)
|
||||
@Composable
|
||||
@@ -85,11 +87,13 @@ fun ResidenceFormScreen(
|
||||
}
|
||||
}
|
||||
|
||||
val nameRequiredError = stringResource(Res.string.properties_form_name_error)
|
||||
|
||||
fun validateForm(): Boolean {
|
||||
var isValid = true
|
||||
|
||||
if (name.isBlank()) {
|
||||
nameError = "Name is required"
|
||||
nameError = nameRequiredError
|
||||
isValid = false
|
||||
} else {
|
||||
nameError = ""
|
||||
@@ -101,10 +105,10 @@ fun ResidenceFormScreen(
|
||||
Scaffold(
|
||||
topBar = {
|
||||
TopAppBar(
|
||||
title = { Text(if (isEditMode) "Edit Residence" else "Add Residence") },
|
||||
title = { Text(if (isEditMode) stringResource(Res.string.properties_edit_title) else stringResource(Res.string.properties_add_title)) },
|
||||
navigationIcon = {
|
||||
IconButton(onClick = onNavigateBack) {
|
||||
Icon(Icons.Default.ArrowBack, contentDescription = "Back")
|
||||
Icon(Icons.Default.ArrowBack, contentDescription = stringResource(Res.string.common_back))
|
||||
}
|
||||
}
|
||||
)
|
||||
@@ -120,7 +124,7 @@ fun ResidenceFormScreen(
|
||||
) {
|
||||
// Basic Information section
|
||||
Text(
|
||||
text = "Property Details",
|
||||
text = stringResource(Res.string.properties_details),
|
||||
style = MaterialTheme.typography.titleMedium,
|
||||
color = MaterialTheme.colorScheme.primary
|
||||
)
|
||||
@@ -128,13 +132,13 @@ fun ResidenceFormScreen(
|
||||
OutlinedTextField(
|
||||
value = name,
|
||||
onValueChange = { name = it },
|
||||
label = { Text("Property Name *") },
|
||||
label = { Text(stringResource(Res.string.properties_form_name_required)) },
|
||||
modifier = Modifier.fillMaxWidth(),
|
||||
isError = nameError.isNotEmpty(),
|
||||
supportingText = if (nameError.isNotEmpty()) {
|
||||
{ Text(nameError, color = MaterialTheme.colorScheme.error) }
|
||||
} else {
|
||||
{ Text("Required", color = MaterialTheme.colorScheme.error) }
|
||||
{ Text(stringResource(Res.string.properties_form_required), color = MaterialTheme.colorScheme.error) }
|
||||
}
|
||||
)
|
||||
|
||||
@@ -146,7 +150,7 @@ fun ResidenceFormScreen(
|
||||
value = propertyType?.name?.replaceFirstChar { it.uppercase() } ?: "",
|
||||
onValueChange = {},
|
||||
readOnly = true,
|
||||
label = { Text("Property Type") },
|
||||
label = { Text(stringResource(Res.string.properties_type_label)) },
|
||||
trailingIcon = { ExposedDropdownMenuDefaults.TrailingIcon(expanded = expanded) },
|
||||
modifier = Modifier
|
||||
.fillMaxWidth()
|
||||
@@ -171,7 +175,7 @@ fun ResidenceFormScreen(
|
||||
|
||||
// Address section
|
||||
Text(
|
||||
text = "Address",
|
||||
text = stringResource(Res.string.properties_address_section),
|
||||
style = MaterialTheme.typography.titleMedium,
|
||||
color = MaterialTheme.colorScheme.primary
|
||||
)
|
||||
@@ -179,49 +183,49 @@ fun ResidenceFormScreen(
|
||||
OutlinedTextField(
|
||||
value = streetAddress,
|
||||
onValueChange = { streetAddress = it },
|
||||
label = { Text("Street Address") },
|
||||
label = { Text(stringResource(Res.string.properties_form_street)) },
|
||||
modifier = Modifier.fillMaxWidth()
|
||||
)
|
||||
|
||||
OutlinedTextField(
|
||||
value = apartmentUnit,
|
||||
onValueChange = { apartmentUnit = it },
|
||||
label = { Text("Apartment/Unit #") },
|
||||
label = { Text(stringResource(Res.string.properties_form_apartment)) },
|
||||
modifier = Modifier.fillMaxWidth()
|
||||
)
|
||||
|
||||
OutlinedTextField(
|
||||
value = city,
|
||||
onValueChange = { city = it },
|
||||
label = { Text("City") },
|
||||
label = { Text(stringResource(Res.string.properties_form_city)) },
|
||||
modifier = Modifier.fillMaxWidth()
|
||||
)
|
||||
|
||||
OutlinedTextField(
|
||||
value = stateProvince,
|
||||
onValueChange = { stateProvince = it },
|
||||
label = { Text("State/Province") },
|
||||
label = { Text(stringResource(Res.string.properties_form_state)) },
|
||||
modifier = Modifier.fillMaxWidth()
|
||||
)
|
||||
|
||||
OutlinedTextField(
|
||||
value = postalCode,
|
||||
onValueChange = { postalCode = it },
|
||||
label = { Text("Postal Code") },
|
||||
label = { Text(stringResource(Res.string.properties_form_postal)) },
|
||||
modifier = Modifier.fillMaxWidth()
|
||||
)
|
||||
|
||||
OutlinedTextField(
|
||||
value = country,
|
||||
onValueChange = { country = it },
|
||||
label = { Text("Country") },
|
||||
label = { Text(stringResource(Res.string.properties_form_country)) },
|
||||
modifier = Modifier.fillMaxWidth()
|
||||
)
|
||||
|
||||
// Optional fields section
|
||||
Divider()
|
||||
Text(
|
||||
text = "Optional Details",
|
||||
text = stringResource(Res.string.properties_form_optional),
|
||||
style = MaterialTheme.typography.titleMedium,
|
||||
color = MaterialTheme.colorScheme.primary
|
||||
)
|
||||
@@ -233,7 +237,7 @@ fun ResidenceFormScreen(
|
||||
OutlinedTextField(
|
||||
value = bedrooms,
|
||||
onValueChange = { bedrooms = it.filter { char -> char.isDigit() } },
|
||||
label = { Text("Bedrooms") },
|
||||
label = { Text(stringResource(Res.string.properties_bedrooms)) },
|
||||
keyboardOptions = KeyboardOptions(keyboardType = KeyboardType.Number),
|
||||
modifier = Modifier.weight(1f)
|
||||
)
|
||||
@@ -241,7 +245,7 @@ fun ResidenceFormScreen(
|
||||
OutlinedTextField(
|
||||
value = bathrooms,
|
||||
onValueChange = { bathrooms = it },
|
||||
label = { Text("Bathrooms") },
|
||||
label = { Text(stringResource(Res.string.properties_bathrooms)) },
|
||||
keyboardOptions = KeyboardOptions(keyboardType = KeyboardType.Decimal),
|
||||
modifier = Modifier.weight(1f)
|
||||
)
|
||||
@@ -250,7 +254,7 @@ fun ResidenceFormScreen(
|
||||
OutlinedTextField(
|
||||
value = squareFootage,
|
||||
onValueChange = { squareFootage = it.filter { char -> char.isDigit() } },
|
||||
label = { Text("Square Footage") },
|
||||
label = { Text(stringResource(Res.string.properties_form_sqft)) },
|
||||
keyboardOptions = KeyboardOptions(keyboardType = KeyboardType.Number),
|
||||
modifier = Modifier.fillMaxWidth()
|
||||
)
|
||||
@@ -258,7 +262,7 @@ fun ResidenceFormScreen(
|
||||
OutlinedTextField(
|
||||
value = lotSize,
|
||||
onValueChange = { lotSize = it },
|
||||
label = { Text("Lot Size (acres)") },
|
||||
label = { Text(stringResource(Res.string.properties_form_lot_size)) },
|
||||
keyboardOptions = KeyboardOptions(keyboardType = KeyboardType.Decimal),
|
||||
modifier = Modifier.fillMaxWidth()
|
||||
)
|
||||
@@ -266,7 +270,7 @@ fun ResidenceFormScreen(
|
||||
OutlinedTextField(
|
||||
value = yearBuilt,
|
||||
onValueChange = { yearBuilt = it.filter { char -> char.isDigit() } },
|
||||
label = { Text("Year Built") },
|
||||
label = { Text(stringResource(Res.string.properties_year_built)) },
|
||||
keyboardOptions = KeyboardOptions(keyboardType = KeyboardType.Number),
|
||||
modifier = Modifier.fillMaxWidth()
|
||||
)
|
||||
@@ -274,7 +278,7 @@ fun ResidenceFormScreen(
|
||||
OutlinedTextField(
|
||||
value = description,
|
||||
onValueChange = { description = it },
|
||||
label = { Text("Description") },
|
||||
label = { Text(stringResource(Res.string.properties_form_description)) },
|
||||
modifier = Modifier.fillMaxWidth(),
|
||||
minLines = 3,
|
||||
maxLines = 5
|
||||
@@ -284,7 +288,7 @@ fun ResidenceFormScreen(
|
||||
modifier = Modifier.fillMaxWidth(),
|
||||
horizontalArrangement = Arrangement.SpaceBetween
|
||||
) {
|
||||
Text("Primary Residence")
|
||||
Text(stringResource(Res.string.properties_form_primary))
|
||||
Switch(
|
||||
checked = isPrimary,
|
||||
onCheckedChange = { isPrimary = it }
|
||||
@@ -338,7 +342,7 @@ fun ResidenceFormScreen(
|
||||
color = MaterialTheme.colorScheme.onPrimary
|
||||
)
|
||||
} else {
|
||||
Text(if (isEditMode) "Update Residence" else "Create Residence")
|
||||
Text(if (isEditMode) stringResource(Res.string.properties_form_update) else stringResource(Res.string.properties_form_create))
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -29,6 +29,8 @@ import com.example.casera.network.ApiResult
|
||||
import com.example.casera.utils.SubscriptionHelper
|
||||
import com.example.casera.ui.subscription.UpgradePromptDialog
|
||||
import com.example.casera.cache.SubscriptionCache
|
||||
import casera.composeapp.generated.resources.*
|
||||
import org.jetbrains.compose.resources.stringResource
|
||||
|
||||
@OptIn(ExperimentalMaterial3Api::class)
|
||||
@Composable
|
||||
@@ -107,7 +109,7 @@ fun ResidencesScreen(
|
||||
TopAppBar(
|
||||
title = {
|
||||
Text(
|
||||
"My Properties",
|
||||
stringResource(Res.string.properties_title),
|
||||
fontWeight = FontWeight.Bold
|
||||
)
|
||||
},
|
||||
@@ -123,14 +125,14 @@ fun ResidencesScreen(
|
||||
showUpgradePrompt = true
|
||||
}
|
||||
}) {
|
||||
Icon(Icons.Default.GroupAdd, contentDescription = "Join with Code")
|
||||
Icon(Icons.Default.GroupAdd, contentDescription = stringResource(Res.string.properties_join_title))
|
||||
}
|
||||
}
|
||||
IconButton(onClick = onNavigateToProfile) {
|
||||
Icon(Icons.Default.AccountCircle, contentDescription = "Profile")
|
||||
Icon(Icons.Default.AccountCircle, contentDescription = stringResource(Res.string.profile_title))
|
||||
}
|
||||
IconButton(onClick = onLogout) {
|
||||
Icon(Icons.Default.ExitToApp, contentDescription = "Logout")
|
||||
Icon(Icons.Default.ExitToApp, contentDescription = stringResource(Res.string.home_logout))
|
||||
}
|
||||
},
|
||||
colors = TopAppBarDefaults.topAppBarColors(
|
||||
@@ -163,7 +165,7 @@ fun ResidencesScreen(
|
||||
) {
|
||||
Icon(
|
||||
Icons.Default.Add,
|
||||
contentDescription = "Add Property",
|
||||
contentDescription = stringResource(Res.string.properties_add_button),
|
||||
tint = MaterialTheme.colorScheme.onPrimary
|
||||
)
|
||||
}
|
||||
@@ -176,7 +178,7 @@ fun ResidencesScreen(
|
||||
state = myResidencesState,
|
||||
onRetry = { viewModel.loadMyResidences() },
|
||||
modifier = Modifier.padding(paddingValues),
|
||||
errorTitle = "Failed to Load Properties"
|
||||
errorTitle = stringResource(Res.string.error_generic)
|
||||
) { response ->
|
||||
if (response.residences.isEmpty()) {
|
||||
Box(
|
||||
@@ -197,12 +199,12 @@ fun ResidencesScreen(
|
||||
tint = MaterialTheme.colorScheme.primary.copy(alpha = 0.6f)
|
||||
)
|
||||
Text(
|
||||
"No properties yet",
|
||||
stringResource(Res.string.properties_empty_title),
|
||||
style = MaterialTheme.typography.headlineSmall,
|
||||
fontWeight = FontWeight.SemiBold
|
||||
)
|
||||
Text(
|
||||
"Add your first property to get started!",
|
||||
stringResource(Res.string.properties_empty_subtitle),
|
||||
style = MaterialTheme.typography.bodyLarge,
|
||||
color = MaterialTheme.colorScheme.onSurfaceVariant
|
||||
)
|
||||
@@ -230,7 +232,7 @@ fun ResidencesScreen(
|
||||
) {
|
||||
Icon(Icons.Default.Add, contentDescription = null)
|
||||
Text(
|
||||
"Add Property",
|
||||
stringResource(Res.string.properties_add_button),
|
||||
style = MaterialTheme.typography.titleMedium,
|
||||
fontWeight = FontWeight.SemiBold
|
||||
)
|
||||
@@ -258,7 +260,7 @@ fun ResidencesScreen(
|
||||
) {
|
||||
Icon(Icons.Default.GroupAdd, contentDescription = null)
|
||||
Text(
|
||||
"Join with Code",
|
||||
stringResource(Res.string.properties_join_button),
|
||||
style = MaterialTheme.typography.titleMedium,
|
||||
fontWeight = FontWeight.SemiBold
|
||||
)
|
||||
@@ -339,7 +341,7 @@ fun ResidencesScreen(
|
||||
)
|
||||
Spacer(modifier = Modifier.width(8.dp))
|
||||
Text(
|
||||
text = "Overview",
|
||||
text = stringResource(Res.string.home_overview),
|
||||
style = MaterialTheme.typography.titleLarge,
|
||||
fontWeight = FontWeight.Bold,
|
||||
color = MaterialTheme.colorScheme.onPrimaryContainer
|
||||
@@ -353,12 +355,12 @@ fun ResidencesScreen(
|
||||
StatItem(
|
||||
icon = Icons.Default.Home,
|
||||
value = "${response.summary.totalResidences}",
|
||||
label = "Properties"
|
||||
label = stringResource(Res.string.home_properties)
|
||||
)
|
||||
StatItem(
|
||||
icon = Icons.Default.Assignment,
|
||||
value = "${response.summary.totalTasks}",
|
||||
label = "Total Tasks"
|
||||
label = stringResource(Res.string.home_total_tasks)
|
||||
)
|
||||
}
|
||||
|
||||
@@ -373,12 +375,12 @@ fun ResidencesScreen(
|
||||
StatItem(
|
||||
icon = Icons.Default.CalendarToday,
|
||||
value = "${response.summary.tasksDueNextWeek}",
|
||||
label = "Due This Week"
|
||||
label = stringResource(Res.string.tasks_column_due_soon)
|
||||
)
|
||||
StatItem(
|
||||
icon = Icons.Default.Event,
|
||||
value = "${response.summary.tasksDueNextMonth}",
|
||||
label = "Next 30 Days"
|
||||
label = stringResource(Res.string.tasks_column_upcoming)
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -19,6 +19,8 @@ import com.example.casera.ui.utils.hexToColor
|
||||
import com.example.casera.viewmodel.TaskCompletionViewModel
|
||||
import com.example.casera.viewmodel.TaskViewModel
|
||||
import com.example.casera.network.ApiResult
|
||||
import casera.composeapp.generated.resources.*
|
||||
import org.jetbrains.compose.resources.stringResource
|
||||
|
||||
@OptIn(ExperimentalMaterial3Api::class)
|
||||
@Composable
|
||||
@@ -64,10 +66,10 @@ fun TasksScreen(
|
||||
Scaffold(
|
||||
topBar = {
|
||||
TopAppBar(
|
||||
title = { Text("All Tasks") },
|
||||
title = { Text(stringResource(Res.string.tasks_title)) },
|
||||
navigationIcon = {
|
||||
IconButton(onClick = onNavigateBack) {
|
||||
Icon(Icons.Default.ArrowBack, contentDescription = "Back")
|
||||
Icon(Icons.Default.ArrowBack, contentDescription = stringResource(Res.string.common_back))
|
||||
}
|
||||
}
|
||||
)
|
||||
@@ -108,20 +110,15 @@ fun TasksScreen(
|
||||
tint = MaterialTheme.colorScheme.primary.copy(alpha = 0.6f)
|
||||
)
|
||||
Text(
|
||||
"No tasks yet",
|
||||
stringResource(Res.string.tasks_empty_title),
|
||||
style = MaterialTheme.typography.headlineSmall,
|
||||
fontWeight = androidx.compose.ui.text.font.FontWeight.SemiBold
|
||||
)
|
||||
Text(
|
||||
"Tasks are created from your properties.",
|
||||
stringResource(Res.string.tasks_empty_subtitle),
|
||||
style = MaterialTheme.typography.bodyLarge,
|
||||
color = MaterialTheme.colorScheme.onSurfaceVariant
|
||||
)
|
||||
Text(
|
||||
"Go to Residences tab to add a property, then add tasks to it!",
|
||||
style = MaterialTheme.typography.bodyMedium,
|
||||
color = MaterialTheme.colorScheme.onSurfaceVariant.copy(alpha = 0.7f)
|
||||
)
|
||||
}
|
||||
}
|
||||
} else {
|
||||
|
||||
@@ -21,6 +21,8 @@ import com.example.casera.ui.components.auth.AuthHeader
|
||||
import com.example.casera.ui.components.common.ErrorCard
|
||||
import com.example.casera.viewmodel.AuthViewModel
|
||||
import com.example.casera.network.ApiResult
|
||||
import casera.composeapp.generated.resources.*
|
||||
import org.jetbrains.compose.resources.stringResource
|
||||
|
||||
@OptIn(ExperimentalMaterial3Api::class)
|
||||
@Composable
|
||||
@@ -65,7 +67,7 @@ fun VerifyEmailScreen(
|
||||
Scaffold(
|
||||
topBar = {
|
||||
TopAppBar(
|
||||
title = { Text("Verify Email", fontWeight = FontWeight.SemiBold) },
|
||||
title = { Text(stringResource(Res.string.auth_verify_title), fontWeight = FontWeight.SemiBold) },
|
||||
actions = {
|
||||
TextButton(onClick = onLogout) {
|
||||
Row(
|
||||
@@ -77,7 +79,7 @@ fun VerifyEmailScreen(
|
||||
contentDescription = null,
|
||||
modifier = Modifier.size(18.dp)
|
||||
)
|
||||
Text("Logout")
|
||||
Text(stringResource(Res.string.home_logout))
|
||||
}
|
||||
}
|
||||
},
|
||||
@@ -100,8 +102,8 @@ fun VerifyEmailScreen(
|
||||
|
||||
AuthHeader(
|
||||
icon = Icons.Default.MarkEmailRead,
|
||||
title = "Verify Your Email",
|
||||
subtitle = "You must verify your email address to continue"
|
||||
title = stringResource(Res.string.auth_verify_title),
|
||||
subtitle = stringResource(Res.string.auth_verify_subtitle)
|
||||
)
|
||||
|
||||
Spacer(modifier = Modifier.height(16.dp))
|
||||
@@ -140,7 +142,7 @@ fun VerifyEmailScreen(
|
||||
code = it
|
||||
}
|
||||
},
|
||||
label = { Text("Verification Code") },
|
||||
label = { Text(stringResource(Res.string.auth_verify_code_label)) },
|
||||
leadingIcon = {
|
||||
Icon(Icons.Default.Pin, contentDescription = null)
|
||||
},
|
||||
@@ -184,7 +186,7 @@ fun VerifyEmailScreen(
|
||||
) {
|
||||
Icon(Icons.Default.CheckCircle, contentDescription = null)
|
||||
Text(
|
||||
"Verify Email",
|
||||
stringResource(Res.string.auth_verify_button),
|
||||
style = MaterialTheme.typography.titleMedium,
|
||||
fontWeight = FontWeight.SemiBold
|
||||
)
|
||||
|
||||
@@ -19,6 +19,8 @@ import com.example.casera.ui.components.auth.AuthHeader
|
||||
import com.example.casera.ui.components.common.ErrorCard
|
||||
import com.example.casera.viewmodel.PasswordResetViewModel
|
||||
import com.example.casera.network.ApiResult
|
||||
import casera.composeapp.generated.resources.*
|
||||
import org.jetbrains.compose.resources.stringResource
|
||||
|
||||
@OptIn(ExperimentalMaterial3Api::class)
|
||||
@Composable
|
||||
@@ -56,10 +58,10 @@ fun VerifyResetCodeScreen(
|
||||
Scaffold(
|
||||
topBar = {
|
||||
TopAppBar(
|
||||
title = { Text("Verify Code") },
|
||||
title = { Text(stringResource(Res.string.auth_verify_title)) },
|
||||
navigationIcon = {
|
||||
IconButton(onClick = onNavigateBack) {
|
||||
Icon(Icons.Default.ArrowBack, contentDescription = "Back")
|
||||
Icon(Icons.Default.ArrowBack, contentDescription = stringResource(Res.string.common_back))
|
||||
}
|
||||
},
|
||||
colors = TopAppBarDefaults.topAppBarColors(
|
||||
@@ -140,7 +142,7 @@ fun VerifyResetCodeScreen(
|
||||
viewModel.resetVerifyCodeState()
|
||||
}
|
||||
},
|
||||
label = { Text("Verification Code") },
|
||||
label = { Text(stringResource(Res.string.auth_verify_code_label)) },
|
||||
placeholder = { Text("000000") },
|
||||
modifier = Modifier.fillMaxWidth(),
|
||||
singleLine = true,
|
||||
@@ -210,7 +212,7 @@ fun VerifyResetCodeScreen(
|
||||
Icon(Icons.Default.CheckCircle, contentDescription = null)
|
||||
Spacer(modifier = Modifier.width(8.dp))
|
||||
Text(
|
||||
"Verify Code",
|
||||
stringResource(Res.string.auth_verify_button),
|
||||
style = MaterialTheme.typography.titleMedium,
|
||||
fontWeight = FontWeight.SemiBold
|
||||
)
|
||||
@@ -234,7 +236,7 @@ fun VerifyResetCodeScreen(
|
||||
onNavigateBack()
|
||||
}) {
|
||||
Text(
|
||||
"Send New Code",
|
||||
stringResource(Res.string.auth_verify_resend),
|
||||
style = MaterialTheme.typography.bodyMedium,
|
||||
fontWeight = FontWeight.SemiBold
|
||||
)
|
||||
|
||||
@@ -2,13 +2,23 @@ package com.example.casera.network
|
||||
|
||||
import io.ktor.client.*
|
||||
import io.ktor.client.engine.darwin.*
|
||||
import io.ktor.client.plugins.*
|
||||
import io.ktor.client.plugins.contentnegotiation.*
|
||||
import io.ktor.client.plugins.logging.*
|
||||
import io.ktor.serialization.kotlinx.json.*
|
||||
import kotlinx.serialization.json.Json
|
||||
import platform.Foundation.NSLocale
|
||||
import platform.Foundation.preferredLanguages
|
||||
|
||||
actual fun getLocalhostAddress(): String = "127.0.0.1"
|
||||
|
||||
actual fun getDeviceLanguage(): String {
|
||||
val preferredLanguages = NSLocale.preferredLanguages
|
||||
val firstLanguage = preferredLanguages.firstOrNull() as? String
|
||||
// Extract just the language code (e.g., "en" from "en-US")
|
||||
return firstLanguage?.split("-")?.firstOrNull() ?: "en"
|
||||
}
|
||||
|
||||
actual fun createHttpClient(): HttpClient {
|
||||
return HttpClient(Darwin) {
|
||||
install(ContentNegotiation) {
|
||||
@@ -24,6 +34,10 @@ actual fun createHttpClient(): HttpClient {
|
||||
level = LogLevel.ALL
|
||||
}
|
||||
|
||||
install(DefaultRequest) {
|
||||
headers.append("Accept-Language", getDeviceLanguage())
|
||||
}
|
||||
|
||||
engine {
|
||||
configureRequest {
|
||||
setAllowsCellularAccess(true)
|
||||
|
||||
@@ -1,14 +1,20 @@
|
||||
package com.casera.shared.network
|
||||
package com.example.casera.network
|
||||
|
||||
import io.ktor.client.*
|
||||
import io.ktor.client.engine.js.*
|
||||
import io.ktor.client.plugins.*
|
||||
import io.ktor.client.plugins.contentnegotiation.*
|
||||
import io.ktor.client.plugins.logging.*
|
||||
import io.ktor.serialization.kotlinx.json.*
|
||||
import kotlinx.browser.window
|
||||
import kotlinx.serialization.json.Json
|
||||
|
||||
actual fun getLocalhostAddress(): String = "127.0.0.1"
|
||||
|
||||
actual fun getDeviceLanguage(): String {
|
||||
return window.navigator.language.split("-").firstOrNull() ?: "en"
|
||||
}
|
||||
|
||||
actual fun createHttpClient(): HttpClient {
|
||||
return HttpClient(Js) {
|
||||
install(ContentNegotiation) {
|
||||
@@ -23,5 +29,9 @@ actual fun createHttpClient(): HttpClient {
|
||||
logger = Logger.DEFAULT
|
||||
level = LogLevel.ALL
|
||||
}
|
||||
|
||||
install(DefaultRequest) {
|
||||
headers.append("Accept-Language", getDeviceLanguage())
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,14 +1,20 @@
|
||||
package com.casera.shared.network
|
||||
package com.example.casera.network
|
||||
|
||||
import io.ktor.client.*
|
||||
import io.ktor.client.engine.cio.*
|
||||
import io.ktor.client.plugins.*
|
||||
import io.ktor.client.plugins.contentnegotiation.*
|
||||
import io.ktor.client.plugins.logging.*
|
||||
import io.ktor.serialization.kotlinx.json.*
|
||||
import kotlinx.serialization.json.Json
|
||||
import java.util.Locale
|
||||
|
||||
actual fun getLocalhostAddress(): String = "127.0.0.1"
|
||||
|
||||
actual fun getDeviceLanguage(): String {
|
||||
return Locale.getDefault().language
|
||||
}
|
||||
|
||||
actual fun createHttpClient(): HttpClient {
|
||||
return HttpClient(CIO) {
|
||||
install(ContentNegotiation) {
|
||||
@@ -23,5 +29,9 @@ actual fun createHttpClient(): HttpClient {
|
||||
logger = Logger.DEFAULT
|
||||
level = LogLevel.ALL
|
||||
}
|
||||
|
||||
install(DefaultRequest) {
|
||||
headers.append("Accept-Language", getDeviceLanguage())
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,14 +1,20 @@
|
||||
package com.casera.shared.network
|
||||
package com.example.casera.network
|
||||
|
||||
import io.ktor.client.*
|
||||
import io.ktor.client.engine.js.*
|
||||
import io.ktor.client.plugins.*
|
||||
import io.ktor.client.plugins.contentnegotiation.*
|
||||
import io.ktor.client.plugins.logging.*
|
||||
import io.ktor.serialization.kotlinx.json.*
|
||||
import kotlinx.browser.window
|
||||
import kotlinx.serialization.json.Json
|
||||
|
||||
actual fun getLocalhostAddress(): String = "127.0.0.1"
|
||||
|
||||
actual fun getDeviceLanguage(): String {
|
||||
return window.navigator.language.split("-").firstOrNull() ?: "en"
|
||||
}
|
||||
|
||||
actual fun createHttpClient(): HttpClient {
|
||||
return HttpClient(Js) {
|
||||
install(ContentNegotiation) {
|
||||
@@ -23,5 +29,9 @@ actual fun createHttpClient(): HttpClient {
|
||||
logger = Logger.DEFAULT
|
||||
level = LogLevel.ALL
|
||||
}
|
||||
|
||||
install(DefaultRequest) {
|
||||
headers.append("Accept-Language", getDeviceLanguage())
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -29,19 +29,19 @@ struct ContractorDetailView: View {
|
||||
viewModel.loadContractorDetail(id: contractorId)
|
||||
}}) {
|
||||
Label(
|
||||
contractor.isFavorite ? "Remove from Favorites" : "Add to Favorites",
|
||||
contractor.isFavorite ? L10n.Contractors.removeFromFavorites : L10n.Contractors.addToFavorites,
|
||||
systemImage: contractor.isFavorite ? "star.slash" : "star"
|
||||
)
|
||||
}
|
||||
|
||||
Button(action: { showingEditSheet = true }) {
|
||||
Label("Edit", systemImage: "pencil")
|
||||
Label(L10n.Common.edit, systemImage: "pencil")
|
||||
}
|
||||
|
||||
Divider()
|
||||
|
||||
Button(role: .destructive, action: { showingDeleteAlert = true }) {
|
||||
Label("Delete", systemImage: "trash")
|
||||
Label(L10n.Common.delete, systemImage: "trash")
|
||||
}
|
||||
} label: {
|
||||
Image(systemName: "ellipsis.circle")
|
||||
@@ -59,13 +59,13 @@ struct ContractorDetailView: View {
|
||||
)
|
||||
.presentationDetents([.large])
|
||||
}
|
||||
.alert("Delete Contractor", isPresented: $showingDeleteAlert) {
|
||||
Button("Cancel", role: .cancel) {}
|
||||
Button("Delete", role: .destructive) {
|
||||
.alert(L10n.Contractors.deleteConfirm, isPresented: $showingDeleteAlert) {
|
||||
Button(L10n.Common.cancel, role: .cancel) {}
|
||||
Button(L10n.Common.delete, role: .destructive) {
|
||||
deleteContractor()
|
||||
}
|
||||
} message: {
|
||||
Text("Are you sure you want to delete this contractor? This action cannot be undone.")
|
||||
Text(L10n.Contractors.deleteMessage)
|
||||
}
|
||||
.onAppear {
|
||||
viewModel.loadContractorDetail(id: contractorId)
|
||||
@@ -200,7 +200,7 @@ struct ContractorDetailView: View {
|
||||
}
|
||||
|
||||
if contractor.taskCount > 0 {
|
||||
Text("\(contractor.taskCount) completed tasks")
|
||||
Text(String(format: L10n.Contractors.completedTasks, contractor.taskCount))
|
||||
.font(.callout)
|
||||
.foregroundColor(Color.appTextSecondary)
|
||||
}
|
||||
@@ -232,7 +232,7 @@ struct ContractorDetailView: View {
|
||||
if let phone = phone, !phone.isEmpty {
|
||||
QuickActionButton(
|
||||
icon: "phone.fill",
|
||||
label: "Call",
|
||||
label: L10n.Contractors.callAction,
|
||||
color: Color.appPrimary
|
||||
) {
|
||||
if let url = URL(string: "tel:\(phone.replacingOccurrences(of: " ", with: ""))") {
|
||||
@@ -247,7 +247,7 @@ struct ContractorDetailView: View {
|
||||
if let email = email, !email.isEmpty {
|
||||
QuickActionButton(
|
||||
icon: "envelope.fill",
|
||||
label: "Email",
|
||||
label: L10n.Contractors.emailAction,
|
||||
color: Color.appSecondary
|
||||
) {
|
||||
if let url = URL(string: "mailto:\(email)") {
|
||||
@@ -262,7 +262,7 @@ struct ContractorDetailView: View {
|
||||
if let website = website, !website.isEmpty {
|
||||
QuickActionButton(
|
||||
icon: "safari.fill",
|
||||
label: "Website",
|
||||
label: L10n.Contractors.websiteAction,
|
||||
color: Color.appAccent
|
||||
) {
|
||||
var urlString = website
|
||||
@@ -283,7 +283,7 @@ struct ContractorDetailView: View {
|
||||
if hasAddress {
|
||||
QuickActionButton(
|
||||
icon: "map.fill",
|
||||
label: "Directions",
|
||||
label: L10n.Contractors.directionsAction,
|
||||
color: Color.appError
|
||||
) {
|
||||
let address = [
|
||||
@@ -310,7 +310,7 @@ struct ContractorDetailView: View {
|
||||
let hasWebsite = contractor.website != nil && !contractor.website!.isEmpty
|
||||
|
||||
if hasPhone || hasEmail || hasWebsite {
|
||||
DetailSection(title: "Contact Information") {
|
||||
DetailSection(title: L10n.Contractors.contactInfoSection) {
|
||||
phoneContactRow(phone: contractor.phone)
|
||||
emailContactRow(email: contractor.email)
|
||||
websiteContactRow(website: contractor.website)
|
||||
@@ -323,7 +323,7 @@ struct ContractorDetailView: View {
|
||||
if let phone = phone, !phone.isEmpty {
|
||||
ContactDetailRow(
|
||||
icon: "phone.fill",
|
||||
label: "Phone",
|
||||
label: L10n.Contractors.phoneLabel,
|
||||
value: phone,
|
||||
iconColor: Color.appPrimary
|
||||
) {
|
||||
@@ -339,7 +339,7 @@ struct ContractorDetailView: View {
|
||||
if let email = email, !email.isEmpty {
|
||||
ContactDetailRow(
|
||||
icon: "envelope.fill",
|
||||
label: "Email",
|
||||
label: L10n.Contractors.emailLabel,
|
||||
value: email,
|
||||
iconColor: Color.appSecondary
|
||||
) {
|
||||
@@ -355,7 +355,7 @@ struct ContractorDetailView: View {
|
||||
if let website = website, !website.isEmpty {
|
||||
ContactDetailRow(
|
||||
icon: "safari.fill",
|
||||
label: "Website",
|
||||
label: L10n.Contractors.websiteLabel,
|
||||
value: website,
|
||||
iconColor: Color.appAccent
|
||||
) {
|
||||
@@ -385,7 +385,7 @@ struct ContractorDetailView: View {
|
||||
].compactMap { $0 }.filter { !$0.isEmpty }
|
||||
|
||||
if !addressComponents.isEmpty {
|
||||
DetailSection(title: "Address") {
|
||||
DetailSection(title: L10n.Contractors.addressSection) {
|
||||
addressButton(contractor: contractor, addressComponents: addressComponents)
|
||||
}
|
||||
}
|
||||
@@ -414,7 +414,7 @@ struct ContractorDetailView: View {
|
||||
.frame(width: 20)
|
||||
|
||||
VStack(alignment: .leading, spacing: AppSpacing.xxs) {
|
||||
Text("Location")
|
||||
Text(L10n.Contractors.locationLabel)
|
||||
.font(.caption.weight(.medium))
|
||||
.foregroundColor(Color.appTextSecondary)
|
||||
|
||||
@@ -439,14 +439,14 @@ struct ContractorDetailView: View {
|
||||
@ViewBuilder
|
||||
private func residenceSection(residenceId: Int32?) -> some View {
|
||||
if let residenceId = residenceId {
|
||||
DetailSection(title: "Associated Property") {
|
||||
DetailSection(title: L10n.Contractors.associatedPropertySection) {
|
||||
HStack(spacing: AppSpacing.sm) {
|
||||
Image(systemName: "house.fill")
|
||||
.foregroundColor(Color.appPrimary)
|
||||
.frame(width: 20)
|
||||
|
||||
VStack(alignment: .leading, spacing: AppSpacing.xxs) {
|
||||
Text("Property")
|
||||
Text(L10n.Contractors.propertyLabel)
|
||||
.font(.caption.weight(.medium))
|
||||
.foregroundColor(Color.appTextSecondary)
|
||||
|
||||
@@ -473,7 +473,7 @@ struct ContractorDetailView: View {
|
||||
@ViewBuilder
|
||||
private func notesSection(notes: String?) -> some View {
|
||||
if let notes = notes, !notes.isEmpty {
|
||||
DetailSection(title: "Notes") {
|
||||
DetailSection(title: L10n.Contractors.notesSection) {
|
||||
HStack(alignment: .top, spacing: AppSpacing.sm) {
|
||||
Image(systemName: "note.text")
|
||||
.foregroundColor(Color.appAccent)
|
||||
@@ -493,12 +493,12 @@ struct ContractorDetailView: View {
|
||||
|
||||
@ViewBuilder
|
||||
private func statisticsSection(contractor: Contractor) -> some View {
|
||||
DetailSection(title: "Statistics") {
|
||||
DetailSection(title: L10n.Contractors.statisticsSection) {
|
||||
HStack(spacing: AppSpacing.lg) {
|
||||
StatCard(
|
||||
icon: "checkmark.circle.fill",
|
||||
value: "\(contractor.taskCount)",
|
||||
label: "Tasks Completed",
|
||||
label: L10n.Contractors.tasksCompletedLabel,
|
||||
color: Color.appPrimary
|
||||
)
|
||||
|
||||
@@ -506,7 +506,7 @@ struct ContractorDetailView: View {
|
||||
StatCard(
|
||||
icon: "star.fill",
|
||||
value: String(format: "%.1f", rating.doubleValue),
|
||||
label: "Average Rating",
|
||||
label: L10n.Contractors.averageRatingLabel,
|
||||
color: Color.appAccent
|
||||
)
|
||||
}
|
||||
@@ -519,7 +519,7 @@ struct ContractorDetailView: View {
|
||||
|
||||
@ViewBuilder
|
||||
private func metadataSection(contractor: Contractor) -> some View {
|
||||
DetailSection(title: "Info") {
|
||||
DetailSection(title: L10n.Contractors.infoSection) {
|
||||
VStack(spacing: 0) {
|
||||
createdByRow(createdBy: contractor.createdBy)
|
||||
memberSinceRow(createdAt: contractor.createdAt)
|
||||
@@ -536,7 +536,7 @@ struct ContractorDetailView: View {
|
||||
.frame(width: 20)
|
||||
|
||||
VStack(alignment: .leading, spacing: AppSpacing.xxs) {
|
||||
Text("Added By")
|
||||
Text(L10n.Contractors.addedByLabel)
|
||||
.font(.caption.weight(.medium))
|
||||
.foregroundColor(Color.appTextSecondary)
|
||||
|
||||
@@ -562,7 +562,7 @@ struct ContractorDetailView: View {
|
||||
.frame(width: 20)
|
||||
|
||||
VStack(alignment: .leading, spacing: AppSpacing.xxs) {
|
||||
Text("Member Since")
|
||||
Text(L10n.Contractors.memberSinceLabel)
|
||||
.font(.caption.weight(.medium))
|
||||
.foregroundColor(Color.appTextSecondary)
|
||||
|
||||
|
||||
@@ -57,7 +57,7 @@ struct ContractorFormSheet: View {
|
||||
Image(systemName: "person")
|
||||
.foregroundColor(Color.appPrimary)
|
||||
.frame(width: 24)
|
||||
TextField("Name", text: $name)
|
||||
TextField(L10n.Contractors.nameLabel, text: $name)
|
||||
.focused($focusedField, equals: .name)
|
||||
}
|
||||
|
||||
@@ -65,13 +65,13 @@ struct ContractorFormSheet: View {
|
||||
Image(systemName: "building.2")
|
||||
.foregroundColor(Color.appPrimary)
|
||||
.frame(width: 24)
|
||||
TextField("Company", text: $company)
|
||||
TextField(L10n.Contractors.companyLabel, text: $company)
|
||||
.focused($focusedField, equals: .company)
|
||||
}
|
||||
} header: {
|
||||
Text("Basic Information")
|
||||
Text(L10n.Contractors.basicInfoSection)
|
||||
} footer: {
|
||||
Text("Required: Name")
|
||||
Text(L10n.Contractors.basicInfoFooter)
|
||||
.font(.caption)
|
||||
.foregroundColor(Color.appError)
|
||||
}
|
||||
@@ -84,7 +84,7 @@ struct ContractorFormSheet: View {
|
||||
Image(systemName: "house")
|
||||
.foregroundColor(Color.appPrimary)
|
||||
.frame(width: 24)
|
||||
Text(selectedResidenceName ?? "Personal (No Residence)")
|
||||
Text(selectedResidenceName ?? L10n.Contractors.personalNoResidence)
|
||||
.foregroundColor(selectedResidenceName == nil ? Color.appTextSecondary.opacity(0.7) : Color.appTextPrimary)
|
||||
Spacer()
|
||||
Image(systemName: "chevron.down")
|
||||
@@ -93,11 +93,11 @@ struct ContractorFormSheet: View {
|
||||
}
|
||||
}
|
||||
} header: {
|
||||
Text("Residence (Optional)")
|
||||
Text(L10n.Contractors.residenceSection)
|
||||
} footer: {
|
||||
Text(selectedResidenceId == nil
|
||||
? "Only you will see this contractor"
|
||||
: "All users of \(selectedResidenceName ?? "") will see this contractor")
|
||||
? L10n.Contractors.residenceFooterPersonal
|
||||
: String(format: L10n.Contractors.residenceFooterShared, selectedResidenceName ?? ""))
|
||||
.font(.caption)
|
||||
}
|
||||
.listRowBackground(Color.appBackgroundSecondary)
|
||||
@@ -108,7 +108,7 @@ struct ContractorFormSheet: View {
|
||||
Image(systemName: "phone.fill")
|
||||
.foregroundColor(Color.appPrimary)
|
||||
.frame(width: 24)
|
||||
TextField("Phone", text: $phone)
|
||||
TextField(L10n.Contractors.phoneLabel, text: $phone)
|
||||
.keyboardType(.phonePad)
|
||||
.focused($focusedField, equals: .phone)
|
||||
}
|
||||
@@ -117,7 +117,7 @@ struct ContractorFormSheet: View {
|
||||
Image(systemName: "envelope.fill")
|
||||
.foregroundColor(Color.appAccent)
|
||||
.frame(width: 24)
|
||||
TextField("Email", text: $email)
|
||||
TextField(L10n.Contractors.emailLabel, text: $email)
|
||||
.keyboardType(.emailAddress)
|
||||
.textInputAutocapitalization(.never)
|
||||
.autocorrectionDisabled()
|
||||
@@ -128,14 +128,14 @@ struct ContractorFormSheet: View {
|
||||
Image(systemName: "globe")
|
||||
.foregroundColor(Color.appAccent)
|
||||
.frame(width: 24)
|
||||
TextField("Website", text: $website)
|
||||
TextField(L10n.Contractors.websiteLabel, text: $website)
|
||||
.keyboardType(.URL)
|
||||
.textInputAutocapitalization(.never)
|
||||
.autocorrectionDisabled()
|
||||
.focused($focusedField, equals: .website)
|
||||
}
|
||||
} header: {
|
||||
Text("Contact Information")
|
||||
Text(L10n.Contractors.contactInfoSection)
|
||||
}
|
||||
.listRowBackground(Color.appBackgroundSecondary)
|
||||
|
||||
@@ -147,7 +147,7 @@ struct ContractorFormSheet: View {
|
||||
.foregroundColor(Color.appPrimary)
|
||||
.frame(width: 24)
|
||||
if selectedSpecialtyIds.isEmpty {
|
||||
Text("Select Specialties")
|
||||
Text(L10n.Contractors.selectSpecialtiesPlaceholder)
|
||||
.foregroundColor(Color.appTextSecondary.opacity(0.5))
|
||||
} else {
|
||||
let selectedNames = specialties
|
||||
@@ -164,7 +164,7 @@ struct ContractorFormSheet: View {
|
||||
}
|
||||
}
|
||||
} header: {
|
||||
Text("Specialties")
|
||||
Text(L10n.Contractors.specialtiesSection)
|
||||
}
|
||||
.listRowBackground(Color.appBackgroundSecondary)
|
||||
|
||||
@@ -174,7 +174,7 @@ struct ContractorFormSheet: View {
|
||||
Image(systemName: "location.fill")
|
||||
.foregroundColor(Color.appError)
|
||||
.frame(width: 24)
|
||||
TextField("Street Address", text: $streetAddress)
|
||||
TextField(L10n.Contractors.streetAddressLabel, text: $streetAddress)
|
||||
.focused($focusedField, equals: .streetAddress)
|
||||
}
|
||||
|
||||
@@ -182,7 +182,7 @@ struct ContractorFormSheet: View {
|
||||
Image(systemName: "building.2.crop.circle")
|
||||
.foregroundColor(Color.appPrimary)
|
||||
.frame(width: 24)
|
||||
TextField("City", text: $city)
|
||||
TextField(L10n.Contractors.cityLabel, text: $city)
|
||||
.focused($focusedField, equals: .city)
|
||||
}
|
||||
|
||||
@@ -191,20 +191,20 @@ struct ContractorFormSheet: View {
|
||||
Image(systemName: "map")
|
||||
.foregroundColor(Color.appAccent)
|
||||
.frame(width: 24)
|
||||
TextField("State", text: $stateProvince)
|
||||
TextField(L10n.Contractors.stateLabel, text: $stateProvince)
|
||||
.focused($focusedField, equals: .stateProvince)
|
||||
}
|
||||
|
||||
Divider()
|
||||
.frame(height: 24)
|
||||
|
||||
TextField("ZIP", text: $postalCode)
|
||||
TextField(L10n.Contractors.zipLabel, text: $postalCode)
|
||||
.keyboardType(.numberPad)
|
||||
.focused($focusedField, equals: .postalCode)
|
||||
.frame(maxWidth: 100)
|
||||
}
|
||||
} header: {
|
||||
Text("Address")
|
||||
Text(L10n.Contractors.addressSection)
|
||||
}
|
||||
.listRowBackground(Color.appBackgroundSecondary)
|
||||
|
||||
@@ -221,9 +221,9 @@ struct ContractorFormSheet: View {
|
||||
.focused($focusedField, equals: .notes)
|
||||
}
|
||||
} header: {
|
||||
Text("Notes")
|
||||
Text(L10n.Contractors.notesSection)
|
||||
} footer: {
|
||||
Text("Private notes about this contractor")
|
||||
Text(L10n.Contractors.notesFooter)
|
||||
.font(.caption)
|
||||
}
|
||||
.listRowBackground(Color.appBackgroundSecondary)
|
||||
@@ -231,7 +231,7 @@ struct ContractorFormSheet: View {
|
||||
// Favorite
|
||||
Section {
|
||||
Toggle(isOn: $isFavorite) {
|
||||
Label("Mark as Favorite", systemImage: "star.fill")
|
||||
Label(L10n.Contractors.favoriteLabel, systemImage: "star.fill")
|
||||
.foregroundColor(isFavorite ? Color.appAccent : Color.appTextPrimary)
|
||||
}
|
||||
.tint(Color.appAccent)
|
||||
@@ -255,11 +255,11 @@ struct ContractorFormSheet: View {
|
||||
.listStyle(.plain)
|
||||
.scrollContentBackground(.hidden)
|
||||
.background(Color.appBackgroundPrimary)
|
||||
.navigationTitle(contractor == nil ? "Add Contractor" : "Edit Contractor")
|
||||
.navigationTitle(contractor == nil ? L10n.Contractors.addTitle : L10n.Contractors.editTitle)
|
||||
.navigationBarTitleDisplayMode(.inline)
|
||||
.toolbar {
|
||||
ToolbarItem(placement: .cancellationAction) {
|
||||
Button("Cancel") {
|
||||
Button(L10n.Common.cancel) {
|
||||
dismiss()
|
||||
}
|
||||
}
|
||||
@@ -269,7 +269,7 @@ struct ContractorFormSheet: View {
|
||||
if viewModel.isCreating || viewModel.isUpdating {
|
||||
ProgressView()
|
||||
} else {
|
||||
Text(contractor == nil ? "Add" : "Save")
|
||||
Text(contractor == nil ? L10n.Contractors.addButton : L10n.Common.save)
|
||||
.bold()
|
||||
}
|
||||
}
|
||||
@@ -305,7 +305,7 @@ struct ContractorFormSheet: View {
|
||||
showingResidencePicker = false
|
||||
}) {
|
||||
HStack {
|
||||
Text("Personal (No Residence)")
|
||||
Text(L10n.Contractors.personalNoResidence)
|
||||
.foregroundColor(Color.appTextPrimary)
|
||||
Spacer()
|
||||
if selectedResidenceId == nil {
|
||||
@@ -348,11 +348,11 @@ struct ContractorFormSheet: View {
|
||||
.listStyle(.plain)
|
||||
.scrollContentBackground(.hidden)
|
||||
.background(Color.appBackgroundPrimary)
|
||||
.navigationTitle("Select Residence")
|
||||
.navigationTitle(L10n.Contractors.selectResidence)
|
||||
.navigationBarTitleDisplayMode(.inline)
|
||||
.toolbar {
|
||||
ToolbarItem(placement: .confirmationAction) {
|
||||
Button("Done") {
|
||||
Button(L10n.Common.done) {
|
||||
showingResidencePicker = false
|
||||
}
|
||||
}
|
||||
@@ -390,16 +390,16 @@ struct ContractorFormSheet: View {
|
||||
.listStyle(.plain)
|
||||
.scrollContentBackground(.hidden)
|
||||
.background(Color.appBackgroundPrimary)
|
||||
.navigationTitle("Select Specialties")
|
||||
.navigationTitle(L10n.Contractors.selectSpecialties)
|
||||
.navigationBarTitleDisplayMode(.inline)
|
||||
.toolbar {
|
||||
ToolbarItem(placement: .cancellationAction) {
|
||||
Button("Clear") {
|
||||
Button(L10n.Contractors.clearAction) {
|
||||
selectedSpecialtyIds.removeAll()
|
||||
}
|
||||
}
|
||||
ToolbarItem(placement: .confirmationAction) {
|
||||
Button("Done") {
|
||||
Button(L10n.Common.done) {
|
||||
showingSpecialtyPicker = false
|
||||
}
|
||||
}
|
||||
|
||||
@@ -46,7 +46,7 @@ struct ContractorsListView: View {
|
||||
|
||||
VStack(spacing: 0) {
|
||||
// Search Bar
|
||||
SearchBar(text: $searchText, placeholder: "Search contractors...")
|
||||
SearchBar(text: $searchText, placeholder: L10n.Contractors.searchPlaceholder)
|
||||
.padding(.horizontal, AppSpacing.md)
|
||||
.padding(.top, AppSpacing.sm)
|
||||
|
||||
@@ -56,7 +56,7 @@ struct ContractorsListView: View {
|
||||
HStack(spacing: AppSpacing.xs) {
|
||||
if showFavoritesOnly {
|
||||
FilterChip(
|
||||
title: "Favorites",
|
||||
title: L10n.Contractors.favorites,
|
||||
icon: "star.fill",
|
||||
onRemove: { showFavoritesOnly = false }
|
||||
)
|
||||
@@ -106,7 +106,7 @@ struct ContractorsListView: View {
|
||||
)
|
||||
}
|
||||
}
|
||||
.navigationTitle("Contractors")
|
||||
.navigationTitle(L10n.Contractors.title)
|
||||
.navigationBarTitleDisplayMode(.inline)
|
||||
.toolbar {
|
||||
ToolbarItem(placement: .navigationBarTrailing) {
|
||||
@@ -124,7 +124,7 @@ struct ContractorsListView: View {
|
||||
Button(action: {
|
||||
selectedSpecialty = nil
|
||||
}) {
|
||||
Label("All Specialties", systemImage: selectedSpecialty == nil ? "checkmark" : "")
|
||||
Label(L10n.Contractors.allSpecialties, systemImage: selectedSpecialty == nil ? "checkmark" : "")
|
||||
}
|
||||
|
||||
Divider()
|
||||
@@ -302,12 +302,12 @@ struct EmptyContractorsView: View {
|
||||
.font(.system(size: 64))
|
||||
.foregroundColor(Color.appTextSecondary.opacity(0.7))
|
||||
|
||||
Text(hasFilters ? "No contractors found" : "No contractors yet")
|
||||
Text(hasFilters ? L10n.Contractors.emptyFiltered : L10n.Contractors.emptyTitle)
|
||||
.font(.title3.weight(.semibold))
|
||||
.foregroundColor(Color.appTextSecondary)
|
||||
|
||||
if !hasFilters {
|
||||
Text("Add your first contractor to get started")
|
||||
Text(L10n.Contractors.emptyNoFilters)
|
||||
.font(.callout)
|
||||
.foregroundColor(Color.appTextSecondary.opacity(0.7))
|
||||
}
|
||||
|
||||
@@ -28,8 +28,8 @@ struct DocumentsTabContent: View {
|
||||
if !subscriptionCache.shouldShowUpgradePrompt(currentCount: 0, limitKey: "documents") {
|
||||
EmptyStateView(
|
||||
icon: "doc",
|
||||
title: "No documents found",
|
||||
message: "Add documents related to your residence"
|
||||
title: L10n.Documents.noDocumentsFound,
|
||||
message: L10n.Documents.noDocumentsMessage
|
||||
)
|
||||
} else {
|
||||
UpgradeFeatureView(
|
||||
|
||||
@@ -30,8 +30,8 @@ struct WarrantiesTabContent: View {
|
||||
if !subscriptionCache.shouldShowUpgradePrompt(currentCount: 0, limitKey: "documents") {
|
||||
EmptyStateView(
|
||||
icon: "doc.text.viewfinder",
|
||||
title: "No warranties found",
|
||||
message: "Add warranties to track coverage periods"
|
||||
title: L10n.Documents.noWarrantiesFound,
|
||||
message: L10n.Documents.noWarrantiesMessage
|
||||
)
|
||||
} else {
|
||||
UpgradeFeatureView(
|
||||
|
||||
@@ -29,10 +29,10 @@ struct WarrantyCard: View {
|
||||
}
|
||||
|
||||
// Fallback to client-side calculation (shouldn't happen with updated backend)
|
||||
if !document.isActive { return "Inactive" }
|
||||
if daysUntilExpiration < 0 { return "Expired" }
|
||||
if daysUntilExpiration < 30 { return "Expiring soon" }
|
||||
return "Active"
|
||||
if !document.isActive { return L10n.Documents.inactive }
|
||||
if daysUntilExpiration < 0 { return L10n.Documents.expired }
|
||||
if daysUntilExpiration < 30 { return L10n.Documents.expiringSoon }
|
||||
return L10n.Documents.activeStatus
|
||||
}
|
||||
|
||||
var body: some View {
|
||||
@@ -68,10 +68,10 @@ struct WarrantyCard: View {
|
||||
// Details
|
||||
HStack {
|
||||
VStack(alignment: .leading, spacing: 2) {
|
||||
Text("Provider")
|
||||
Text(L10n.Documents.provider)
|
||||
.font(.caption.weight(.medium))
|
||||
.foregroundColor(Color.appTextSecondary)
|
||||
Text(document.provider ?? "N/A")
|
||||
Text(document.provider ?? L10n.Documents.na)
|
||||
.font(.body)
|
||||
.fontWeight(.medium)
|
||||
.foregroundColor(Color.appTextPrimary)
|
||||
@@ -80,10 +80,10 @@ struct WarrantyCard: View {
|
||||
Spacer()
|
||||
|
||||
VStack(alignment: .trailing, spacing: 2) {
|
||||
Text("Expires")
|
||||
Text(L10n.Documents.expires)
|
||||
.font(.caption.weight(.medium))
|
||||
.foregroundColor(Color.appTextSecondary)
|
||||
Text(DateUtils.formatDateMedium(document.endDate) ?? "N/A")
|
||||
Text(DateUtils.formatDateMedium(document.endDate) ?? L10n.Documents.na)
|
||||
.font(.body)
|
||||
.fontWeight(.medium)
|
||||
.foregroundColor(Color.appTextPrimary)
|
||||
@@ -91,7 +91,7 @@ struct WarrantyCard: View {
|
||||
}
|
||||
|
||||
if document.isActive && daysUntilExpiration >= 0 {
|
||||
Text("\(daysUntilExpiration) days remaining")
|
||||
Text(String(format: L10n.Documents.daysRemainingCount, daysUntilExpiration))
|
||||
.font(.footnote.weight(.medium))
|
||||
.foregroundColor(statusColor)
|
||||
}
|
||||
|
||||
@@ -14,7 +14,7 @@ struct DocumentDetailView: View {
|
||||
var body: some View {
|
||||
ZStack {
|
||||
if viewModel.documentDetailState is DocumentDetailStateLoading {
|
||||
ProgressView("Loading document...")
|
||||
ProgressView(L10n.Documents.loadingDocument)
|
||||
} else if let successState = viewModel.documentDetailState as? DocumentDetailStateSuccess {
|
||||
documentDetailContent(document: successState.document)
|
||||
} else if let errorState = viewModel.documentDetailState as? DocumentDetailStateError {
|
||||
@@ -24,7 +24,7 @@ struct DocumentDetailView: View {
|
||||
.foregroundColor(.red)
|
||||
Text(errorState.message)
|
||||
.foregroundColor(.secondary)
|
||||
Button("Retry") {
|
||||
Button(L10n.Common.retry) {
|
||||
viewModel.loadDocumentDetail(id: documentId)
|
||||
}
|
||||
.buttonStyle(.borderedProminent)
|
||||
@@ -33,7 +33,7 @@ struct DocumentDetailView: View {
|
||||
EmptyView()
|
||||
}
|
||||
}
|
||||
.navigationTitle("Document Details")
|
||||
.navigationTitle(L10n.Documents.documentDetails)
|
||||
.navigationBarTitleDisplayMode(.inline)
|
||||
.background(
|
||||
// Hidden NavigationLink for programmatic navigation to edit
|
||||
@@ -55,13 +55,13 @@ struct DocumentDetailView: View {
|
||||
Button {
|
||||
navigateToEdit = true
|
||||
} label: {
|
||||
Label("Edit", systemImage: "pencil")
|
||||
Label(L10n.Common.edit, systemImage: "pencil")
|
||||
}
|
||||
|
||||
Button(role: .destructive) {
|
||||
showDeleteAlert = true
|
||||
} label: {
|
||||
Label("Delete", systemImage: "trash")
|
||||
Label(L10n.Common.delete, systemImage: "trash")
|
||||
}
|
||||
} label: {
|
||||
Image(systemName: "ellipsis.circle")
|
||||
@@ -72,13 +72,13 @@ struct DocumentDetailView: View {
|
||||
.onAppear {
|
||||
viewModel.loadDocumentDetail(id: documentId)
|
||||
}
|
||||
.alert("Delete Document", isPresented: $showDeleteAlert) {
|
||||
Button("Cancel", role: .cancel) { }
|
||||
Button("Delete", role: .destructive) {
|
||||
.alert(L10n.Documents.deleteDocument, isPresented: $showDeleteAlert) {
|
||||
Button(L10n.Common.cancel, role: .cancel) { }
|
||||
Button(L10n.Common.delete, role: .destructive) {
|
||||
viewModel.deleteDocument(id: documentId)
|
||||
}
|
||||
} message: {
|
||||
Text("Are you sure you want to delete this document? This action cannot be undone.")
|
||||
Text(L10n.Documents.deleteConfirmMessage)
|
||||
}
|
||||
.onReceive(viewModel.$deleteState) { newState in
|
||||
if newState is DeleteStateSuccess {
|
||||
@@ -112,17 +112,17 @@ struct DocumentDetailView: View {
|
||||
|
||||
// Basic Information
|
||||
VStack(alignment: .leading, spacing: 12) {
|
||||
sectionHeader("Basic Information")
|
||||
sectionHeader(L10n.Documents.basicInformation)
|
||||
|
||||
detailRow(label: "Title", value: document.title)
|
||||
detailRow(label: "Type", value: DocumentTypeHelper.displayName(for: document.documentType))
|
||||
detailRow(label: L10n.Documents.titleField, value: document.title)
|
||||
detailRow(label: L10n.Documents.documentType, value: DocumentTypeHelper.displayName(for: document.documentType))
|
||||
|
||||
if let category = document.category {
|
||||
detailRow(label: "Category", value: DocumentCategoryHelper.displayName(for: category))
|
||||
detailRow(label: L10n.Documents.category, value: DocumentCategoryHelper.displayName(for: category))
|
||||
}
|
||||
|
||||
if let description = document.description_, !description.isEmpty {
|
||||
detailRow(label: "Description", value: description)
|
||||
detailRow(label: L10n.Documents.description, value: description)
|
||||
}
|
||||
}
|
||||
.padding()
|
||||
@@ -135,22 +135,22 @@ struct DocumentDetailView: View {
|
||||
if document.itemName != nil || document.modelNumber != nil ||
|
||||
document.serialNumber != nil || document.provider != nil {
|
||||
VStack(alignment: .leading, spacing: 12) {
|
||||
sectionHeader("Item Details")
|
||||
sectionHeader(L10n.Documents.itemDetails)
|
||||
|
||||
if let itemName = document.itemName {
|
||||
detailRow(label: "Item Name", value: itemName)
|
||||
detailRow(label: L10n.Documents.itemName, value: itemName)
|
||||
}
|
||||
if let modelNumber = document.modelNumber {
|
||||
detailRow(label: "Model Number", value: modelNumber)
|
||||
detailRow(label: L10n.Documents.modelNumber, value: modelNumber)
|
||||
}
|
||||
if let serialNumber = document.serialNumber {
|
||||
detailRow(label: "Serial Number", value: serialNumber)
|
||||
detailRow(label: L10n.Documents.serialNumber, value: serialNumber)
|
||||
}
|
||||
if let provider = document.provider {
|
||||
detailRow(label: "Provider", value: provider)
|
||||
detailRow(label: L10n.Documents.provider, value: provider)
|
||||
}
|
||||
if let providerContact = document.providerContact {
|
||||
detailRow(label: "Provider Contact", value: providerContact)
|
||||
detailRow(label: L10n.Documents.providerContact, value: providerContact)
|
||||
}
|
||||
}
|
||||
.padding()
|
||||
@@ -163,16 +163,16 @@ struct DocumentDetailView: View {
|
||||
if document.claimPhone != nil || document.claimEmail != nil ||
|
||||
document.claimWebsite != nil {
|
||||
VStack(alignment: .leading, spacing: 12) {
|
||||
sectionHeader("Claim Information")
|
||||
sectionHeader(L10n.Documents.claimInformation)
|
||||
|
||||
if let claimPhone = document.claimPhone {
|
||||
detailRow(label: "Claim Phone", value: claimPhone)
|
||||
detailRow(label: L10n.Documents.claimPhone, value: claimPhone)
|
||||
}
|
||||
if let claimEmail = document.claimEmail {
|
||||
detailRow(label: "Claim Email", value: claimEmail)
|
||||
detailRow(label: L10n.Documents.claimEmail, value: claimEmail)
|
||||
}
|
||||
if let claimWebsite = document.claimWebsite {
|
||||
detailRow(label: "Claim Website", value: claimWebsite)
|
||||
detailRow(label: L10n.Documents.claimWebsite, value: claimWebsite)
|
||||
}
|
||||
}
|
||||
.padding()
|
||||
@@ -185,16 +185,16 @@ struct DocumentDetailView: View {
|
||||
if document.purchaseDate != nil || document.startDate != nil ||
|
||||
document.endDate != nil {
|
||||
VStack(alignment: .leading, spacing: 12) {
|
||||
sectionHeader("Important Dates")
|
||||
sectionHeader(L10n.Documents.importantDates)
|
||||
|
||||
if let purchaseDate = document.purchaseDate {
|
||||
detailRow(label: "Purchase Date", value: DateUtils.formatDateMedium(purchaseDate))
|
||||
detailRow(label: L10n.Documents.purchaseDate, value: DateUtils.formatDateMedium(purchaseDate))
|
||||
}
|
||||
if let startDate = document.startDate {
|
||||
detailRow(label: "Start Date", value: DateUtils.formatDateMedium(startDate))
|
||||
detailRow(label: L10n.Documents.startDate, value: DateUtils.formatDateMedium(startDate))
|
||||
}
|
||||
if let endDate = document.endDate {
|
||||
detailRow(label: "End Date", value: DateUtils.formatDateMedium(endDate))
|
||||
detailRow(label: L10n.Documents.endDate, value: DateUtils.formatDateMedium(endDate))
|
||||
}
|
||||
}
|
||||
.padding()
|
||||
@@ -207,7 +207,7 @@ struct DocumentDetailView: View {
|
||||
// Images
|
||||
if !document.images.isEmpty {
|
||||
VStack(alignment: .leading, spacing: 12) {
|
||||
sectionHeader("Images (\(document.images.count))")
|
||||
sectionHeader("\(L10n.Documents.images) (\(document.images.count))")
|
||||
|
||||
LazyVGrid(columns: [GridItem(.flexible()), GridItem(.flexible()), GridItem(.flexible())], spacing: 8) {
|
||||
ForEach(Array(document.images.prefix(6).enumerated()), id: \.element.id) { index, image in
|
||||
@@ -256,16 +256,16 @@ struct DocumentDetailView: View {
|
||||
|
||||
// Associations
|
||||
VStack(alignment: .leading, spacing: 12) {
|
||||
sectionHeader("Associations")
|
||||
sectionHeader(L10n.Documents.associations)
|
||||
|
||||
if let residenceAddress = document.residenceAddress {
|
||||
detailRow(label: "Residence", value: residenceAddress)
|
||||
detailRow(label: L10n.Documents.residence, value: residenceAddress)
|
||||
}
|
||||
if let contractorName = document.contractorName {
|
||||
detailRow(label: "Contractor", value: contractorName)
|
||||
detailRow(label: L10n.Documents.contractor, value: contractorName)
|
||||
}
|
||||
if let contractorPhone = document.contractorPhone {
|
||||
detailRow(label: "Contractor Phone", value: contractorPhone)
|
||||
detailRow(label: L10n.Documents.contractorPhone, value: contractorPhone)
|
||||
}
|
||||
}
|
||||
.padding()
|
||||
@@ -276,13 +276,13 @@ struct DocumentDetailView: View {
|
||||
// Additional Information
|
||||
if document.tags != nil || document.notes != nil {
|
||||
VStack(alignment: .leading, spacing: 12) {
|
||||
sectionHeader("Additional Information")
|
||||
sectionHeader(L10n.Documents.additionalInformation)
|
||||
|
||||
if let tags = document.tags, !tags.isEmpty {
|
||||
detailRow(label: "Tags", value: tags)
|
||||
detailRow(label: L10n.Documents.tags, value: tags)
|
||||
}
|
||||
if let notes = document.notes, !notes.isEmpty {
|
||||
detailRow(label: "Notes", value: notes)
|
||||
detailRow(label: L10n.Documents.notes, value: notes)
|
||||
}
|
||||
}
|
||||
.padding()
|
||||
@@ -294,13 +294,13 @@ struct DocumentDetailView: View {
|
||||
// File Information
|
||||
if document.fileUrl != nil {
|
||||
VStack(alignment: .leading, spacing: 12) {
|
||||
sectionHeader("Attached File")
|
||||
sectionHeader(L10n.Documents.attachedFile)
|
||||
|
||||
if let fileType = document.fileType {
|
||||
detailRow(label: "File Type", value: fileType)
|
||||
detailRow(label: L10n.Documents.fileType, value: fileType)
|
||||
}
|
||||
if let fileSize = document.fileSize {
|
||||
detailRow(label: "File Size", value: formatFileSize(bytes: Int(fileSize)))
|
||||
detailRow(label: L10n.Documents.fileSize, value: formatFileSize(bytes: Int(fileSize)))
|
||||
}
|
||||
|
||||
Button(action: {
|
||||
@@ -308,7 +308,7 @@ struct DocumentDetailView: View {
|
||||
}) {
|
||||
HStack {
|
||||
Image(systemName: "arrow.down.circle")
|
||||
Text("Download File")
|
||||
Text(L10n.Documents.downloadFile)
|
||||
}
|
||||
.frame(maxWidth: .infinity)
|
||||
.padding()
|
||||
@@ -325,16 +325,16 @@ struct DocumentDetailView: View {
|
||||
|
||||
// Metadata
|
||||
VStack(alignment: .leading, spacing: 12) {
|
||||
sectionHeader("Metadata")
|
||||
sectionHeader(L10n.Documents.metadata)
|
||||
|
||||
if let uploadedBy = document.uploadedByUsername {
|
||||
detailRow(label: "Uploaded By", value: uploadedBy)
|
||||
detailRow(label: L10n.Documents.uploadedBy, value: uploadedBy)
|
||||
}
|
||||
if let createdAt = document.createdAt {
|
||||
detailRow(label: "Created", value: DateUtils.formatDateTime(createdAt))
|
||||
detailRow(label: L10n.Documents.created, value: DateUtils.formatDateTime(createdAt))
|
||||
}
|
||||
if let updatedAt = document.updatedAt {
|
||||
detailRow(label: "Updated", value: DateUtils.formatDateTime(updatedAt))
|
||||
detailRow(label: L10n.Documents.updated, value: DateUtils.formatDateTime(updatedAt))
|
||||
}
|
||||
}
|
||||
.padding()
|
||||
@@ -355,7 +355,7 @@ struct DocumentDetailView: View {
|
||||
|
||||
HStack {
|
||||
VStack(alignment: .leading, spacing: 4) {
|
||||
Text("Status")
|
||||
Text(L10n.Documents.status)
|
||||
.font(.caption)
|
||||
.foregroundColor(.secondary)
|
||||
Text(statusText)
|
||||
@@ -368,7 +368,7 @@ struct DocumentDetailView: View {
|
||||
|
||||
if document.isActive && daysUntilExpiration >= 0 {
|
||||
VStack(alignment: .trailing, spacing: 4) {
|
||||
Text("Days Remaining")
|
||||
Text(L10n.Documents.daysRemaining)
|
||||
.font(.caption)
|
||||
.foregroundColor(.secondary)
|
||||
Text("\(daysUntilExpiration)")
|
||||
@@ -421,13 +421,13 @@ struct DocumentDetailView: View {
|
||||
|
||||
private func getStatusText(isActive: Bool, daysUntilExpiration: Int32) -> String {
|
||||
if !isActive {
|
||||
return "Inactive"
|
||||
return L10n.Documents.inactive
|
||||
} else if daysUntilExpiration < 0 {
|
||||
return "Expired"
|
||||
return L10n.Documents.expired
|
||||
} else if daysUntilExpiration < 30 {
|
||||
return "Expiring Soon"
|
||||
return L10n.Documents.expiringSoon
|
||||
} else {
|
||||
return "Active"
|
||||
return L10n.Documents.activeStatus
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -115,47 +115,47 @@ struct DocumentFormView: View {
|
||||
private var warrantySection: some View {
|
||||
if isWarranty {
|
||||
Section {
|
||||
TextField("Item Name", text: $itemName)
|
||||
TextField(L10n.Documents.itemName, text: $itemName)
|
||||
if !itemNameError.isEmpty {
|
||||
Text(itemNameError)
|
||||
.font(.caption)
|
||||
.foregroundColor(Color.appError)
|
||||
}
|
||||
|
||||
TextField("Model Number (optional)", text: $modelNumber)
|
||||
TextField("Serial Number (optional)", text: $serialNumber)
|
||||
TextField(L10n.Documents.modelNumberOptional, text: $modelNumber)
|
||||
TextField(L10n.Documents.serialNumberOptional, text: $serialNumber)
|
||||
|
||||
TextField("Provider/Company", text: $provider)
|
||||
TextField(L10n.Documents.providerCompany, text: $provider)
|
||||
if !providerError.isEmpty {
|
||||
Text(providerError)
|
||||
.font(.caption)
|
||||
.foregroundColor(Color.appError)
|
||||
}
|
||||
|
||||
TextField("Provider Contact (optional)", text: $providerContact)
|
||||
TextField(L10n.Documents.providerContactOptional, text: $providerContact)
|
||||
} header: {
|
||||
Text("Warranty Details")
|
||||
Text(L10n.Documents.warrantyDetails)
|
||||
} footer: {
|
||||
Text("Required for warranties: Item Name and Provider")
|
||||
Text(L10n.Documents.requiredWarrantyFields)
|
||||
.font(.caption)
|
||||
.foregroundColor(Color.appError)
|
||||
}
|
||||
.listRowBackground(Color.appBackgroundSecondary)
|
||||
|
||||
Section("Warranty Claims") {
|
||||
TextField("Claim Phone (optional)", text: $claimPhone)
|
||||
Section(L10n.Documents.warrantyClaims) {
|
||||
TextField(L10n.Documents.claimPhoneOptional, text: $claimPhone)
|
||||
.keyboardType(.phonePad)
|
||||
TextField("Claim Email (optional)", text: $claimEmail)
|
||||
TextField(L10n.Documents.claimEmailOptional, text: $claimEmail)
|
||||
.keyboardType(.emailAddress)
|
||||
TextField("Claim Website (optional)", text: $claimWebsite)
|
||||
TextField(L10n.Documents.claimWebsiteOptional, text: $claimWebsite)
|
||||
.keyboardType(.URL)
|
||||
}
|
||||
.listRowBackground(Color.appBackgroundSecondary)
|
||||
|
||||
Section("Warranty Dates") {
|
||||
TextField("Purchase Date (YYYY-MM-DD)", text: $purchaseDate)
|
||||
TextField("Warranty Start Date (YYYY-MM-DD)", text: $startDate)
|
||||
TextField("Warranty End Date (YYYY-MM-DD)", text: $endDate)
|
||||
Section(L10n.Documents.warrantyDates) {
|
||||
TextField(L10n.Documents.purchaseDate, text: $purchaseDate)
|
||||
TextField(L10n.Documents.startDate, text: $startDate)
|
||||
TextField(L10n.Documents.endDate, text: $endDate)
|
||||
}
|
||||
.listRowBackground(Color.appBackgroundSecondary)
|
||||
}
|
||||
@@ -164,7 +164,7 @@ struct DocumentFormView: View {
|
||||
@ViewBuilder
|
||||
private var photosSections: some View {
|
||||
if isEditMode && !existingImages.isEmpty {
|
||||
Section("Existing Photos") {
|
||||
Section(L10n.Documents.existingPhotos) {
|
||||
ForEach(existingImages, id: \.id) { image in
|
||||
AsyncImage(url: URL(string: image.imageUrl)) { phase in
|
||||
switch phase {
|
||||
@@ -187,19 +187,19 @@ struct DocumentFormView: View {
|
||||
.listRowBackground(Color.appBackgroundSecondary)
|
||||
}
|
||||
|
||||
Section("Photos") {
|
||||
Section(L10n.Documents.photos) {
|
||||
PhotosPicker(selection: $selectedPhotoItems, maxSelectionCount: isEditMode ? 10 : 5, matching: .images) {
|
||||
Label("Select from Library", systemImage: "photo")
|
||||
Label(L10n.Documents.selectFromLibrary, systemImage: "photo")
|
||||
}
|
||||
|
||||
Button {
|
||||
showCamera = true
|
||||
} label: {
|
||||
Label("Take Photo", systemImage: "camera")
|
||||
Label(L10n.Documents.takePhoto, systemImage: "camera")
|
||||
}
|
||||
|
||||
if !selectedImages.isEmpty {
|
||||
Text("\(selectedImages.count) photo(s) selected")
|
||||
Text(String(format: L10n.Documents.photosSelected, selectedImages.count))
|
||||
.font(.caption)
|
||||
.foregroundColor(.secondary)
|
||||
}
|
||||
@@ -215,11 +215,11 @@ struct DocumentFormView: View {
|
||||
.listStyle(.plain)
|
||||
.scrollContentBackground(.hidden)
|
||||
.background(Color.appBackgroundPrimary)
|
||||
.navigationTitle(isEditMode ? (isWarranty ? "Edit Warranty" : "Edit Document") : (isWarranty ? "Add Warranty" : "Add Document"))
|
||||
.navigationTitle(isEditMode ? (isWarranty ? L10n.Documents.editWarranty : L10n.Documents.editDocument) : (isWarranty ? L10n.Documents.addWarranty : L10n.Documents.addDocument))
|
||||
.navigationBarTitleDisplayMode(.inline)
|
||||
.toolbar {
|
||||
ToolbarItem(placement: .cancellationAction) {
|
||||
Button("Cancel") {
|
||||
Button(L10n.Common.cancel) {
|
||||
if isEditMode {
|
||||
dismiss()
|
||||
} else {
|
||||
@@ -229,7 +229,7 @@ struct DocumentFormView: View {
|
||||
}
|
||||
|
||||
ToolbarItem(placement: .confirmationAction) {
|
||||
Button(isEditMode ? "Update" : "Save") {
|
||||
Button(isEditMode ? L10n.Documents.update : L10n.Common.save) {
|
||||
submitForm()
|
||||
}
|
||||
.disabled(!canSave || isProcessing)
|
||||
@@ -264,8 +264,8 @@ struct DocumentFormView: View {
|
||||
existingImages = doc.images
|
||||
}
|
||||
}
|
||||
.alert("Error", isPresented: $showAlert) {
|
||||
Button("OK", role: .cancel) {}
|
||||
.alert(L10n.Common.error, isPresented: $showAlert) {
|
||||
Button(L10n.Common.ok, role: .cancel) {}
|
||||
} message: {
|
||||
Text(alertMessage)
|
||||
}
|
||||
@@ -280,8 +280,8 @@ struct DocumentFormView: View {
|
||||
if residenceViewModel.isLoading {
|
||||
ProgressView()
|
||||
} else {
|
||||
Picker("Select Property", selection: $selectedResidenceId) {
|
||||
Text("Select Property").tag(nil as Int?)
|
||||
Picker(L10n.Documents.selectProperty, selection: $selectedResidenceId) {
|
||||
Text(L10n.Documents.selectProperty).tag(nil as Int?)
|
||||
ForEach(residencesArray, id: \.id) { residence in
|
||||
Text(residence.name).tag(residence.id as Int?)
|
||||
}
|
||||
@@ -294,9 +294,9 @@ struct DocumentFormView: View {
|
||||
}
|
||||
}
|
||||
} header: {
|
||||
Text("Property")
|
||||
Text(L10n.Documents.property)
|
||||
} footer: {
|
||||
Text("Required")
|
||||
Text(L10n.Documents.required)
|
||||
.font(.caption)
|
||||
.foregroundColor(Color.appError)
|
||||
}
|
||||
@@ -307,16 +307,16 @@ struct DocumentFormView: View {
|
||||
Section {
|
||||
if isEditMode {
|
||||
HStack {
|
||||
Text("Document Type")
|
||||
Text(L10n.Documents.documentType)
|
||||
Spacer()
|
||||
Text(DocumentTypeHelper.displayName(for: selectedDocumentType))
|
||||
.foregroundColor(.secondary)
|
||||
}
|
||||
Text("Document type cannot be changed")
|
||||
Text(L10n.Documents.documentTypeCannotChange)
|
||||
.font(.caption)
|
||||
.foregroundColor(.secondary)
|
||||
} else {
|
||||
Picker("Document Type", selection: $selectedDocumentType) {
|
||||
Picker(L10n.Documents.documentType, selection: $selectedDocumentType) {
|
||||
ForEach(DocumentTypeHelper.allTypes, id: \.self) { type in
|
||||
Text(DocumentTypeHelper.displayName(for: type)).tag(type)
|
||||
}
|
||||
@@ -327,19 +327,19 @@ struct DocumentFormView: View {
|
||||
|
||||
// Basic Information
|
||||
Section {
|
||||
TextField("Title", text: $title)
|
||||
TextField(L10n.Documents.titleField, text: $title)
|
||||
if !titleError.isEmpty {
|
||||
Text(titleError)
|
||||
.font(.caption)
|
||||
.foregroundColor(Color.appError)
|
||||
}
|
||||
|
||||
TextField("Description (optional)", text: $description, axis: .vertical)
|
||||
TextField(L10n.Documents.descriptionOptional, text: $description, axis: .vertical)
|
||||
.lineLimit(3...6)
|
||||
} header: {
|
||||
Text("Basic Information")
|
||||
Text(L10n.Documents.basicInformation)
|
||||
} footer: {
|
||||
Text("Required: Title")
|
||||
Text(L10n.Documents.requiredTitle)
|
||||
.font(.caption)
|
||||
.foregroundColor(Color.appError)
|
||||
}
|
||||
@@ -350,9 +350,9 @@ struct DocumentFormView: View {
|
||||
|
||||
// Category
|
||||
if isWarranty || ["inspection", "manual", "receipt"].contains(selectedDocumentType) {
|
||||
Section("Category") {
|
||||
Picker("Category (optional)", selection: $selectedCategory) {
|
||||
Text("None").tag(nil as String?)
|
||||
Section(L10n.Documents.category) {
|
||||
Picker(L10n.Documents.categoryOptional, selection: $selectedCategory) {
|
||||
Text(L10n.Documents.none).tag(nil as String?)
|
||||
ForEach(DocumentCategoryHelper.allCategories, id: \.self) { category in
|
||||
Text(DocumentCategoryHelper.displayName(for: category)).tag(category as String?)
|
||||
}
|
||||
@@ -362,10 +362,10 @@ struct DocumentFormView: View {
|
||||
}
|
||||
|
||||
// Additional Information
|
||||
Section("Additional Information") {
|
||||
TextField("Tags (optional)", text: $tags)
|
||||
Section(L10n.Documents.additionalInformation) {
|
||||
TextField(L10n.Documents.tagsOptional, text: $tags)
|
||||
.textInputAutocapitalization(.never)
|
||||
TextField("Notes (optional)", text: $notes, axis: .vertical)
|
||||
TextField(L10n.Documents.notesOptional, text: $notes, axis: .vertical)
|
||||
.lineLimit(3...6)
|
||||
}
|
||||
.listRowBackground(Color.appBackgroundSecondary)
|
||||
@@ -373,7 +373,7 @@ struct DocumentFormView: View {
|
||||
// Active Status (Edit mode only)
|
||||
if isEditMode {
|
||||
Section {
|
||||
Toggle("Active", isOn: $isActive)
|
||||
Toggle(L10n.Documents.active, isOn: $isActive)
|
||||
}
|
||||
.listRowBackground(Color.appBackgroundSecondary)
|
||||
}
|
||||
@@ -391,22 +391,22 @@ struct DocumentFormView: View {
|
||||
residenceError = ""
|
||||
|
||||
if title.isEmpty {
|
||||
titleError = "Title is required"
|
||||
titleError = L10n.Documents.titleRequired
|
||||
isValid = false
|
||||
}
|
||||
|
||||
if needsResidenceSelection && selectedResidenceId == nil {
|
||||
residenceError = "Property is required"
|
||||
residenceError = L10n.Documents.propertyRequired
|
||||
isValid = false
|
||||
}
|
||||
|
||||
if isWarranty {
|
||||
if itemName.isEmpty {
|
||||
itemNameError = "Item name is required for warranties"
|
||||
itemNameError = L10n.Documents.itemNameRequired
|
||||
isValid = false
|
||||
}
|
||||
if provider.isEmpty {
|
||||
providerError = "Provider is required for warranties"
|
||||
providerError = L10n.Documents.providerRequired
|
||||
isValid = false
|
||||
}
|
||||
}
|
||||
@@ -416,7 +416,7 @@ struct DocumentFormView: View {
|
||||
|
||||
private func submitForm() {
|
||||
guard validateForm() else {
|
||||
alertMessage = "Please fill in all required fields"
|
||||
alertMessage = L10n.Documents.fillRequiredFields
|
||||
showAlert = true
|
||||
return
|
||||
}
|
||||
@@ -432,7 +432,7 @@ struct DocumentFormView: View {
|
||||
actualResidenceId = Int32(selectedId)
|
||||
} else {
|
||||
isProcessing = false
|
||||
alertMessage = "No residence selected"
|
||||
alertMessage = L10n.Documents.noResidenceSelected
|
||||
showAlert = true
|
||||
return
|
||||
}
|
||||
@@ -441,7 +441,7 @@ struct DocumentFormView: View {
|
||||
// Update document
|
||||
guard let docId = doc.id else {
|
||||
isProcessing = false
|
||||
alertMessage = "Document ID is missing"
|
||||
alertMessage = L10n.Documents.documentIdMissing
|
||||
showAlert = true
|
||||
return
|
||||
}
|
||||
@@ -474,7 +474,7 @@ struct DocumentFormView: View {
|
||||
documentViewModel.loadDocuments(residenceId: residenceId)
|
||||
dismiss()
|
||||
} else {
|
||||
alertMessage = error ?? "Failed to update document"
|
||||
alertMessage = error ?? L10n.Documents.failedToUpdate
|
||||
showAlert = true
|
||||
}
|
||||
}
|
||||
@@ -509,7 +509,7 @@ struct DocumentFormView: View {
|
||||
documentViewModel.loadDocuments(residenceId: actualResidenceId)
|
||||
isPresented = false
|
||||
} else {
|
||||
alertMessage = error ?? "Failed to create document"
|
||||
alertMessage = error ?? L10n.Documents.failedToCreate
|
||||
showAlert = true
|
||||
}
|
||||
}
|
||||
|
||||
@@ -40,9 +40,9 @@ struct DocumentsWarrantiesView: View {
|
||||
VStack(spacing: 0) {
|
||||
// Segmented Control for Tabs
|
||||
Picker("", selection: $selectedTab) {
|
||||
Label("Warranties", systemImage: "checkmark.shield")
|
||||
Label(L10n.Documents.warranties, systemImage: "checkmark.shield")
|
||||
.tag(DocumentWarrantyTab.warranties)
|
||||
Label("Documents", systemImage: "doc.text")
|
||||
Label(L10n.Documents.documents, systemImage: "doc.text")
|
||||
.tag(DocumentWarrantyTab.documents)
|
||||
}
|
||||
.pickerStyle(SegmentedPickerStyle())
|
||||
@@ -50,7 +50,7 @@ struct DocumentsWarrantiesView: View {
|
||||
.padding(.top, AppSpacing.sm)
|
||||
|
||||
// Search Bar
|
||||
SearchBar(text: $searchText, placeholder: "Search...")
|
||||
SearchBar(text: $searchText, placeholder: L10n.Documents.searchPlaceholder)
|
||||
.padding(.horizontal, AppSpacing.md)
|
||||
.padding(.top, AppSpacing.xs)
|
||||
|
||||
@@ -60,7 +60,7 @@ struct DocumentsWarrantiesView: View {
|
||||
HStack(spacing: AppSpacing.xs) {
|
||||
if selectedTab == .warranties && showActiveOnly {
|
||||
FilterChip(
|
||||
title: "Active Only",
|
||||
title: L10n.Documents.activeOnly,
|
||||
icon: "checkmark.circle.fill",
|
||||
onRemove: { showActiveOnly = false }
|
||||
)
|
||||
@@ -99,7 +99,7 @@ struct DocumentsWarrantiesView: View {
|
||||
}
|
||||
}
|
||||
}
|
||||
.navigationTitle("Documents & Warranties")
|
||||
.navigationTitle(L10n.Documents.documentsAndWarranties)
|
||||
.navigationBarTitleDisplayMode(.inline)
|
||||
.toolbar {
|
||||
ToolbarItem(placement: .navigationBarTrailing) {
|
||||
@@ -122,7 +122,7 @@ struct DocumentsWarrantiesView: View {
|
||||
selectedCategory = nil
|
||||
loadWarranties()
|
||||
}) {
|
||||
Label("All Categories", systemImage: selectedCategory == nil ? "checkmark" : "")
|
||||
Label(L10n.Documents.allCategories, systemImage: selectedCategory == nil ? "checkmark" : "")
|
||||
}
|
||||
|
||||
Divider()
|
||||
@@ -140,7 +140,7 @@ struct DocumentsWarrantiesView: View {
|
||||
selectedDocType = nil
|
||||
loadDocuments()
|
||||
}) {
|
||||
Label("All Types", systemImage: selectedDocType == nil ? "checkmark" : "")
|
||||
Label(L10n.Documents.allTypes, systemImage: selectedDocType == nil ? "checkmark" : "")
|
||||
}
|
||||
|
||||
Divider()
|
||||
|
||||
577
iosApp/iosApp/Helpers/L10n.swift
Normal file
577
iosApp/iosApp/Helpers/L10n.swift
Normal file
@@ -0,0 +1,577 @@
|
||||
import Foundation
|
||||
|
||||
// MARK: - L10n - Type-safe Localized Strings
|
||||
// Usage: L10n.auth.loginTitle
|
||||
|
||||
enum L10n {
|
||||
// MARK: - App
|
||||
enum App {
|
||||
static var name: String { String(localized: "app_name") }
|
||||
static var tagline: String { String(localized: "app_tagline") }
|
||||
}
|
||||
|
||||
// MARK: - Auth
|
||||
enum Auth {
|
||||
// Login
|
||||
static var loginTitle: String { String(localized: "auth_login_title") }
|
||||
static var loginSubtitle: String { String(localized: "auth_login_subtitle") }
|
||||
static var loginUsernameLabel: String { String(localized: "auth_login_username_label") }
|
||||
static var loginPasswordLabel: String { String(localized: "auth_login_password_label") }
|
||||
static var loginButton: String { String(localized: "auth_login_button") }
|
||||
static var forgotPassword: String { String(localized: "auth_forgot_password") }
|
||||
static var noAccount: String { String(localized: "auth_no_account") }
|
||||
static var loginFailed: String { String(localized: "auth_login_failed") }
|
||||
static var showPassword: String { String(localized: "auth_show_password") }
|
||||
static var hidePassword: String { String(localized: "auth_hide_password") }
|
||||
static var welcomeBack: String { String(localized: "auth_welcome_back") }
|
||||
static var signInSubtitle: String { String(localized: "auth_sign_in_subtitle") }
|
||||
static var enterEmail: String { String(localized: "auth_enter_email") }
|
||||
static var enterPassword: String { String(localized: "auth_enter_password") }
|
||||
static var signingIn: String { String(localized: "auth_signing_in") }
|
||||
static var orDivider: String { String(localized: "auth_or_divider") }
|
||||
static var signingInWithApple: String { String(localized: "auth_signing_in_with_apple") }
|
||||
static var dontHaveAccount: String { String(localized: "auth_dont_have_account") }
|
||||
static var signUp: String { String(localized: "auth_sign_up") }
|
||||
|
||||
// Register
|
||||
static var registerTitle: String { String(localized: "auth_register_title") }
|
||||
static var registerSubtitle: String { String(localized: "auth_register_subtitle") }
|
||||
static var registerFirstName: String { String(localized: "auth_register_first_name") }
|
||||
static var registerLastName: String { String(localized: "auth_register_last_name") }
|
||||
static var registerEmail: String { String(localized: "auth_register_email") }
|
||||
static var registerUsername: String { String(localized: "auth_register_username") }
|
||||
static var registerPassword: String { String(localized: "auth_register_password") }
|
||||
static var registerConfirmPassword: String { String(localized: "auth_register_confirm_password") }
|
||||
static var registerButton: String { String(localized: "auth_register_button") }
|
||||
static var haveAccount: String { String(localized: "auth_have_account") }
|
||||
static var passwordsDontMatch: String { String(localized: "auth_passwords_dont_match") }
|
||||
static var joinCasera: String { String(localized: "auth_join_casera") }
|
||||
static var startManaging: String { String(localized: "auth_start_managing") }
|
||||
static var accountInfo: String { String(localized: "auth_account_info") }
|
||||
static var security: String { String(localized: "auth_security") }
|
||||
static var passwordSuggestion: String { String(localized: "auth_password_suggestion") }
|
||||
|
||||
// Verify Email
|
||||
static var verifyTitle: String { String(localized: "auth_verify_title") }
|
||||
static var verifySubtitle: String { String(localized: "auth_verify_subtitle") }
|
||||
static var verifyCodeLabel: String { String(localized: "auth_verify_code_label") }
|
||||
static var verifyButton: String { String(localized: "auth_verify_button") }
|
||||
static var verifyResend: String { String(localized: "auth_verify_resend") }
|
||||
static var verifySuccess: String { String(localized: "auth_verify_success") }
|
||||
static var verifyYourEmail: String { String(localized: "auth_verify_your_email") }
|
||||
static var verifyMustVerify: String { String(localized: "auth_verify_must_verify") }
|
||||
static var verifyCheckInbox: String { String(localized: "auth_verify_check_inbox") }
|
||||
static var verifyCodeMustBe6: String { String(localized: "auth_verify_code_must_be_6") }
|
||||
static var verifyEmailButton: String { String(localized: "auth_verify_email_button") }
|
||||
static var verifyHelpText: String { String(localized: "auth_verify_help_text") }
|
||||
static var logout: String { String(localized: "auth_logout") }
|
||||
|
||||
// Forgot Password
|
||||
static var forgotTitle: String { String(localized: "auth_forgot_title") }
|
||||
static var forgotSubtitle: String { String(localized: "auth_forgot_subtitle") }
|
||||
static var forgotEmailLabel: String { String(localized: "auth_forgot_email_label") }
|
||||
static var forgotButton: String { String(localized: "auth_forgot_button") }
|
||||
static var forgotSuccess: String { String(localized: "auth_forgot_success") }
|
||||
|
||||
// Reset Password
|
||||
static var resetTitle: String { String(localized: "auth_reset_title") }
|
||||
static var resetSubtitle: String { String(localized: "auth_reset_subtitle") }
|
||||
static var resetCodeLabel: String { String(localized: "auth_reset_code_label") }
|
||||
static var resetNewPassword: String { String(localized: "auth_reset_new_password") }
|
||||
static var resetConfirmPassword: String { String(localized: "auth_reset_confirm_password") }
|
||||
static var resetButton: String { String(localized: "auth_reset_button") }
|
||||
static var resetSuccess: String { String(localized: "auth_reset_success") }
|
||||
}
|
||||
|
||||
// MARK: - Properties
|
||||
enum Properties {
|
||||
static var title: String { String(localized: "properties_title") }
|
||||
static var emptyTitle: String { String(localized: "properties_empty_title") }
|
||||
static var emptySubtitle: String { String(localized: "properties_empty_subtitle") }
|
||||
static var addButton: String { String(localized: "properties_add_button") }
|
||||
static var addTitle: String { String(localized: "properties_add_title") }
|
||||
static var editTitle: String { String(localized: "properties_edit_title") }
|
||||
static var nameLabel: String { String(localized: "properties_name_label") }
|
||||
static var addressLabel: String { String(localized: "properties_address_label") }
|
||||
static var typeLabel: String { String(localized: "properties_type_label") }
|
||||
static var notesLabel: String { String(localized: "properties_notes_label") }
|
||||
static var deleteConfirm: String { String(localized: "properties_delete_confirm") }
|
||||
static var deleted: String { String(localized: "properties_deleted") }
|
||||
}
|
||||
|
||||
// MARK: - Residences
|
||||
enum Residences {
|
||||
// List
|
||||
static var title: String { String(localized: "residences_title") }
|
||||
static var yourProperties: String { String(localized: "residences_your_properties") }
|
||||
static var property: String { String(localized: "residences_property") }
|
||||
static var properties: String { String(localized: "residences_properties") }
|
||||
|
||||
// Form
|
||||
static var addTitle: String { String(localized: "residences_add_title") }
|
||||
static var editTitle: String { String(localized: "residences_edit_title") }
|
||||
static var propertyName: String { String(localized: "residences_property_name") }
|
||||
static var propertyType: String { String(localized: "residences_property_type") }
|
||||
static var selectType: String { String(localized: "residences_select_type") }
|
||||
static var propertyDetails: String { String(localized: "residences_property_details") }
|
||||
static var requiredName: String { String(localized: "residences_required_name") }
|
||||
static var nameRequired: String { String(localized: "residences_name_required") }
|
||||
|
||||
// Address
|
||||
static var address: String { String(localized: "residences_address") }
|
||||
static var streetAddress: String { String(localized: "residences_street_address") }
|
||||
static var apartmentUnit: String { String(localized: "residences_apartment_unit") }
|
||||
static var city: String { String(localized: "residences_city") }
|
||||
static var stateProvince: String { String(localized: "residences_state_province") }
|
||||
static var postalCode: String { String(localized: "residences_postal_code") }
|
||||
static var country: String { String(localized: "residences_country") }
|
||||
|
||||
// Features
|
||||
static var propertyFeatures: String { String(localized: "residences_property_features") }
|
||||
static var bedrooms: String { String(localized: "residences_bedrooms") }
|
||||
static var bathrooms: String { String(localized: "residences_bathrooms") }
|
||||
static var squareFootage: String { String(localized: "residences_square_footage") }
|
||||
static var lotSize: String { String(localized: "residences_lot_size") }
|
||||
static var yearBuilt: String { String(localized: "residences_year_built") }
|
||||
|
||||
// Additional
|
||||
static var additionalDetails: String { String(localized: "residences_additional_details") }
|
||||
static var description: String { String(localized: "residences_description") }
|
||||
static var primaryResidence: String { String(localized: "residences_primary_residence") }
|
||||
|
||||
// Detail
|
||||
static var loadingResidence: String { String(localized: "residences_loading_residence") }
|
||||
static var generateReport: String { String(localized: "residences_generate_report") }
|
||||
static var generateReportMessage: String { String(localized: "residences_generate_report_message") }
|
||||
static var maintenanceReport: String { String(localized: "residences_maintenance_report") }
|
||||
static var contractors: String { String(localized: "residences_contractors") }
|
||||
static var noContractors: String { String(localized: "residences_no_contractors") }
|
||||
static var addContractorsPrompt: String { String(localized: "residences_add_contractors_prompt") }
|
||||
static var loadingTasks: String { String(localized: "residences_loading_tasks") }
|
||||
static var errorLoadingTasks: String { String(localized: "residences_error_loading_tasks") }
|
||||
static var generate: String { String(localized: "residences_generate") }
|
||||
|
||||
// Delete
|
||||
static var deleteTitle: String { String(localized: "residences_delete_title") }
|
||||
static var deleteConfirmMessage: String { String(localized: "residences_delete_confirm_message") }
|
||||
|
||||
// Join
|
||||
static var joinTitle: String { String(localized: "residences_join_title") }
|
||||
static var shareCode: String { String(localized: "residences_share_code") }
|
||||
static var enterShareCode: String { String(localized: "residences_enter_share_code") }
|
||||
static var shareCodeFooter: String { String(localized: "residences_share_code_footer") }
|
||||
static var joinButton: String { String(localized: "residences_join_button") }
|
||||
static var shareCodeMust6: String { String(localized: "residences_share_code_must_6") }
|
||||
|
||||
// Manage Users
|
||||
static var manageUsers: String { String(localized: "residences_manage_users") }
|
||||
static var users: String { String(localized: "residences_users") }
|
||||
}
|
||||
|
||||
// MARK: - Tasks
|
||||
enum Tasks {
|
||||
static var title: String { String(localized: "tasks_title") }
|
||||
static var emptyTitle: String { String(localized: "tasks_empty_title") }
|
||||
static var emptySubtitle: String { String(localized: "tasks_empty_subtitle") }
|
||||
static var addButton: String { String(localized: "tasks_add_button") }
|
||||
static var addTitle: String { String(localized: "tasks_add_title") }
|
||||
static var editTitle: String { String(localized: "tasks_edit_title") }
|
||||
static var titleLabel: String { String(localized: "tasks_title_label") }
|
||||
static var descriptionLabel: String { String(localized: "tasks_description_label") }
|
||||
static var deleteConfirm: String { String(localized: "tasks_delete_confirm") }
|
||||
|
||||
// Form Fields
|
||||
static var property: String { String(localized: "tasks_property") }
|
||||
static var selectProperty: String { String(localized: "tasks_select_property") }
|
||||
static var required: String { String(localized: "tasks_required") }
|
||||
static var taskDetails: String { String(localized: "tasks_task_details") }
|
||||
static var titleRequired: String { String(localized: "tasks_title_required") }
|
||||
static var descriptionOptional: String { String(localized: "tasks_description_optional") }
|
||||
static var category: String { String(localized: "tasks_category") }
|
||||
static var selectCategory: String { String(localized: "tasks_select_category") }
|
||||
static var scheduling: String { String(localized: "tasks_scheduling") }
|
||||
static var frequency: String { String(localized: "tasks_frequency") }
|
||||
static var selectFrequency: String { String(localized: "tasks_select_frequency") }
|
||||
static var customInterval: String { String(localized: "tasks_custom_interval") }
|
||||
static var dueDate: String { String(localized: "tasks_due_date") }
|
||||
static var priorityAndStatus: String { String(localized: "tasks_priority_status") }
|
||||
static var priority: String { String(localized: "tasks_priority") }
|
||||
static var selectPriority: String { String(localized: "tasks_select_priority") }
|
||||
static var status: String { String(localized: "tasks_status") }
|
||||
static var selectStatus: String { String(localized: "tasks_select_status") }
|
||||
static var bothRequired: String { String(localized: "tasks_both_required") }
|
||||
static var cost: String { String(localized: "tasks_cost") }
|
||||
static var estimatedCost: String { String(localized: "tasks_estimated_cost") }
|
||||
static var loading: String { String(localized: "tasks_loading") }
|
||||
|
||||
// All Tasks View
|
||||
static var allTasks: String { String(localized: "tasks_all_tasks") }
|
||||
static var noTasksYet: String { String(localized: "tasks_no_tasks_yet") }
|
||||
static var createFirst: String { String(localized: "tasks_create_first") }
|
||||
static var addPropertyFirst: String { String(localized: "tasks_add_property_first") }
|
||||
static var archiveTask: String { String(localized: "tasks_archive_task") }
|
||||
static var deleteTask: String { String(localized: "tasks_delete_task") }
|
||||
static var archiveConfirm: String { String(localized: "tasks_archive_confirm") }
|
||||
static var archive: String { String(localized: "tasks_archive") }
|
||||
static var noTasks: String { String(localized: "tasks_no_tasks") }
|
||||
|
||||
// Complete Task View
|
||||
static var completeTask: String { String(localized: "tasks_complete_task") }
|
||||
static var selectContractor: String { String(localized: "tasks_select_contractor") }
|
||||
static var contractorOptional: String { String(localized: "tasks_contractor_optional") }
|
||||
static var contractorHelper: String { String(localized: "tasks_contractor_helper") }
|
||||
static var completedBy: String { String(localized: "tasks_completed_by") }
|
||||
static var yourName: String { String(localized: "tasks_your_name") }
|
||||
static var actualCost: String { String(localized: "tasks_actual_cost") }
|
||||
static var optionalInfo: String { String(localized: "tasks_optional_info") }
|
||||
static var optionalDetails: String { String(localized: "tasks_optional_details") }
|
||||
static var notes: String { String(localized: "tasks_notes") }
|
||||
static var optionalNotes: String { String(localized: "tasks_optional_notes") }
|
||||
static var qualityRating: String { String(localized: "tasks_quality_rating") }
|
||||
static var rateQuality: String { String(localized: "tasks_rate_quality") }
|
||||
static var photos: String { String(localized: "tasks_photos") }
|
||||
static var addPhotos: String { String(localized: "tasks_add_photos") }
|
||||
static var takePhoto: String { String(localized: "tasks_take_photo") }
|
||||
static var library: String { String(localized: "tasks_library") }
|
||||
static var none: String { String(localized: "tasks_none") }
|
||||
static var noneManual: String { String(localized: "tasks_none_manual") }
|
||||
static var enterManually: String { String(localized: "tasks_enter_manually") }
|
||||
static var error: String { String(localized: "tasks_error") }
|
||||
|
||||
// Completion History
|
||||
static var completionHistory: String { String(localized: "tasks_completion_history") }
|
||||
static var loadingCompletions: String { String(localized: "tasks_loading_completions") }
|
||||
static var failedToLoad: String { String(localized: "tasks_failed_to_load") }
|
||||
static var noCompletionsYet: String { String(localized: "tasks_no_completions_yet") }
|
||||
static var notCompleted: String { String(localized: "tasks_not_completed") }
|
||||
static var completions: String { String(localized: "tasks_completions") }
|
||||
static var completion: String { String(localized: "tasks_completion") }
|
||||
static var completedByName: String { String(localized: "tasks_completed_by_name") }
|
||||
static var viewPhotos: String { String(localized: "tasks_view_photos") }
|
||||
|
||||
// Task Card Actions
|
||||
static var inProgress: String { String(localized: "tasks_in_progress") }
|
||||
static var complete: String { String(localized: "tasks_complete") }
|
||||
static var edit: String { String(localized: "tasks_edit") }
|
||||
static var cancel: String { String(localized: "tasks_cancel") }
|
||||
static var restore: String { String(localized: "tasks_restore") }
|
||||
static var unarchive: String { String(localized: "tasks_unarchive") }
|
||||
}
|
||||
|
||||
// MARK: - Contractors
|
||||
enum Contractors {
|
||||
static var title: String { String(localized: "contractors_title") }
|
||||
static var emptyTitle: String { String(localized: "contractors_empty_title") }
|
||||
static var emptySubtitle: String { String(localized: "contractors_empty_subtitle") }
|
||||
static var emptyNoFilters: String { String(localized: "contractors_empty_no_filters") }
|
||||
static var emptyFiltered: String { String(localized: "contractors_empty_filtered") }
|
||||
static var addButton: String { String(localized: "contractors_add_button") }
|
||||
static var addTitle: String { String(localized: "contractors_add_title") }
|
||||
static var editTitle: String { String(localized: "contractors_edit_title") }
|
||||
|
||||
// Search & Filter
|
||||
static var searchPlaceholder: String { String(localized: "contractors_search_placeholder") }
|
||||
static var favorites: String { String(localized: "contractors_favorites") }
|
||||
static var allSpecialties: String { String(localized: "contractors_all_specialties") }
|
||||
|
||||
// Form Fields
|
||||
static var nameLabel: String { String(localized: "contractors_name_label") }
|
||||
static var companyLabel: String { String(localized: "contractors_company_label") }
|
||||
static var phoneLabel: String { String(localized: "contractors_phone_label") }
|
||||
static var emailLabel: String { String(localized: "contractors_email_label") }
|
||||
static var websiteLabel: String { String(localized: "contractors_website_label") }
|
||||
static var streetAddressLabel: String { String(localized: "contractors_street_address_label") }
|
||||
static var cityLabel: String { String(localized: "contractors_city_label") }
|
||||
static var stateLabel: String { String(localized: "contractors_state_label") }
|
||||
static var zipLabel: String { String(localized: "contractors_zip_label") }
|
||||
static var notesLabel: String { String(localized: "contractors_notes_label") }
|
||||
|
||||
// Form Sections
|
||||
static var basicInfoSection: String { String(localized: "contractors_basic_info_section") }
|
||||
static var basicInfoFooter: String { String(localized: "contractors_basic_info_footer") }
|
||||
static var residenceSection: String { String(localized: "contractors_residence_section") }
|
||||
static var residenceFooterPersonal: String { String(localized: "contractors_residence_footer_personal") }
|
||||
static var residenceFooterShared: String { String(localized: "contractors_residence_footer_shared") }
|
||||
static var contactInfoSection: String { String(localized: "contractors_contact_info_section") }
|
||||
static var specialtiesSection: String { String(localized: "contractors_specialties_section") }
|
||||
static var addressSection: String { String(localized: "contractors_address_section") }
|
||||
static var notesSection: String { String(localized: "contractors_notes_section") }
|
||||
static var notesFooter: String { String(localized: "contractors_notes_footer") }
|
||||
static var favoriteLabel: String { String(localized: "contractors_favorite_label") }
|
||||
|
||||
// Detail View
|
||||
static var removeFromFavorites: String { String(localized: "contractors_remove_from_favorites") }
|
||||
static var addToFavorites: String { String(localized: "contractors_add_to_favorites") }
|
||||
static var deleteConfirm: String { String(localized: "contractors_delete_confirm") }
|
||||
static var deleteMessage: String { String(localized: "contractors_delete_message") }
|
||||
static var completedTasks: String { String(localized: "contractors_completed_tasks") }
|
||||
static var callAction: String { String(localized: "contractors_call_action") }
|
||||
static var emailAction: String { String(localized: "contractors_email_action") }
|
||||
static var websiteAction: String { String(localized: "contractors_website_action") }
|
||||
static var directionsAction: String { String(localized: "contractors_directions_action") }
|
||||
static var locationLabel: String { String(localized: "contractors_location_label") }
|
||||
static var propertyLabel: String { String(localized: "contractors_property_label") }
|
||||
static var associatedPropertySection: String { String(localized: "contractors_associated_property_section") }
|
||||
static var statisticsSection: String { String(localized: "contractors_statistics_section") }
|
||||
static var tasksCompletedLabel: String { String(localized: "contractors_tasks_completed_label") }
|
||||
static var averageRatingLabel: String { String(localized: "contractors_average_rating_label") }
|
||||
static var infoSection: String { String(localized: "contractors_info_section") }
|
||||
static var addedByLabel: String { String(localized: "contractors_added_by_label") }
|
||||
static var memberSinceLabel: String { String(localized: "contractors_member_since_label") }
|
||||
|
||||
// Picker Sheets
|
||||
static var selectResidence: String { String(localized: "contractors_select_residence") }
|
||||
static var personalNoResidence: String { String(localized: "contractors_personal_no_residence") }
|
||||
static var selectSpecialties: String { String(localized: "contractors_select_specialties") }
|
||||
static var selectSpecialtiesPlaceholder: String { String(localized: "contractors_select_specialties_placeholder") }
|
||||
static var clearAction: String { String(localized: "contractors_clear_action") }
|
||||
|
||||
// Stats
|
||||
static var tasksLabel: String { String(localized: "contractors_tasks_label") }
|
||||
}
|
||||
|
||||
// MARK: - Documents
|
||||
enum Documents {
|
||||
// Main view
|
||||
static var title: String { String(localized: "documents_title") }
|
||||
static var documentsAndWarranties: String { String(localized: "documents_and_warranties") }
|
||||
static var warranties: String { String(localized: "documents_warranties") }
|
||||
static var documents: String { String(localized: "documents_documents") }
|
||||
static var searchPlaceholder: String { String(localized: "documents_search_placeholder") }
|
||||
|
||||
// Filters
|
||||
static var activeOnly: String { String(localized: "documents_active_only") }
|
||||
static var allCategories: String { String(localized: "documents_all_categories") }
|
||||
static var allTypes: String { String(localized: "documents_all_types") }
|
||||
|
||||
// Empty states
|
||||
static var emptyTitle: String { String(localized: "documents_empty_title") }
|
||||
static var emptySubtitle: String { String(localized: "documents_empty_subtitle") }
|
||||
static var noDocumentsFound: String { String(localized: "documents_no_documents_found") }
|
||||
static var noDocumentsMessage: String { String(localized: "documents_no_documents_message") }
|
||||
static var noWarrantiesFound: String { String(localized: "documents_no_warranties_found") }
|
||||
static var noWarrantiesMessage: String { String(localized: "documents_no_warranties_message") }
|
||||
|
||||
// Actions
|
||||
static var addButton: String { String(localized: "documents_add_button") }
|
||||
|
||||
// Form titles
|
||||
static var addWarranty: String { String(localized: "documents_add_warranty") }
|
||||
static var addDocument: String { String(localized: "documents_add_document") }
|
||||
static var editWarranty: String { String(localized: "documents_edit_warranty") }
|
||||
static var editDocument: String { String(localized: "documents_edit_document") }
|
||||
|
||||
// Form sections
|
||||
static var property: String { String(localized: "documents_property") }
|
||||
static var selectProperty: String { String(localized: "documents_select_property") }
|
||||
static var documentType: String { String(localized: "documents_document_type") }
|
||||
static var documentTypeCannotChange: String { String(localized: "documents_document_type_cannot_change") }
|
||||
static var basicInformation: String { String(localized: "documents_basic_information") }
|
||||
static var warrantyDetails: String { String(localized: "documents_warranty_details") }
|
||||
static var warrantyClaims: String { String(localized: "documents_warranty_claims") }
|
||||
static var warrantyDates: String { String(localized: "documents_warranty_dates") }
|
||||
static var category: String { String(localized: "documents_category") }
|
||||
static var additionalInformation: String { String(localized: "documents_additional_information") }
|
||||
static var active: String { String(localized: "documents_active") }
|
||||
static var photos: String { String(localized: "documents_photos") }
|
||||
static var existingPhotos: String { String(localized: "documents_existing_photos") }
|
||||
|
||||
// Form fields
|
||||
static var titleField: String { String(localized: "documents_title_field") }
|
||||
static var description: String { String(localized: "documents_description") }
|
||||
static var descriptionOptional: String { String(localized: "documents_description_optional") }
|
||||
static var itemName: String { String(localized: "documents_item_name") }
|
||||
static var modelNumber: String { String(localized: "documents_model_number") }
|
||||
static var modelNumberOptional: String { String(localized: "documents_model_number_optional") }
|
||||
static var serialNumber: String { String(localized: "documents_serial_number") }
|
||||
static var serialNumberOptional: String { String(localized: "documents_serial_number_optional") }
|
||||
static var provider: String { String(localized: "documents_provider") }
|
||||
static var providerCompany: String { String(localized: "documents_provider_company") }
|
||||
static var providerContact: String { String(localized: "documents_provider_contact") }
|
||||
static var providerContactOptional: String { String(localized: "documents_provider_contact_optional") }
|
||||
static var claimPhone: String { String(localized: "documents_claim_phone") }
|
||||
static var claimPhoneOptional: String { String(localized: "documents_claim_phone_optional") }
|
||||
static var claimEmail: String { String(localized: "documents_claim_email") }
|
||||
static var claimEmailOptional: String { String(localized: "documents_claim_email_optional") }
|
||||
static var claimWebsite: String { String(localized: "documents_claim_website") }
|
||||
static var claimWebsiteOptional: String { String(localized: "documents_claim_website_optional") }
|
||||
static var purchaseDate: String { String(localized: "documents_purchase_date") }
|
||||
static var startDate: String { String(localized: "documents_start_date") }
|
||||
static var endDate: String { String(localized: "documents_end_date") }
|
||||
static var tags: String { String(localized: "documents_tags") }
|
||||
static var tagsOptional: String { String(localized: "documents_tags_optional") }
|
||||
static var notes: String { String(localized: "documents_notes") }
|
||||
static var notesOptional: String { String(localized: "documents_notes_optional") }
|
||||
static var categoryOptional: String { String(localized: "documents_category_optional") }
|
||||
|
||||
// Form footer messages
|
||||
static var required: String { String(localized: "documents_required") }
|
||||
static var requiredTitle: String { String(localized: "documents_required_title") }
|
||||
static var requiredWarrantyFields: String { String(localized: "documents_required_warranty_fields") }
|
||||
|
||||
// Photo actions
|
||||
static var selectFromLibrary: String { String(localized: "documents_select_from_library") }
|
||||
static var takePhoto: String { String(localized: "documents_take_photo") }
|
||||
static var photosSelected: String { String(localized: "documents_photos_selected") }
|
||||
|
||||
// Detail view
|
||||
static var documentDetails: String { String(localized: "documents_document_details") }
|
||||
static var loadingDocument: String { String(localized: "documents_loading_document") }
|
||||
static var status: String { String(localized: "documents_status") }
|
||||
static var daysRemaining: String { String(localized: "documents_days_remaining") }
|
||||
static var itemDetails: String { String(localized: "documents_item_details") }
|
||||
static var claimInformation: String { String(localized: "documents_claim_information") }
|
||||
static var importantDates: String { String(localized: "documents_important_dates") }
|
||||
static var images: String { String(localized: "documents_images") }
|
||||
static var associations: String { String(localized: "documents_associations") }
|
||||
static var residence: String { String(localized: "documents_residence") }
|
||||
static var contractor: String { String(localized: "documents_contractor") }
|
||||
static var contractorPhone: String { String(localized: "documents_contractor_phone") }
|
||||
static var attachedFile: String { String(localized: "documents_attached_file") }
|
||||
static var fileType: String { String(localized: "documents_file_type") }
|
||||
static var fileSize: String { String(localized: "documents_file_size") }
|
||||
static var downloadFile: String { String(localized: "documents_download_file") }
|
||||
static var metadata: String { String(localized: "documents_metadata") }
|
||||
static var uploadedBy: String { String(localized: "documents_uploaded_by") }
|
||||
static var created: String { String(localized: "documents_created") }
|
||||
static var updated: String { String(localized: "documents_updated") }
|
||||
|
||||
// Warranty statuses
|
||||
static var inactive: String { String(localized: "documents_inactive") }
|
||||
static var expired: String { String(localized: "documents_expired") }
|
||||
static var expiringSoon: String { String(localized: "documents_expiring_soon") }
|
||||
static var activeStatus: String { String(localized: "documents_active_status") }
|
||||
|
||||
// Warranty card
|
||||
static var expires: String { String(localized: "documents_expires") }
|
||||
static var daysRemainingCount: String { String(localized: "documents_days_remaining_count") }
|
||||
|
||||
// Delete
|
||||
static var deleteDocument: String { String(localized: "documents_delete_document") }
|
||||
static var deleteConfirmMessage: String { String(localized: "documents_delete_confirm_message") }
|
||||
|
||||
// Validation errors
|
||||
static var titleRequired: String { String(localized: "documents_title_required") }
|
||||
static var propertyRequired: String { String(localized: "documents_property_required") }
|
||||
static var itemNameRequired: String { String(localized: "documents_item_name_required") }
|
||||
static var providerRequired: String { String(localized: "documents_provider_required") }
|
||||
static var fillRequiredFields: String { String(localized: "documents_fill_required_fields") }
|
||||
static var noResidenceSelected: String { String(localized: "documents_no_residence_selected") }
|
||||
static var documentIdMissing: String { String(localized: "documents_document_id_missing") }
|
||||
static var failedToUpdate: String { String(localized: "documents_failed_to_update") }
|
||||
static var failedToCreate: String { String(localized: "documents_failed_to_create") }
|
||||
|
||||
// Document type names
|
||||
static var typeWarranty: String { String(localized: "documents_type_warranty") }
|
||||
static var typeManual: String { String(localized: "documents_type_manual") }
|
||||
static var typeReceipt: String { String(localized: "documents_type_receipt") }
|
||||
static var typeInspection: String { String(localized: "documents_type_inspection") }
|
||||
static var typePermit: String { String(localized: "documents_type_permit") }
|
||||
static var typeDeed: String { String(localized: "documents_type_deed") }
|
||||
static var typeInsurance: String { String(localized: "documents_type_insurance") }
|
||||
static var typeContract: String { String(localized: "documents_type_contract") }
|
||||
static var typePhoto: String { String(localized: "documents_type_photo") }
|
||||
static var typeOther: String { String(localized: "documents_type_other") }
|
||||
static var typeUnknown: String { String(localized: "documents_type_unknown") }
|
||||
|
||||
// Category names
|
||||
static var categoryAppliance: String { String(localized: "documents_category_appliance") }
|
||||
static var categoryHvac: String { String(localized: "documents_category_hvac") }
|
||||
static var categoryPlumbing: String { String(localized: "documents_category_plumbing") }
|
||||
static var categoryElectrical: String { String(localized: "documents_category_electrical") }
|
||||
static var categoryRoofing: String { String(localized: "documents_category_roofing") }
|
||||
static var categoryStructural: String { String(localized: "documents_category_structural") }
|
||||
static var categoryOther: String { String(localized: "documents_category_other") }
|
||||
static var categoryUnknown: String { String(localized: "documents_category_unknown") }
|
||||
|
||||
// Common labels
|
||||
static var none: String { String(localized: "documents_none") }
|
||||
static var na: String { String(localized: "documents_na") }
|
||||
static var update: String { String(localized: "documents_update") }
|
||||
}
|
||||
|
||||
// MARK: - Profile
|
||||
enum Profile {
|
||||
static var title: String { String(localized: "profile_title") }
|
||||
static var logout: String { String(localized: "profile_logout") }
|
||||
static var logoutConfirm: String { String(localized: "profile_logout_confirm") }
|
||||
static var loadingProfile: String { String(localized: "profile_loading_profile") }
|
||||
static var profileSettings: String { String(localized: "profile_profile_settings") }
|
||||
static var firstName: String { String(localized: "profile_first_name") }
|
||||
static var lastName: String { String(localized: "profile_last_name") }
|
||||
static var personalInformation: String { String(localized: "profile_personal_information") }
|
||||
static var email: String { String(localized: "profile_email") }
|
||||
static var contact: String { String(localized: "profile_contact") }
|
||||
static var emailRequiredUnique: String { String(localized: "profile_email_required_unique") }
|
||||
static var saveChanges: String { String(localized: "profile_save_changes") }
|
||||
static var editProfile: String { String(localized: "profile_edit_profile") }
|
||||
static var account: String { String(localized: "profile_account") }
|
||||
static var notifications: String { String(localized: "profile_notifications") }
|
||||
static var privacy: String { String(localized: "profile_privacy") }
|
||||
static var subscription: String { String(localized: "profile_subscription") }
|
||||
static var proPlan: String { String(localized: "profile_pro_plan") }
|
||||
static var freePlan: String { String(localized: "profile_free_plan") }
|
||||
static var activeUntil: String { String(localized: "profile_active_until") }
|
||||
static var limitedFeatures: String { String(localized: "profile_limited_features") }
|
||||
static var upgradeToPro: String { String(localized: "profile_upgrade_to_pro") }
|
||||
static var manageSubscription: String { String(localized: "profile_manage_subscription") }
|
||||
static var restorePurchases: String { String(localized: "profile_restore_purchases") }
|
||||
static var appearance: String { String(localized: "profile_appearance") }
|
||||
static var theme: String { String(localized: "profile_theme") }
|
||||
static var appName: String { String(localized: "profile_app_name") }
|
||||
static var version: String { String(localized: "profile_version") }
|
||||
static var purchasesRestored: String { String(localized: "profile_purchases_restored") }
|
||||
static var purchasesRestoredMessage: String { String(localized: "profile_purchases_restored_message") }
|
||||
|
||||
// Notification Preferences
|
||||
static var notificationPreferences: String { String(localized: "profile_notification_preferences") }
|
||||
static var notificationPreferencesSubtitle: String { String(localized: "profile_notification_preferences_subtitle") }
|
||||
static var taskDueSoon: String { String(localized: "profile_task_due_soon") }
|
||||
static var taskDueSoonDescription: String { String(localized: "profile_task_due_soon_description") }
|
||||
static var taskOverdue: String { String(localized: "profile_task_overdue") }
|
||||
static var taskOverdueDescription: String { String(localized: "profile_task_overdue_description") }
|
||||
static var taskCompleted: String { String(localized: "profile_task_completed") }
|
||||
static var taskCompletedDescription: String { String(localized: "profile_task_completed_description") }
|
||||
static var taskAssigned: String { String(localized: "profile_task_assigned") }
|
||||
static var taskAssignedDescription: String { String(localized: "profile_task_assigned_description") }
|
||||
static var taskNotifications: String { String(localized: "profile_task_notifications") }
|
||||
static var propertyShared: String { String(localized: "profile_property_shared") }
|
||||
static var propertySharedDescription: String { String(localized: "profile_property_shared_description") }
|
||||
static var warrantyExpiring: String { String(localized: "profile_warranty_expiring") }
|
||||
static var warrantyExpiringDescription: String { String(localized: "profile_warranty_expiring_description") }
|
||||
static var otherNotifications: String { String(localized: "profile_other_notifications") }
|
||||
}
|
||||
|
||||
// MARK: - Settings
|
||||
enum Settings {
|
||||
static var title: String { String(localized: "settings_title") }
|
||||
static var language: String { String(localized: "settings_language") }
|
||||
}
|
||||
|
||||
// MARK: - Common
|
||||
enum Common {
|
||||
static var save: String { String(localized: "common_save") }
|
||||
static var cancel: String { String(localized: "common_cancel") }
|
||||
static var delete: String { String(localized: "common_delete") }
|
||||
static var edit: String { String(localized: "common_edit") }
|
||||
static var done: String { String(localized: "common_done") }
|
||||
static var close: String { String(localized: "common_close") }
|
||||
static var back: String { String(localized: "common_back") }
|
||||
static var loading: String { String(localized: "common_loading") }
|
||||
static var error: String { String(localized: "common_error") }
|
||||
static var retry: String { String(localized: "common_retry") }
|
||||
static var success: String { String(localized: "common_success") }
|
||||
static var yes: String { String(localized: "common_yes") }
|
||||
static var no: String { String(localized: "common_no") }
|
||||
static var ok: String { String(localized: "common_ok") }
|
||||
}
|
||||
|
||||
// MARK: - Errors
|
||||
enum Error {
|
||||
static var generic: String { String(localized: "error_generic") }
|
||||
static var network: String { String(localized: "error_network") }
|
||||
static var unauthorized: String { String(localized: "error_unauthorized") }
|
||||
static var notFound: String { String(localized: "error_not_found") }
|
||||
static var requiredField: String { String(localized: "error_required_field") }
|
||||
}
|
||||
}
|
||||
16033
iosApp/iosApp/Localizable.xcstrings
Normal file
16033
iosApp/iosApp/Localizable.xcstrings
Normal file
File diff suppressed because it is too large
Load Diff
@@ -59,11 +59,11 @@ struct LoginView: View {
|
||||
}
|
||||
|
||||
VStack(spacing: AppSpacing.xs) {
|
||||
Text("Welcome Back")
|
||||
Text(L10n.Auth.welcomeBack)
|
||||
.font(.title2.weight(.bold))
|
||||
.foregroundColor(Color.appTextPrimary)
|
||||
|
||||
Text("Sign in to manage your properties")
|
||||
Text(L10n.Auth.signInSubtitle)
|
||||
.font(.body)
|
||||
.foregroundColor(Color.appTextSecondary)
|
||||
}
|
||||
@@ -73,7 +73,7 @@ struct LoginView: View {
|
||||
VStack(spacing: AppSpacing.lg) {
|
||||
// Username Field
|
||||
VStack(alignment: .leading, spacing: AppSpacing.xs) {
|
||||
Text("Email or Username")
|
||||
Text(L10n.Auth.loginUsernameLabel)
|
||||
.font(.subheadline.weight(.medium))
|
||||
.foregroundColor(Color.appTextSecondary)
|
||||
|
||||
@@ -82,7 +82,7 @@ struct LoginView: View {
|
||||
.foregroundColor(Color.appTextSecondary)
|
||||
.frame(width: 20)
|
||||
|
||||
TextField("Enter your email", text: $viewModel.username)
|
||||
TextField(L10n.Auth.enterEmail, text: $viewModel.username)
|
||||
.textInputAutocapitalization(.never)
|
||||
.autocorrectionDisabled()
|
||||
.keyboardType(.emailAddress)
|
||||
@@ -110,7 +110,7 @@ struct LoginView: View {
|
||||
|
||||
// Password Field
|
||||
VStack(alignment: .leading, spacing: AppSpacing.xs) {
|
||||
Text("Password")
|
||||
Text(L10n.Auth.loginPasswordLabel)
|
||||
.font(.subheadline.weight(.medium))
|
||||
.foregroundColor(Color.appTextSecondary)
|
||||
|
||||
@@ -121,7 +121,7 @@ struct LoginView: View {
|
||||
|
||||
Group {
|
||||
if isPasswordVisible {
|
||||
TextField("Enter your password", text: $viewModel.password)
|
||||
TextField(L10n.Auth.enterPassword, text: $viewModel.password)
|
||||
.textInputAutocapitalization(.never)
|
||||
.autocorrectionDisabled()
|
||||
.textContentType(.password)
|
||||
@@ -132,7 +132,7 @@ struct LoginView: View {
|
||||
}
|
||||
.accessibilityIdentifier(AccessibilityIdentifiers.Authentication.passwordField)
|
||||
} else {
|
||||
SecureField("Enter your password", text: $viewModel.password)
|
||||
SecureField(L10n.Auth.enterPassword, text: $viewModel.password)
|
||||
.textContentType(.password)
|
||||
.focused($focusedField, equals: .password)
|
||||
.submitLabel(.go)
|
||||
@@ -169,7 +169,7 @@ struct LoginView: View {
|
||||
// Forgot Password
|
||||
HStack {
|
||||
Spacer()
|
||||
Button("Forgot Password?") {
|
||||
Button(L10n.Auth.forgotPassword) {
|
||||
showPasswordReset = true
|
||||
}
|
||||
.font(.subheadline.weight(.medium))
|
||||
@@ -204,7 +204,7 @@ struct LoginView: View {
|
||||
Rectangle()
|
||||
.fill(Color.appTextSecondary.opacity(0.3))
|
||||
.frame(height: 1)
|
||||
Text("or")
|
||||
Text(L10n.Auth.orDivider)
|
||||
.font(.subheadline)
|
||||
.foregroundColor(Color.appTextSecondary)
|
||||
.padding(.horizontal, AppSpacing.sm)
|
||||
@@ -241,7 +241,7 @@ struct LoginView: View {
|
||||
HStack {
|
||||
ProgressView()
|
||||
.progressViewStyle(CircularProgressViewStyle())
|
||||
Text("Signing in with Apple...")
|
||||
Text(L10n.Auth.signingInWithApple)
|
||||
.font(.subheadline)
|
||||
.foregroundColor(Color.appTextSecondary)
|
||||
}
|
||||
@@ -265,11 +265,11 @@ struct LoginView: View {
|
||||
|
||||
// Sign Up Link
|
||||
HStack(spacing: AppSpacing.xs) {
|
||||
Text("Don't have an account?")
|
||||
Text(L10n.Auth.dontHaveAccount)
|
||||
.font(.body)
|
||||
.foregroundColor(Color.appTextSecondary)
|
||||
|
||||
Button("Sign Up") {
|
||||
Button(L10n.Auth.signUp) {
|
||||
showingRegister = true
|
||||
}
|
||||
.font(.body)
|
||||
@@ -357,7 +357,7 @@ struct LoginView: View {
|
||||
ProgressView()
|
||||
.progressViewStyle(CircularProgressViewStyle(tint: .white))
|
||||
}
|
||||
Text(viewModel.isLoading ? "Signing In..." : "Sign In")
|
||||
Text(viewModel.isLoading ? L10n.Auth.signingIn : L10n.Auth.loginButton)
|
||||
.font(.headline)
|
||||
.fontWeight(.semibold)
|
||||
}
|
||||
|
||||
@@ -15,12 +15,12 @@ struct NotificationPreferencesView: View {
|
||||
.font(.system(size: 60))
|
||||
.foregroundStyle(Color.appPrimary.gradient)
|
||||
|
||||
Text("Notification Preferences")
|
||||
Text(L10n.Profile.notificationPreferences)
|
||||
.font(.title2)
|
||||
.fontWeight(.bold)
|
||||
.foregroundColor(Color.appTextPrimary)
|
||||
|
||||
Text("Choose which notifications you'd like to receive")
|
||||
Text(L10n.Profile.notificationPreferencesSubtitle)
|
||||
.font(.subheadline)
|
||||
.foregroundColor(Color.appTextSecondary)
|
||||
.multilineTextAlignment(.center)
|
||||
@@ -51,7 +51,7 @@ struct NotificationPreferencesView: View {
|
||||
.font(.subheadline)
|
||||
}
|
||||
|
||||
Button("Retry") {
|
||||
Button(L10n.Common.retry) {
|
||||
viewModel.loadPreferences()
|
||||
}
|
||||
.foregroundColor(Color.appPrimary)
|
||||
@@ -63,9 +63,9 @@ struct NotificationPreferencesView: View {
|
||||
Toggle(isOn: $viewModel.taskDueSoon) {
|
||||
Label {
|
||||
VStack(alignment: .leading, spacing: 2) {
|
||||
Text("Task Due Soon")
|
||||
Text(L10n.Profile.taskDueSoon)
|
||||
.foregroundColor(Color.appTextPrimary)
|
||||
Text("Reminders for upcoming tasks")
|
||||
Text(L10n.Profile.taskDueSoonDescription)
|
||||
.font(.caption)
|
||||
.foregroundColor(Color.appTextSecondary)
|
||||
}
|
||||
@@ -82,9 +82,9 @@ struct NotificationPreferencesView: View {
|
||||
Toggle(isOn: $viewModel.taskOverdue) {
|
||||
Label {
|
||||
VStack(alignment: .leading, spacing: 2) {
|
||||
Text("Task Overdue")
|
||||
Text(L10n.Profile.taskOverdue)
|
||||
.foregroundColor(Color.appTextPrimary)
|
||||
Text("Alerts for overdue tasks")
|
||||
Text(L10n.Profile.taskOverdueDescription)
|
||||
.font(.caption)
|
||||
.foregroundColor(Color.appTextSecondary)
|
||||
}
|
||||
@@ -101,9 +101,9 @@ struct NotificationPreferencesView: View {
|
||||
Toggle(isOn: $viewModel.taskCompleted) {
|
||||
Label {
|
||||
VStack(alignment: .leading, spacing: 2) {
|
||||
Text("Task Completed")
|
||||
Text(L10n.Profile.taskCompleted)
|
||||
.foregroundColor(Color.appTextPrimary)
|
||||
Text("When someone completes a task")
|
||||
Text(L10n.Profile.taskCompletedDescription)
|
||||
.font(.caption)
|
||||
.foregroundColor(Color.appTextSecondary)
|
||||
}
|
||||
@@ -120,9 +120,9 @@ struct NotificationPreferencesView: View {
|
||||
Toggle(isOn: $viewModel.taskAssigned) {
|
||||
Label {
|
||||
VStack(alignment: .leading, spacing: 2) {
|
||||
Text("Task Assigned")
|
||||
Text(L10n.Profile.taskAssigned)
|
||||
.foregroundColor(Color.appTextPrimary)
|
||||
Text("When a task is assigned to you")
|
||||
Text(L10n.Profile.taskAssignedDescription)
|
||||
.font(.caption)
|
||||
.foregroundColor(Color.appTextSecondary)
|
||||
}
|
||||
@@ -136,7 +136,7 @@ struct NotificationPreferencesView: View {
|
||||
viewModel.updatePreference(taskAssigned: newValue)
|
||||
}
|
||||
} header: {
|
||||
Text("Task Notifications")
|
||||
Text(L10n.Profile.taskNotifications)
|
||||
}
|
||||
.listRowBackground(Color.appBackgroundSecondary)
|
||||
|
||||
@@ -145,9 +145,9 @@ struct NotificationPreferencesView: View {
|
||||
Toggle(isOn: $viewModel.residenceShared) {
|
||||
Label {
|
||||
VStack(alignment: .leading, spacing: 2) {
|
||||
Text("Property Shared")
|
||||
Text(L10n.Profile.propertyShared)
|
||||
.foregroundColor(Color.appTextPrimary)
|
||||
Text("When someone shares a property with you")
|
||||
Text(L10n.Profile.propertySharedDescription)
|
||||
.font(.caption)
|
||||
.foregroundColor(Color.appTextSecondary)
|
||||
}
|
||||
@@ -172,9 +172,9 @@ struct NotificationPreferencesView: View {
|
||||
Toggle(isOn: $viewModel.warrantyExpiring) {
|
||||
Label {
|
||||
VStack(alignment: .leading, spacing: 2) {
|
||||
Text("Warranty Expiring")
|
||||
Text(L10n.Profile.warrantyExpiring)
|
||||
.foregroundColor(Color.appTextPrimary)
|
||||
Text("Reminders for expiring warranties")
|
||||
Text(L10n.Profile.warrantyExpiringDescription)
|
||||
.font(.caption)
|
||||
.foregroundColor(Color.appTextSecondary)
|
||||
}
|
||||
@@ -188,7 +188,7 @@ struct NotificationPreferencesView: View {
|
||||
viewModel.updatePreference(warrantyExpiring: newValue)
|
||||
}
|
||||
} header: {
|
||||
Text("Other Notifications")
|
||||
Text(L10n.Profile.otherNotifications)
|
||||
}
|
||||
.listRowBackground(Color.appBackgroundSecondary)
|
||||
}
|
||||
@@ -196,11 +196,11 @@ struct NotificationPreferencesView: View {
|
||||
.listStyle(.plain)
|
||||
.scrollContentBackground(.hidden)
|
||||
.background(Color.appBackgroundPrimary)
|
||||
.navigationTitle("Notifications")
|
||||
.navigationTitle(L10n.Profile.notifications)
|
||||
.navigationBarTitleDisplayMode(.inline)
|
||||
.toolbar {
|
||||
ToolbarItem(placement: .cancellationAction) {
|
||||
Button("Done") {
|
||||
Button(L10n.Common.done) {
|
||||
dismiss()
|
||||
}
|
||||
.foregroundColor(Color.appPrimary)
|
||||
|
||||
@@ -35,11 +35,11 @@ struct ProfileTabView: View {
|
||||
// .listRowBackground(Color.appBackgroundSecondary)
|
||||
// }
|
||||
|
||||
Section("Account") {
|
||||
Section(L10n.Profile.account) {
|
||||
Button(action: {
|
||||
showingProfileEdit = true
|
||||
}) {
|
||||
Label("Edit Profile", systemImage: "person.crop.circle")
|
||||
Label(L10n.Profile.editProfile, systemImage: "person.crop.circle")
|
||||
.foregroundColor(Color.appTextPrimary)
|
||||
}
|
||||
|
||||
@@ -47,7 +47,7 @@ struct ProfileTabView: View {
|
||||
showingNotificationPreferences = true
|
||||
}) {
|
||||
HStack {
|
||||
Label("Notifications", systemImage: "bell")
|
||||
Label(L10n.Profile.notifications, systemImage: "bell")
|
||||
.foregroundColor(Color.appTextPrimary)
|
||||
Spacer()
|
||||
Image(systemName: "chevron.right")
|
||||
@@ -56,31 +56,31 @@ struct ProfileTabView: View {
|
||||
}
|
||||
}
|
||||
|
||||
NavigationLink(destination: Text("Privacy")) {
|
||||
Label("Privacy", systemImage: "lock.shield")
|
||||
NavigationLink(destination: Text(L10n.Profile.privacy)) {
|
||||
Label(L10n.Profile.privacy, systemImage: "lock.shield")
|
||||
}
|
||||
}
|
||||
.listRowBackground(Color.appBackgroundSecondary)
|
||||
|
||||
// Subscription Section - Only show if limitations are enabled on backend
|
||||
if let subscription = subscriptionCache.currentSubscription, subscription.limitationsEnabled {
|
||||
Section("Subscription") {
|
||||
Section(L10n.Profile.subscription) {
|
||||
HStack {
|
||||
Image(systemName: "crown.fill")
|
||||
.foregroundColor(subscriptionCache.currentTier == "pro" ? Color.appAccent : Color.appTextSecondary)
|
||||
|
||||
VStack(alignment: .leading, spacing: 4) {
|
||||
Text(subscriptionCache.currentTier == "pro" ? "Pro Plan" : "Free Plan")
|
||||
Text(subscriptionCache.currentTier == "pro" ? L10n.Profile.proPlan : L10n.Profile.freePlan)
|
||||
.font(.headline)
|
||||
.foregroundColor(Color.appTextPrimary)
|
||||
|
||||
if subscriptionCache.currentTier == "pro",
|
||||
let expiresAt = subscription.expiresAt {
|
||||
Text("Active until \(DateUtils.formatDateMedium(expiresAt))")
|
||||
Text("\(L10n.Profile.activeUntil) \(DateUtils.formatDateMedium(expiresAt))")
|
||||
.font(.caption)
|
||||
.foregroundColor(Color.appTextSecondary)
|
||||
} else {
|
||||
Text("Limited features")
|
||||
Text(L10n.Profile.limitedFeatures)
|
||||
.font(.caption)
|
||||
.foregroundColor(Color.appTextSecondary)
|
||||
}
|
||||
@@ -90,7 +90,7 @@ struct ProfileTabView: View {
|
||||
|
||||
if subscriptionCache.currentTier != "pro" {
|
||||
Button(action: { showUpgradePrompt = true }) {
|
||||
Label("Upgrade to Pro", systemImage: "arrow.up.circle.fill")
|
||||
Label(L10n.Profile.upgradeToPro, systemImage: "arrow.up.circle.fill")
|
||||
.foregroundColor(Color.appPrimary)
|
||||
}
|
||||
} else {
|
||||
@@ -99,7 +99,7 @@ struct ProfileTabView: View {
|
||||
UIApplication.shared.open(url)
|
||||
}
|
||||
}) {
|
||||
Label("Manage Subscription", systemImage: "gearshape.fill")
|
||||
Label(L10n.Profile.manageSubscription, systemImage: "gearshape.fill")
|
||||
.foregroundColor(Color.appTextPrimary)
|
||||
}
|
||||
}
|
||||
@@ -110,19 +110,19 @@ struct ProfileTabView: View {
|
||||
showRestoreSuccess = true
|
||||
}
|
||||
}) {
|
||||
Label("Restore Purchases", systemImage: "arrow.clockwise")
|
||||
Label(L10n.Profile.restorePurchases, systemImage: "arrow.clockwise")
|
||||
.foregroundColor(Color.appTextSecondary)
|
||||
}
|
||||
}
|
||||
.listRowBackground(Color.appBackgroundSecondary)
|
||||
}
|
||||
|
||||
Section("Appearance") {
|
||||
Section(L10n.Profile.appearance) {
|
||||
Button(action: {
|
||||
showingThemeSelection = true
|
||||
}) {
|
||||
HStack {
|
||||
Label("Theme", systemImage: "paintpalette")
|
||||
Label(L10n.Profile.theme, systemImage: "paintpalette")
|
||||
.foregroundColor(Color.appTextPrimary)
|
||||
|
||||
Spacer()
|
||||
@@ -143,7 +143,7 @@ struct ProfileTabView: View {
|
||||
Button(action: {
|
||||
showingLogoutAlert = true
|
||||
}) {
|
||||
Label("Log Out", systemImage: "rectangle.portrait.and.arrow.right")
|
||||
Label(L10n.Profile.logout, systemImage: "rectangle.portrait.and.arrow.right")
|
||||
.foregroundColor(Color.appError)
|
||||
}
|
||||
.accessibilityIdentifier(AccessibilityIdentifiers.Profile.logoutButton)
|
||||
@@ -152,12 +152,12 @@ struct ProfileTabView: View {
|
||||
|
||||
Section {
|
||||
VStack(alignment: .leading, spacing: 4) {
|
||||
Text("Casera")
|
||||
Text(L10n.Profile.appName)
|
||||
.font(.caption)
|
||||
.fontWeight(.semibold)
|
||||
.foregroundColor(Color.appTextPrimary)
|
||||
|
||||
Text("Version 1.0.0")
|
||||
Text(L10n.Profile.version)
|
||||
.font(.caption2)
|
||||
.foregroundColor(Color.appTextSecondary)
|
||||
}
|
||||
@@ -166,7 +166,7 @@ struct ProfileTabView: View {
|
||||
}
|
||||
.scrollContentBackground(.hidden)
|
||||
.background(Color.appBackgroundPrimary)
|
||||
.navigationTitle("Profile")
|
||||
.navigationTitle(L10n.Profile.title)
|
||||
.sheet(isPresented: $showingProfileEdit) {
|
||||
ProfileView()
|
||||
}
|
||||
@@ -176,21 +176,21 @@ struct ProfileTabView: View {
|
||||
.sheet(isPresented: $showingNotificationPreferences) {
|
||||
NotificationPreferencesView()
|
||||
}
|
||||
.alert("Log Out", isPresented: $showingLogoutAlert) {
|
||||
Button("Cancel", role: .cancel) { }
|
||||
Button("Log Out", role: .destructive) {
|
||||
.alert(L10n.Profile.logout, isPresented: $showingLogoutAlert) {
|
||||
Button(L10n.Common.cancel, role: .cancel) { }
|
||||
Button(L10n.Profile.logout, role: .destructive) {
|
||||
AuthenticationManager.shared.logout()
|
||||
}
|
||||
} message: {
|
||||
Text("Are you sure you want to log out?")
|
||||
Text(L10n.Profile.logoutConfirm)
|
||||
}
|
||||
.sheet(isPresented: $showUpgradePrompt) {
|
||||
UpgradePromptView(triggerKey: "user_profile", isPresented: $showUpgradePrompt)
|
||||
}
|
||||
.alert("Purchases Restored", isPresented: $showRestoreSuccess) {
|
||||
Button("OK", role: .cancel) { }
|
||||
.alert(L10n.Profile.purchasesRestored, isPresented: $showRestoreSuccess) {
|
||||
Button(L10n.Common.ok, role: .cancel) { }
|
||||
} message: {
|
||||
Text("Your purchases have been restored successfully.")
|
||||
Text(L10n.Profile.purchasesRestoredMessage)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -14,7 +14,7 @@ struct ProfileView: View {
|
||||
if viewModel.isLoadingUser {
|
||||
VStack {
|
||||
ProgressView()
|
||||
Text("Loading profile...")
|
||||
Text(L10n.Profile.loadingProfile)
|
||||
.font(.subheadline)
|
||||
.foregroundColor(Color.appTextSecondary)
|
||||
.padding(.top, 8)
|
||||
@@ -27,7 +27,7 @@ struct ProfileView: View {
|
||||
.font(.system(size: 60))
|
||||
.foregroundStyle(Color.appPrimary.gradient)
|
||||
|
||||
Text("Profile Settings")
|
||||
Text(L10n.Profile.profileSettings)
|
||||
.font(.title2)
|
||||
.fontWeight(.bold)
|
||||
.foregroundColor(Color.appTextPrimary)
|
||||
@@ -38,7 +38,7 @@ struct ProfileView: View {
|
||||
.listRowBackground(Color.clear)
|
||||
|
||||
Section {
|
||||
TextField("First Name", text: $viewModel.firstName)
|
||||
TextField(L10n.Profile.firstName, text: $viewModel.firstName)
|
||||
.textInputAutocapitalization(.words)
|
||||
.autocorrectionDisabled()
|
||||
.focused($focusedField, equals: .firstName)
|
||||
@@ -47,7 +47,7 @@ struct ProfileView: View {
|
||||
focusedField = .lastName
|
||||
}
|
||||
|
||||
TextField("Last Name", text: $viewModel.lastName)
|
||||
TextField(L10n.Profile.lastName, text: $viewModel.lastName)
|
||||
.textInputAutocapitalization(.words)
|
||||
.autocorrectionDisabled()
|
||||
.focused($focusedField, equals: .lastName)
|
||||
@@ -56,12 +56,12 @@ struct ProfileView: View {
|
||||
focusedField = .email
|
||||
}
|
||||
} header: {
|
||||
Text("Personal Information")
|
||||
Text(L10n.Profile.personalInformation)
|
||||
}
|
||||
.listRowBackground(Color.appBackgroundSecondary)
|
||||
|
||||
Section {
|
||||
TextField("Email", text: $viewModel.email)
|
||||
TextField(L10n.Profile.email, text: $viewModel.email)
|
||||
.textInputAutocapitalization(.never)
|
||||
.autocorrectionDisabled()
|
||||
.keyboardType(.emailAddress)
|
||||
@@ -71,9 +71,9 @@ struct ProfileView: View {
|
||||
viewModel.updateProfile()
|
||||
}
|
||||
} header: {
|
||||
Text("Contact")
|
||||
Text(L10n.Profile.contact)
|
||||
} footer: {
|
||||
Text("Email is required and must be unique")
|
||||
Text(L10n.Profile.emailRequiredUnique)
|
||||
}
|
||||
.listRowBackground(Color.appBackgroundSecondary)
|
||||
|
||||
@@ -110,7 +110,7 @@ struct ProfileView: View {
|
||||
if viewModel.isLoading {
|
||||
ProgressView()
|
||||
} else {
|
||||
Text("Save Changes")
|
||||
Text(L10n.Profile.saveChanges)
|
||||
.fontWeight(.semibold)
|
||||
}
|
||||
Spacer()
|
||||
@@ -123,11 +123,11 @@ struct ProfileView: View {
|
||||
.listStyle(.plain)
|
||||
.scrollContentBackground(.hidden)
|
||||
.background(Color.appBackgroundPrimary)
|
||||
.navigationTitle("Profile")
|
||||
.navigationTitle(L10n.Profile.title)
|
||||
.navigationBarTitleDisplayMode(.inline)
|
||||
.toolbar {
|
||||
ToolbarItem(placement: .navigationBarLeading) {
|
||||
Button("Cancel") {
|
||||
Button(L10n.Common.cancel) {
|
||||
dismiss()
|
||||
}
|
||||
}
|
||||
|
||||
@@ -22,11 +22,11 @@ struct ThemeSelectionView: View {
|
||||
.listStyle(.plain)
|
||||
.scrollContentBackground(.hidden)
|
||||
.background(Color.appBackgroundPrimary)
|
||||
.navigationTitle("Appearance")
|
||||
.navigationTitle(L10n.Profile.appearance)
|
||||
.navigationBarTitleDisplayMode(.inline)
|
||||
.toolbar {
|
||||
ToolbarItem(placement: .confirmationAction) {
|
||||
Button("Done") {
|
||||
Button(L10n.Common.done) {
|
||||
dismiss()
|
||||
}
|
||||
}
|
||||
|
||||
@@ -20,11 +20,11 @@ struct RegisterView: View {
|
||||
.font(.system(size: 60))
|
||||
.foregroundStyle(Color.appPrimary.gradient)
|
||||
|
||||
Text("Join Casera")
|
||||
Text(L10n.Auth.joinCasera)
|
||||
.font(.largeTitle)
|
||||
.fontWeight(.bold)
|
||||
|
||||
Text("Start managing your properties today")
|
||||
Text(L10n.Auth.startManaging)
|
||||
.font(.subheadline)
|
||||
.foregroundColor(Color.appTextSecondary)
|
||||
}
|
||||
@@ -34,7 +34,7 @@ struct RegisterView: View {
|
||||
.listRowBackground(Color.clear)
|
||||
|
||||
Section {
|
||||
TextField("Username", text: $viewModel.username)
|
||||
TextField(L10n.Auth.registerUsername, text: $viewModel.username)
|
||||
.textInputAutocapitalization(.never)
|
||||
.autocorrectionDisabled()
|
||||
.textContentType(.username)
|
||||
@@ -45,7 +45,7 @@ struct RegisterView: View {
|
||||
}
|
||||
.accessibilityIdentifier(AccessibilityIdentifiers.Authentication.registerUsernameField)
|
||||
|
||||
TextField("Email", text: $viewModel.email)
|
||||
TextField(L10n.Auth.registerEmail, text: $viewModel.email)
|
||||
.textInputAutocapitalization(.never)
|
||||
.autocorrectionDisabled()
|
||||
.keyboardType(.emailAddress)
|
||||
@@ -57,14 +57,14 @@ struct RegisterView: View {
|
||||
}
|
||||
.accessibilityIdentifier(AccessibilityIdentifiers.Authentication.registerEmailField)
|
||||
} header: {
|
||||
Text("Account Information")
|
||||
Text(L10n.Auth.accountInfo)
|
||||
}
|
||||
.listRowBackground(Color.appBackgroundSecondary)
|
||||
|
||||
Section {
|
||||
// Using .newPassword enables iOS Strong Password generation
|
||||
// iOS will automatically offer to save to iCloud Keychain after successful registration
|
||||
SecureField("Password", text: $viewModel.password)
|
||||
SecureField(L10n.Auth.registerPassword, text: $viewModel.password)
|
||||
.textContentType(.newPassword)
|
||||
.focused($focusedField, equals: .password)
|
||||
.submitLabel(.next)
|
||||
@@ -73,7 +73,7 @@ struct RegisterView: View {
|
||||
}
|
||||
.accessibilityIdentifier(AccessibilityIdentifiers.Authentication.registerPasswordField)
|
||||
|
||||
SecureField("Confirm Password", text: $viewModel.confirmPassword)
|
||||
SecureField(L10n.Auth.registerConfirmPassword, text: $viewModel.confirmPassword)
|
||||
.textContentType(.newPassword)
|
||||
.focused($focusedField, equals: .confirmPassword)
|
||||
.submitLabel(.go)
|
||||
@@ -82,9 +82,9 @@ struct RegisterView: View {
|
||||
}
|
||||
.accessibilityIdentifier(AccessibilityIdentifiers.Authentication.registerConfirmPasswordField)
|
||||
} header: {
|
||||
Text("Security")
|
||||
Text(L10n.Auth.security)
|
||||
} footer: {
|
||||
Text("Tap the password field for a strong password suggestion")
|
||||
Text(L10n.Auth.passwordSuggestion)
|
||||
}
|
||||
.listRowBackground(Color.appBackgroundSecondary)
|
||||
|
||||
@@ -108,7 +108,7 @@ struct RegisterView: View {
|
||||
if viewModel.isLoading {
|
||||
ProgressView()
|
||||
} else {
|
||||
Text("Create Account")
|
||||
Text(L10n.Auth.registerButton)
|
||||
.fontWeight(.semibold)
|
||||
}
|
||||
Spacer()
|
||||
@@ -122,11 +122,11 @@ struct RegisterView: View {
|
||||
.listStyle(.plain)
|
||||
.scrollContentBackground(.hidden)
|
||||
.background(Color.appBackgroundPrimary)
|
||||
.navigationTitle("Create Account")
|
||||
.navigationTitle(L10n.Auth.registerTitle)
|
||||
.navigationBarTitleDisplayMode(.inline)
|
||||
.toolbar {
|
||||
ToolbarItem(placement: .navigationBarLeading) {
|
||||
Button("Cancel") {
|
||||
Button(L10n.Common.cancel) {
|
||||
dismiss()
|
||||
}
|
||||
.accessibilityIdentifier(AccessibilityIdentifiers.Authentication.registerCancelButton)
|
||||
|
||||
@@ -12,7 +12,7 @@ struct JoinResidenceView: View {
|
||||
NavigationView {
|
||||
Form {
|
||||
Section {
|
||||
TextField("Share Code", text: $shareCode)
|
||||
TextField(L10n.Residences.shareCode, text: $shareCode)
|
||||
.textInputAutocapitalization(.characters)
|
||||
.autocorrectionDisabled()
|
||||
.onChange(of: shareCode) { newValue in
|
||||
@@ -25,9 +25,9 @@ struct JoinResidenceView: View {
|
||||
}
|
||||
.disabled(viewModel.isLoading)
|
||||
} header: {
|
||||
Text("Enter Share Code")
|
||||
Text(L10n.Residences.enterShareCode)
|
||||
} footer: {
|
||||
Text("Enter the 6-character code shared with you to join a residence")
|
||||
Text(L10n.Residences.shareCodeFooter)
|
||||
.foregroundColor(Color.appTextSecondary)
|
||||
}
|
||||
.listRowBackground(Color.appBackgroundSecondary)
|
||||
@@ -48,7 +48,7 @@ struct JoinResidenceView: View {
|
||||
ProgressView()
|
||||
.progressViewStyle(CircularProgressViewStyle())
|
||||
} else {
|
||||
Text("Join Residence")
|
||||
Text(L10n.Residences.joinButton)
|
||||
.fontWeight(.semibold)
|
||||
}
|
||||
Spacer()
|
||||
@@ -61,11 +61,11 @@ struct JoinResidenceView: View {
|
||||
.listStyle(.plain)
|
||||
.scrollContentBackground(.hidden)
|
||||
.background(Color.appBackgroundPrimary)
|
||||
.navigationTitle("Join Residence")
|
||||
.navigationTitle(L10n.Residences.joinTitle)
|
||||
.navigationBarTitleDisplayMode(.inline)
|
||||
.toolbar {
|
||||
ToolbarItem(placement: .navigationBarLeading) {
|
||||
Button("Cancel") {
|
||||
Button(L10n.Common.cancel) {
|
||||
dismiss()
|
||||
}
|
||||
.disabled(viewModel.isLoading)
|
||||
@@ -76,7 +76,7 @@ struct JoinResidenceView: View {
|
||||
|
||||
private func joinResidence() {
|
||||
guard shareCode.count == 6 else {
|
||||
viewModel.errorMessage = "Share code must be 6 characters"
|
||||
viewModel.errorMessage = L10n.Residences.shareCodeMust6
|
||||
return
|
||||
}
|
||||
|
||||
|
||||
@@ -43,7 +43,7 @@ struct ManageUsersView: View {
|
||||
|
||||
// Users list
|
||||
VStack(alignment: .leading, spacing: 12) {
|
||||
Text("Users (\(users.count))")
|
||||
Text("\(L10n.Residences.users) (\(users.count))")
|
||||
.font(.headline)
|
||||
.padding(.horizontal)
|
||||
|
||||
@@ -66,11 +66,11 @@ struct ManageUsersView: View {
|
||||
.listStyle(.plain)
|
||||
.scrollContentBackground(.hidden)
|
||||
.background(Color.appBackgroundPrimary)
|
||||
.navigationTitle("Manage Users")
|
||||
.navigationTitle(L10n.Residences.manageUsers)
|
||||
.navigationBarTitleDisplayMode(.inline)
|
||||
.toolbar {
|
||||
ToolbarItem(placement: .navigationBarTrailing) {
|
||||
Button("Close") {
|
||||
Button(L10n.Common.close) {
|
||||
dismiss()
|
||||
}
|
||||
}
|
||||
|
||||
@@ -54,35 +54,35 @@ struct ResidenceDetailView: View {
|
||||
leadingToolbar
|
||||
trailingToolbar
|
||||
}
|
||||
|
||||
|
||||
// MARK: Alerts
|
||||
.alert("Generate Report", isPresented: $showReportConfirmation) {
|
||||
Button("Cancel", role: .cancel) {
|
||||
.alert(L10n.Residences.generateReport, isPresented: $showReportConfirmation) {
|
||||
Button(L10n.Common.cancel, role: .cancel) {
|
||||
showReportConfirmation = false
|
||||
}
|
||||
Button("Generate") {
|
||||
Button(L10n.Residences.generate) {
|
||||
viewModel.generateTasksReport(residenceId: residenceId, email: "")
|
||||
showReportConfirmation = false
|
||||
}
|
||||
} message: {
|
||||
Text("This will generate a comprehensive report of your property including all tasks, documents, and contractors.")
|
||||
Text(L10n.Residences.generateReportMessage)
|
||||
}
|
||||
|
||||
.alert("Delete Residence", isPresented: $showDeleteConfirmation) {
|
||||
Button("Cancel", role: .cancel) { }
|
||||
|
||||
.alert(L10n.Residences.deleteTitle, isPresented: $showDeleteConfirmation) {
|
||||
Button(L10n.Common.cancel, role: .cancel) { }
|
||||
.accessibilityIdentifier(AccessibilityIdentifiers.Alert.cancelButton)
|
||||
Button("Delete", role: .destructive) {
|
||||
Button(L10n.Common.delete, role: .destructive) {
|
||||
deleteResidence()
|
||||
}
|
||||
.accessibilityIdentifier(AccessibilityIdentifiers.Alert.deleteButton)
|
||||
} message: {
|
||||
if let residence = viewModel.selectedResidence {
|
||||
Text("Are you sure you want to delete \(residence.name)? This action cannot be undone and will delete all associated tasks, documents, and data.")
|
||||
Text("\(L10n.Residences.deleteConfirmMessage)")
|
||||
}
|
||||
}
|
||||
|
||||
.alert("Maintenance Report", isPresented: $showReportAlert) {
|
||||
Button("OK", role: .cancel) { }
|
||||
|
||||
.alert(L10n.Residences.maintenanceReport, isPresented: $showReportAlert) {
|
||||
Button(L10n.Common.ok, role: .cancel) { }
|
||||
} message: {
|
||||
Text(viewModel.reportMessage ?? "")
|
||||
}
|
||||
@@ -189,7 +189,7 @@ private extension ResidenceDetailView {
|
||||
var loadingView: some View {
|
||||
VStack(spacing: 16) {
|
||||
ProgressView()
|
||||
Text("Loading residence...")
|
||||
Text(L10n.Residences.loadingResidence)
|
||||
.font(.subheadline)
|
||||
.foregroundColor(Color.appTextSecondary)
|
||||
}
|
||||
@@ -226,9 +226,9 @@ private extension ResidenceDetailView {
|
||||
reloadTasks: { loadResidenceTasks() }
|
||||
)
|
||||
} else if isLoadingTasks {
|
||||
ProgressView("Loading tasks...")
|
||||
ProgressView(L10n.Residences.loadingTasks)
|
||||
} else if let tasksError = tasksError {
|
||||
Text("Error loading tasks: \(tasksError)")
|
||||
Text("\(L10n.Residences.errorLoadingTasks): \(tasksError)")
|
||||
.foregroundColor(Color.appError)
|
||||
.padding()
|
||||
}
|
||||
@@ -242,7 +242,7 @@ private extension ResidenceDetailView {
|
||||
Image(systemName: "person.2.fill")
|
||||
.font(.title2)
|
||||
.foregroundColor(Color.appPrimary)
|
||||
Text("Contractors")
|
||||
Text(L10n.Residences.contractors)
|
||||
.font(.title2.weight(.bold))
|
||||
.foregroundColor(Color.appPrimary)
|
||||
}
|
||||
@@ -256,7 +256,7 @@ private extension ResidenceDetailView {
|
||||
}
|
||||
.padding()
|
||||
} else if let error = contractorsError {
|
||||
Text("Error: \(error)")
|
||||
Text("\(L10n.Common.error): \(error)")
|
||||
.foregroundColor(Color.appError)
|
||||
.padding()
|
||||
} else if contractors.isEmpty {
|
||||
@@ -265,10 +265,10 @@ private extension ResidenceDetailView {
|
||||
Image(systemName: "person.crop.circle.badge.plus")
|
||||
.font(.system(size: 48))
|
||||
.foregroundColor(Color.appTextSecondary.opacity(0.6))
|
||||
Text("No contractors yet")
|
||||
Text(L10n.Residences.noContractors)
|
||||
.font(.headline)
|
||||
.foregroundColor(Color.appTextPrimary)
|
||||
Text("Add contractors from the Contractors tab")
|
||||
Text(L10n.Residences.addContractorsPrompt)
|
||||
.font(.subheadline)
|
||||
.foregroundColor(Color.appTextSecondary)
|
||||
}
|
||||
@@ -303,7 +303,7 @@ private extension ResidenceDetailView {
|
||||
var leadingToolbar: some ToolbarContent {
|
||||
ToolbarItem(placement: .navigationBarLeading) {
|
||||
if viewModel.selectedResidence != nil {
|
||||
Button("Edit") {
|
||||
Button(L10n.Common.edit) {
|
||||
showEditResidence = true
|
||||
}
|
||||
.accessibilityIdentifier(AccessibilityIdentifiers.Residence.editButton)
|
||||
|
||||
@@ -43,7 +43,7 @@ struct ResidencesListView: View {
|
||||
})
|
||||
}
|
||||
}
|
||||
.navigationTitle("My Properties")
|
||||
.navigationTitle(L10n.Residences.title)
|
||||
.navigationBarTitleDisplayMode(.inline)
|
||||
.toolbar {
|
||||
ToolbarItemGroup(placement: .navigationBarTrailing) {
|
||||
@@ -134,10 +134,10 @@ private struct ResidencesContent: View {
|
||||
// Properties Header
|
||||
HStack {
|
||||
VStack(alignment: .leading, spacing: AppSpacing.xxs) {
|
||||
Text("Your Properties")
|
||||
Text(L10n.Residences.yourProperties)
|
||||
.font(.title3.weight(.semibold))
|
||||
.foregroundColor(Color.appTextPrimary)
|
||||
Text("\(residences.count) \(residences.count == 1 ? "property" : "properties")")
|
||||
Text("\(residences.count) \(residences.count == 1 ? L10n.Residences.property : L10n.Residences.properties)")
|
||||
.font(.callout)
|
||||
.foregroundColor(Color.appTextSecondary)
|
||||
}
|
||||
|
||||
@@ -48,7 +48,7 @@ struct ResidenceFormView: View {
|
||||
NavigationView {
|
||||
Form {
|
||||
Section {
|
||||
TextField("Property Name", text: $name)
|
||||
TextField(L10n.Residences.propertyName, text: $name)
|
||||
.focused($focusedField, equals: .name)
|
||||
.accessibilityIdentifier(AccessibilityIdentifiers.Residence.nameField)
|
||||
|
||||
@@ -58,54 +58,54 @@ struct ResidenceFormView: View {
|
||||
.foregroundColor(Color.appError)
|
||||
}
|
||||
|
||||
Picker("Property Type", selection: $selectedPropertyType) {
|
||||
Text("Select Type").tag(nil as ResidenceType?)
|
||||
Picker(L10n.Residences.propertyType, selection: $selectedPropertyType) {
|
||||
Text(L10n.Residences.selectType).tag(nil as ResidenceType?)
|
||||
ForEach(residenceTypes, id: \.id) { type in
|
||||
Text(type.name).tag(type as ResidenceType?)
|
||||
}
|
||||
}
|
||||
.accessibilityIdentifier(AccessibilityIdentifiers.Residence.propertyTypePicker)
|
||||
} header: {
|
||||
Text("Property Details")
|
||||
Text(L10n.Residences.propertyDetails)
|
||||
} footer: {
|
||||
Text("Required: Name")
|
||||
Text(L10n.Residences.requiredName)
|
||||
.font(.caption)
|
||||
.foregroundColor(Color.appError)
|
||||
}
|
||||
.listRowBackground(Color.appBackgroundSecondary)
|
||||
|
||||
Section {
|
||||
TextField("Street Address", text: $streetAddress)
|
||||
TextField(L10n.Residences.streetAddress, text: $streetAddress)
|
||||
.focused($focusedField, equals: .streetAddress)
|
||||
.accessibilityIdentifier(AccessibilityIdentifiers.Residence.streetAddressField)
|
||||
|
||||
TextField("Apartment/Unit (optional)", text: $apartmentUnit)
|
||||
TextField(L10n.Residences.apartmentUnit, text: $apartmentUnit)
|
||||
.focused($focusedField, equals: .apartmentUnit)
|
||||
.accessibilityIdentifier(AccessibilityIdentifiers.Residence.apartmentUnitField)
|
||||
|
||||
TextField("City", text: $city)
|
||||
TextField(L10n.Residences.city, text: $city)
|
||||
.focused($focusedField, equals: .city)
|
||||
.accessibilityIdentifier(AccessibilityIdentifiers.Residence.cityField)
|
||||
|
||||
TextField("State/Province", text: $stateProvince)
|
||||
TextField(L10n.Residences.stateProvince, text: $stateProvince)
|
||||
.focused($focusedField, equals: .stateProvince)
|
||||
.accessibilityIdentifier(AccessibilityIdentifiers.Residence.stateProvinceField)
|
||||
|
||||
TextField("Postal Code", text: $postalCode)
|
||||
TextField(L10n.Residences.postalCode, text: $postalCode)
|
||||
.focused($focusedField, equals: .postalCode)
|
||||
.accessibilityIdentifier(AccessibilityIdentifiers.Residence.postalCodeField)
|
||||
|
||||
TextField("Country", text: $country)
|
||||
TextField(L10n.Residences.country, text: $country)
|
||||
.focused($focusedField, equals: .country)
|
||||
.accessibilityIdentifier(AccessibilityIdentifiers.Residence.countryField)
|
||||
} header: {
|
||||
Text("Address")
|
||||
Text(L10n.Residences.address)
|
||||
}
|
||||
.listRowBackground(Color.appBackgroundSecondary)
|
||||
|
||||
Section(header: Text("Property Features")) {
|
||||
Section(header: Text(L10n.Residences.propertyFeatures)) {
|
||||
HStack {
|
||||
Text("Bedrooms")
|
||||
Text(L10n.Residences.bedrooms)
|
||||
Spacer()
|
||||
TextField("0", text: $bedrooms)
|
||||
.keyboardType(.numberPad)
|
||||
@@ -116,7 +116,7 @@ struct ResidenceFormView: View {
|
||||
}
|
||||
|
||||
HStack {
|
||||
Text("Bathrooms")
|
||||
Text(L10n.Residences.bathrooms)
|
||||
Spacer()
|
||||
TextField("0.0", text: $bathrooms)
|
||||
.keyboardType(.decimalPad)
|
||||
@@ -126,29 +126,29 @@ struct ResidenceFormView: View {
|
||||
.accessibilityIdentifier(AccessibilityIdentifiers.Residence.bathroomsField)
|
||||
}
|
||||
|
||||
TextField("Square Footage", text: $squareFootage)
|
||||
TextField(L10n.Residences.squareFootage, text: $squareFootage)
|
||||
.keyboardType(.numberPad)
|
||||
.focused($focusedField, equals: .squareFootage)
|
||||
.accessibilityIdentifier(AccessibilityIdentifiers.Residence.squareFootageField)
|
||||
|
||||
TextField("Lot Size (acres)", text: $lotSize)
|
||||
TextField(L10n.Residences.lotSize, text: $lotSize)
|
||||
.keyboardType(.decimalPad)
|
||||
.focused($focusedField, equals: .lotSize)
|
||||
.accessibilityIdentifier(AccessibilityIdentifiers.Residence.lotSizeField)
|
||||
|
||||
TextField("Year Built", text: $yearBuilt)
|
||||
TextField(L10n.Residences.yearBuilt, text: $yearBuilt)
|
||||
.keyboardType(.numberPad)
|
||||
.focused($focusedField, equals: .yearBuilt)
|
||||
.accessibilityIdentifier(AccessibilityIdentifiers.Residence.yearBuiltField)
|
||||
}
|
||||
.listRowBackground(Color.appBackgroundSecondary)
|
||||
|
||||
Section(header: Text("Additional Details")) {
|
||||
TextField("Description (optional)", text: $description, axis: .vertical)
|
||||
Section(header: Text(L10n.Residences.additionalDetails)) {
|
||||
TextField(L10n.Residences.description, text: $description, axis: .vertical)
|
||||
.lineLimit(3...6)
|
||||
.accessibilityIdentifier(AccessibilityIdentifiers.Residence.descriptionField)
|
||||
|
||||
Toggle("Primary Residence", isOn: $isPrimary)
|
||||
Toggle(L10n.Residences.primaryResidence, isOn: $isPrimary)
|
||||
.accessibilityIdentifier(AccessibilityIdentifiers.Residence.isPrimaryToggle)
|
||||
}
|
||||
.listRowBackground(Color.appBackgroundSecondary)
|
||||
@@ -165,18 +165,18 @@ struct ResidenceFormView: View {
|
||||
.listStyle(.plain)
|
||||
.scrollContentBackground(.hidden)
|
||||
.background(Color.appBackgroundPrimary)
|
||||
.navigationTitle(isEditMode ? "Edit Residence" : "Add Residence")
|
||||
.navigationTitle(isEditMode ? L10n.Residences.editTitle : L10n.Residences.addTitle)
|
||||
.navigationBarTitleDisplayMode(.inline)
|
||||
.toolbar {
|
||||
ToolbarItem(placement: .navigationBarLeading) {
|
||||
Button("Cancel") {
|
||||
Button(L10n.Common.cancel) {
|
||||
isPresented = false
|
||||
}
|
||||
.accessibilityIdentifier(AccessibilityIdentifiers.Residence.formCancelButton)
|
||||
}
|
||||
|
||||
ToolbarItem(placement: .navigationBarTrailing) {
|
||||
Button("Save") {
|
||||
Button(L10n.Common.save) {
|
||||
submitForm()
|
||||
}
|
||||
.disabled(!canSave || viewModel.isLoading)
|
||||
@@ -244,7 +244,7 @@ struct ResidenceFormView: View {
|
||||
var isValid = true
|
||||
|
||||
if name.isEmpty {
|
||||
nameError = "Name is required"
|
||||
nameError = L10n.Residences.nameRequired
|
||||
isValid = false
|
||||
} else {
|
||||
nameError = ""
|
||||
|
||||
@@ -53,7 +53,7 @@ struct DynamicTaskColumnView: View {
|
||||
.font(.system(size: 40))
|
||||
.foregroundColor(columnColor.opacity(0.3))
|
||||
|
||||
Text("No tasks")
|
||||
Text(L10n.Tasks.noTasks)
|
||||
.font(.caption)
|
||||
.foregroundColor(Color.appTextSecondary)
|
||||
}
|
||||
|
||||
@@ -89,7 +89,7 @@ struct TaskCard: View {
|
||||
.font(.system(size: 14, weight: .semibold))
|
||||
.foregroundColor(Color.appAccent)
|
||||
}
|
||||
Text("Completions (\(task.completions.count))")
|
||||
Text("\(L10n.Tasks.completions.capitalized) (\(task.completions.count))")
|
||||
.font(.footnote.weight(.medium))
|
||||
.fontWeight(.semibold)
|
||||
.foregroundColor(Color.appTextPrimary)
|
||||
@@ -121,7 +121,7 @@ struct TaskCard: View {
|
||||
HStack(spacing: AppSpacing.xs) {
|
||||
Image(systemName: "play.circle.fill")
|
||||
.font(.system(size: 16, weight: .semibold))
|
||||
Text("In Progress")
|
||||
Text(L10n.Tasks.inProgress)
|
||||
.font(.subheadline.weight(.medium))
|
||||
.fontWeight(.semibold)
|
||||
}
|
||||
@@ -138,7 +138,7 @@ struct TaskCard: View {
|
||||
HStack(spacing: AppSpacing.xs) {
|
||||
Image(systemName: "checkmark.circle.fill")
|
||||
.font(.system(size: 16, weight: .semibold))
|
||||
Text("Complete")
|
||||
Text(L10n.Tasks.complete)
|
||||
.font(.subheadline.weight(.medium))
|
||||
.fontWeight(.semibold)
|
||||
}
|
||||
@@ -159,7 +159,7 @@ struct TaskCard: View {
|
||||
HStack(spacing: AppSpacing.xxs) {
|
||||
Image(systemName: "pencil")
|
||||
.font(.system(size: 14, weight: .medium))
|
||||
Text("Edit")
|
||||
Text(L10n.Tasks.edit)
|
||||
.font(.footnote.weight(.medium))
|
||||
}
|
||||
.frame(maxWidth: .infinity)
|
||||
@@ -174,7 +174,7 @@ struct TaskCard: View {
|
||||
HStack(spacing: AppSpacing.xxs) {
|
||||
Image(systemName: "xmark.circle")
|
||||
.font(.system(size: 14, weight: .medium))
|
||||
Text("Cancel")
|
||||
Text(L10n.Tasks.cancel)
|
||||
.font(.footnote.weight(.medium))
|
||||
}
|
||||
.frame(maxWidth: .infinity)
|
||||
@@ -188,7 +188,7 @@ struct TaskCard: View {
|
||||
HStack(spacing: AppSpacing.xxs) {
|
||||
Image(systemName: "arrow.uturn.backward")
|
||||
.font(.system(size: 14, weight: .medium))
|
||||
Text("Restore")
|
||||
Text(L10n.Tasks.restore)
|
||||
.font(.footnote.weight(.medium))
|
||||
}
|
||||
.frame(maxWidth: .infinity)
|
||||
@@ -206,7 +206,7 @@ struct TaskCard: View {
|
||||
HStack(spacing: AppSpacing.xxs) {
|
||||
Image(systemName: "tray.and.arrow.up")
|
||||
.font(.system(size: 14, weight: .medium))
|
||||
Text("Unarchive")
|
||||
Text(L10n.Tasks.unarchive)
|
||||
.font(.footnote.weight(.medium))
|
||||
}
|
||||
.frame(maxWidth: .infinity)
|
||||
@@ -222,7 +222,7 @@ struct TaskCard: View {
|
||||
HStack(spacing: AppSpacing.xxs) {
|
||||
Image(systemName: "archivebox")
|
||||
.font(.system(size: 14, weight: .medium))
|
||||
Text("Archive")
|
||||
Text(L10n.Tasks.archive)
|
||||
.font(.footnote.weight(.medium))
|
||||
}
|
||||
.frame(maxWidth: .infinity)
|
||||
|
||||
@@ -57,11 +57,11 @@ struct AllTasksView: View {
|
||||
.sheet(isPresented: $showingUpgradePrompt) {
|
||||
UpgradePromptView(triggerKey: "add_11th_task", isPresented: $showingUpgradePrompt)
|
||||
}
|
||||
.alert("Archive Task", isPresented: $showArchiveConfirmation) {
|
||||
Button("Cancel", role: .cancel) {
|
||||
.alert(L10n.Tasks.archiveTask, isPresented: $showArchiveConfirmation) {
|
||||
Button(L10n.Common.cancel, role: .cancel) {
|
||||
selectedTaskForArchive = nil
|
||||
}
|
||||
Button("Archive", role: .destructive) {
|
||||
Button(L10n.Tasks.archive, role: .destructive) {
|
||||
if let task = selectedTaskForArchive {
|
||||
taskViewModel.archiveTask(id: task.id) { _ in
|
||||
loadAllTasks()
|
||||
@@ -71,14 +71,14 @@ struct AllTasksView: View {
|
||||
}
|
||||
} message: {
|
||||
if let task = selectedTaskForArchive {
|
||||
Text("Are you sure you want to archive \"\(task.title)\"? You can unarchive it later from archived tasks.")
|
||||
Text(L10n.Tasks.archiveConfirm.replacingOccurrences(of: "this task", with: "\"\(task.title)\""))
|
||||
}
|
||||
}
|
||||
.alert("Delete Task", isPresented: $showCancelConfirmation) {
|
||||
Button("Cancel", role: .cancel) {
|
||||
.alert(L10n.Tasks.deleteTask, isPresented: $showCancelConfirmation) {
|
||||
Button(L10n.Common.cancel, role: .cancel) {
|
||||
selectedTaskForCancel = nil
|
||||
}
|
||||
Button("Archive", role: .destructive) {
|
||||
Button(L10n.Tasks.archive, role: .destructive) {
|
||||
if let task = selectedTaskForCancel {
|
||||
taskViewModel.cancelTask(id: task.id) { _ in
|
||||
loadAllTasks()
|
||||
@@ -88,7 +88,7 @@ struct AllTasksView: View {
|
||||
}
|
||||
} message: {
|
||||
if let task = selectedTaskForCancel {
|
||||
Text("Are you sure you want to archive \"\(task.title)\"? You can unarchive it later from archived tasks.")
|
||||
Text(L10n.Tasks.archiveConfirm.replacingOccurrences(of: "this task", with: "\"\(task.title)\""))
|
||||
}
|
||||
}
|
||||
.onChange(of: showAddTask) { isShowing in
|
||||
@@ -129,16 +129,16 @@ struct AllTasksView: View {
|
||||
.font(.system(size: 64))
|
||||
.foregroundStyle(Color.appPrimary.opacity(0.6))
|
||||
|
||||
Text("No tasks yet")
|
||||
Text(L10n.Tasks.noTasksYet)
|
||||
.font(.title2)
|
||||
.fontWeight(.semibold)
|
||||
.foregroundColor(Color.appTextPrimary)
|
||||
|
||||
Text("Create your first task to get started")
|
||||
Text(L10n.Tasks.createFirst)
|
||||
.font(.body)
|
||||
.foregroundColor(Color.appTextSecondary)
|
||||
.multilineTextAlignment(.center)
|
||||
|
||||
|
||||
Button(action: {
|
||||
// Check if we should show upgrade prompt before adding
|
||||
if subscriptionCache.shouldShowUpgradePrompt(currentCount: totalTaskCount, limitKey: "tasks") {
|
||||
@@ -149,7 +149,7 @@ struct AllTasksView: View {
|
||||
}) {
|
||||
HStack(spacing: 8) {
|
||||
Image(systemName: "plus")
|
||||
Text("Add Task")
|
||||
Text(L10n.Tasks.addButton)
|
||||
.fontWeight(.semibold)
|
||||
}
|
||||
.frame(maxWidth: .infinity)
|
||||
@@ -160,13 +160,13 @@ struct AllTasksView: View {
|
||||
.padding(.horizontal, 48)
|
||||
.disabled(residenceViewModel.myResidences?.residences.isEmpty ?? true)
|
||||
.accessibilityIdentifier(AccessibilityIdentifiers.Task.addButton)
|
||||
|
||||
|
||||
if residenceViewModel.myResidences?.residences.isEmpty ?? true {
|
||||
Text("Add a property first from the Residences tab")
|
||||
Text(L10n.Tasks.addPropertyFirst)
|
||||
.font(.caption)
|
||||
.foregroundColor(Color.appError)
|
||||
}
|
||||
|
||||
|
||||
Spacer()
|
||||
}
|
||||
.padding()
|
||||
@@ -235,7 +235,7 @@ struct AllTasksView: View {
|
||||
}
|
||||
.scrollContentBackground(.hidden)
|
||||
.background(Color.appBackgroundPrimary)
|
||||
.navigationTitle("All Tasks")
|
||||
.navigationTitle(L10n.Tasks.allTasks)
|
||||
.navigationBarTitleDisplayMode(.inline)
|
||||
.toolbar {
|
||||
ToolbarItem(placement: .navigationBarTrailing) {
|
||||
|
||||
@@ -51,7 +51,7 @@ struct CompleteTaskView: View {
|
||||
}
|
||||
}
|
||||
} header: {
|
||||
Text("Task Details")
|
||||
Text(L10n.Tasks.taskDetails)
|
||||
}
|
||||
.listRowBackground(Color.appBackgroundSecondary)
|
||||
|
||||
@@ -61,7 +61,7 @@ struct CompleteTaskView: View {
|
||||
showContractorPicker = true
|
||||
}) {
|
||||
HStack {
|
||||
Label("Select Contractor", systemImage: "wrench.and.screwdriver")
|
||||
Label(L10n.Tasks.selectContractor, systemImage: "wrench.and.screwdriver")
|
||||
.foregroundStyle(.primary)
|
||||
|
||||
Spacer()
|
||||
@@ -77,7 +77,7 @@ struct CompleteTaskView: View {
|
||||
}
|
||||
}
|
||||
} else {
|
||||
Text("None")
|
||||
Text(L10n.Tasks.none)
|
||||
.foregroundStyle(.tertiary)
|
||||
}
|
||||
|
||||
@@ -87,20 +87,20 @@ struct CompleteTaskView: View {
|
||||
}
|
||||
}
|
||||
} header: {
|
||||
Text("Contractor (Optional)")
|
||||
Text(L10n.Tasks.contractorOptional)
|
||||
} footer: {
|
||||
Text("Select a contractor if they completed this work, or leave blank for manual entry.")
|
||||
Text(L10n.Tasks.contractorHelper)
|
||||
}
|
||||
.listRowBackground(Color.appBackgroundSecondary)
|
||||
|
||||
// Completion Details Section
|
||||
Section {
|
||||
LabeledContent {
|
||||
TextField("Your name", text: $completedByName)
|
||||
TextField(L10n.Tasks.yourName, text: $completedByName)
|
||||
.multilineTextAlignment(.trailing)
|
||||
.disabled(selectedContractor != nil)
|
||||
} label: {
|
||||
Label("Completed By", systemImage: "person")
|
||||
Label(L10n.Tasks.completedBy, systemImage: "person")
|
||||
}
|
||||
|
||||
LabeledContent {
|
||||
@@ -113,19 +113,19 @@ struct CompleteTaskView: View {
|
||||
}
|
||||
.padding(.leading, 12)
|
||||
} label: {
|
||||
Label("Actual Cost", systemImage: "dollarsign.circle")
|
||||
Label(L10n.Tasks.actualCost, systemImage: "dollarsign.circle")
|
||||
}
|
||||
} header: {
|
||||
Text("Optional Information")
|
||||
Text(L10n.Tasks.optionalInfo)
|
||||
} footer: {
|
||||
Text("Add any additional details about completing this task.")
|
||||
Text(L10n.Tasks.optionalDetails)
|
||||
}
|
||||
.listRowBackground(Color.appBackgroundSecondary)
|
||||
|
||||
// Notes Section
|
||||
Section {
|
||||
VStack(alignment: .leading, spacing: 8) {
|
||||
Label("Notes", systemImage: "note.text")
|
||||
Label(L10n.Tasks.notes, systemImage: "note.text")
|
||||
.font(.subheadline)
|
||||
.foregroundStyle(.secondary)
|
||||
|
||||
@@ -134,7 +134,7 @@ struct CompleteTaskView: View {
|
||||
.scrollContentBackground(.hidden)
|
||||
}
|
||||
} footer: {
|
||||
Text("Optional notes about the work completed.")
|
||||
Text(L10n.Tasks.optionalNotes)
|
||||
}
|
||||
.listRowBackground(Color.appBackgroundSecondary)
|
||||
|
||||
@@ -142,7 +142,7 @@ struct CompleteTaskView: View {
|
||||
Section {
|
||||
VStack(spacing: 12) {
|
||||
HStack {
|
||||
Label("Quality Rating", systemImage: "star")
|
||||
Label(L10n.Tasks.qualityRating, systemImage: "star")
|
||||
.font(.subheadline)
|
||||
|
||||
Spacer()
|
||||
@@ -168,7 +168,7 @@ struct CompleteTaskView: View {
|
||||
.frame(maxWidth: .infinity)
|
||||
}
|
||||
} footer: {
|
||||
Text("Rate the quality of work from 1 to 5 stars.")
|
||||
Text(L10n.Tasks.rateQuality)
|
||||
}
|
||||
.listRowBackground(Color.appBackgroundSecondary)
|
||||
|
||||
@@ -179,7 +179,7 @@ struct CompleteTaskView: View {
|
||||
Button(action: {
|
||||
showCamera = true
|
||||
}) {
|
||||
Label("Take Photo", systemImage: "camera")
|
||||
Label(L10n.Tasks.takePhoto, systemImage: "camera")
|
||||
.frame(maxWidth: .infinity)
|
||||
.foregroundStyle(Color.appPrimary)
|
||||
}
|
||||
@@ -191,7 +191,7 @@ struct CompleteTaskView: View {
|
||||
matching: .images,
|
||||
photoLibrary: .shared()
|
||||
) {
|
||||
Label("Library", systemImage: "photo.on.rectangle.angled")
|
||||
Label(L10n.Tasks.library, systemImage: "photo.on.rectangle.angled")
|
||||
.frame(maxWidth: .infinity)
|
||||
.foregroundStyle(Color.appPrimary)
|
||||
}
|
||||
@@ -230,9 +230,9 @@ struct CompleteTaskView: View {
|
||||
}
|
||||
}
|
||||
} header: {
|
||||
Text("Photos (\(selectedImages.count)/5)")
|
||||
Text("\(L10n.Tasks.photos) (\(selectedImages.count)/5)")
|
||||
} footer: {
|
||||
Text("Add up to 5 photos documenting the completed work.")
|
||||
Text(L10n.Tasks.addPhotos)
|
||||
}
|
||||
.listRowBackground(Color.appBackgroundSecondary)
|
||||
|
||||
@@ -244,7 +244,7 @@ struct CompleteTaskView: View {
|
||||
ProgressView()
|
||||
.tint(.white)
|
||||
} else {
|
||||
Label("Complete Task", systemImage: "checkmark.circle.fill")
|
||||
Label(L10n.Tasks.completeTask, systemImage: "checkmark.circle.fill")
|
||||
}
|
||||
}
|
||||
.frame(maxWidth: .infinity)
|
||||
@@ -258,17 +258,17 @@ struct CompleteTaskView: View {
|
||||
.listStyle(.plain)
|
||||
.scrollContentBackground(.hidden)
|
||||
.background(Color.appBackgroundPrimary)
|
||||
.navigationTitle("Complete Task")
|
||||
.navigationTitle(L10n.Tasks.completeTask)
|
||||
.navigationBarTitleDisplayMode(.inline)
|
||||
.toolbar {
|
||||
ToolbarItem(placement: .cancellationAction) {
|
||||
Button("Cancel") {
|
||||
Button(L10n.Common.cancel) {
|
||||
dismiss()
|
||||
}
|
||||
}
|
||||
}
|
||||
.alert("Error", isPresented: $showError) {
|
||||
Button("OK", role: .cancel) {}
|
||||
.alert(L10n.Tasks.error, isPresented: $showError) {
|
||||
Button(L10n.Common.ok, role: .cancel) {}
|
||||
} message: {
|
||||
Text(errorMessage)
|
||||
}
|
||||
@@ -386,9 +386,9 @@ struct ContractorPickerView: View {
|
||||
}) {
|
||||
HStack {
|
||||
VStack(alignment: .leading) {
|
||||
Text("None (Manual Entry)")
|
||||
Text(L10n.Tasks.noneManual)
|
||||
.foregroundStyle(.primary)
|
||||
Text("Enter name manually")
|
||||
Text(L10n.Tasks.enterManually)
|
||||
.font(.caption)
|
||||
.foregroundStyle(.secondary)
|
||||
}
|
||||
@@ -450,11 +450,11 @@ struct ContractorPickerView: View {
|
||||
}
|
||||
}
|
||||
}
|
||||
.navigationTitle("Select Contractor")
|
||||
.navigationTitle(L10n.Tasks.selectContractor)
|
||||
.navigationBarTitleDisplayMode(.inline)
|
||||
.toolbar {
|
||||
ToolbarItem(placement: .cancellationAction) {
|
||||
Button("Cancel") {
|
||||
Button(L10n.Common.cancel) {
|
||||
dismiss()
|
||||
}
|
||||
}
|
||||
|
||||
@@ -21,11 +21,11 @@ struct CompletionHistorySheet: View {
|
||||
completionsList
|
||||
}
|
||||
}
|
||||
.navigationTitle("Completion History")
|
||||
.navigationTitle(L10n.Tasks.completionHistory)
|
||||
.navigationBarTitleDisplayMode(.inline)
|
||||
.toolbar {
|
||||
ToolbarItem(placement: .cancellationAction) {
|
||||
Button("Done") {
|
||||
Button(L10n.Common.done) {
|
||||
isPresented = false
|
||||
}
|
||||
}
|
||||
@@ -47,7 +47,7 @@ struct CompletionHistorySheet: View {
|
||||
ProgressView()
|
||||
.progressViewStyle(CircularProgressViewStyle(tint: Color.appPrimary))
|
||||
.scaleEffect(1.5)
|
||||
Text("Loading completions...")
|
||||
Text(L10n.Tasks.loadingCompletions)
|
||||
.font(.subheadline)
|
||||
.foregroundColor(Color.appTextSecondary)
|
||||
}
|
||||
@@ -60,7 +60,7 @@ struct CompletionHistorySheet: View {
|
||||
.font(.system(size: 48))
|
||||
.foregroundColor(Color.appError)
|
||||
|
||||
Text("Failed to load completions")
|
||||
Text(L10n.Tasks.failedToLoad)
|
||||
.font(.headline)
|
||||
.foregroundColor(Color.appTextPrimary)
|
||||
|
||||
@@ -73,7 +73,7 @@ struct CompletionHistorySheet: View {
|
||||
Button(action: {
|
||||
viewModel.loadCompletions(taskId: taskId)
|
||||
}) {
|
||||
Label("Retry", systemImage: "arrow.clockwise")
|
||||
Label(L10n.Common.retry, systemImage: "arrow.clockwise")
|
||||
.foregroundColor(Color.appPrimary)
|
||||
}
|
||||
.padding(.top, AppSpacing.sm)
|
||||
@@ -87,11 +87,11 @@ struct CompletionHistorySheet: View {
|
||||
.font(.system(size: 48))
|
||||
.foregroundColor(Color.appTextSecondary.opacity(0.5))
|
||||
|
||||
Text("No Completions Yet")
|
||||
Text(L10n.Tasks.noCompletionsYet)
|
||||
.font(.headline)
|
||||
.foregroundColor(Color.appTextPrimary)
|
||||
|
||||
Text("This task has not been completed.")
|
||||
Text(L10n.Tasks.notCompleted)
|
||||
.font(.subheadline)
|
||||
.foregroundColor(Color.appTextSecondary)
|
||||
}
|
||||
@@ -110,7 +110,7 @@ struct CompletionHistorySheet: View {
|
||||
.fontWeight(.semibold)
|
||||
.foregroundColor(Color.appTextPrimary)
|
||||
Spacer()
|
||||
Text("\(viewModel.completions.count) \(viewModel.completions.count == 1 ? "completion" : "completions")")
|
||||
Text("\(viewModel.completions.count) \(viewModel.completions.count == 1 ? L10n.Tasks.completion : L10n.Tasks.completions)")
|
||||
.font(.caption)
|
||||
.foregroundColor(Color.appTextSecondary)
|
||||
}
|
||||
@@ -146,7 +146,7 @@ struct CompletionHistoryCard: View {
|
||||
HStack(spacing: 4) {
|
||||
Image(systemName: "person.fill")
|
||||
.font(.caption2)
|
||||
Text("Completed by \(completedBy)")
|
||||
Text("\(L10n.Tasks.completedByName) \(completedBy)")
|
||||
.font(.caption)
|
||||
}
|
||||
.foregroundColor(Color.appTextSecondary)
|
||||
@@ -213,7 +213,7 @@ struct CompletionHistoryCard: View {
|
||||
// Notes
|
||||
if !completion.notes.isEmpty {
|
||||
VStack(alignment: .leading, spacing: 4) {
|
||||
Text("Notes")
|
||||
Text(L10n.Tasks.notes)
|
||||
.font(.caption)
|
||||
.fontWeight(.semibold)
|
||||
.foregroundColor(Color.appTextSecondary)
|
||||
@@ -233,7 +233,7 @@ struct CompletionHistoryCard: View {
|
||||
HStack {
|
||||
Image(systemName: "photo.on.rectangle.angled")
|
||||
.font(.subheadline)
|
||||
Text("View Photos (\(completion.images.count))")
|
||||
Text("\(L10n.Tasks.viewPhotos) (\(completion.images.count))")
|
||||
.font(.subheadline)
|
||||
.fontWeight(.semibold)
|
||||
}
|
||||
|
||||
@@ -97,8 +97,8 @@ struct TaskFormView: View {
|
||||
// Residence Picker (only if needed)
|
||||
if needsResidenceSelection, let residences = residences {
|
||||
Section {
|
||||
Picker("Property", selection: $selectedResidence) {
|
||||
Text("Select Property").tag(nil as ResidenceResponse?)
|
||||
Picker(L10n.Tasks.property, selection: $selectedResidence) {
|
||||
Text(L10n.Tasks.selectProperty).tag(nil as ResidenceResponse?)
|
||||
ForEach(residences, id: \.id) { residence in
|
||||
Text(residence.name).tag(residence as ResidenceResponse?)
|
||||
}
|
||||
@@ -110,9 +110,9 @@ struct TaskFormView: View {
|
||||
.foregroundColor(Color.appError)
|
||||
}
|
||||
} header: {
|
||||
Text("Property")
|
||||
Text(L10n.Tasks.property)
|
||||
} footer: {
|
||||
Text("Required")
|
||||
Text(L10n.Tasks.required)
|
||||
.font(.caption)
|
||||
.foregroundColor(Color.appError)
|
||||
}
|
||||
@@ -120,7 +120,7 @@ struct TaskFormView: View {
|
||||
}
|
||||
|
||||
Section {
|
||||
TextField("Title", text: $title)
|
||||
TextField(L10n.Tasks.titleLabel, text: $title)
|
||||
.focused($focusedField, equals: .title)
|
||||
|
||||
if !titleError.isEmpty {
|
||||
@@ -129,83 +129,83 @@ struct TaskFormView: View {
|
||||
.foregroundColor(Color.appError)
|
||||
}
|
||||
|
||||
TextField("Description (optional)", text: $description, axis: .vertical)
|
||||
TextField(L10n.Tasks.descriptionOptional, text: $description, axis: .vertical)
|
||||
.lineLimit(3...6)
|
||||
.focused($focusedField, equals: .description)
|
||||
} header: {
|
||||
Text("Task Details")
|
||||
Text(L10n.Tasks.taskDetails)
|
||||
} footer: {
|
||||
Text("Required: Title")
|
||||
Text(L10n.Tasks.titleRequired)
|
||||
.font(.caption)
|
||||
.foregroundColor(Color.appError)
|
||||
}
|
||||
.listRowBackground(Color.appBackgroundSecondary)
|
||||
|
||||
Section {
|
||||
Picker("Category", selection: $selectedCategory) {
|
||||
Text("Select Category").tag(nil as TaskCategory?)
|
||||
Picker(L10n.Tasks.category, selection: $selectedCategory) {
|
||||
Text(L10n.Tasks.selectCategory).tag(nil as TaskCategory?)
|
||||
ForEach(taskCategories, id: \.id) { category in
|
||||
Text(category.name.capitalized).tag(category as TaskCategory?)
|
||||
}
|
||||
}
|
||||
} header: {
|
||||
Text("Category")
|
||||
Text(L10n.Tasks.category)
|
||||
} footer: {
|
||||
Text("Required")
|
||||
Text(L10n.Tasks.required)
|
||||
.font(.caption)
|
||||
.foregroundColor(Color.appError)
|
||||
}
|
||||
.listRowBackground(Color.appBackgroundSecondary)
|
||||
|
||||
Section {
|
||||
Picker("Frequency", selection: $selectedFrequency) {
|
||||
Text("Select Frequency").tag(nil as TaskFrequency?)
|
||||
Picker(L10n.Tasks.frequency, selection: $selectedFrequency) {
|
||||
Text(L10n.Tasks.selectFrequency).tag(nil as TaskFrequency?)
|
||||
ForEach(taskFrequencies, id: \.id) { frequency in
|
||||
Text(frequency.displayName).tag(frequency as TaskFrequency?)
|
||||
}
|
||||
}
|
||||
|
||||
if selectedFrequency?.name != "once" {
|
||||
TextField("Custom Interval (days, optional)", text: $intervalDays)
|
||||
TextField(L10n.Tasks.customInterval, text: $intervalDays)
|
||||
.keyboardType(.numberPad)
|
||||
.focused($focusedField, equals: .intervalDays)
|
||||
}
|
||||
|
||||
DatePicker("Due Date", selection: $dueDate, displayedComponents: .date)
|
||||
DatePicker(L10n.Tasks.dueDate, selection: $dueDate, displayedComponents: .date)
|
||||
} header: {
|
||||
Text("Scheduling")
|
||||
Text(L10n.Tasks.scheduling)
|
||||
} footer: {
|
||||
Text("Required: Frequency")
|
||||
Text(L10n.Tasks.required)
|
||||
.font(.caption)
|
||||
.foregroundColor(Color.appError)
|
||||
}
|
||||
.listRowBackground(Color.appBackgroundSecondary)
|
||||
|
||||
Section {
|
||||
Picker("Priority", selection: $selectedPriority) {
|
||||
Text("Select Priority").tag(nil as TaskPriority?)
|
||||
Picker(L10n.Tasks.priority, selection: $selectedPriority) {
|
||||
Text(L10n.Tasks.selectPriority).tag(nil as TaskPriority?)
|
||||
ForEach(taskPriorities, id: \.id) { priority in
|
||||
Text(priority.displayName).tag(priority as TaskPriority?)
|
||||
}
|
||||
}
|
||||
|
||||
Picker("Status", selection: $selectedStatus) {
|
||||
Text("Select Status").tag(nil as TaskStatus?)
|
||||
Picker(L10n.Tasks.status, selection: $selectedStatus) {
|
||||
Text(L10n.Tasks.selectStatus).tag(nil as TaskStatus?)
|
||||
ForEach(taskStatuses, id: \.id) { status in
|
||||
Text(status.displayName).tag(status as TaskStatus?)
|
||||
}
|
||||
}
|
||||
} header: {
|
||||
Text("Priority & Status")
|
||||
Text(L10n.Tasks.priorityAndStatus)
|
||||
} footer: {
|
||||
Text("Required: Both Priority and Status")
|
||||
Text(L10n.Tasks.bothRequired)
|
||||
.font(.caption)
|
||||
.foregroundColor(Color.appError)
|
||||
}
|
||||
.listRowBackground(Color.appBackgroundSecondary)
|
||||
|
||||
Section(header: Text("Cost")) {
|
||||
TextField("Estimated Cost (optional)", text: $estimatedCost)
|
||||
Section(header: Text(L10n.Tasks.cost)) {
|
||||
TextField(L10n.Tasks.estimatedCost, text: $estimatedCost)
|
||||
.keyboardType(.decimalPad)
|
||||
.focused($focusedField, equals: .estimatedCost)
|
||||
}
|
||||
@@ -227,7 +227,7 @@ struct TaskFormView: View {
|
||||
VStack(spacing: 16) {
|
||||
ProgressView()
|
||||
.scaleEffect(1.5)
|
||||
Text("Loading...")
|
||||
Text(L10n.Tasks.loading)
|
||||
.foregroundColor(Color.appTextSecondary)
|
||||
}
|
||||
.frame(maxWidth: .infinity, maxHeight: .infinity)
|
||||
@@ -237,18 +237,18 @@ struct TaskFormView: View {
|
||||
.listStyle(.plain)
|
||||
.scrollContentBackground(.hidden)
|
||||
.background(Color.appBackgroundPrimary)
|
||||
.navigationTitle(isEditMode ? "Edit Task" : "Add Task")
|
||||
.navigationTitle(isEditMode ? L10n.Tasks.editTitle : L10n.Tasks.addTitle)
|
||||
.navigationBarTitleDisplayMode(.inline)
|
||||
.toolbar {
|
||||
ToolbarItem(placement: .navigationBarLeading) {
|
||||
Button("Cancel") {
|
||||
Button(L10n.Common.cancel) {
|
||||
isPresented = false
|
||||
}
|
||||
.disabled(isLoadingLookups)
|
||||
}
|
||||
|
||||
ToolbarItem(placement: .navigationBarTrailing) {
|
||||
Button("Save") {
|
||||
Button(L10n.Common.save) {
|
||||
submitForm()
|
||||
}
|
||||
.disabled(!canSave || viewModel.isLoading || isLoadingLookups)
|
||||
|
||||
@@ -23,12 +23,12 @@ struct VerifyEmailView: View {
|
||||
.foregroundStyle(Color.appPrimary.gradient)
|
||||
.padding(.bottom, 8)
|
||||
|
||||
Text("Verify Your Email")
|
||||
Text(L10n.Auth.verifyYourEmail)
|
||||
.font(.title)
|
||||
.fontWeight(.bold)
|
||||
.foregroundColor(Color.appTextPrimary)
|
||||
|
||||
Text("You must verify your email address to continue")
|
||||
Text(L10n.Auth.verifyMustVerify)
|
||||
.font(.subheadline)
|
||||
.foregroundColor(Color.appTextSecondary)
|
||||
.multilineTextAlignment(.center)
|
||||
@@ -42,7 +42,7 @@ struct VerifyEmailView: View {
|
||||
.foregroundColor(Color.appAccent)
|
||||
.font(.title2)
|
||||
|
||||
Text("Email verification is required. Check your inbox for a 6-digit code.")
|
||||
Text(L10n.Auth.verifyCheckInbox)
|
||||
.font(.subheadline)
|
||||
.foregroundColor(Color.appTextPrimary)
|
||||
.fontWeight(.semibold)
|
||||
@@ -53,7 +53,7 @@ struct VerifyEmailView: View {
|
||||
|
||||
// Code Input
|
||||
VStack(alignment: .leading, spacing: 12) {
|
||||
Text("Verification Code")
|
||||
Text(L10n.Auth.verifyCodeLabel)
|
||||
.font(.headline)
|
||||
.foregroundColor(Color.appTextPrimary)
|
||||
.padding(.horizontal)
|
||||
@@ -76,7 +76,7 @@ struct VerifyEmailView: View {
|
||||
viewModel.code = newValue.filter { $0.isNumber }
|
||||
}
|
||||
|
||||
Text("Code must be 6 digits")
|
||||
Text(L10n.Auth.verifyCodeMustBe6)
|
||||
.font(.caption)
|
||||
.foregroundColor(Color.appTextSecondary)
|
||||
.padding(.horizontal)
|
||||
@@ -98,7 +98,7 @@ struct VerifyEmailView: View {
|
||||
.progressViewStyle(CircularProgressViewStyle(tint: .white))
|
||||
} else {
|
||||
Image(systemName: "checkmark.shield.fill")
|
||||
Text("Verify Email")
|
||||
Text(L10n.Auth.verifyEmailButton)
|
||||
.fontWeight(.semibold)
|
||||
}
|
||||
}
|
||||
@@ -118,7 +118,7 @@ struct VerifyEmailView: View {
|
||||
Spacer().frame(height: 20)
|
||||
|
||||
// Help Text
|
||||
Text("Didn't receive the code? Check your spam folder or contact support.")
|
||||
Text(L10n.Auth.verifyHelpText)
|
||||
.font(.caption)
|
||||
.foregroundColor(Color.appTextSecondary)
|
||||
.multilineTextAlignment(.center)
|
||||
@@ -133,7 +133,7 @@ struct VerifyEmailView: View {
|
||||
HStack(spacing: 4) {
|
||||
Image(systemName: "rectangle.portrait.and.arrow.right")
|
||||
.font(.system(size: 16))
|
||||
Text("Logout")
|
||||
Text(L10n.Auth.logout)
|
||||
.font(.subheadline)
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user