feat(i18n): Add time blocks translations and fix deployment
- Add comprehensive timeBlocks translations (ES, FR, DE, EN) - Add myAvailability translations (ES, FR, DE, EN) - Add full helpTimeBlocks guide content (ES, FR, DE, EN) - Add contracts guide translations (ES) - Fix DATABASE_URL env var in deploy.sh for seed_platform_plugins - Update Contracts page and HelpContracts guide 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
12
deploy.sh
12
deploy.sh
@@ -183,15 +183,15 @@ if [[ "$SKIP_MIGRATE" != "true" ]]; then
|
||||
docker compose -f docker-compose.production.yml exec -T django sh -c 'export DATABASE_URL=postgres://\${POSTGRES_USER}:\${POSTGRES_PASSWORD}@\${POSTGRES_HOST}:\${POSTGRES_PORT}/\${POSTGRES_DB} && python manage.py collectstatic --noinput'
|
||||
|
||||
echo ">>> Seeding/updating platform plugins for all tenants..."
|
||||
docker compose -f docker-compose.production.yml exec -T django python -c "
|
||||
docker compose -f docker-compose.production.yml exec -T django sh -c 'export DATABASE_URL=postgres://\${POSTGRES_USER}:\${POSTGRES_PASSWORD}@\${POSTGRES_HOST}:\${POSTGRES_PORT}/\${POSTGRES_DB} && python -c "
|
||||
from django_tenants.utils import get_tenant_model
|
||||
from django.core.management import call_command
|
||||
Tenant = get_tenant_model()
|
||||
for tenant in Tenant.objects.exclude(schema_name='public'):
|
||||
print(f' Seeding plugins for {tenant.schema_name}...')
|
||||
call_command('tenant_command', 'seed_platform_plugins', schema=tenant.schema_name, verbosity=0)
|
||||
print(' Done!')
|
||||
"
|
||||
for tenant in Tenant.objects.exclude(schema_name=\"public\"):
|
||||
print(f\" Seeding plugins for {tenant.schema_name}...\")
|
||||
call_command(\"tenant_command\", \"seed_platform_plugins\", schema=tenant.schema_name, verbosity=0)
|
||||
print(\" Done!\")
|
||||
"'
|
||||
else
|
||||
echo ">>> Skipping migrations (--no-migrate flag used)"
|
||||
fi
|
||||
|
||||
@@ -1067,5 +1067,344 @@
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"contracts": {
|
||||
"title": "Verträge",
|
||||
"description": "Verwalten Sie Vertragsvorlagen und gesendete Verträge",
|
||||
"templates": "Vorlagen",
|
||||
"sentContracts": "Gesendete Verträge",
|
||||
"allContracts": "Alle Verträge",
|
||||
"createTemplate": "Vorlage Erstellen",
|
||||
"newTemplate": "Neue Vorlage",
|
||||
"createContract": "Vertrag Erstellen",
|
||||
"editTemplate": "Vorlage Bearbeiten",
|
||||
"viewContract": "Vertrag Anzeigen",
|
||||
"noTemplates": "Noch keine Vertragsvorlagen",
|
||||
"noTemplatesEmpty": "Noch keine Vorlagen. Erstellen Sie Ihre erste Vorlage, um zu beginnen.",
|
||||
"noTemplatesSearch": "Keine Vorlagen gefunden",
|
||||
"noContracts": "Noch keine Verträge",
|
||||
"noContractsEmpty": "Noch keine Verträge gesendet.",
|
||||
"noContractsSearch": "Keine Verträge gefunden",
|
||||
"templateName": "Vorlagenname",
|
||||
"templateDescription": "Beschreibung",
|
||||
"content": "Inhalt",
|
||||
"contentHtml": "Vertragsinhalt (HTML)",
|
||||
"searchTemplates": "Vorlagen suchen...",
|
||||
"searchContracts": "Verträge suchen...",
|
||||
"all": "Alle",
|
||||
"scope": {
|
||||
"label": "Geltungsbereich",
|
||||
"customer": "Kundenebene",
|
||||
"appointment": "Pro Termin",
|
||||
"customerDesc": "Einmalige Verträge pro Kunde (z.B. Datenschutzrichtlinie, AGB)",
|
||||
"appointmentDesc": "Wird bei jeder Buchung unterschrieben (z.B. Haftungsausschlüsse, Dienstleistungsvereinbarungen)"
|
||||
},
|
||||
"status": {
|
||||
"label": "Status",
|
||||
"draft": "Entwurf",
|
||||
"active": "Aktiv",
|
||||
"archived": "Archiviert",
|
||||
"pending": "Ausstehend",
|
||||
"signed": "Unterschrieben",
|
||||
"expired": "Abgelaufen",
|
||||
"voided": "Storniert"
|
||||
},
|
||||
"table": {
|
||||
"template": "Vorlage",
|
||||
"scope": "Geltungsbereich",
|
||||
"status": "Status",
|
||||
"version": "Version",
|
||||
"actions": "Aktionen",
|
||||
"customer": "Kunde",
|
||||
"contract": "Vertrag",
|
||||
"created": "Erstellt",
|
||||
"sent": "Gesendet"
|
||||
},
|
||||
"expiresAfterDays": "Läuft ab nach (Tagen)",
|
||||
"expiresAfterDaysHint": "Leer lassen für keine Ablaufzeit",
|
||||
"versionNotes": "Versionshinweise",
|
||||
"versionNotesPlaceholder": "Was hat sich in dieser Version geändert?",
|
||||
"services": "Anwendbare Dienstleistungen",
|
||||
"servicesHint": "Leer lassen um auf alle Dienstleistungen anzuwenden",
|
||||
"customer": "Kunde",
|
||||
"appointment": "Termin",
|
||||
"service": "Dienstleistung",
|
||||
"sentAt": "Gesendet",
|
||||
"signedAt": "Unterschrieben",
|
||||
"expiresAt": "Läuft ab am",
|
||||
"createdAt": "Erstellt",
|
||||
"availableVariables": "Verfügbare Variablen",
|
||||
"actions": {
|
||||
"send": "Vertrag Senden",
|
||||
"resend": "E-Mail Erneut Senden",
|
||||
"void": "Vertrag Stornieren",
|
||||
"duplicate": "Vorlage Duplizieren",
|
||||
"preview": "PDF-Vorschau",
|
||||
"previewFailed": "PDF-Vorschau konnte nicht geladen werden.",
|
||||
"delete": "Löschen",
|
||||
"edit": "Bearbeiten",
|
||||
"viewDetails": "Details Anzeigen",
|
||||
"copyLink": "Unterschriftslink Kopieren",
|
||||
"sendEmail": "E-Mail Senden",
|
||||
"openSigningPage": "Unterschriftsseite Öffnen",
|
||||
"saveChanges": "Änderungen Speichern"
|
||||
},
|
||||
"sendContract": {
|
||||
"title": "Vertrag Senden",
|
||||
"selectTemplate": "Vertragsvorlage",
|
||||
"selectTemplatePlaceholder": "Vorlage auswählen...",
|
||||
"selectCustomer": "Kunde",
|
||||
"searchCustomers": "Kunden suchen...",
|
||||
"selectAppointment": "Termin Auswählen (Optional)",
|
||||
"selectService": "Dienstleistung Auswählen (Optional)",
|
||||
"send": "Vertrag Senden",
|
||||
"sendImmediately": "Unterschriftsanfrage sofort per E-Mail senden",
|
||||
"success": "Vertrag erfolgreich gesendet",
|
||||
"error": "Vertrag konnte nicht gesendet werden",
|
||||
"loadingCustomers": "Kunden werden geladen...",
|
||||
"loadCustomersFailed": "Kunden konnten nicht geladen werden",
|
||||
"noCustomers": "Keine Kunden verfügbar. Erstellen Sie zuerst Kunden.",
|
||||
"noMatchingCustomers": "Keine passenden Kunden"
|
||||
},
|
||||
"voidContract": {
|
||||
"title": "Vertrag Stornieren",
|
||||
"description": "Das Stornieren dieses Vertrags wird ihn widerrufen. Der Kunde kann nicht mehr unterschreiben.",
|
||||
"reason": "Grund für die Stornierung",
|
||||
"reasonPlaceholder": "Grund eingeben...",
|
||||
"confirm": "Vertrag Stornieren",
|
||||
"success": "Vertrag erfolgreich storniert",
|
||||
"error": "Vertrag konnte nicht storniert werden"
|
||||
},
|
||||
"deleteTemplate": {
|
||||
"title": "Vorlage Löschen",
|
||||
"description": "Sind Sie sicher, dass Sie diese Vorlage löschen möchten? Diese Aktion kann nicht rückgängig gemacht werden.",
|
||||
"confirm": "Löschen",
|
||||
"success": "Vorlage erfolgreich gelöscht",
|
||||
"error": "Vorlage konnte nicht gelöscht werden"
|
||||
},
|
||||
"contractDetails": {
|
||||
"title": "Vertragsdetails",
|
||||
"customer": "Kunde",
|
||||
"template": "Vorlage",
|
||||
"status": "Status",
|
||||
"created": "Erstellt",
|
||||
"contentPreview": "Inhaltsvorschau",
|
||||
"signingLink": "Unterschriftslink"
|
||||
},
|
||||
"preview": {
|
||||
"title": "Vertragsvorschau",
|
||||
"sampleData": "Verwendung von Beispieldaten für die Vorschau"
|
||||
},
|
||||
"signing": {
|
||||
"title": "Vertrag Unterschreiben",
|
||||
"businessName": "{{businessName}}",
|
||||
"contractFor": "Vertrag für {{customerName}}",
|
||||
"pleaseReview": "Bitte überprüfen und unterschreiben Sie diesen Vertrag",
|
||||
"signerName": "Ihr Vollständiger Name",
|
||||
"signerNamePlaceholder": "Geben Sie Ihren rechtlichen Namen ein",
|
||||
"signerEmail": "Ihre E-Mail",
|
||||
"signatureLabel": "Hier Unterschreiben",
|
||||
"signaturePlaceholder": "Zeichnen Sie hier Ihre Unterschrift",
|
||||
"clearSignature": "Löschen",
|
||||
"agreeToTerms": "Ich habe die in diesem Dokument beschriebenen Bedingungen gelesen und stimme ihnen zu. Durch das Ankreuzen dieses Kästchens verstehe ich, dass dies eine rechtsgültige elektronische Unterschrift darstellt.",
|
||||
"consentToElectronic": "Ich stimme zu, Geschäfte elektronisch abzuwickeln. Ich verstehe, dass ich das Recht habe, Dokumente auf Anfrage in Papierform zu erhalten, und kann diese Zustimmung jederzeit widerrufen.",
|
||||
"submitSignature": "Vertrag Unterschreiben",
|
||||
"submitting": "Unterschrift wird verarbeitet...",
|
||||
"success": "Vertrag erfolgreich unterschrieben!",
|
||||
"successMessage": "Sie erhalten eine Bestätigungs-E-Mail mit einer Kopie des unterschriebenen Vertrags.",
|
||||
"error": "Vertrag konnte nicht unterschrieben werden",
|
||||
"expired": "Dieser Vertrag ist abgelaufen",
|
||||
"alreadySigned": "Dieser Vertrag wurde bereits unterschrieben",
|
||||
"notFound": "Vertrag nicht gefunden",
|
||||
"voided": "Dieser Vertrag wurde storniert",
|
||||
"signedBy": "Unterschrieben von {{name}} am {{date}}",
|
||||
"thankYou": "Vielen Dank für Ihre Unterschrift!",
|
||||
"loading": "Vertrag wird geladen...",
|
||||
"geolocationHint": "Der Standort wird für rechtliche Konformität aufgezeichnet"
|
||||
},
|
||||
"errors": {
|
||||
"loadFailed": "Verträge konnten nicht geladen werden",
|
||||
"createFailed": "Vertrag konnte nicht erstellt werden",
|
||||
"updateFailed": "Vertrag konnte nicht aktualisiert werden",
|
||||
"deleteFailed": "Vertrag konnte nicht gelöscht werden",
|
||||
"sendFailed": "Vertrag konnte nicht gesendet werden",
|
||||
"voidFailed": "Vertrag konnte nicht storniert werden"
|
||||
}
|
||||
},
|
||||
"timeBlocks": {
|
||||
"title": "Zeitblöcke",
|
||||
"subtitle": "Verwalten Sie Betriebsschließungen, Feiertage und Ressourcenverfügbarkeit",
|
||||
"addBlock": "Block Hinzufügen",
|
||||
"businessTab": "Geschäftsblöcke",
|
||||
"resourceTab": "Ressourcenblöcke",
|
||||
"calendarTab": "Jahresansicht",
|
||||
"businessInfo": "Geschäftsblöcke gelten für alle Ressourcen. Verwenden Sie diese für Feiertage, Betriebsschließungen und unternehmensweite Ereignisse.",
|
||||
"noBusinessBlocks": "Keine Geschäftsblöcke",
|
||||
"noBusinessBlocksDesc": "Fügen Sie Feiertage und Betriebsschließungen hinzu, um Buchungen während dieser Zeiten zu verhindern.",
|
||||
"addFirstBlock": "Ersten Block Hinzufügen",
|
||||
"titleCol": "Titel",
|
||||
"typeCol": "Typ",
|
||||
"patternCol": "Muster",
|
||||
"actionsCol": "Aktionen",
|
||||
"resourceInfo": "Ressourcenblöcke gelten für bestimmtes Personal oder Ausrüstung. Verwenden Sie diese für Urlaub, Wartung oder persönliche Zeit.",
|
||||
"noResourceBlocks": "Keine Ressourcenblöcke",
|
||||
"noResourceBlocksDesc": "Fügen Sie Zeitblöcke für bestimmte Ressourcen hinzu, um deren Verfügbarkeit zu verwalten.",
|
||||
"deleteConfirmTitle": "Zeitblock Löschen?",
|
||||
"deleteConfirmDesc": "Diese Aktion kann nicht rückgängig gemacht werden.",
|
||||
"blockTypes": {
|
||||
"hard": "Harte Blockierung",
|
||||
"soft": "Weiche Blockierung"
|
||||
},
|
||||
"recurrenceTypes": {
|
||||
"none": "Einmalig",
|
||||
"weekly": "Wöchentlich",
|
||||
"monthly": "Monatlich",
|
||||
"yearly": "Jährlich",
|
||||
"holiday": "Feiertag"
|
||||
},
|
||||
"inactive": "Inaktiv",
|
||||
"activate": "Aktivieren",
|
||||
"deactivate": "Deaktivieren"
|
||||
},
|
||||
"myAvailability": {
|
||||
"title": "Meine Verfügbarkeit",
|
||||
"subtitle": "Verwalten Sie Ihre freien Tage und Abwesenheiten",
|
||||
"noResource": "Keine Ressource Verknüpft",
|
||||
"noResourceDesc": "Ihr Konto ist nicht mit einer Ressource verknüpft. Bitte kontaktieren Sie Ihren Manager, um Ihre Verfügbarkeit einzurichten.",
|
||||
"addBlock": "Zeit Blockieren",
|
||||
"businessBlocks": "Betriebsschließungen",
|
||||
"businessBlocksInfo": "Diese Blöcke werden von Ihrem Unternehmen festgelegt und gelten für alle.",
|
||||
"myBlocks": "Meine Zeitblöcke",
|
||||
"noBlocks": "Keine Zeitblöcke",
|
||||
"noBlocksDesc": "Fügen Sie Zeitblöcke für Urlaub, Mittagspausen oder andere benötigte Auszeiten hinzu.",
|
||||
"addFirstBlock": "Ersten Block Hinzufügen",
|
||||
"titleCol": "Titel",
|
||||
"typeCol": "Typ",
|
||||
"patternCol": "Muster",
|
||||
"actionsCol": "Aktionen",
|
||||
"editBlock": "Zeitblock Bearbeiten",
|
||||
"createBlock": "Auszeit Blockieren",
|
||||
"create": "Blockieren",
|
||||
"deleteConfirmTitle": "Zeitblock Löschen?",
|
||||
"deleteConfirmDesc": "Diese Aktion kann nicht rückgängig gemacht werden.",
|
||||
"form": {
|
||||
"title": "Titel",
|
||||
"description": "Beschreibung",
|
||||
"blockType": "Blocktyp",
|
||||
"recurrenceType": "Wiederholung",
|
||||
"allDay": "Ganztägig",
|
||||
"startDate": "Startdatum",
|
||||
"endDate": "Enddatum",
|
||||
"startTime": "Startzeit",
|
||||
"endTime": "Endzeit",
|
||||
"daysOfWeek": "Wochentage",
|
||||
"daysOfMonth": "Tage des Monats"
|
||||
}
|
||||
},
|
||||
"helpTimeBlocks": {
|
||||
"title": "Zeitblöcke Anleitung",
|
||||
"subtitle": "Erfahren Sie, wie Sie Zeit für Schließungen, Feiertage und Abwesenheiten blockieren",
|
||||
"overview": {
|
||||
"title": "Was sind Zeitblöcke?",
|
||||
"description": "Mit Zeitblöcken können Sie bestimmte Daten, Zeiten oder wiederkehrende Zeiträume als nicht buchbar markieren. Verwenden Sie sie für Feiertage, Betriebsschließungen, Mitarbeiterurlaub, Wartungsfenster und mehr.",
|
||||
"businessBlocks": "Geschäftsblöcke",
|
||||
"businessBlocksDesc": "Gelten für alle Ressourcen. Perfekt für Unternehmensfeiertage, Büroschließungen und Wartung.",
|
||||
"resourceBlocks": "Ressourcenblöcke",
|
||||
"resourceBlocksDesc": "Gelten für bestimmte Ressourcen. Verwenden Sie sie für individuelle Urlaube, Termine oder Schulungen.",
|
||||
"hardBlocks": "Harte Blockierungen",
|
||||
"hardBlocksDesc": "Verhindern Buchungen während der blockierten Zeit vollständig. Können nicht überschrieben werden.",
|
||||
"softBlocks": "Weiche Blockierungen",
|
||||
"softBlocksDesc": "Zeigen eine Warnung an, erlauben aber Buchungen mit Bestätigung."
|
||||
},
|
||||
"levels": {
|
||||
"title": "Blockebenen",
|
||||
"levelCol": "Ebene",
|
||||
"scopeCol": "Umfang",
|
||||
"examplesCol": "Anwendungsbeispiele",
|
||||
"business": "Geschäft",
|
||||
"businessScope": "Alle Ressourcen in Ihrem Unternehmen",
|
||||
"businessExamples": "Feiertage, Büroschließungen, Firmenveranstaltungen, Wartung",
|
||||
"resource": "Ressource",
|
||||
"resourceScope": "Eine bestimmte Ressource (Mitarbeiter, Raum, etc.)",
|
||||
"resourceExamples": "Urlaub, persönliche Termine, Mittagspausen, Schulung",
|
||||
"additiveNote": "Blöcke sind Additiv",
|
||||
"additiveDesc": "Sowohl Geschäfts- als auch Ressourcenblöcke gelten. Wenn das Geschäft an einem Feiertag geschlossen ist, spielen individuelle Ressourcenblöcke für diesen Tag keine Rolle."
|
||||
},
|
||||
"types": {
|
||||
"title": "Blocktypen: Hart vs Weich",
|
||||
"hardBlock": "Harte Blockierung",
|
||||
"hardBlockDesc": "Verhindert jegliche Buchungen während der blockierten Zeit vollständig. Kunden können nicht buchen und Mitarbeiter können nicht überschreiben. Der Kalender zeigt eine rot gestreifte Überlagerung.",
|
||||
"cannotOverride": "Kann nicht überschrieben werden",
|
||||
"showsInBooking": "Wird bei Kundenbuchungen angezeigt",
|
||||
"redOverlay": "Rot gestreifte Überlagerung",
|
||||
"softBlock": "Weiche Blockierung",
|
||||
"softBlockDesc": "Zeigt eine Warnung an, erlaubt aber Buchungen mit Bestätigung. Nützlich um bevorzugte Ruhezeiten anzuzeigen, die bei Bedarf überschrieben werden können.",
|
||||
"canOverride": "Kann überschrieben werden",
|
||||
"showsWarning": "Zeigt nur Warnung an",
|
||||
"yellowOverlay": "Gelb gestrichelte Überlagerung"
|
||||
},
|
||||
"recurrence": {
|
||||
"title": "Wiederholungsmuster",
|
||||
"patternCol": "Muster",
|
||||
"descriptionCol": "Beschreibung",
|
||||
"exampleCol": "Beispiel",
|
||||
"oneTime": "Einmalig",
|
||||
"oneTimeDesc": "Ein bestimmtes Datum oder Datumsbereich, der einmal auftritt",
|
||||
"oneTimeExample": "24.-26. Dez (Weihnachtspause), 15. Feb (Presidents Day)",
|
||||
"weekly": "Wöchentlich",
|
||||
"weeklyDesc": "Wiederholt sich an bestimmten Wochentagen",
|
||||
"weeklyExample": "Jeden Samstag und Sonntag, Jeden Montag Mittagspause",
|
||||
"monthly": "Monatlich",
|
||||
"monthlyDesc": "Wiederholt sich an bestimmten Tagen des Monats",
|
||||
"monthlyExample": "1. jeden Monats (Inventur), 15. (Gehaltsabrechnung)",
|
||||
"yearly": "Jährlich",
|
||||
"yearlyDesc": "Wiederholt sich an einem bestimmten Monat und Tag jedes Jahr",
|
||||
"yearlyExample": "4. Juli, 25. Dezember, 1. Januar",
|
||||
"holiday": "Feiertag",
|
||||
"holidayDesc": "Wählen Sie aus beliebten US-Feiertagen. Mehrfachauswahl unterstützt - jeder Feiertag erstellt seinen eigenen Block.",
|
||||
"holidayExample": "Weihnachten, Thanksgiving, Memorial Day, Unabhängigkeitstag"
|
||||
},
|
||||
"visualization": {
|
||||
"title": "Zeitblöcke Anzeigen",
|
||||
"description": "Zeitblöcke erscheinen in mehreren Ansichten der Anwendung mit farbcodierten Indikatoren:",
|
||||
"colorLegend": "Farblegende",
|
||||
"businessHard": "Harter Geschäftsblock",
|
||||
"businessSoft": "Weicher Geschäftsblock",
|
||||
"resourceHard": "Harter Ressourcenblock",
|
||||
"resourceSoft": "Weicher Ressourcenblock",
|
||||
"schedulerOverlay": "Kalenderüberlagerung",
|
||||
"schedulerOverlayDesc": "Blockierte Zeiten erscheinen direkt im Kalender mit visuellen Indikatoren. Geschäftsblöcke verwenden Rot/Gelb, Ressourcenblöcke verwenden Lila/Cyan. Klicken Sie in der Wochenansicht auf einen blockierten Bereich, um zu diesem Tag zu navigieren.",
|
||||
"monthView": "Monatsansicht",
|
||||
"monthViewDesc": "Blockierte Daten werden mit farbigen Hintergründen und Badge-Indikatoren angezeigt. Mehrere Blocktypen am selben Tag zeigen alle anwendbaren Badges.",
|
||||
"listView": "Listenansicht",
|
||||
"listViewDesc": "Verwalten Sie alle Zeitblöcke in tabellarischem Format mit Filteroptionen. Bearbeiten, aktivieren/deaktivieren oder löschen Sie Blöcke von hier."
|
||||
},
|
||||
"staffAvailability": {
|
||||
"title": "Mitarbeiterverfügbarkeit (Meine Verfügbarkeit)",
|
||||
"description": "Mitarbeiter können ihre eigenen Zeitblöcke über die Seite \"Meine Verfügbarkeit\" verwalten. Dies ermöglicht es ihnen, Zeit für persönliche Termine, Urlaub oder andere Verpflichtungen zu blockieren.",
|
||||
"viewBusiness": "Geschäftsblöcke anzeigen (schreibgeschützt)",
|
||||
"createPersonal": "Persönliche Zeitblöcke erstellen und verwalten",
|
||||
"seeCalendar": "Jahreskalender ihrer Verfügbarkeit anzeigen",
|
||||
"hardBlockPermission": "Berechtigung für Harte Blockierungen",
|
||||
"hardBlockPermissionDesc": "Standardmäßig können Mitarbeiter nur weiche Blockierungen erstellen. Um einem Mitarbeiter die Erstellung harter Blockierungen zu ermöglichen, aktivieren Sie die Berechtigung \"Kann harte Blockierungen erstellen\" in dessen Mitarbeitereinstellungen."
|
||||
},
|
||||
"bestPractices": {
|
||||
"title": "Bewährte Praktiken",
|
||||
"tip1Title": "Planen Sie Feiertage im Voraus",
|
||||
"tip1Desc": "Richten Sie jährliche Feiertage zu Beginn jedes Jahres mit dem Wiederholungstyp Feiertag ein.",
|
||||
"tip2Title": "Verwenden Sie weiche Blockierungen für Präferenzen",
|
||||
"tip2Desc": "Reservieren Sie harte Blockierungen für absolute Schließungen. Verwenden Sie weiche Blockierungen für bevorzugte Ruhezeiten, die überschrieben werden könnten.",
|
||||
"tip3Title": "Prüfen Sie Konflikte vor dem Erstellen",
|
||||
"tip3Desc": "Das System zeigt bestehende Termine an, die mit neuen Blöcken in Konflikt stehen. Überprüfen Sie vor der Bestätigung.",
|
||||
"tip4Title": "Setzen Sie Enddaten für Wiederholungen",
|
||||
"tip4Desc": "Für wiederkehrende Blöcke, die nicht permanent sind, setzen Sie ein Enddatum, um zu verhindern, dass sie sich unbegrenzt erstrecken.",
|
||||
"tip5Title": "Verwenden Sie beschreibende Titel",
|
||||
"tip5Desc": "Fügen Sie klare Titel wie \"Weihnachtstag\", \"Teambesprechung\" oder \"Jährliche Wartung\" zur einfachen Identifikation hinzu."
|
||||
},
|
||||
"quickAccess": {
|
||||
"title": "Schnellzugriff",
|
||||
"manageTimeBlocks": "Zeitblöcke Verwalten",
|
||||
"myAvailability": "Meine Verfügbarkeit"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -225,6 +225,198 @@
|
||||
"noTestTokensMessage": "Create a <strong>test/sandbox</strong> API token in your Settings to see personalized code examples with your actual token. Make sure to check the \"Sandbox Mode\" option when creating the token. The examples below use placeholder tokens.",
|
||||
"errorLoadingTokens": "Error Loading Tokens",
|
||||
"errorLoadingTokensMessage": "Failed to load API tokens. Please check your connection and try refreshing the page."
|
||||
},
|
||||
"contracts": {
|
||||
"title": "Contracts Guide",
|
||||
"subtitle": "Create and manage digital contracts with e-signatures",
|
||||
"overview": {
|
||||
"title": "Overview",
|
||||
"description": "The Contracts system allows you to create reusable contract templates, send them to customers for digital signature, and maintain legally compliant records with full audit trails.",
|
||||
"compliance": "All signatures are captured with ESIGN Act and UETA compliance, including IP address, timestamp, browser information, and optional geolocation for maximum legal protection."
|
||||
},
|
||||
"pageLayout": {
|
||||
"title": "Page Layout",
|
||||
"description": "The Contracts page is organized into two collapsible sections for easy management:",
|
||||
"templatesSection": {
|
||||
"title": "Templates Section",
|
||||
"description": "Create and manage reusable contract templates. Includes search, status filters (All, Active, Draft, Archived), and actions to create, edit, preview PDF, and delete templates."
|
||||
},
|
||||
"sentContractsSection": {
|
||||
"title": "Sent Contracts Section",
|
||||
"description": "Track contracts sent to customers. Includes search, status filters (All, Pending, Signed, Expired, Voided), and actions to view, copy link, resend, or void contracts."
|
||||
},
|
||||
"tip": "Click the section header to collapse/expand each section. The count badge shows how many items are in each section."
|
||||
},
|
||||
"templates": {
|
||||
"title": "Contract Templates",
|
||||
"description": "Templates are reusable contract documents that can be personalized with variable placeholders.",
|
||||
"variablesTitle": "Template Variables",
|
||||
"variablesDescription": "Use these placeholders in your templates - they'll be automatically replaced when the contract is created:",
|
||||
"variables": {
|
||||
"customerName": "Full name",
|
||||
"customerFirstName": "First name",
|
||||
"customerEmail": "Email address",
|
||||
"customerPhone": "Phone number",
|
||||
"businessName": "Your business name",
|
||||
"businessEmail": "Contact email",
|
||||
"businessPhone": "Business phone",
|
||||
"date": "Current date",
|
||||
"year": "Current year"
|
||||
},
|
||||
"scopesTitle": "Template Scopes",
|
||||
"scopes": {
|
||||
"customerLevel": {
|
||||
"title": "Customer-Level",
|
||||
"description": "One-time contracts per customer (e.g., privacy policy, terms of service)"
|
||||
},
|
||||
"perAppointment": {
|
||||
"title": "Per Appointment",
|
||||
"description": "Signed for each booking (e.g., liability waivers, service agreements)"
|
||||
}
|
||||
}
|
||||
},
|
||||
"creating": {
|
||||
"title": "Creating Templates",
|
||||
"description": "Click the \"New Template\" button in the Templates section to create a new contract template:",
|
||||
"steps": {
|
||||
"name": {
|
||||
"title": "Enter Template Name",
|
||||
"description": "Give your template a clear, descriptive name (e.g., \"Service Agreement\", \"Liability Waiver\")."
|
||||
},
|
||||
"scope": {
|
||||
"title": "Select Scope",
|
||||
"description": "Choose \"Per Appointment\" for waivers or \"Customer-Level\" for one-time agreements."
|
||||
},
|
||||
"status": {
|
||||
"title": "Set Status",
|
||||
"description": "Start as \"Draft\" while editing. Change to \"Active\" when ready to send to customers."
|
||||
},
|
||||
"expiration": {
|
||||
"title": "Set Expiration (Optional)",
|
||||
"description": "Enter days until contracts expire. Leave blank for no expiration."
|
||||
},
|
||||
"content": {
|
||||
"title": "Write Contract Content",
|
||||
"description": "Enter your contract text using HTML formatting. Click variable chips to insert placeholders."
|
||||
}
|
||||
}
|
||||
},
|
||||
"managing": {
|
||||
"title": "Managing Templates",
|
||||
"description": "Each template in the list has action buttons on the right:",
|
||||
"actions": {
|
||||
"preview": {
|
||||
"title": "Preview PDF",
|
||||
"description": "See how the contract looks as a PDF with sample data"
|
||||
},
|
||||
"edit": {
|
||||
"title": "Edit",
|
||||
"description": "Modify template name, content, scope, or status"
|
||||
},
|
||||
"delete": {
|
||||
"title": "Delete",
|
||||
"description": "Remove the template (requires confirmation)"
|
||||
}
|
||||
},
|
||||
"note": "Only \"Active\" templates can be used to send contracts. Switch templates to Active status when they're ready for use."
|
||||
},
|
||||
"sending": {
|
||||
"title": "Sending Contracts",
|
||||
"description": "Click the \"Send Contract\" button in the Sent Contracts section:",
|
||||
"steps": {
|
||||
"selectTemplate": {
|
||||
"title": "Select a Template",
|
||||
"description": "Choose from your active contract templates"
|
||||
},
|
||||
"selectCustomer": {
|
||||
"title": "Choose a Customer",
|
||||
"description": "Search and select a customer. Variables are automatically filled with their data."
|
||||
},
|
||||
"sendImmediately": {
|
||||
"title": "Send Immediately (Optional)",
|
||||
"description": "Check the box to email the signing request right away, or uncheck to send later."
|
||||
},
|
||||
"trackStatus": {
|
||||
"title": "Track Status",
|
||||
"description": "Monitor the contract in the Sent Contracts list"
|
||||
}
|
||||
}
|
||||
},
|
||||
"statusActions": {
|
||||
"title": "Contract Status & Actions",
|
||||
"statuses": {
|
||||
"pending": {
|
||||
"title": "Pending",
|
||||
"description": "Awaiting customer signature"
|
||||
},
|
||||
"signed": {
|
||||
"title": "Signed",
|
||||
"description": "Customer has signed the contract"
|
||||
},
|
||||
"expired": {
|
||||
"title": "Expired",
|
||||
"description": "Contract expired before signing"
|
||||
},
|
||||
"voided": {
|
||||
"title": "Voided",
|
||||
"description": "Contract was cancelled by business"
|
||||
}
|
||||
},
|
||||
"actionsTitle": "Available Actions",
|
||||
"actions": {
|
||||
"viewDetails": "See full contract information",
|
||||
"copyLink": "Copy signing URL to clipboard (pending only)",
|
||||
"sendResend": "Email the signing request (pending only)",
|
||||
"openSigningPage": "View the customer signing experience",
|
||||
"void": "Cancel a pending contract"
|
||||
}
|
||||
},
|
||||
"legalCompliance": {
|
||||
"title": "Legal Compliance",
|
||||
"notice": "All signatures include comprehensive audit trails that meet federal and state requirements for electronic signatures.",
|
||||
"auditDataTitle": "Captured Audit Data",
|
||||
"auditData": {
|
||||
"documentHash": "Document hash (SHA-256)",
|
||||
"timestamp": "Signature timestamp (ISO)",
|
||||
"ipAddress": "Signer's IP address",
|
||||
"browserInfo": "Browser/device information",
|
||||
"consentCheckbox": "Consent checkbox states",
|
||||
"geolocation": "Geolocation (if permitted)"
|
||||
}
|
||||
},
|
||||
"pdfGeneration": {
|
||||
"title": "PDF Generation",
|
||||
"description": "Once a contract is signed, a PDF is automatically generated that includes:",
|
||||
"includes": {
|
||||
"branding": "Your business branding and logo",
|
||||
"content": "The full contract content with substituted variables",
|
||||
"signature": "Signature section with signer's name and consent confirmations",
|
||||
"auditTrail": "Complete audit trail with verification data",
|
||||
"legalNotice": "Legal notice about electronic signatures"
|
||||
},
|
||||
"tip": "Use the eye icon on any template to preview how the final PDF will look with sample customer data."
|
||||
},
|
||||
"bestPractices": {
|
||||
"title": "Best Practices",
|
||||
"tips": {
|
||||
"clearLanguage": "Write contracts in plain language that customers can easily understand",
|
||||
"startDraft": "Create templates in Draft status, test with PDF preview, then activate",
|
||||
"setExpiration": "Use the expiration feature to ensure contracts are signed promptly",
|
||||
"createCustomersFirst": "Ensure customers exist in the system before sending contracts",
|
||||
"archiveOld": "Rather than deleting, archive templates you no longer use",
|
||||
"downloadPdfs": "Keep copies of signed contracts for your records"
|
||||
}
|
||||
},
|
||||
"relatedFeatures": {
|
||||
"title": "Related Features",
|
||||
"servicesGuide": "Services Guide",
|
||||
"customersGuide": "Customers Guide"
|
||||
},
|
||||
"needHelp": {
|
||||
"title": "Need More Help?",
|
||||
"description": "Our support team is ready to help with any questions about contracts.",
|
||||
"contactSupport": "Contact Support"
|
||||
}
|
||||
}
|
||||
},
|
||||
"staff": {
|
||||
@@ -414,33 +606,56 @@
|
||||
"title": "Contracts",
|
||||
"description": "Manage contract templates and sent contracts",
|
||||
"templates": "Templates",
|
||||
"sentContracts": "Sent Contracts",
|
||||
"allContracts": "All Contracts",
|
||||
"createTemplate": "Create Template",
|
||||
"newTemplate": "New Template",
|
||||
"createContract": "Create Contract",
|
||||
"editTemplate": "Edit Template",
|
||||
"viewContract": "View Contract",
|
||||
"noTemplates": "No contract templates yet",
|
||||
"noTemplatesEmpty": "No templates yet. Create your first template to get started.",
|
||||
"noTemplatesSearch": "No templates found",
|
||||
"noContracts": "No contracts yet",
|
||||
"noContractsEmpty": "No contracts sent yet.",
|
||||
"noContractsSearch": "No contracts found",
|
||||
"templateName": "Template Name",
|
||||
"templateDescription": "Description",
|
||||
"content": "Content",
|
||||
"contentHtml": "Contract Content (HTML)",
|
||||
"searchTemplates": "Search templates...",
|
||||
"searchContracts": "Search contracts...",
|
||||
"all": "All",
|
||||
"scope": {
|
||||
"label": "Scope",
|
||||
"customer": "Customer Agreement",
|
||||
"appointment": "Appointment Agreement"
|
||||
"customer": "Customer-Level",
|
||||
"appointment": "Per Appointment",
|
||||
"customerDesc": "One-time contracts per customer (e.g., privacy policy, terms of service)",
|
||||
"appointmentDesc": "Signed for each booking (e.g., liability waivers, service agreements)"
|
||||
},
|
||||
"status": {
|
||||
"label": "Status",
|
||||
"draft": "Draft",
|
||||
"active": "Active",
|
||||
"archived": "Archived",
|
||||
"pending": "Pending Signature",
|
||||
"pending": "Pending",
|
||||
"signed": "Signed",
|
||||
"expired": "Expired",
|
||||
"voided": "Voided"
|
||||
},
|
||||
"expiresAfterDays": "Expires After (Days)",
|
||||
"expiresAfterDaysHint": "Leave empty for no expiration",
|
||||
"table": {
|
||||
"template": "Template",
|
||||
"scope": "Scope",
|
||||
"status": "Status",
|
||||
"version": "Version",
|
||||
"actions": "Actions",
|
||||
"customer": "Customer",
|
||||
"contract": "Contract",
|
||||
"created": "Created",
|
||||
"sent": "Sent"
|
||||
},
|
||||
"expiresAfterDays": "Expires After (days)",
|
||||
"expiresAfterDaysHint": "Leave blank for no expiration",
|
||||
"versionNotes": "Version Notes",
|
||||
"versionNotesPlaceholder": "What changed in this version?",
|
||||
"services": "Applicable Services",
|
||||
@@ -448,34 +663,68 @@
|
||||
"customer": "Customer",
|
||||
"appointment": "Appointment",
|
||||
"service": "Service",
|
||||
"sentAt": "Sent At",
|
||||
"signedAt": "Signed At",
|
||||
"sentAt": "Sent",
|
||||
"signedAt": "Signed",
|
||||
"expiresAt": "Expires At",
|
||||
"createdAt": "Created",
|
||||
"availableVariables": "Available Variables",
|
||||
"actions": {
|
||||
"send": "Send Contract",
|
||||
"resend": "Resend Contract",
|
||||
"resend": "Resend Email",
|
||||
"void": "Void Contract",
|
||||
"duplicate": "Duplicate Template",
|
||||
"preview": "Preview",
|
||||
"delete": "Delete"
|
||||
"preview": "Preview PDF",
|
||||
"previewFailed": "Failed to load PDF preview.",
|
||||
"delete": "Delete",
|
||||
"edit": "Edit",
|
||||
"viewDetails": "View Details",
|
||||
"copyLink": "Copy Signing Link",
|
||||
"sendEmail": "Send Email",
|
||||
"openSigningPage": "Open Signing Page",
|
||||
"saveChanges": "Save Changes"
|
||||
},
|
||||
"sendContract": {
|
||||
"title": "Send Contract",
|
||||
"selectCustomer": "Select Customer",
|
||||
"selectTemplate": "Contract Template",
|
||||
"selectTemplatePlaceholder": "Select a template...",
|
||||
"selectCustomer": "Customer",
|
||||
"searchCustomers": "Search customers...",
|
||||
"selectAppointment": "Select Appointment (Optional)",
|
||||
"selectService": "Select Service (Optional)",
|
||||
"send": "Send",
|
||||
"send": "Send Contract",
|
||||
"sendImmediately": "Send signing request email immediately",
|
||||
"success": "Contract sent successfully",
|
||||
"error": "Failed to send contract"
|
||||
"error": "Failed to send contract",
|
||||
"loadingCustomers": "Loading customers...",
|
||||
"loadCustomersFailed": "Failed to load customers",
|
||||
"noCustomers": "No customers available. Create customers first.",
|
||||
"noMatchingCustomers": "No matching customers"
|
||||
},
|
||||
"voidContract": {
|
||||
"title": "Void Contract",
|
||||
"description": "Voiding this contract will cancel it. The customer will no longer be able to sign.",
|
||||
"reason": "Reason for voiding",
|
||||
"reasonPlaceholder": "Why is this contract being voided?",
|
||||
"reasonPlaceholder": "Enter the reason...",
|
||||
"confirm": "Void Contract",
|
||||
"success": "Contract voided successfully",
|
||||
"error": "Failed to void contract"
|
||||
},
|
||||
"deleteTemplate": {
|
||||
"title": "Delete Template",
|
||||
"description": "Are you sure you want to delete this template? This action cannot be undone.",
|
||||
"confirm": "Delete",
|
||||
"success": "Template deleted successfully",
|
||||
"error": "Failed to delete template"
|
||||
},
|
||||
"contractDetails": {
|
||||
"title": "Contract Details",
|
||||
"customer": "Customer",
|
||||
"template": "Template",
|
||||
"status": "Status",
|
||||
"created": "Created",
|
||||
"contentPreview": "Content Preview",
|
||||
"signingLink": "Signing Link"
|
||||
},
|
||||
"preview": {
|
||||
"title": "Preview Contract",
|
||||
"sampleData": "Using sample data for preview"
|
||||
@@ -486,20 +735,26 @@
|
||||
"contractFor": "Contract for {{customerName}}",
|
||||
"pleaseReview": "Please review and sign this contract",
|
||||
"signerName": "Your Full Name",
|
||||
"signerNamePlaceholder": "Enter your legal name",
|
||||
"signerEmail": "Your Email",
|
||||
"signatureLabel": "Sign Below",
|
||||
"signaturePlaceholder": "Draw your signature here",
|
||||
"clearSignature": "Clear",
|
||||
"agreeToTerms": "I agree to the terms and conditions",
|
||||
"submitSignature": "Submit Signature",
|
||||
"submitting": "Submitting...",
|
||||
"agreeToTerms": "I have read and agree to the terms and conditions outlined in this document. By checking this box, I understand that this constitutes a legal electronic signature.",
|
||||
"consentToElectronic": "I consent to conduct business electronically. I understand that I have the right to receive documents in paper form upon request and can withdraw this consent at any time.",
|
||||
"submitSignature": "Sign Contract",
|
||||
"submitting": "Signing...",
|
||||
"success": "Contract signed successfully!",
|
||||
"successMessage": "You will receive a confirmation email with a copy of the signed contract.",
|
||||
"error": "Failed to sign contract",
|
||||
"expired": "This contract has expired",
|
||||
"alreadySigned": "This contract has already been signed",
|
||||
"notFound": "Contract not found",
|
||||
"voided": "This contract has been voided",
|
||||
"signedBy": "Signed by {{name}} on {{date}}",
|
||||
"thankYou": "Thank you for signing!"
|
||||
"thankYou": "Thank you for signing!",
|
||||
"loading": "Loading contract...",
|
||||
"geolocationHint": "Location will be recorded for legal compliance"
|
||||
},
|
||||
"errors": {
|
||||
"loadFailed": "Failed to load contracts",
|
||||
@@ -2278,5 +2533,181 @@
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"timeBlocks": {
|
||||
"title": "Time Blocks",
|
||||
"subtitle": "Manage business closures, holidays, and resource unavailability",
|
||||
"addBlock": "Add Block",
|
||||
"businessTab": "Business Blocks",
|
||||
"resourceTab": "Resource Blocks",
|
||||
"calendarTab": "Yearly View",
|
||||
"businessInfo": "Business blocks apply to all resources. Use these for holidays, company closures, and business-wide events.",
|
||||
"noBusinessBlocks": "No Business Blocks",
|
||||
"noBusinessBlocksDesc": "Add holidays and business closures to prevent bookings during those times.",
|
||||
"addFirstBlock": "Add First Block",
|
||||
"titleCol": "Title",
|
||||
"typeCol": "Type",
|
||||
"patternCol": "Pattern",
|
||||
"actionsCol": "Actions",
|
||||
"resourceInfo": "Resource blocks apply to specific staff or equipment. Use these for vacations, maintenance, or personal time.",
|
||||
"noResourceBlocks": "No Resource Blocks",
|
||||
"noResourceBlocksDesc": "Add time blocks for specific resources to manage their availability.",
|
||||
"deleteConfirmTitle": "Delete Time Block?",
|
||||
"deleteConfirmDesc": "This action cannot be undone.",
|
||||
"blockTypes": {
|
||||
"hard": "Hard Block",
|
||||
"soft": "Soft Block"
|
||||
},
|
||||
"recurrenceTypes": {
|
||||
"none": "One-time",
|
||||
"weekly": "Weekly",
|
||||
"monthly": "Monthly",
|
||||
"yearly": "Yearly",
|
||||
"holiday": "Holiday"
|
||||
},
|
||||
"inactive": "Inactive",
|
||||
"activate": "Activate",
|
||||
"deactivate": "Deactivate"
|
||||
},
|
||||
"myAvailability": {
|
||||
"title": "My Availability",
|
||||
"subtitle": "Manage your time off and unavailability",
|
||||
"noResource": "No Resource Linked",
|
||||
"noResourceDesc": "Your account is not linked to a resource. Please contact your manager to set up your availability.",
|
||||
"addBlock": "Block Time",
|
||||
"businessBlocks": "Business Closures",
|
||||
"businessBlocksInfo": "These blocks are set by your business and apply to everyone.",
|
||||
"myBlocks": "My Time Blocks",
|
||||
"noBlocks": "No Time Blocks",
|
||||
"noBlocksDesc": "Add time blocks for vacations, lunch breaks, or any time you need off.",
|
||||
"addFirstBlock": "Add First Block",
|
||||
"titleCol": "Title",
|
||||
"typeCol": "Type",
|
||||
"patternCol": "Pattern",
|
||||
"actionsCol": "Actions",
|
||||
"editBlock": "Edit Time Block",
|
||||
"createBlock": "Block Time Off",
|
||||
"create": "Block Time",
|
||||
"deleteConfirmTitle": "Delete Time Block?",
|
||||
"deleteConfirmDesc": "This action cannot be undone.",
|
||||
"form": {
|
||||
"title": "Title",
|
||||
"description": "Description",
|
||||
"blockType": "Block Type",
|
||||
"recurrenceType": "Recurrence",
|
||||
"allDay": "All day",
|
||||
"startDate": "Start Date",
|
||||
"endDate": "End Date",
|
||||
"startTime": "Start Time",
|
||||
"endTime": "End Time",
|
||||
"daysOfWeek": "Days of Week",
|
||||
"daysOfMonth": "Days of Month"
|
||||
}
|
||||
},
|
||||
"helpTimeBlocks": {
|
||||
"title": "Time Blocks Guide",
|
||||
"subtitle": "Learn how to block off time for closures, holidays, and unavailability",
|
||||
"overview": {
|
||||
"title": "What are Time Blocks?",
|
||||
"description": "Time blocks allow you to mark specific dates, times, or recurring periods as unavailable for bookings. Use them to manage holidays, business closures, staff vacations, maintenance windows, and more.",
|
||||
"businessBlocks": "Business Blocks",
|
||||
"businessBlocksDesc": "Apply to all resources. Perfect for company holidays, office closures, and maintenance.",
|
||||
"resourceBlocks": "Resource Blocks",
|
||||
"resourceBlocksDesc": "Apply to specific resources. Use for individual vacations, appointments, or training.",
|
||||
"hardBlocks": "Hard Blocks",
|
||||
"hardBlocksDesc": "Completely prevent bookings during the blocked period. Cannot be overridden.",
|
||||
"softBlocks": "Soft Blocks",
|
||||
"softBlocksDesc": "Show a warning but still allow bookings with confirmation."
|
||||
},
|
||||
"levels": {
|
||||
"title": "Block Levels",
|
||||
"levelCol": "Level",
|
||||
"scopeCol": "Scope",
|
||||
"examplesCol": "Example Uses",
|
||||
"business": "Business",
|
||||
"businessScope": "All resources in your business",
|
||||
"businessExamples": "Holidays, office closures, company events, maintenance",
|
||||
"resource": "Resource",
|
||||
"resourceScope": "A specific resource (staff member, room, etc.)",
|
||||
"resourceExamples": "Vacation, personal appointments, lunch breaks, training",
|
||||
"additiveNote": "Blocks are Additive",
|
||||
"additiveDesc": "Both business-level and resource-level blocks apply. If the business is closed on a holiday, individual resource blocks don't matter for that day."
|
||||
},
|
||||
"types": {
|
||||
"title": "Block Types: Hard vs Soft",
|
||||
"hardBlock": "Hard Block",
|
||||
"hardBlockDesc": "Completely prevents any bookings during the blocked period. Customers cannot book, and staff cannot override. The scheduler shows a striped red overlay.",
|
||||
"cannotOverride": "Cannot be overridden",
|
||||
"showsInBooking": "Shows in customer booking",
|
||||
"redOverlay": "Red striped overlay",
|
||||
"softBlock": "Soft Block",
|
||||
"softBlockDesc": "Shows a warning but allows bookings with confirmation. Useful for indicating preferred-off times that can be overridden if necessary.",
|
||||
"canOverride": "Can be overridden",
|
||||
"showsWarning": "Shows warning only",
|
||||
"yellowOverlay": "Yellow dashed overlay"
|
||||
},
|
||||
"recurrence": {
|
||||
"title": "Recurrence Patterns",
|
||||
"patternCol": "Pattern",
|
||||
"descriptionCol": "Description",
|
||||
"exampleCol": "Example",
|
||||
"oneTime": "One-time",
|
||||
"oneTimeDesc": "A specific date or date range that occurs once",
|
||||
"oneTimeExample": "Dec 24-26 (Christmas break), Feb 15 (President's Day)",
|
||||
"weekly": "Weekly",
|
||||
"weeklyDesc": "Repeats on specific days of the week",
|
||||
"weeklyExample": "Every Saturday and Sunday, Every Monday lunch",
|
||||
"monthly": "Monthly",
|
||||
"monthlyDesc": "Repeats on specific days of the month",
|
||||
"monthlyExample": "1st of every month (inventory), 15th (payroll)",
|
||||
"yearly": "Yearly",
|
||||
"yearlyDesc": "Repeats on a specific month and day each year",
|
||||
"yearlyExample": "July 4th, December 25th, January 1st",
|
||||
"holiday": "Holiday",
|
||||
"holidayDesc": "Select from popular US holidays. Multi-select supported - each holiday creates its own block.",
|
||||
"holidayExample": "Christmas, Thanksgiving, Memorial Day, Independence Day"
|
||||
},
|
||||
"visualization": {
|
||||
"title": "Viewing Time Blocks",
|
||||
"description": "Time blocks appear in multiple views throughout the application with color-coded indicators:",
|
||||
"colorLegend": "Color Legend",
|
||||
"businessHard": "Business Hard Block",
|
||||
"businessSoft": "Business Soft Block",
|
||||
"resourceHard": "Resource Hard Block",
|
||||
"resourceSoft": "Resource Soft Block",
|
||||
"schedulerOverlay": "Scheduler Overlay",
|
||||
"schedulerOverlayDesc": "Blocked times appear directly on the scheduler calendar with visual indicators. Business blocks use red/yellow colors, resource blocks use purple/cyan. Click on any blocked area in week view to navigate to that day.",
|
||||
"monthView": "Month View",
|
||||
"monthViewDesc": "Blocked dates show with colored backgrounds and badge indicators. Multiple block types on the same day show all applicable badges.",
|
||||
"listView": "List View",
|
||||
"listViewDesc": "Manage all time blocks in a tabular format with filtering options. Edit, activate/deactivate, or delete blocks from here."
|
||||
},
|
||||
"staffAvailability": {
|
||||
"title": "Staff Availability (My Availability)",
|
||||
"description": "Staff members can manage their own time blocks through the \"My Availability\" page. This allows them to block off time for personal appointments, vacations, or other commitments.",
|
||||
"viewBusiness": "View business-level blocks (read-only)",
|
||||
"createPersonal": "Create and manage personal time blocks",
|
||||
"seeCalendar": "See yearly calendar of their availability",
|
||||
"hardBlockPermission": "Hard Block Permission",
|
||||
"hardBlockPermissionDesc": "By default, staff can only create soft blocks. To allow a staff member to create hard blocks, enable the \"Can create hard blocks\" permission in their staff settings."
|
||||
},
|
||||
"bestPractices": {
|
||||
"title": "Best Practices",
|
||||
"tip1Title": "Plan holidays in advance",
|
||||
"tip1Desc": "Set up annual holidays at the beginning of each year using the Holiday recurrence type.",
|
||||
"tip2Title": "Use soft blocks for preferences",
|
||||
"tip2Desc": "Reserve hard blocks for absolute closures. Use soft blocks for preferred-off times that could be overridden.",
|
||||
"tip3Title": "Check for conflicts before creating",
|
||||
"tip3Desc": "The system shows existing appointments that conflict with new blocks. Review before confirming.",
|
||||
"tip4Title": "Set recurrence end dates",
|
||||
"tip4Desc": "For recurring blocks that aren't permanent, set an end date to prevent them from extending indefinitely.",
|
||||
"tip5Title": "Use descriptive titles",
|
||||
"tip5Desc": "Include clear titles like \"Christmas Day\", \"Team Meeting\", or \"Annual Maintenance\" for easy identification."
|
||||
},
|
||||
"quickAccess": {
|
||||
"title": "Quick Access",
|
||||
"manageTimeBlocks": "Manage Time Blocks",
|
||||
"myAvailability": "My Availability"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -179,6 +179,198 @@
|
||||
"endpoint": "Endpoint",
|
||||
"request": "Solicitud",
|
||||
"response": "Respuesta"
|
||||
},
|
||||
"contracts": {
|
||||
"title": "Guía de Contratos",
|
||||
"subtitle": "Crea y gestiona contratos digitales con firmas electrónicas",
|
||||
"overview": {
|
||||
"title": "Resumen",
|
||||
"description": "El sistema de Contratos te permite crear plantillas de contratos reutilizables, enviarlas a clientes para firma digital y mantener registros legalmente conformes con rastros de auditoría completos.",
|
||||
"compliance": "Todas las firmas se capturan con cumplimiento de ESIGN Act y UETA, incluyendo dirección IP, marca de tiempo, información del navegador y geolocalización opcional para máxima protección legal."
|
||||
},
|
||||
"pageLayout": {
|
||||
"title": "Diseño de la Página",
|
||||
"description": "La página de Contratos está organizada en dos secciones colapsables para una gestión fácil:",
|
||||
"templatesSection": {
|
||||
"title": "Sección de Plantillas",
|
||||
"description": "Crea y gestiona plantillas de contratos reutilizables. Incluye búsqueda, filtros de estado (Todos, Activo, Borrador, Archivado) y acciones para crear, editar, previsualizar PDF y eliminar plantillas."
|
||||
},
|
||||
"sentContractsSection": {
|
||||
"title": "Sección de Contratos Enviados",
|
||||
"description": "Rastrea los contratos enviados a clientes. Incluye búsqueda, filtros de estado (Todos, Pendiente, Firmado, Expirado, Anulado) y acciones para ver, copiar enlace, reenviar o anular contratos."
|
||||
},
|
||||
"tip": "Haz clic en el encabezado de la sección para colapsar/expandir cada sección. La insignia de conteo muestra cuántos elementos hay en cada sección."
|
||||
},
|
||||
"templates": {
|
||||
"title": "Plantillas de Contratos",
|
||||
"description": "Las plantillas son documentos de contrato reutilizables que se pueden personalizar con marcadores de posición de variables.",
|
||||
"variablesTitle": "Variables de Plantilla",
|
||||
"variablesDescription": "Usa estos marcadores en tus plantillas - se reemplazarán automáticamente cuando se cree el contrato:",
|
||||
"variables": {
|
||||
"customerName": "Nombre completo",
|
||||
"customerFirstName": "Nombre",
|
||||
"customerEmail": "Dirección de correo",
|
||||
"customerPhone": "Número de teléfono",
|
||||
"businessName": "Nombre de tu negocio",
|
||||
"businessEmail": "Correo de contacto",
|
||||
"businessPhone": "Teléfono del negocio",
|
||||
"date": "Fecha actual",
|
||||
"year": "Año actual"
|
||||
},
|
||||
"scopesTitle": "Alcances de Plantilla",
|
||||
"scopes": {
|
||||
"customerLevel": {
|
||||
"title": "Nivel de Cliente",
|
||||
"description": "Contratos únicos por cliente (ej: política de privacidad, términos de servicio)"
|
||||
},
|
||||
"perAppointment": {
|
||||
"title": "Por Cita",
|
||||
"description": "Se firma en cada reserva (ej: exenciones de responsabilidad, acuerdos de servicio)"
|
||||
}
|
||||
}
|
||||
},
|
||||
"creating": {
|
||||
"title": "Creando Plantillas",
|
||||
"description": "Haz clic en el botón \"Nueva Plantilla\" en la sección de Plantillas para crear una nueva plantilla de contrato:",
|
||||
"steps": {
|
||||
"name": {
|
||||
"title": "Ingresa el Nombre de la Plantilla",
|
||||
"description": "Dale a tu plantilla un nombre claro y descriptivo (ej: \"Acuerdo de Servicio\", \"Exención de Responsabilidad\")."
|
||||
},
|
||||
"scope": {
|
||||
"title": "Selecciona el Alcance",
|
||||
"description": "Elige \"Por Cita\" para exenciones o \"Nivel de Cliente\" para acuerdos únicos."
|
||||
},
|
||||
"status": {
|
||||
"title": "Establece el Estado",
|
||||
"description": "Comienza como \"Borrador\" mientras editas. Cambia a \"Activo\" cuando esté listo para enviar a clientes."
|
||||
},
|
||||
"expiration": {
|
||||
"title": "Establece la Expiración (Opcional)",
|
||||
"description": "Ingresa los días hasta que los contratos expiren. Deja en blanco para sin expiración."
|
||||
},
|
||||
"content": {
|
||||
"title": "Escribe el Contenido del Contrato",
|
||||
"description": "Ingresa el texto de tu contrato usando formato HTML. Haz clic en los chips de variables para insertar marcadores."
|
||||
}
|
||||
}
|
||||
},
|
||||
"managing": {
|
||||
"title": "Gestionando Plantillas",
|
||||
"description": "Cada plantilla en la lista tiene botones de acción a la derecha:",
|
||||
"actions": {
|
||||
"preview": {
|
||||
"title": "Vista Previa PDF",
|
||||
"description": "Ve cómo se ve el contrato como PDF con datos de muestra"
|
||||
},
|
||||
"edit": {
|
||||
"title": "Editar",
|
||||
"description": "Modifica el nombre, contenido, alcance o estado de la plantilla"
|
||||
},
|
||||
"delete": {
|
||||
"title": "Eliminar",
|
||||
"description": "Elimina la plantilla (requiere confirmación)"
|
||||
}
|
||||
},
|
||||
"note": "Solo las plantillas \"Activas\" pueden usarse para enviar contratos. Cambia las plantillas a estado Activo cuando estén listas para usar."
|
||||
},
|
||||
"sending": {
|
||||
"title": "Enviando Contratos",
|
||||
"description": "Haz clic en el botón \"Enviar Contrato\" en la sección de Contratos Enviados:",
|
||||
"steps": {
|
||||
"selectTemplate": {
|
||||
"title": "Selecciona una Plantilla",
|
||||
"description": "Elige de tus plantillas de contrato activas"
|
||||
},
|
||||
"selectCustomer": {
|
||||
"title": "Elige un Cliente",
|
||||
"description": "Busca y selecciona un cliente. Las variables se llenan automáticamente con sus datos."
|
||||
},
|
||||
"sendImmediately": {
|
||||
"title": "Enviar Inmediatamente (Opcional)",
|
||||
"description": "Marca la casilla para enviar la solicitud de firma por correo de inmediato, o desmarca para enviar después."
|
||||
},
|
||||
"trackStatus": {
|
||||
"title": "Rastrea el Estado",
|
||||
"description": "Monitorea el contrato en la lista de Contratos Enviados"
|
||||
}
|
||||
}
|
||||
},
|
||||
"statusActions": {
|
||||
"title": "Estado y Acciones del Contrato",
|
||||
"statuses": {
|
||||
"pending": {
|
||||
"title": "Pendiente",
|
||||
"description": "Esperando firma del cliente"
|
||||
},
|
||||
"signed": {
|
||||
"title": "Firmado",
|
||||
"description": "El cliente ha firmado el contrato"
|
||||
},
|
||||
"expired": {
|
||||
"title": "Expirado",
|
||||
"description": "El contrato expiró antes de ser firmado"
|
||||
},
|
||||
"voided": {
|
||||
"title": "Anulado",
|
||||
"description": "El contrato fue cancelado por el negocio"
|
||||
}
|
||||
},
|
||||
"actionsTitle": "Acciones Disponibles",
|
||||
"actions": {
|
||||
"viewDetails": "Ver información completa del contrato",
|
||||
"copyLink": "Copiar URL de firma al portapapeles (solo pendiente)",
|
||||
"sendResend": "Enviar la solicitud de firma por correo (solo pendiente)",
|
||||
"openSigningPage": "Ver la experiencia de firma del cliente",
|
||||
"void": "Cancelar un contrato pendiente"
|
||||
}
|
||||
},
|
||||
"legalCompliance": {
|
||||
"title": "Cumplimiento Legal",
|
||||
"notice": "Todas las firmas incluyen rastros de auditoría completos que cumplen con los requisitos federales y estatales para firmas electrónicas.",
|
||||
"auditDataTitle": "Datos de Auditoría Capturados",
|
||||
"auditData": {
|
||||
"documentHash": "Hash del documento (SHA-256)",
|
||||
"timestamp": "Marca de tiempo de firma (ISO)",
|
||||
"ipAddress": "Dirección IP del firmante",
|
||||
"browserInfo": "Información del navegador/dispositivo",
|
||||
"consentCheckbox": "Estados de casilla de consentimiento",
|
||||
"geolocation": "Geolocalización (si se permite)"
|
||||
}
|
||||
},
|
||||
"pdfGeneration": {
|
||||
"title": "Generación de PDF",
|
||||
"description": "Una vez que se firma un contrato, se genera automáticamente un PDF que incluye:",
|
||||
"includes": {
|
||||
"branding": "Tu marca y logo del negocio",
|
||||
"content": "El contenido completo del contrato con variables sustituidas",
|
||||
"signature": "Sección de firma con el nombre del firmante y confirmaciones de consentimiento",
|
||||
"auditTrail": "Rastro de auditoría completo con datos de verificación",
|
||||
"legalNotice": "Aviso legal sobre firmas electrónicas"
|
||||
},
|
||||
"tip": "Usa el ícono de ojo en cualquier plantilla para previsualizar cómo se verá el PDF final con datos de cliente de muestra."
|
||||
},
|
||||
"bestPractices": {
|
||||
"title": "Mejores Prácticas",
|
||||
"tips": {
|
||||
"clearLanguage": "Escribe contratos en lenguaje claro que los clientes puedan entender fácilmente",
|
||||
"startDraft": "Crea plantillas en estado Borrador, prueba con vista previa de PDF, luego activa",
|
||||
"setExpiration": "Usa la función de expiración para asegurar que los contratos se firmen puntualmente",
|
||||
"createCustomersFirst": "Asegúrate de que los clientes existan en el sistema antes de enviar contratos",
|
||||
"archiveOld": "En lugar de eliminar, archiva las plantillas que ya no uses",
|
||||
"downloadPdfs": "Guarda copias de los contratos firmados para tus registros"
|
||||
}
|
||||
},
|
||||
"relatedFeatures": {
|
||||
"title": "Características Relacionadas",
|
||||
"servicesGuide": "Guía de Servicios",
|
||||
"customersGuide": "Guía de Clientes"
|
||||
},
|
||||
"needHelp": {
|
||||
"title": "¿Necesitas Más Ayuda?",
|
||||
"description": "Nuestro equipo de soporte está listo para ayudar con cualquier pregunta sobre contratos.",
|
||||
"contactSupport": "Contactar Soporte"
|
||||
}
|
||||
}
|
||||
},
|
||||
"dashboard": {
|
||||
@@ -1078,5 +1270,344 @@
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"contracts": {
|
||||
"title": "Contratos",
|
||||
"description": "Gestiona plantillas de contratos y contratos enviados",
|
||||
"templates": "Plantillas",
|
||||
"sentContracts": "Contratos Enviados",
|
||||
"allContracts": "Todos los Contratos",
|
||||
"createTemplate": "Crear Plantilla",
|
||||
"newTemplate": "Nueva Plantilla",
|
||||
"createContract": "Crear Contrato",
|
||||
"editTemplate": "Editar Plantilla",
|
||||
"viewContract": "Ver Contrato",
|
||||
"noTemplates": "Aún no hay plantillas de contratos",
|
||||
"noTemplatesEmpty": "Aún no hay plantillas. Crea tu primera plantilla para comenzar.",
|
||||
"noTemplatesSearch": "No se encontraron plantillas",
|
||||
"noContracts": "Aún no hay contratos",
|
||||
"noContractsEmpty": "Aún no se han enviado contratos.",
|
||||
"noContractsSearch": "No se encontraron contratos",
|
||||
"templateName": "Nombre de la Plantilla",
|
||||
"templateDescription": "Descripción",
|
||||
"content": "Contenido",
|
||||
"contentHtml": "Contenido del Contrato (HTML)",
|
||||
"searchTemplates": "Buscar plantillas...",
|
||||
"searchContracts": "Buscar contratos...",
|
||||
"all": "Todos",
|
||||
"scope": {
|
||||
"label": "Alcance",
|
||||
"customer": "Nivel de Cliente",
|
||||
"appointment": "Por Cita",
|
||||
"customerDesc": "Contratos únicos por cliente (ej: política de privacidad, términos de servicio)",
|
||||
"appointmentDesc": "Se firma en cada reserva (ej: exenciones de responsabilidad, acuerdos de servicio)"
|
||||
},
|
||||
"status": {
|
||||
"label": "Estado",
|
||||
"draft": "Borrador",
|
||||
"active": "Activo",
|
||||
"archived": "Archivado",
|
||||
"pending": "Pendiente",
|
||||
"signed": "Firmado",
|
||||
"expired": "Expirado",
|
||||
"voided": "Anulado"
|
||||
},
|
||||
"table": {
|
||||
"template": "Plantilla",
|
||||
"scope": "Alcance",
|
||||
"status": "Estado",
|
||||
"version": "Versión",
|
||||
"actions": "Acciones",
|
||||
"customer": "Cliente",
|
||||
"contract": "Contrato",
|
||||
"created": "Creado",
|
||||
"sent": "Enviado"
|
||||
},
|
||||
"expiresAfterDays": "Expira Después de (días)",
|
||||
"expiresAfterDaysHint": "Dejar en blanco para sin expiración",
|
||||
"versionNotes": "Notas de Versión",
|
||||
"versionNotesPlaceholder": "¿Qué cambió en esta versión?",
|
||||
"services": "Servicios Aplicables",
|
||||
"servicesHint": "Dejar vacío para aplicar a todos los servicios",
|
||||
"customer": "Cliente",
|
||||
"appointment": "Cita",
|
||||
"service": "Servicio",
|
||||
"sentAt": "Enviado",
|
||||
"signedAt": "Firmado",
|
||||
"expiresAt": "Expira En",
|
||||
"createdAt": "Creado",
|
||||
"availableVariables": "Variables Disponibles",
|
||||
"actions": {
|
||||
"send": "Enviar Contrato",
|
||||
"resend": "Reenviar Correo",
|
||||
"void": "Anular Contrato",
|
||||
"duplicate": "Duplicar Plantilla",
|
||||
"preview": "Vista Previa PDF",
|
||||
"previewFailed": "Error al cargar la vista previa del PDF.",
|
||||
"delete": "Eliminar",
|
||||
"edit": "Editar",
|
||||
"viewDetails": "Ver Detalles",
|
||||
"copyLink": "Copiar Enlace de Firma",
|
||||
"sendEmail": "Enviar Correo",
|
||||
"openSigningPage": "Abrir Página de Firma",
|
||||
"saveChanges": "Guardar Cambios"
|
||||
},
|
||||
"sendContract": {
|
||||
"title": "Enviar Contrato",
|
||||
"selectTemplate": "Plantilla de Contrato",
|
||||
"selectTemplatePlaceholder": "Selecciona una plantilla...",
|
||||
"selectCustomer": "Cliente",
|
||||
"searchCustomers": "Buscar clientes...",
|
||||
"selectAppointment": "Seleccionar Cita (Opcional)",
|
||||
"selectService": "Seleccionar Servicio (Opcional)",
|
||||
"send": "Enviar Contrato",
|
||||
"sendImmediately": "Enviar solicitud de firma por correo inmediatamente",
|
||||
"success": "Contrato enviado exitosamente",
|
||||
"error": "Error al enviar el contrato",
|
||||
"loadingCustomers": "Cargando clientes...",
|
||||
"loadCustomersFailed": "Error al cargar clientes",
|
||||
"noCustomers": "No hay clientes disponibles. Crea clientes primero.",
|
||||
"noMatchingCustomers": "No se encontraron clientes"
|
||||
},
|
||||
"voidContract": {
|
||||
"title": "Anular Contrato",
|
||||
"description": "Anular este contrato lo cancelará. El cliente ya no podrá firmar.",
|
||||
"reason": "Razón de la anulación",
|
||||
"reasonPlaceholder": "Ingresa la razón...",
|
||||
"confirm": "Anular Contrato",
|
||||
"success": "Contrato anulado exitosamente",
|
||||
"error": "Error al anular el contrato"
|
||||
},
|
||||
"deleteTemplate": {
|
||||
"title": "Eliminar Plantilla",
|
||||
"description": "¿Estás seguro de que deseas eliminar esta plantilla? Esta acción no se puede deshacer.",
|
||||
"confirm": "Eliminar",
|
||||
"success": "Plantilla eliminada exitosamente",
|
||||
"error": "Error al eliminar la plantilla"
|
||||
},
|
||||
"contractDetails": {
|
||||
"title": "Detalles del Contrato",
|
||||
"customer": "Cliente",
|
||||
"template": "Plantilla",
|
||||
"status": "Estado",
|
||||
"created": "Creado",
|
||||
"contentPreview": "Vista Previa del Contenido",
|
||||
"signingLink": "Enlace de Firma"
|
||||
},
|
||||
"preview": {
|
||||
"title": "Vista Previa del Contrato",
|
||||
"sampleData": "Usando datos de muestra para la vista previa"
|
||||
},
|
||||
"signing": {
|
||||
"title": "Firmar Contrato",
|
||||
"businessName": "{{businessName}}",
|
||||
"contractFor": "Contrato para {{customerName}}",
|
||||
"pleaseReview": "Por favor revisa y firma este contrato",
|
||||
"signerName": "Tu Nombre Completo",
|
||||
"signerNamePlaceholder": "Ingresa tu nombre legal",
|
||||
"signerEmail": "Tu Correo Electrónico",
|
||||
"signatureLabel": "Firma Abajo",
|
||||
"signaturePlaceholder": "Dibuja tu firma aquí",
|
||||
"clearSignature": "Borrar",
|
||||
"agreeToTerms": "He leído y acepto los términos y condiciones descritos en este documento. Al marcar esta casilla, entiendo que esto constituye una firma electrónica legal.",
|
||||
"consentToElectronic": "Consiento realizar negocios electrónicamente. Entiendo que tengo derecho a recibir documentos en papel a petición y puedo retirar este consentimiento en cualquier momento.",
|
||||
"submitSignature": "Firmar Contrato",
|
||||
"submitting": "Firmando...",
|
||||
"success": "¡Contrato firmado exitosamente!",
|
||||
"successMessage": "Recibirás un correo de confirmación con una copia del contrato firmado.",
|
||||
"error": "Error al firmar el contrato",
|
||||
"expired": "Este contrato ha expirado",
|
||||
"alreadySigned": "Este contrato ya ha sido firmado",
|
||||
"notFound": "Contrato no encontrado",
|
||||
"voided": "Este contrato ha sido anulado",
|
||||
"signedBy": "Firmado por {{name}} el {{date}}",
|
||||
"thankYou": "¡Gracias por firmar!",
|
||||
"loading": "Cargando contrato...",
|
||||
"geolocationHint": "La ubicación será registrada para cumplimiento legal"
|
||||
},
|
||||
"errors": {
|
||||
"loadFailed": "Error al cargar contratos",
|
||||
"createFailed": "Error al crear contrato",
|
||||
"updateFailed": "Error al actualizar contrato",
|
||||
"deleteFailed": "Error al eliminar contrato",
|
||||
"sendFailed": "Error al enviar contrato",
|
||||
"voidFailed": "Error al anular contrato"
|
||||
}
|
||||
},
|
||||
"timeBlocks": {
|
||||
"title": "Bloques de Tiempo",
|
||||
"subtitle": "Gestionar cierres del negocio, días festivos y no disponibilidad de recursos",
|
||||
"addBlock": "Agregar Bloque",
|
||||
"businessTab": "Bloques del Negocio",
|
||||
"resourceTab": "Bloques de Recursos",
|
||||
"calendarTab": "Vista Anual",
|
||||
"businessInfo": "Los bloques del negocio aplican a todos los recursos. Úselos para días festivos, cierres de la empresa y eventos de toda la empresa.",
|
||||
"noBusinessBlocks": "Sin Bloques del Negocio",
|
||||
"noBusinessBlocksDesc": "Agregue días festivos y cierres del negocio para evitar reservas durante esos períodos.",
|
||||
"addFirstBlock": "Agregar Primer Bloque",
|
||||
"titleCol": "Título",
|
||||
"typeCol": "Tipo",
|
||||
"patternCol": "Patrón",
|
||||
"actionsCol": "Acciones",
|
||||
"resourceInfo": "Los bloques de recursos aplican a personal o equipos específicos. Úselos para vacaciones, mantenimiento o tiempo personal.",
|
||||
"noResourceBlocks": "Sin Bloques de Recursos",
|
||||
"noResourceBlocksDesc": "Agregue bloques de tiempo para recursos específicos para gestionar su disponibilidad.",
|
||||
"deleteConfirmTitle": "¿Eliminar Bloque de Tiempo?",
|
||||
"deleteConfirmDesc": "Esta acción no se puede deshacer.",
|
||||
"blockTypes": {
|
||||
"hard": "Bloque Duro",
|
||||
"soft": "Bloque Suave"
|
||||
},
|
||||
"recurrenceTypes": {
|
||||
"none": "Una vez",
|
||||
"weekly": "Semanal",
|
||||
"monthly": "Mensual",
|
||||
"yearly": "Anual",
|
||||
"holiday": "Día Festivo"
|
||||
},
|
||||
"inactive": "Inactivo",
|
||||
"activate": "Activar",
|
||||
"deactivate": "Desactivar"
|
||||
},
|
||||
"myAvailability": {
|
||||
"title": "Mi Disponibilidad",
|
||||
"subtitle": "Gestionar tiempo libre y no disponibilidad",
|
||||
"noResource": "Sin Recurso Vinculado",
|
||||
"noResourceDesc": "Tu cuenta no está vinculada a un recurso. Por favor contacta a tu gerente para configurar tu disponibilidad.",
|
||||
"addBlock": "Bloquear Tiempo",
|
||||
"businessBlocks": "Cierres del Negocio",
|
||||
"businessBlocksInfo": "Estos bloques son establecidos por tu negocio y aplican a todos.",
|
||||
"myBlocks": "Mis Bloques de Tiempo",
|
||||
"noBlocks": "Sin Bloques de Tiempo",
|
||||
"noBlocksDesc": "Agrega bloques de tiempo para vacaciones, almuerzos o cualquier tiempo que necesites libre.",
|
||||
"addFirstBlock": "Agregar Primer Bloque",
|
||||
"titleCol": "Título",
|
||||
"typeCol": "Tipo",
|
||||
"patternCol": "Patrón",
|
||||
"actionsCol": "Acciones",
|
||||
"editBlock": "Editar Bloque de Tiempo",
|
||||
"createBlock": "Bloquear Tiempo Libre",
|
||||
"create": "Bloquear Tiempo",
|
||||
"deleteConfirmTitle": "¿Eliminar Bloque de Tiempo?",
|
||||
"deleteConfirmDesc": "Esta acción no se puede deshacer.",
|
||||
"form": {
|
||||
"title": "Título",
|
||||
"description": "Descripción",
|
||||
"blockType": "Tipo de Bloque",
|
||||
"recurrenceType": "Recurrencia",
|
||||
"allDay": "Todo el día",
|
||||
"startDate": "Fecha de Inicio",
|
||||
"endDate": "Fecha de Fin",
|
||||
"startTime": "Hora de Inicio",
|
||||
"endTime": "Hora de Fin",
|
||||
"daysOfWeek": "Días de la Semana",
|
||||
"daysOfMonth": "Días del Mes"
|
||||
}
|
||||
},
|
||||
"helpTimeBlocks": {
|
||||
"title": "Guía de Bloques de Tiempo",
|
||||
"subtitle": "Aprende cómo bloquear tiempo para cierres, días festivos y no disponibilidad",
|
||||
"overview": {
|
||||
"title": "¿Qué son los Bloques de Tiempo?",
|
||||
"description": "Los bloques de tiempo te permiten marcar fechas, horas o períodos recurrentes específicos como no disponibles para reservas. Úsalos para gestionar días festivos, cierres del negocio, vacaciones del personal, ventanas de mantenimiento y más.",
|
||||
"businessBlocks": "Bloques del Negocio",
|
||||
"businessBlocksDesc": "Aplican a todos los recursos. Perfectos para días festivos de la empresa, cierres de oficina y mantenimiento.",
|
||||
"resourceBlocks": "Bloques de Recursos",
|
||||
"resourceBlocksDesc": "Aplican a recursos específicos. Úsalos para vacaciones individuales, citas o capacitación.",
|
||||
"hardBlocks": "Bloques Duros",
|
||||
"hardBlocksDesc": "Previenen completamente las reservas durante el período bloqueado. No se pueden anular.",
|
||||
"softBlocks": "Bloques Suaves",
|
||||
"softBlocksDesc": "Muestran una advertencia pero aún permiten reservas con confirmación."
|
||||
},
|
||||
"levels": {
|
||||
"title": "Niveles de Bloque",
|
||||
"levelCol": "Nivel",
|
||||
"scopeCol": "Alcance",
|
||||
"examplesCol": "Ejemplos de Uso",
|
||||
"business": "Negocio",
|
||||
"businessScope": "Todos los recursos en tu negocio",
|
||||
"businessExamples": "Días festivos, cierres de oficina, eventos de empresa, mantenimiento",
|
||||
"resource": "Recurso",
|
||||
"resourceScope": "Un recurso específico (empleado, sala, etc.)",
|
||||
"resourceExamples": "Vacaciones, citas personales, almuerzos, capacitación",
|
||||
"additiveNote": "Los Bloques son Aditivos",
|
||||
"additiveDesc": "Ambos bloques de nivel de negocio y de recurso aplican. Si el negocio está cerrado en un día festivo, los bloques individuales de recursos no importan para ese día."
|
||||
},
|
||||
"types": {
|
||||
"title": "Tipos de Bloque: Duro vs Suave",
|
||||
"hardBlock": "Bloque Duro",
|
||||
"hardBlockDesc": "Previene completamente cualquier reserva durante el período bloqueado. Los clientes no pueden reservar y el personal no puede anular. El calendario muestra una superposición rayada roja.",
|
||||
"cannotOverride": "No se puede anular",
|
||||
"showsInBooking": "Se muestra en reservas de clientes",
|
||||
"redOverlay": "Superposición rayada roja",
|
||||
"softBlock": "Bloque Suave",
|
||||
"softBlockDesc": "Muestra una advertencia pero permite reservas con confirmación. Útil para indicar tiempos preferidos de descanso que pueden anularse si es necesario.",
|
||||
"canOverride": "Se puede anular",
|
||||
"showsWarning": "Solo muestra advertencia",
|
||||
"yellowOverlay": "Superposición punteada amarilla"
|
||||
},
|
||||
"recurrence": {
|
||||
"title": "Patrones de Recurrencia",
|
||||
"patternCol": "Patrón",
|
||||
"descriptionCol": "Descripción",
|
||||
"exampleCol": "Ejemplo",
|
||||
"oneTime": "Una vez",
|
||||
"oneTimeDesc": "Una fecha o rango de fechas específico que ocurre una vez",
|
||||
"oneTimeExample": "Dic 24-26 (descanso navideño), Feb 15 (Día del Presidente)",
|
||||
"weekly": "Semanal",
|
||||
"weeklyDesc": "Se repite en días específicos de la semana",
|
||||
"weeklyExample": "Cada sábado y domingo, Cada lunes almuerzo",
|
||||
"monthly": "Mensual",
|
||||
"monthlyDesc": "Se repite en días específicos del mes",
|
||||
"monthlyExample": "1ro de cada mes (inventario), 15 (nómina)",
|
||||
"yearly": "Anual",
|
||||
"yearlyDesc": "Se repite en un mes y día específico cada año",
|
||||
"yearlyExample": "4 de julio, 25 de diciembre, 1 de enero",
|
||||
"holiday": "Día Festivo",
|
||||
"holidayDesc": "Selecciona de días festivos populares de EE.UU. Se admite selección múltiple - cada día festivo crea su propio bloque.",
|
||||
"holidayExample": "Navidad, Acción de Gracias, Memorial Day, Día de la Independencia"
|
||||
},
|
||||
"visualization": {
|
||||
"title": "Ver Bloques de Tiempo",
|
||||
"description": "Los bloques de tiempo aparecen en múltiples vistas de la aplicación con indicadores codificados por color:",
|
||||
"colorLegend": "Leyenda de Colores",
|
||||
"businessHard": "Bloque Duro del Negocio",
|
||||
"businessSoft": "Bloque Suave del Negocio",
|
||||
"resourceHard": "Bloque Duro de Recurso",
|
||||
"resourceSoft": "Bloque Suave de Recurso",
|
||||
"schedulerOverlay": "Superposición del Calendario",
|
||||
"schedulerOverlayDesc": "Los tiempos bloqueados aparecen directamente en el calendario con indicadores visuales. Los bloques del negocio usan colores rojo/amarillo, los bloques de recursos usan púrpura/cian. Haz clic en cualquier área bloqueada en vista semanal para navegar a ese día.",
|
||||
"monthView": "Vista Mensual",
|
||||
"monthViewDesc": "Las fechas bloqueadas se muestran con fondos coloreados e indicadores de insignia. Múltiples tipos de bloque en el mismo día muestran todas las insignias aplicables.",
|
||||
"listView": "Vista de Lista",
|
||||
"listViewDesc": "Gestiona todos los bloques de tiempo en formato tabular con opciones de filtrado. Edita, activa/desactiva o elimina bloques desde aquí."
|
||||
},
|
||||
"staffAvailability": {
|
||||
"title": "Disponibilidad del Personal (Mi Disponibilidad)",
|
||||
"description": "Los miembros del personal pueden gestionar sus propios bloques de tiempo a través de la página \"Mi Disponibilidad\". Esto les permite bloquear tiempo para citas personales, vacaciones u otros compromisos.",
|
||||
"viewBusiness": "Ver bloques de nivel de negocio (solo lectura)",
|
||||
"createPersonal": "Crear y gestionar bloques de tiempo personales",
|
||||
"seeCalendar": "Ver calendario anual de su disponibilidad",
|
||||
"hardBlockPermission": "Permiso de Bloque Duro",
|
||||
"hardBlockPermissionDesc": "Por defecto, el personal solo puede crear bloques suaves. Para permitir que un miembro del personal cree bloques duros, habilita el permiso \"Puede crear bloques duros\" en la configuración de su personal."
|
||||
},
|
||||
"bestPractices": {
|
||||
"title": "Mejores Prácticas",
|
||||
"tip1Title": "Planifica días festivos con anticipación",
|
||||
"tip1Desc": "Configura los días festivos anuales al comienzo de cada año usando el tipo de recurrencia de Día Festivo.",
|
||||
"tip2Title": "Usa bloques suaves para preferencias",
|
||||
"tip2Desc": "Reserva los bloques duros para cierres absolutos. Usa bloques suaves para tiempos preferidos de descanso que podrían anularse.",
|
||||
"tip3Title": "Verifica conflictos antes de crear",
|
||||
"tip3Desc": "El sistema muestra las citas existentes que entran en conflicto con nuevos bloques. Revisa antes de confirmar.",
|
||||
"tip4Title": "Establece fechas de fin de recurrencia",
|
||||
"tip4Desc": "Para bloques recurrentes que no son permanentes, establece una fecha de fin para evitar que se extiendan indefinidamente.",
|
||||
"tip5Title": "Usa títulos descriptivos",
|
||||
"tip5Desc": "Incluye títulos claros como \"Día de Navidad\", \"Reunión de Equipo\" o \"Mantenimiento Anual\" para fácil identificación."
|
||||
},
|
||||
"quickAccess": {
|
||||
"title": "Acceso Rápido",
|
||||
"manageTimeBlocks": "Gestionar Bloques de Tiempo",
|
||||
"myAvailability": "Mi Disponibilidad"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1078,5 +1078,344 @@
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"contracts": {
|
||||
"title": "Contrats",
|
||||
"description": "Gérez les modèles de contrats et les contrats envoyés",
|
||||
"templates": "Modèles",
|
||||
"sentContracts": "Contrats Envoyés",
|
||||
"allContracts": "Tous les Contrats",
|
||||
"createTemplate": "Créer un Modèle",
|
||||
"newTemplate": "Nouveau Modèle",
|
||||
"createContract": "Créer un Contrat",
|
||||
"editTemplate": "Modifier le Modèle",
|
||||
"viewContract": "Voir le Contrat",
|
||||
"noTemplates": "Pas encore de modèles de contrats",
|
||||
"noTemplatesEmpty": "Pas encore de modèles. Créez votre premier modèle pour commencer.",
|
||||
"noTemplatesSearch": "Aucun modèle trouvé",
|
||||
"noContracts": "Pas encore de contrats",
|
||||
"noContractsEmpty": "Aucun contrat envoyé pour le moment.",
|
||||
"noContractsSearch": "Aucun contrat trouvé",
|
||||
"templateName": "Nom du Modèle",
|
||||
"templateDescription": "Description",
|
||||
"content": "Contenu",
|
||||
"contentHtml": "Contenu du Contrat (HTML)",
|
||||
"searchTemplates": "Rechercher des modèles...",
|
||||
"searchContracts": "Rechercher des contrats...",
|
||||
"all": "Tous",
|
||||
"scope": {
|
||||
"label": "Portée",
|
||||
"customer": "Niveau Client",
|
||||
"appointment": "Par Rendez-vous",
|
||||
"customerDesc": "Contrats uniques par client (ex: politique de confidentialité, conditions d'utilisation)",
|
||||
"appointmentDesc": "Signé à chaque réservation (ex: décharges de responsabilité, accords de service)"
|
||||
},
|
||||
"status": {
|
||||
"label": "Statut",
|
||||
"draft": "Brouillon",
|
||||
"active": "Actif",
|
||||
"archived": "Archivé",
|
||||
"pending": "En Attente",
|
||||
"signed": "Signé",
|
||||
"expired": "Expiré",
|
||||
"voided": "Annulé"
|
||||
},
|
||||
"table": {
|
||||
"template": "Modèle",
|
||||
"scope": "Portée",
|
||||
"status": "Statut",
|
||||
"version": "Version",
|
||||
"actions": "Actions",
|
||||
"customer": "Client",
|
||||
"contract": "Contrat",
|
||||
"created": "Créé",
|
||||
"sent": "Envoyé"
|
||||
},
|
||||
"expiresAfterDays": "Expire Après (jours)",
|
||||
"expiresAfterDaysHint": "Laisser vide pour sans expiration",
|
||||
"versionNotes": "Notes de Version",
|
||||
"versionNotesPlaceholder": "Qu'est-ce qui a changé dans cette version ?",
|
||||
"services": "Services Applicables",
|
||||
"servicesHint": "Laisser vide pour appliquer à tous les services",
|
||||
"customer": "Client",
|
||||
"appointment": "Rendez-vous",
|
||||
"service": "Service",
|
||||
"sentAt": "Envoyé",
|
||||
"signedAt": "Signé",
|
||||
"expiresAt": "Expire Le",
|
||||
"createdAt": "Créé",
|
||||
"availableVariables": "Variables Disponibles",
|
||||
"actions": {
|
||||
"send": "Envoyer le Contrat",
|
||||
"resend": "Renvoyer l'E-mail",
|
||||
"void": "Annuler le Contrat",
|
||||
"duplicate": "Dupliquer le Modèle",
|
||||
"preview": "Aperçu PDF",
|
||||
"previewFailed": "Échec du chargement de l'aperçu PDF.",
|
||||
"delete": "Supprimer",
|
||||
"edit": "Modifier",
|
||||
"viewDetails": "Voir les Détails",
|
||||
"copyLink": "Copier le Lien de Signature",
|
||||
"sendEmail": "Envoyer l'E-mail",
|
||||
"openSigningPage": "Ouvrir la Page de Signature",
|
||||
"saveChanges": "Enregistrer les Modifications"
|
||||
},
|
||||
"sendContract": {
|
||||
"title": "Envoyer le Contrat",
|
||||
"selectTemplate": "Modèle de Contrat",
|
||||
"selectTemplatePlaceholder": "Sélectionnez un modèle...",
|
||||
"selectCustomer": "Client",
|
||||
"searchCustomers": "Rechercher des clients...",
|
||||
"selectAppointment": "Sélectionner un Rendez-vous (Optionnel)",
|
||||
"selectService": "Sélectionner un Service (Optionnel)",
|
||||
"send": "Envoyer le Contrat",
|
||||
"sendImmediately": "Envoyer la demande de signature par e-mail immédiatement",
|
||||
"success": "Contrat envoyé avec succès",
|
||||
"error": "Échec de l'envoi du contrat",
|
||||
"loadingCustomers": "Chargement des clients...",
|
||||
"loadCustomersFailed": "Échec du chargement des clients",
|
||||
"noCustomers": "Aucun client disponible. Créez d'abord des clients.",
|
||||
"noMatchingCustomers": "Aucun client correspondant"
|
||||
},
|
||||
"voidContract": {
|
||||
"title": "Annuler le Contrat",
|
||||
"description": "L'annulation de ce contrat le révoquera. Le client ne pourra plus signer.",
|
||||
"reason": "Raison de l'annulation",
|
||||
"reasonPlaceholder": "Entrez la raison...",
|
||||
"confirm": "Annuler le Contrat",
|
||||
"success": "Contrat annulé avec succès",
|
||||
"error": "Échec de l'annulation du contrat"
|
||||
},
|
||||
"deleteTemplate": {
|
||||
"title": "Supprimer le Modèle",
|
||||
"description": "Êtes-vous sûr de vouloir supprimer ce modèle ? Cette action est irréversible.",
|
||||
"confirm": "Supprimer",
|
||||
"success": "Modèle supprimé avec succès",
|
||||
"error": "Échec de la suppression du modèle"
|
||||
},
|
||||
"contractDetails": {
|
||||
"title": "Détails du Contrat",
|
||||
"customer": "Client",
|
||||
"template": "Modèle",
|
||||
"status": "Statut",
|
||||
"created": "Créé",
|
||||
"contentPreview": "Aperçu du Contenu",
|
||||
"signingLink": "Lien de Signature"
|
||||
},
|
||||
"preview": {
|
||||
"title": "Aperçu du Contrat",
|
||||
"sampleData": "Utilisation de données d'exemple pour l'aperçu"
|
||||
},
|
||||
"signing": {
|
||||
"title": "Signer le Contrat",
|
||||
"businessName": "{{businessName}}",
|
||||
"contractFor": "Contrat pour {{customerName}}",
|
||||
"pleaseReview": "Veuillez examiner et signer ce contrat",
|
||||
"signerName": "Votre Nom Complet",
|
||||
"signerNamePlaceholder": "Entrez votre nom légal",
|
||||
"signerEmail": "Votre E-mail",
|
||||
"signatureLabel": "Signez Ci-dessous",
|
||||
"signaturePlaceholder": "Dessinez votre signature ici",
|
||||
"clearSignature": "Effacer",
|
||||
"agreeToTerms": "J'ai lu et j'accepte les termes et conditions décrits dans ce document. En cochant cette case, je comprends que cela constitue une signature électronique légale.",
|
||||
"consentToElectronic": "Je consens à effectuer des affaires électroniquement. Je comprends que j'ai le droit de recevoir des documents sous forme papier sur demande et peux retirer ce consentement à tout moment.",
|
||||
"submitSignature": "Signer le Contrat",
|
||||
"submitting": "Signature en cours...",
|
||||
"success": "Contrat signé avec succès !",
|
||||
"successMessage": "Vous recevrez un e-mail de confirmation avec une copie du contrat signé.",
|
||||
"error": "Échec de la signature du contrat",
|
||||
"expired": "Ce contrat a expiré",
|
||||
"alreadySigned": "Ce contrat a déjà été signé",
|
||||
"notFound": "Contrat non trouvé",
|
||||
"voided": "Ce contrat a été annulé",
|
||||
"signedBy": "Signé par {{name}} le {{date}}",
|
||||
"thankYou": "Merci d'avoir signé !",
|
||||
"loading": "Chargement du contrat...",
|
||||
"geolocationHint": "La localisation sera enregistrée pour conformité légale"
|
||||
},
|
||||
"errors": {
|
||||
"loadFailed": "Échec du chargement des contrats",
|
||||
"createFailed": "Échec de la création du contrat",
|
||||
"updateFailed": "Échec de la mise à jour du contrat",
|
||||
"deleteFailed": "Échec de la suppression du contrat",
|
||||
"sendFailed": "Échec de l'envoi du contrat",
|
||||
"voidFailed": "Échec de l'annulation du contrat"
|
||||
}
|
||||
},
|
||||
"timeBlocks": {
|
||||
"title": "Blocs de Temps",
|
||||
"subtitle": "Gérer les fermetures, jours fériés et indisponibilités des ressources",
|
||||
"addBlock": "Ajouter un Bloc",
|
||||
"businessTab": "Blocs de l'Entreprise",
|
||||
"resourceTab": "Blocs de Ressources",
|
||||
"calendarTab": "Vue Annuelle",
|
||||
"businessInfo": "Les blocs de l'entreprise s'appliquent à toutes les ressources. Utilisez-les pour les jours fériés, fermetures et événements d'entreprise.",
|
||||
"noBusinessBlocks": "Aucun Bloc d'Entreprise",
|
||||
"noBusinessBlocksDesc": "Ajoutez des jours fériés et fermetures pour empêcher les réservations pendant ces périodes.",
|
||||
"addFirstBlock": "Ajouter le Premier Bloc",
|
||||
"titleCol": "Titre",
|
||||
"typeCol": "Type",
|
||||
"patternCol": "Modèle",
|
||||
"actionsCol": "Actions",
|
||||
"resourceInfo": "Les blocs de ressources s'appliquent à du personnel ou équipement spécifique. Utilisez-les pour les vacances, maintenance ou temps personnel.",
|
||||
"noResourceBlocks": "Aucun Bloc de Ressource",
|
||||
"noResourceBlocksDesc": "Ajoutez des blocs de temps pour des ressources spécifiques afin de gérer leur disponibilité.",
|
||||
"deleteConfirmTitle": "Supprimer le Bloc de Temps ?",
|
||||
"deleteConfirmDesc": "Cette action est irréversible.",
|
||||
"blockTypes": {
|
||||
"hard": "Bloc Strict",
|
||||
"soft": "Bloc Souple"
|
||||
},
|
||||
"recurrenceTypes": {
|
||||
"none": "Ponctuel",
|
||||
"weekly": "Hebdomadaire",
|
||||
"monthly": "Mensuel",
|
||||
"yearly": "Annuel",
|
||||
"holiday": "Jour Férié"
|
||||
},
|
||||
"inactive": "Inactif",
|
||||
"activate": "Activer",
|
||||
"deactivate": "Désactiver"
|
||||
},
|
||||
"myAvailability": {
|
||||
"title": "Ma Disponibilité",
|
||||
"subtitle": "Gérer vos congés et indisponibilités",
|
||||
"noResource": "Aucune Ressource Liée",
|
||||
"noResourceDesc": "Votre compte n'est pas lié à une ressource. Veuillez contacter votre responsable pour configurer votre disponibilité.",
|
||||
"addBlock": "Bloquer du Temps",
|
||||
"businessBlocks": "Fermetures de l'Entreprise",
|
||||
"businessBlocksInfo": "Ces blocs sont définis par votre entreprise et s'appliquent à tous.",
|
||||
"myBlocks": "Mes Blocs de Temps",
|
||||
"noBlocks": "Aucun Bloc de Temps",
|
||||
"noBlocksDesc": "Ajoutez des blocs de temps pour les vacances, pauses déjeuner ou tout temps dont vous avez besoin.",
|
||||
"addFirstBlock": "Ajouter le Premier Bloc",
|
||||
"titleCol": "Titre",
|
||||
"typeCol": "Type",
|
||||
"patternCol": "Modèle",
|
||||
"actionsCol": "Actions",
|
||||
"editBlock": "Modifier le Bloc de Temps",
|
||||
"createBlock": "Bloquer du Temps",
|
||||
"create": "Bloquer",
|
||||
"deleteConfirmTitle": "Supprimer le Bloc de Temps ?",
|
||||
"deleteConfirmDesc": "Cette action est irréversible.",
|
||||
"form": {
|
||||
"title": "Titre",
|
||||
"description": "Description",
|
||||
"blockType": "Type de Bloc",
|
||||
"recurrenceType": "Récurrence",
|
||||
"allDay": "Journée entière",
|
||||
"startDate": "Date de Début",
|
||||
"endDate": "Date de Fin",
|
||||
"startTime": "Heure de Début",
|
||||
"endTime": "Heure de Fin",
|
||||
"daysOfWeek": "Jours de la Semaine",
|
||||
"daysOfMonth": "Jours du Mois"
|
||||
}
|
||||
},
|
||||
"helpTimeBlocks": {
|
||||
"title": "Guide des Blocs de Temps",
|
||||
"subtitle": "Apprenez à bloquer du temps pour les fermetures, jours fériés et indisponibilités",
|
||||
"overview": {
|
||||
"title": "Qu'est-ce que les Blocs de Temps ?",
|
||||
"description": "Les blocs de temps vous permettent de marquer des dates, heures ou périodes récurrentes spécifiques comme indisponibles pour les réservations. Utilisez-les pour gérer les jours fériés, fermetures d'entreprise, vacances du personnel, fenêtres de maintenance et plus.",
|
||||
"businessBlocks": "Blocs de l'Entreprise",
|
||||
"businessBlocksDesc": "S'appliquent à toutes les ressources. Parfaits pour les jours fériés, fermetures de bureau et maintenance.",
|
||||
"resourceBlocks": "Blocs de Ressources",
|
||||
"resourceBlocksDesc": "S'appliquent à des ressources spécifiques. Utilisez-les pour les vacances individuelles, rendez-vous ou formations.",
|
||||
"hardBlocks": "Blocs Stricts",
|
||||
"hardBlocksDesc": "Empêchent complètement les réservations pendant la période bloquée. Ne peuvent pas être remplacés.",
|
||||
"softBlocks": "Blocs Souples",
|
||||
"softBlocksDesc": "Affichent un avertissement mais permettent les réservations avec confirmation."
|
||||
},
|
||||
"levels": {
|
||||
"title": "Niveaux de Bloc",
|
||||
"levelCol": "Niveau",
|
||||
"scopeCol": "Portée",
|
||||
"examplesCol": "Exemples d'Utilisation",
|
||||
"business": "Entreprise",
|
||||
"businessScope": "Toutes les ressources de votre entreprise",
|
||||
"businessExamples": "Jours fériés, fermetures de bureau, événements d'entreprise, maintenance",
|
||||
"resource": "Ressource",
|
||||
"resourceScope": "Une ressource spécifique (employé, salle, etc.)",
|
||||
"resourceExamples": "Vacances, rendez-vous personnels, pauses déjeuner, formation",
|
||||
"additiveNote": "Les Blocs sont Additifs",
|
||||
"additiveDesc": "Les blocs de niveau entreprise et ressource s'appliquent tous les deux. Si l'entreprise est fermée un jour férié, les blocs individuels de ressources n'importent pas pour ce jour."
|
||||
},
|
||||
"types": {
|
||||
"title": "Types de Bloc : Strict vs Souple",
|
||||
"hardBlock": "Bloc Strict",
|
||||
"hardBlockDesc": "Empêche complètement toute réservation pendant la période bloquée. Les clients ne peuvent pas réserver et le personnel ne peut pas remplacer. Le calendrier affiche une superposition rayée rouge.",
|
||||
"cannotOverride": "Ne peut pas être remplacé",
|
||||
"showsInBooking": "Affiché dans les réservations clients",
|
||||
"redOverlay": "Superposition rayée rouge",
|
||||
"softBlock": "Bloc Souple",
|
||||
"softBlockDesc": "Affiche un avertissement mais permet les réservations avec confirmation. Utile pour indiquer les temps de repos préférés qui peuvent être remplacés si nécessaire.",
|
||||
"canOverride": "Peut être remplacé",
|
||||
"showsWarning": "Affiche uniquement un avertissement",
|
||||
"yellowOverlay": "Superposition pointillée jaune"
|
||||
},
|
||||
"recurrence": {
|
||||
"title": "Modèles de Récurrence",
|
||||
"patternCol": "Modèle",
|
||||
"descriptionCol": "Description",
|
||||
"exampleCol": "Exemple",
|
||||
"oneTime": "Ponctuel",
|
||||
"oneTimeDesc": "Une date ou plage de dates spécifique qui se produit une fois",
|
||||
"oneTimeExample": "24-26 Déc (vacances de Noël), 15 Fév (Jour des Présidents)",
|
||||
"weekly": "Hebdomadaire",
|
||||
"weeklyDesc": "Se répète certains jours de la semaine",
|
||||
"weeklyExample": "Chaque samedi et dimanche, Chaque lundi déjeuner",
|
||||
"monthly": "Mensuel",
|
||||
"monthlyDesc": "Se répète certains jours du mois",
|
||||
"monthlyExample": "1er de chaque mois (inventaire), 15 (paie)",
|
||||
"yearly": "Annuel",
|
||||
"yearlyDesc": "Se répète à un mois et jour spécifique chaque année",
|
||||
"yearlyExample": "4 juillet, 25 décembre, 1er janvier",
|
||||
"holiday": "Jour Férié",
|
||||
"holidayDesc": "Sélectionnez parmi les jours fériés américains populaires. Sélection multiple supportée - chaque jour férié crée son propre bloc.",
|
||||
"holidayExample": "Noël, Thanksgiving, Memorial Day, Jour de l'Indépendance"
|
||||
},
|
||||
"visualization": {
|
||||
"title": "Afficher les Blocs de Temps",
|
||||
"description": "Les blocs de temps apparaissent dans plusieurs vues de l'application avec des indicateurs colorés :",
|
||||
"colorLegend": "Légende des Couleurs",
|
||||
"businessHard": "Bloc Strict de l'Entreprise",
|
||||
"businessSoft": "Bloc Souple de l'Entreprise",
|
||||
"resourceHard": "Bloc Strict de Ressource",
|
||||
"resourceSoft": "Bloc Souple de Ressource",
|
||||
"schedulerOverlay": "Superposition du Calendrier",
|
||||
"schedulerOverlayDesc": "Les temps bloqués apparaissent directement sur le calendrier avec des indicateurs visuels. Les blocs d'entreprise utilisent des couleurs rouge/jaune, les blocs de ressources utilisent violet/cyan. Cliquez sur une zone bloquée en vue semaine pour naviguer vers ce jour.",
|
||||
"monthView": "Vue Mensuelle",
|
||||
"monthViewDesc": "Les dates bloquées s'affichent avec des fonds colorés et des indicateurs de badge. Plusieurs types de blocs le même jour affichent tous les badges applicables.",
|
||||
"listView": "Vue Liste",
|
||||
"listViewDesc": "Gérez tous les blocs de temps dans un format tabulaire avec des options de filtrage. Modifiez, activez/désactivez ou supprimez des blocs ici."
|
||||
},
|
||||
"staffAvailability": {
|
||||
"title": "Disponibilité du Personnel (Ma Disponibilité)",
|
||||
"description": "Les membres du personnel peuvent gérer leurs propres blocs de temps via la page \"Ma Disponibilité\". Cela leur permet de bloquer du temps pour des rendez-vous personnels, vacances ou autres engagements.",
|
||||
"viewBusiness": "Voir les blocs de niveau entreprise (lecture seule)",
|
||||
"createPersonal": "Créer et gérer des blocs de temps personnels",
|
||||
"seeCalendar": "Voir le calendrier annuel de leur disponibilité",
|
||||
"hardBlockPermission": "Permission de Bloc Strict",
|
||||
"hardBlockPermissionDesc": "Par défaut, le personnel ne peut créer que des blocs souples. Pour permettre à un membre du personnel de créer des blocs stricts, activez la permission \"Peut créer des blocs stricts\" dans les paramètres de son profil."
|
||||
},
|
||||
"bestPractices": {
|
||||
"title": "Bonnes Pratiques",
|
||||
"tip1Title": "Planifiez les jours fériés à l'avance",
|
||||
"tip1Desc": "Configurez les jours fériés annuels au début de chaque année en utilisant le type de récurrence Jour Férié.",
|
||||
"tip2Title": "Utilisez des blocs souples pour les préférences",
|
||||
"tip2Desc": "Réservez les blocs stricts pour les fermetures absolues. Utilisez des blocs souples pour les temps de repos préférés qui pourraient être remplacés.",
|
||||
"tip3Title": "Vérifiez les conflits avant de créer",
|
||||
"tip3Desc": "Le système affiche les rendez-vous existants qui entrent en conflit avec les nouveaux blocs. Vérifiez avant de confirmer.",
|
||||
"tip4Title": "Définissez des dates de fin de récurrence",
|
||||
"tip4Desc": "Pour les blocs récurrents qui ne sont pas permanents, définissez une date de fin pour éviter qu'ils ne s'étendent indéfiniment.",
|
||||
"tip5Title": "Utilisez des titres descriptifs",
|
||||
"tip5Desc": "Incluez des titres clairs comme \"Jour de Noël\", \"Réunion d'Équipe\" ou \"Maintenance Annuelle\" pour une identification facile."
|
||||
},
|
||||
"quickAccess": {
|
||||
"title": "Accès Rapide",
|
||||
"manageTimeBlocks": "Gérer les Blocs de Temps",
|
||||
"myAvailability": "Ma Disponibilité"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -327,7 +327,7 @@ const Contracts: React.FC = () => {
|
||||
};
|
||||
return (
|
||||
<span className={`px-2 py-1 rounded-full text-xs font-medium ${styles[scope]}`}>
|
||||
{scope === 'CUSTOMER' ? 'Customer-Level' : 'Per Appointment'}
|
||||
{t(`contracts.scope.${scope.toLowerCase()}`)}
|
||||
</span>
|
||||
);
|
||||
};
|
||||
@@ -385,7 +385,7 @@ const Contracts: React.FC = () => {
|
||||
className="px-3 py-1.5 bg-blue-600 text-white text-sm rounded-lg hover:bg-blue-700 transition-colors flex items-center gap-2"
|
||||
>
|
||||
<Plus size={16} />
|
||||
New Template
|
||||
{t('contracts.newTemplate')}
|
||||
</button>
|
||||
</button>
|
||||
|
||||
@@ -399,7 +399,7 @@ const Contracts: React.FC = () => {
|
||||
type="text"
|
||||
value={templatesSearch}
|
||||
onChange={(e) => setTemplatesSearch(e.target.value)}
|
||||
placeholder="Search templates..."
|
||||
placeholder={t('contracts.searchTemplates')}
|
||||
className="w-full pl-10 pr-4 py-2 border border-gray-300 dark:border-gray-600 rounded-lg focus:ring-2 focus:ring-blue-500 focus:border-transparent dark:bg-gray-700 dark:text-white text-sm"
|
||||
/>
|
||||
</div>
|
||||
@@ -414,7 +414,7 @@ const Contracts: React.FC = () => {
|
||||
: 'text-gray-600 hover:bg-gray-100 dark:text-gray-400 dark:hover:bg-gray-700'
|
||||
}`}
|
||||
>
|
||||
{tab === 'all' ? 'All' : tab.charAt(0) + tab.slice(1).toLowerCase()}
|
||||
{tab === 'all' ? t('contracts.all') : t(`contracts.status.${tab.toLowerCase()}`)}
|
||||
<span className="ml-1 text-xs">({templateStatusCounts[tab]})</span>
|
||||
</button>
|
||||
))}
|
||||
@@ -425,18 +425,18 @@ const Contracts: React.FC = () => {
|
||||
{filteredTemplates.length === 0 ? (
|
||||
<div className="py-8 text-center text-gray-500 dark:text-gray-400">
|
||||
<FileSignature className="w-12 h-12 mx-auto mb-3 text-gray-300 dark:text-gray-600" />
|
||||
<p>{templatesSearch ? 'No templates found' : 'No templates yet. Create your first template to get started.'}</p>
|
||||
<p>{templatesSearch ? t('contracts.noTemplatesSearch') : t('contracts.noTemplatesEmpty')}</p>
|
||||
</div>
|
||||
) : (
|
||||
<div className="overflow-x-auto">
|
||||
<table className="min-w-full divide-y divide-gray-200 dark:divide-gray-700">
|
||||
<thead>
|
||||
<tr>
|
||||
<th className="px-4 py-2 text-left text-xs font-medium text-gray-500 dark:text-gray-400 uppercase">Template</th>
|
||||
<th className="px-4 py-2 text-left text-xs font-medium text-gray-500 dark:text-gray-400 uppercase">Scope</th>
|
||||
<th className="px-4 py-2 text-left text-xs font-medium text-gray-500 dark:text-gray-400 uppercase">Status</th>
|
||||
<th className="px-4 py-2 text-left text-xs font-medium text-gray-500 dark:text-gray-400 uppercase">Version</th>
|
||||
<th className="px-4 py-2 text-right text-xs font-medium text-gray-500 dark:text-gray-400 uppercase">Actions</th>
|
||||
<th className="px-4 py-2 text-left text-xs font-medium text-gray-500 dark:text-gray-400 uppercase">{t('contracts.table.template')}</th>
|
||||
<th className="px-4 py-2 text-left text-xs font-medium text-gray-500 dark:text-gray-400 uppercase">{t('contracts.table.scope')}</th>
|
||||
<th className="px-4 py-2 text-left text-xs font-medium text-gray-500 dark:text-gray-400 uppercase">{t('contracts.table.status')}</th>
|
||||
<th className="px-4 py-2 text-left text-xs font-medium text-gray-500 dark:text-gray-400 uppercase">{t('contracts.table.version')}</th>
|
||||
<th className="px-4 py-2 text-right text-xs font-medium text-gray-500 dark:text-gray-400 uppercase">{t('contracts.table.actions')}</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody className="divide-y divide-gray-200 dark:divide-gray-700">
|
||||
@@ -461,25 +461,25 @@ const Contracts: React.FC = () => {
|
||||
window.open(URL.createObjectURL(blob), '_blank');
|
||||
} catch (error) {
|
||||
console.error('Failed to load PDF preview:', error);
|
||||
alert('Failed to load PDF preview.');
|
||||
alert(t('contracts.actions.previewFailed'));
|
||||
}
|
||||
}}
|
||||
className="p-1.5 text-gray-500 hover:text-blue-600 hover:bg-gray-100 dark:hover:bg-gray-700 rounded transition-colors"
|
||||
title="Preview PDF"
|
||||
title={t('contracts.actions.preview')}
|
||||
>
|
||||
<Eye size={16} />
|
||||
</button>
|
||||
<button
|
||||
onClick={() => openEditTemplateModal(template)}
|
||||
className="p-1.5 text-gray-500 hover:text-blue-600 hover:bg-gray-100 dark:hover:bg-gray-700 rounded transition-colors"
|
||||
title="Edit"
|
||||
title={t('contracts.actions.edit')}
|
||||
>
|
||||
<Pencil size={16} />
|
||||
</button>
|
||||
<button
|
||||
onClick={() => setDeleteConfirmId(template.id)}
|
||||
className="p-1.5 text-gray-500 hover:text-red-600 hover:bg-gray-100 dark:hover:bg-gray-700 rounded transition-colors"
|
||||
title="Delete"
|
||||
title={t('contracts.actions.delete')}
|
||||
>
|
||||
<Trash2 size={16} />
|
||||
</button>
|
||||
@@ -504,7 +504,7 @@ const Contracts: React.FC = () => {
|
||||
<div className="flex items-center gap-3">
|
||||
{contractsExpanded ? <ChevronDown size={20} /> : <ChevronRight size={20} />}
|
||||
<h2 className="text-xl font-semibold text-gray-900 dark:text-white">
|
||||
Sent Contracts
|
||||
{t('contracts.sentContracts')}
|
||||
</h2>
|
||||
<span className="px-2 py-0.5 rounded-full bg-gray-100 dark:bg-gray-700 text-sm">
|
||||
{contracts?.length || 0}
|
||||
@@ -519,7 +519,7 @@ const Contracts: React.FC = () => {
|
||||
className="px-3 py-1.5 bg-blue-600 text-white text-sm rounded-lg hover:bg-blue-700 transition-colors flex items-center gap-2 disabled:opacity-50 disabled:cursor-not-allowed"
|
||||
>
|
||||
<Plus size={16} />
|
||||
Send Contract
|
||||
{t('contracts.actions.send')}
|
||||
</button>
|
||||
</button>
|
||||
|
||||
@@ -533,7 +533,7 @@ const Contracts: React.FC = () => {
|
||||
type="text"
|
||||
value={contractsSearch}
|
||||
onChange={(e) => setContractsSearch(e.target.value)}
|
||||
placeholder="Search contracts..."
|
||||
placeholder={t('contracts.searchContracts')}
|
||||
className="w-full pl-10 pr-4 py-2 border border-gray-300 dark:border-gray-600 rounded-lg focus:ring-2 focus:ring-blue-500 focus:border-transparent dark:bg-gray-700 dark:text-white text-sm"
|
||||
/>
|
||||
</div>
|
||||
@@ -548,7 +548,7 @@ const Contracts: React.FC = () => {
|
||||
: 'text-gray-600 hover:bg-gray-100 dark:text-gray-400 dark:hover:bg-gray-700'
|
||||
}`}
|
||||
>
|
||||
{tab === 'all' ? 'All' : tab.charAt(0) + tab.slice(1).toLowerCase()}
|
||||
{tab === 'all' ? t('contracts.all') : t(`contracts.status.${tab.toLowerCase()}`)}
|
||||
<span className="ml-1 text-xs">({contractStatusCounts[tab]})</span>
|
||||
</button>
|
||||
))}
|
||||
@@ -559,18 +559,18 @@ const Contracts: React.FC = () => {
|
||||
{filteredContracts.length === 0 ? (
|
||||
<div className="py-8 text-center text-gray-500 dark:text-gray-400">
|
||||
<FileSignature className="w-12 h-12 mx-auto mb-3 text-gray-300 dark:text-gray-600" />
|
||||
<p>{contractsSearch ? 'No contracts found' : 'No contracts sent yet.'}</p>
|
||||
<p>{contractsSearch ? t('contracts.noContractsSearch') : t('contracts.noContractsEmpty')}</p>
|
||||
</div>
|
||||
) : (
|
||||
<div className="overflow-x-auto">
|
||||
<table className="min-w-full divide-y divide-gray-200 dark:divide-gray-700">
|
||||
<thead>
|
||||
<tr>
|
||||
<th className="px-4 py-2 text-left text-xs font-medium text-gray-500 dark:text-gray-400 uppercase">Customer</th>
|
||||
<th className="px-4 py-2 text-left text-xs font-medium text-gray-500 dark:text-gray-400 uppercase">Contract</th>
|
||||
<th className="px-4 py-2 text-left text-xs font-medium text-gray-500 dark:text-gray-400 uppercase">Status</th>
|
||||
<th className="px-4 py-2 text-left text-xs font-medium text-gray-500 dark:text-gray-400 uppercase">Created</th>
|
||||
<th className="px-4 py-2 text-right text-xs font-medium text-gray-500 dark:text-gray-400 uppercase">Actions</th>
|
||||
<th className="px-4 py-2 text-left text-xs font-medium text-gray-500 dark:text-gray-400 uppercase">{t('contracts.table.customer')}</th>
|
||||
<th className="px-4 py-2 text-left text-xs font-medium text-gray-500 dark:text-gray-400 uppercase">{t('contracts.table.contract')}</th>
|
||||
<th className="px-4 py-2 text-left text-xs font-medium text-gray-500 dark:text-gray-400 uppercase">{t('contracts.table.status')}</th>
|
||||
<th className="px-4 py-2 text-left text-xs font-medium text-gray-500 dark:text-gray-400 uppercase">{t('contracts.table.created')}</th>
|
||||
<th className="px-4 py-2 text-right text-xs font-medium text-gray-500 dark:text-gray-400 uppercase">{t('contracts.table.actions')}</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody className="divide-y divide-gray-200 dark:divide-gray-700">
|
||||
@@ -583,42 +583,42 @@ const Contracts: React.FC = () => {
|
||||
<td className="px-4 py-3">
|
||||
<div className="font-medium text-gray-900 dark:text-white">{contract.template_name}</div>
|
||||
{contract.sent_at && (
|
||||
<div className="text-xs text-gray-500 dark:text-gray-400">Sent: {formatDate(contract.sent_at)}</div>
|
||||
<div className="text-xs text-gray-500 dark:text-gray-400">{t('contracts.sentAt')}: {formatDate(contract.sent_at)}</div>
|
||||
)}
|
||||
</td>
|
||||
<td className="px-4 py-3">
|
||||
{getContractStatusBadge(contract.status)}
|
||||
{contract.signed_at && (
|
||||
<div className="text-xs text-gray-500 dark:text-gray-400 mt-1">Signed: {formatDate(contract.signed_at)}</div>
|
||||
<div className="text-xs text-gray-500 dark:text-gray-400 mt-1">{t('contracts.signedAt')}: {formatDate(contract.signed_at)}</div>
|
||||
)}
|
||||
</td>
|
||||
<td className="px-4 py-3 text-sm text-gray-500 dark:text-gray-400">{formatDate(contract.created_at)}</td>
|
||||
<td className="px-4 py-3 text-right">
|
||||
<div className="flex items-center justify-end gap-1">
|
||||
<button onClick={() => setViewingContract(contract)} className="p-1.5 text-gray-500 hover:text-blue-600 hover:bg-gray-100 dark:hover:bg-gray-700 rounded transition-colors" title="View Details">
|
||||
<button onClick={() => setViewingContract(contract)} className="p-1.5 text-gray-500 hover:text-blue-600 hover:bg-gray-100 dark:hover:bg-gray-700 rounded transition-colors" title={t('contracts.actions.viewDetails')}>
|
||||
<Eye size={16} />
|
||||
</button>
|
||||
{contract.status === 'PENDING' && (
|
||||
<>
|
||||
<button onClick={() => copySigningLink(contract)} className="p-1.5 text-gray-500 hover:text-blue-600 hover:bg-gray-100 dark:hover:bg-gray-700 rounded transition-colors" title="Copy Signing Link">
|
||||
<button onClick={() => copySigningLink(contract)} className="p-1.5 text-gray-500 hover:text-blue-600 hover:bg-gray-100 dark:hover:bg-gray-700 rounded transition-colors" title={t('contracts.actions.copyLink')}>
|
||||
<Copy size={16} />
|
||||
</button>
|
||||
{!contract.sent_at ? (
|
||||
<button onClick={() => handleSendContract(contract.id)} disabled={sendContract.isPending} className="p-1.5 text-gray-500 hover:text-green-600 hover:bg-gray-100 dark:hover:bg-gray-700 rounded transition-colors" title="Send Email">
|
||||
<button onClick={() => handleSendContract(contract.id)} disabled={sendContract.isPending} className="p-1.5 text-gray-500 hover:text-green-600 hover:bg-gray-100 dark:hover:bg-gray-700 rounded transition-colors" title={t('contracts.actions.sendEmail')}>
|
||||
<Send size={16} />
|
||||
</button>
|
||||
) : (
|
||||
<button onClick={() => handleResendContract(contract.id)} disabled={resendContract.isPending} className="p-1.5 text-gray-500 hover:text-green-600 hover:bg-gray-100 dark:hover:bg-gray-700 rounded transition-colors" title="Resend Email">
|
||||
<button onClick={() => handleResendContract(contract.id)} disabled={resendContract.isPending} className="p-1.5 text-gray-500 hover:text-green-600 hover:bg-gray-100 dark:hover:bg-gray-700 rounded transition-colors" title={t('contracts.actions.resend')}>
|
||||
<RefreshCw size={16} />
|
||||
</button>
|
||||
)}
|
||||
<button onClick={() => setVoidingContract(contract)} className="p-1.5 text-gray-500 hover:text-red-600 hover:bg-gray-100 dark:hover:bg-gray-700 rounded transition-colors" title="Void Contract">
|
||||
<button onClick={() => setVoidingContract(contract)} className="p-1.5 text-gray-500 hover:text-red-600 hover:bg-gray-100 dark:hover:bg-gray-700 rounded transition-colors" title={t('contracts.actions.void')}>
|
||||
<Ban size={16} />
|
||||
</button>
|
||||
</>
|
||||
)}
|
||||
{contract.public_token && contract.status === 'PENDING' && (
|
||||
<a href={`/sign/${contract.public_token}`} target="_blank" rel="noopener noreferrer" className="p-1.5 text-gray-500 hover:text-blue-600 hover:bg-gray-100 dark:hover:bg-gray-700 rounded transition-colors" title="Open Signing Page">
|
||||
<a href={`/sign/${contract.public_token}`} target="_blank" rel="noopener noreferrer" className="p-1.5 text-gray-500 hover:text-blue-600 hover:bg-gray-100 dark:hover:bg-gray-700 rounded transition-colors" title={t('contracts.actions.openSigningPage')}>
|
||||
<ExternalLink size={16} />
|
||||
</a>
|
||||
)}
|
||||
@@ -639,23 +639,23 @@ const Contracts: React.FC = () => {
|
||||
<div className="fixed inset-0 z-50 bg-black/50 flex items-center justify-center p-4" onClick={() => setIsCreateContractModalOpen(false)}>
|
||||
<div className="bg-white dark:bg-gray-800 rounded-lg shadow-xl max-w-lg w-full max-h-[90vh] overflow-y-auto" onClick={(e) => e.stopPropagation()}>
|
||||
<div className="flex items-center justify-between p-4 border-b border-gray-200 dark:border-gray-700">
|
||||
<h2 className="text-xl font-semibold text-gray-900 dark:text-white">Send Contract</h2>
|
||||
<h2 className="text-xl font-semibold text-gray-900 dark:text-white">{t('contracts.sendContract.title')}</h2>
|
||||
<button onClick={() => setIsCreateContractModalOpen(false)} className="p-2 text-gray-500 hover:text-gray-700 dark:hover:text-gray-300">
|
||||
<X size={20} />
|
||||
</button>
|
||||
</div>
|
||||
<form onSubmit={handleCreateContract} className="p-6 space-y-6">
|
||||
<div>
|
||||
<label className="block text-sm font-medium text-gray-700 dark:text-gray-300 mb-1">Contract Template *</label>
|
||||
<label className="block text-sm font-medium text-gray-700 dark:text-gray-300 mb-1">{t('contracts.sendContract.selectTemplate')} *</label>
|
||||
<select value={selectedTemplateId} onChange={(e) => setSelectedTemplateId(e.target.value)} className="w-full px-4 py-2 border border-gray-300 dark:border-gray-600 rounded-lg focus:ring-2 focus:ring-blue-500 focus:border-transparent dark:bg-gray-700 dark:text-white" required>
|
||||
<option value="">Select a template...</option>
|
||||
<option value="">{t('contracts.sendContract.selectTemplatePlaceholder')}</option>
|
||||
{activeTemplates?.map((template) => (
|
||||
<option key={template.id} value={template.id}>{template.name} (v{template.version})</option>
|
||||
))}
|
||||
</select>
|
||||
</div>
|
||||
<div>
|
||||
<label className="block text-sm font-medium text-gray-700 dark:text-gray-300 mb-1">Customer *</label>
|
||||
<label className="block text-sm font-medium text-gray-700 dark:text-gray-300 mb-1">{t('contracts.sendContract.selectCustomer')} *</label>
|
||||
<div className="relative" ref={customerDropdownRef}>
|
||||
{selectedCustomerId ? (
|
||||
<div className="flex items-center gap-2 w-full px-4 py-2 border border-gray-300 dark:border-gray-600 rounded-lg bg-gray-50 dark:bg-gray-700">
|
||||
@@ -669,7 +669,7 @@ const Contracts: React.FC = () => {
|
||||
<>
|
||||
<div className="relative">
|
||||
<Search size={16} className="absolute left-3 top-1/2 -translate-y-1/2 text-gray-400" />
|
||||
<input type="text" value={customerSearchTerm} onChange={(e) => { setCustomerSearchTerm(e.target.value); setIsCustomerDropdownOpen(true); }} onFocus={() => setIsCustomerDropdownOpen(true)} placeholder="Search customers..." className="w-full pl-10 pr-10 py-2 border border-gray-300 dark:border-gray-600 rounded-lg focus:ring-2 focus:ring-blue-500 focus:border-transparent dark:bg-gray-700 dark:text-white" />
|
||||
<input type="text" value={customerSearchTerm} onChange={(e) => { setCustomerSearchTerm(e.target.value); setIsCustomerDropdownOpen(true); }} onFocus={() => setIsCustomerDropdownOpen(true)} placeholder={t('contracts.sendContract.searchCustomers')} className="w-full pl-10 pr-10 py-2 border border-gray-300 dark:border-gray-600 rounded-lg focus:ring-2 focus:ring-blue-500 focus:border-transparent dark:bg-gray-700 dark:text-white" />
|
||||
<ChevronDown size={16} className={`absolute right-3 top-1/2 -translate-y-1/2 text-gray-400 transition-transform ${isCustomerDropdownOpen ? 'rotate-180' : ''}`} />
|
||||
</div>
|
||||
{isCustomerDropdownOpen && (
|
||||
@@ -677,15 +677,15 @@ const Contracts: React.FC = () => {
|
||||
{customersLoading ? (
|
||||
<div className="px-4 py-3 text-sm text-gray-500 dark:text-gray-400 flex items-center gap-2">
|
||||
<Loader2 size={14} className="animate-spin" />
|
||||
Loading customers...
|
||||
{t('contracts.sendContract.loadingCustomers')}
|
||||
</div>
|
||||
) : customersError ? (
|
||||
<div className="px-4 py-3 text-sm text-red-500 dark:text-red-400">
|
||||
Failed to load customers
|
||||
{t('contracts.sendContract.loadCustomersFailed')}
|
||||
</div>
|
||||
) : filteredCustomers.length === 0 ? (
|
||||
<div className="px-4 py-3 text-sm text-gray-500 dark:text-gray-400">
|
||||
{customers.length === 0 ? 'No customers available. Create customers first.' : 'No matching customers'}
|
||||
{customers.length === 0 ? t('contracts.sendContract.noCustomers') : t('contracts.sendContract.noMatchingCustomers')}
|
||||
</div>
|
||||
) : (
|
||||
filteredCustomers.map((customer) => (
|
||||
@@ -708,13 +708,13 @@ const Contracts: React.FC = () => {
|
||||
</div>
|
||||
<div className="flex items-center gap-3">
|
||||
<input type="checkbox" id="sendEmail" checked={sendEmailOnCreate} onChange={(e) => setSendEmailOnCreate(e.target.checked)} className="w-4 h-4 text-blue-600 border-gray-300 rounded focus:ring-blue-500" />
|
||||
<label htmlFor="sendEmail" className="text-sm text-gray-700 dark:text-gray-300">Send signing request email immediately</label>
|
||||
<label htmlFor="sendEmail" className="text-sm text-gray-700 dark:text-gray-300">{t('contracts.sendContract.sendImmediately')}</label>
|
||||
</div>
|
||||
<div className="flex justify-end gap-3 pt-4 border-t border-gray-200 dark:border-gray-700">
|
||||
<button type="button" onClick={() => setIsCreateContractModalOpen(false)} className="px-4 py-2 text-gray-700 dark:text-gray-300 bg-gray-100 dark:bg-gray-700 rounded-lg hover:bg-gray-200 dark:hover:bg-gray-600 transition-colors">Cancel</button>
|
||||
<button type="button" onClick={() => setIsCreateContractModalOpen(false)} className="px-4 py-2 text-gray-700 dark:text-gray-300 bg-gray-100 dark:bg-gray-700 rounded-lg hover:bg-gray-200 dark:hover:bg-gray-600 transition-colors">{t('common.cancel')}</button>
|
||||
<button type="submit" disabled={createContract.isPending || !selectedTemplateId || !selectedCustomerId} className="px-4 py-2 bg-blue-600 text-white rounded-lg hover:bg-blue-700 transition-colors disabled:opacity-50 flex items-center gap-2">
|
||||
{createContract.isPending && <Loader2 className="w-4 h-4 animate-spin" />}
|
||||
Send Contract
|
||||
{t('contracts.sendContract.send')}
|
||||
</button>
|
||||
</div>
|
||||
</form>
|
||||
@@ -727,7 +727,7 @@ const Contracts: React.FC = () => {
|
||||
<div className="fixed inset-0 z-50 bg-black/50 flex items-center justify-center p-4" onClick={() => setViewingContract(null)}>
|
||||
<div className="bg-white dark:bg-gray-800 rounded-lg shadow-xl max-w-3xl w-full max-h-[90vh] overflow-y-auto" onClick={(e) => e.stopPropagation()}>
|
||||
<div className="flex items-center justify-between p-4 border-b border-gray-200 dark:border-gray-700">
|
||||
<h2 className="text-xl font-semibold text-gray-900 dark:text-white">Contract Details</h2>
|
||||
<h2 className="text-xl font-semibold text-gray-900 dark:text-white">{t('contracts.contractDetails.title')}</h2>
|
||||
<button onClick={() => setViewingContract(null)} className="p-2 text-gray-500 hover:text-gray-700 dark:hover:text-gray-300">
|
||||
<X size={20} />
|
||||
</button>
|
||||
@@ -735,32 +735,32 @@ const Contracts: React.FC = () => {
|
||||
<div className="p-6 space-y-6">
|
||||
<div className="grid grid-cols-2 gap-4">
|
||||
<div>
|
||||
<p className="text-sm text-gray-500 dark:text-gray-400">Customer</p>
|
||||
<p className="text-sm text-gray-500 dark:text-gray-400">{t('contracts.contractDetails.customer')}</p>
|
||||
<p className="font-medium text-gray-900 dark:text-white">{viewingContract.customer_name}</p>
|
||||
<p className="text-sm text-gray-600 dark:text-gray-400">{viewingContract.customer_email}</p>
|
||||
</div>
|
||||
<div>
|
||||
<p className="text-sm text-gray-500 dark:text-gray-400">Template</p>
|
||||
<p className="text-sm text-gray-500 dark:text-gray-400">{t('contracts.contractDetails.template')}</p>
|
||||
<p className="font-medium text-gray-900 dark:text-white">{viewingContract.template_name} (v{viewingContract.template_version})</p>
|
||||
</div>
|
||||
<div>
|
||||
<p className="text-sm text-gray-500 dark:text-gray-400">Status</p>
|
||||
<p className="text-sm text-gray-500 dark:text-gray-400">{t('contracts.contractDetails.status')}</p>
|
||||
{getContractStatusBadge(viewingContract.status)}
|
||||
</div>
|
||||
<div>
|
||||
<p className="text-sm text-gray-500 dark:text-gray-400">Created</p>
|
||||
<p className="text-sm text-gray-500 dark:text-gray-400">{t('contracts.contractDetails.created')}</p>
|
||||
<p className="text-gray-900 dark:text-white">{formatDate(viewingContract.created_at)}</p>
|
||||
</div>
|
||||
</div>
|
||||
{viewingContract.content && (
|
||||
<div>
|
||||
<p className="text-sm text-gray-500 dark:text-gray-400 mb-2">Content Preview</p>
|
||||
<p className="text-sm text-gray-500 dark:text-gray-400 mb-2">{t('contracts.contractDetails.contentPreview')}</p>
|
||||
<div className="prose dark:prose-invert max-w-none p-4 border border-gray-200 dark:border-gray-700 rounded-lg bg-gray-50 dark:bg-gray-900 max-h-64 overflow-y-auto" dangerouslySetInnerHTML={{ __html: viewingContract.content }} />
|
||||
</div>
|
||||
)}
|
||||
{viewingContract.public_token && viewingContract.status === 'PENDING' && (
|
||||
<div className="p-4 bg-blue-50 dark:bg-blue-900/20 rounded-lg">
|
||||
<p className="text-sm font-medium text-blue-700 dark:text-blue-300 mb-2">Signing Link</p>
|
||||
<p className="text-sm font-medium text-blue-700 dark:text-blue-300 mb-2">{t('contracts.contractDetails.signingLink')}</p>
|
||||
<div className="flex items-center gap-2">
|
||||
<input type="text" readOnly value={`${window.location.origin}/sign/${viewingContract.public_token}`} className="flex-1 px-3 py-2 text-sm bg-white dark:bg-gray-800 border border-blue-200 dark:border-blue-700 rounded" />
|
||||
<button onClick={() => copySigningLink(viewingContract)} className="px-3 py-2 bg-blue-600 text-white rounded hover:bg-blue-700">
|
||||
@@ -782,18 +782,18 @@ const Contracts: React.FC = () => {
|
||||
<div className="w-10 h-10 rounded-full bg-red-100 dark:bg-red-900/30 flex items-center justify-center">
|
||||
<Ban className="w-5 h-5 text-red-600 dark:text-red-400" />
|
||||
</div>
|
||||
<h3 className="text-lg font-semibold text-gray-900 dark:text-white">Void Contract</h3>
|
||||
<h3 className="text-lg font-semibold text-gray-900 dark:text-white">{t('contracts.voidContract.title')}</h3>
|
||||
</div>
|
||||
<p className="text-gray-600 dark:text-gray-400 mb-4">Voiding this contract will cancel it. The customer will no longer be able to sign.</p>
|
||||
<p className="text-gray-600 dark:text-gray-400 mb-4">{t('contracts.voidContract.description')}</p>
|
||||
<div className="mb-6">
|
||||
<label className="block text-sm font-medium text-gray-700 dark:text-gray-300 mb-1">Reason for voiding *</label>
|
||||
<textarea value={voidReason} onChange={(e) => setVoidReason(e.target.value)} className="w-full px-4 py-2 border border-gray-300 dark:border-gray-600 rounded-lg focus:ring-2 focus:ring-red-500 focus:border-transparent dark:bg-gray-700 dark:text-white" rows={3} placeholder="Enter the reason..." required />
|
||||
<label className="block text-sm font-medium text-gray-700 dark:text-gray-300 mb-1">{t('contracts.voidContract.reason')} *</label>
|
||||
<textarea value={voidReason} onChange={(e) => setVoidReason(e.target.value)} className="w-full px-4 py-2 border border-gray-300 dark:border-gray-600 rounded-lg focus:ring-2 focus:ring-red-500 focus:border-transparent dark:bg-gray-700 dark:text-white" rows={3} placeholder={t('contracts.voidContract.reasonPlaceholder')} required />
|
||||
</div>
|
||||
<div className="flex justify-end gap-3">
|
||||
<button onClick={() => { setVoidingContract(null); setVoidReason(''); }} className="px-4 py-2 text-gray-700 dark:text-gray-300 bg-gray-100 dark:bg-gray-700 rounded-lg hover:bg-gray-200 dark:hover:bg-gray-600 transition-colors">Cancel</button>
|
||||
<button onClick={() => { setVoidingContract(null); setVoidReason(''); }} className="px-4 py-2 text-gray-700 dark:text-gray-300 bg-gray-100 dark:bg-gray-700 rounded-lg hover:bg-gray-200 dark:hover:bg-gray-600 transition-colors">{t('common.cancel')}</button>
|
||||
<button onClick={handleVoidContract} disabled={voidContract.isPending || !voidReason.trim()} className="px-4 py-2 bg-red-600 text-white rounded-lg hover:bg-red-700 transition-colors disabled:opacity-50 flex items-center gap-2">
|
||||
{voidContract.isPending && <Loader2 className="w-4 h-4 animate-spin" />}
|
||||
Void Contract
|
||||
{t('contracts.voidContract.confirm')}
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
@@ -805,7 +805,7 @@ const Contracts: React.FC = () => {
|
||||
<div className="fixed inset-0 z-50 bg-black/50 flex items-center justify-center p-4" onClick={() => setIsTemplateModalOpen(false)}>
|
||||
<div className="bg-white dark:bg-gray-800 rounded-lg shadow-xl max-w-4xl w-full max-h-[90vh] overflow-y-auto" onClick={(e) => e.stopPropagation()}>
|
||||
<div className="flex items-center justify-between p-4 border-b border-gray-200 dark:border-gray-700">
|
||||
<h2 className="text-xl font-semibold text-gray-900 dark:text-white">{editingTemplate ? 'Edit Template' : 'Create Template'}</h2>
|
||||
<h2 className="text-xl font-semibold text-gray-900 dark:text-white">{editingTemplate ? t('contracts.editTemplate') : t('contracts.createTemplate')}</h2>
|
||||
<button onClick={() => setIsTemplateModalOpen(false)} className="p-2 text-gray-500 hover:text-gray-700 dark:hover:text-gray-300">
|
||||
<X size={20} />
|
||||
</button>
|
||||
@@ -813,37 +813,37 @@ const Contracts: React.FC = () => {
|
||||
<form onSubmit={handleTemplateSubmit} className="p-6 space-y-6">
|
||||
<div className="grid grid-cols-1 md:grid-cols-2 gap-6">
|
||||
<div>
|
||||
<label className="block text-sm font-medium text-gray-700 dark:text-gray-300 mb-1">Template Name *</label>
|
||||
<label className="block text-sm font-medium text-gray-700 dark:text-gray-300 mb-1">{t('contracts.templateName')} *</label>
|
||||
<input type="text" value={templateFormData.name} onChange={(e) => setTemplateFormData({ ...templateFormData, name: e.target.value })} className="w-full px-4 py-2 border border-gray-300 dark:border-gray-600 rounded-lg focus:ring-2 focus:ring-blue-500 focus:border-transparent dark:bg-gray-700 dark:text-white" required />
|
||||
</div>
|
||||
<div>
|
||||
<label className="block text-sm font-medium text-gray-700 dark:text-gray-300 mb-1">Scope *</label>
|
||||
<label className="block text-sm font-medium text-gray-700 dark:text-gray-300 mb-1">{t('contracts.scope.label')} *</label>
|
||||
<select value={templateFormData.scope} onChange={(e) => setTemplateFormData({ ...templateFormData, scope: e.target.value as ContractScope })} className="w-full px-4 py-2 border border-gray-300 dark:border-gray-600 rounded-lg focus:ring-2 focus:ring-blue-500 focus:border-transparent dark:bg-gray-700 dark:text-white">
|
||||
<option value="APPOINTMENT">Per Appointment (e.g., liability waivers)</option>
|
||||
<option value="CUSTOMER">Customer-Level (e.g., terms of service)</option>
|
||||
<option value="APPOINTMENT">{t('contracts.scope.appointment')} ({t('contracts.scope.appointmentDesc')})</option>
|
||||
<option value="CUSTOMER">{t('contracts.scope.customer')} ({t('contracts.scope.customerDesc')})</option>
|
||||
</select>
|
||||
</div>
|
||||
<div>
|
||||
<label className="block text-sm font-medium text-gray-700 dark:text-gray-300 mb-1">Status</label>
|
||||
<label className="block text-sm font-medium text-gray-700 dark:text-gray-300 mb-1">{t('contracts.status.label')}</label>
|
||||
<select value={templateFormData.status} onChange={(e) => setTemplateFormData({ ...templateFormData, status: e.target.value as ContractTemplateStatus })} className="w-full px-4 py-2 border border-gray-300 dark:border-gray-600 rounded-lg focus:ring-2 focus:ring-blue-500 focus:border-transparent dark:bg-gray-700 dark:text-white">
|
||||
<option value="DRAFT">Draft</option>
|
||||
<option value="ACTIVE">Active</option>
|
||||
<option value="ARCHIVED">Archived</option>
|
||||
<option value="DRAFT">{t('contracts.status.draft')}</option>
|
||||
<option value="ACTIVE">{t('contracts.status.active')}</option>
|
||||
<option value="ARCHIVED">{t('contracts.status.archived')}</option>
|
||||
</select>
|
||||
</div>
|
||||
<div>
|
||||
<label className="block text-sm font-medium text-gray-700 dark:text-gray-300 mb-1">Expires After (days)</label>
|
||||
<input type="number" value={templateFormData.expires_after_days || ''} onChange={(e) => setTemplateFormData({ ...templateFormData, expires_after_days: e.target.value ? parseInt(e.target.value) : null })} className="w-full px-4 py-2 border border-gray-300 dark:border-gray-600 rounded-lg focus:ring-2 focus:ring-blue-500 focus:border-transparent dark:bg-gray-700 dark:text-white" placeholder="Leave blank for no expiration" min="1" />
|
||||
<label className="block text-sm font-medium text-gray-700 dark:text-gray-300 mb-1">{t('contracts.expiresAfterDays')}</label>
|
||||
<input type="number" value={templateFormData.expires_after_days || ''} onChange={(e) => setTemplateFormData({ ...templateFormData, expires_after_days: e.target.value ? parseInt(e.target.value) : null })} className="w-full px-4 py-2 border border-gray-300 dark:border-gray-600 rounded-lg focus:ring-2 focus:ring-blue-500 focus:border-transparent dark:bg-gray-700 dark:text-white" placeholder={t('contracts.expiresAfterDaysHint')} min="1" />
|
||||
</div>
|
||||
</div>
|
||||
<div>
|
||||
<label className="block text-sm font-medium text-gray-700 dark:text-gray-300 mb-1">Description</label>
|
||||
<input type="text" value={templateFormData.description} onChange={(e) => setTemplateFormData({ ...templateFormData, description: e.target.value })} className="w-full px-4 py-2 border border-gray-300 dark:border-gray-600 rounded-lg focus:ring-2 focus:ring-blue-500 focus:border-transparent dark:bg-gray-700 dark:text-white" placeholder="Brief description of this template" />
|
||||
<label className="block text-sm font-medium text-gray-700 dark:text-gray-300 mb-1">{t('contracts.templateDescription')}</label>
|
||||
<input type="text" value={templateFormData.description} onChange={(e) => setTemplateFormData({ ...templateFormData, description: e.target.value })} className="w-full px-4 py-2 border border-gray-300 dark:border-gray-600 rounded-lg focus:ring-2 focus:ring-blue-500 focus:border-transparent dark:bg-gray-700 dark:text-white" placeholder={t('contracts.templateDescription')} />
|
||||
</div>
|
||||
<div>
|
||||
<label className="block text-sm font-medium text-gray-700 dark:text-gray-300 mb-1">Contract Content (HTML) *</label>
|
||||
<label className="block text-sm font-medium text-gray-700 dark:text-gray-300 mb-1">{t('contracts.contentHtml')} *</label>
|
||||
<div className="mb-2 p-3 bg-blue-50 dark:bg-blue-900/20 rounded-lg">
|
||||
<p className="text-sm text-blue-700 dark:text-blue-300 mb-2"><strong>Available Variables:</strong></p>
|
||||
<p className="text-sm text-blue-700 dark:text-blue-300 mb-2"><strong>{t('contracts.availableVariables')}:</strong></p>
|
||||
<div className="flex flex-wrap gap-2 text-xs font-mono">
|
||||
{['{{CUSTOMER_NAME}}', '{{CUSTOMER_EMAIL}}', '{{BUSINESS_NAME}}', '{{DATE}}', '{{YEAR}}'].map((v) => (
|
||||
<span key={v} className="px-2 py-1 bg-white dark:bg-gray-700 rounded cursor-pointer hover:bg-blue-100 dark:hover:bg-blue-800" onClick={() => setTemplateFormData({ ...templateFormData, content: templateFormData.content + v })}>{v}</span>
|
||||
@@ -853,10 +853,10 @@ const Contracts: React.FC = () => {
|
||||
<textarea value={templateFormData.content} onChange={(e) => setTemplateFormData({ ...templateFormData, content: e.target.value })} className="w-full px-4 py-2 border border-gray-300 dark:border-gray-600 rounded-lg focus:ring-2 focus:ring-blue-500 focus:border-transparent dark:bg-gray-700 dark:text-white font-mono text-sm" rows={12} required placeholder="<h1>Service Agreement</h1> <p>This agreement is between {{BUSINESS_NAME}} and {{CUSTOMER_NAME}}...</p>" />
|
||||
</div>
|
||||
<div className="flex justify-end gap-3 pt-4 border-t border-gray-200 dark:border-gray-700">
|
||||
<button type="button" onClick={() => setIsTemplateModalOpen(false)} className="px-4 py-2 text-gray-700 dark:text-gray-300 bg-gray-100 dark:bg-gray-700 rounded-lg hover:bg-gray-200 dark:hover:bg-gray-600 transition-colors">Cancel</button>
|
||||
<button type="button" onClick={() => setIsTemplateModalOpen(false)} className="px-4 py-2 text-gray-700 dark:text-gray-300 bg-gray-100 dark:bg-gray-700 rounded-lg hover:bg-gray-200 dark:hover:bg-gray-600 transition-colors">{t('common.cancel')}</button>
|
||||
<button type="submit" disabled={createTemplate.isPending || updateTemplate.isPending} className="px-4 py-2 bg-blue-600 text-white rounded-lg hover:bg-blue-700 transition-colors disabled:opacity-50 flex items-center gap-2">
|
||||
{(createTemplate.isPending || updateTemplate.isPending) && <Loader2 className="w-4 h-4 animate-spin" />}
|
||||
{editingTemplate ? 'Save Changes' : 'Create Template'}
|
||||
{editingTemplate ? t('contracts.actions.saveChanges') : t('contracts.createTemplate')}
|
||||
</button>
|
||||
</div>
|
||||
</form>
|
||||
@@ -872,14 +872,14 @@ const Contracts: React.FC = () => {
|
||||
<div className="w-10 h-10 rounded-full bg-red-100 dark:bg-red-900/30 flex items-center justify-center">
|
||||
<AlertCircle className="w-5 h-5 text-red-600 dark:text-red-400" />
|
||||
</div>
|
||||
<h3 className="text-lg font-semibold text-gray-900 dark:text-white">Delete Template</h3>
|
||||
<h3 className="text-lg font-semibold text-gray-900 dark:text-white">{t('contracts.deleteTemplate.title')}</h3>
|
||||
</div>
|
||||
<p className="text-gray-600 dark:text-gray-400 mb-6">Are you sure you want to delete this template? This action cannot be undone.</p>
|
||||
<p className="text-gray-600 dark:text-gray-400 mb-6">{t('contracts.deleteTemplate.description')}</p>
|
||||
<div className="flex justify-end gap-3">
|
||||
<button onClick={() => setDeleteConfirmId(null)} className="px-4 py-2 text-gray-700 dark:text-gray-300 bg-gray-100 dark:bg-gray-700 rounded-lg hover:bg-gray-200 dark:hover:bg-gray-600 transition-colors">Cancel</button>
|
||||
<button onClick={() => setDeleteConfirmId(null)} className="px-4 py-2 text-gray-700 dark:text-gray-300 bg-gray-100 dark:bg-gray-700 rounded-lg hover:bg-gray-200 dark:hover:bg-gray-600 transition-colors">{t('common.cancel')}</button>
|
||||
<button onClick={() => handleDeleteTemplate(deleteConfirmId)} disabled={deleteTemplate.isPending} className="px-4 py-2 bg-red-600 text-white rounded-lg hover:bg-red-700 transition-colors disabled:opacity-50 flex items-center gap-2">
|
||||
{deleteTemplate.isPending && <Loader2 className="w-4 h-4 animate-spin" />}
|
||||
Delete
|
||||
{t('contracts.deleteTemplate.confirm')}
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -1,21 +1,27 @@
|
||||
/**
|
||||
* Help Contracts Page
|
||||
*
|
||||
* Comprehensive help documentation for the Contracts management page.
|
||||
*/
|
||||
|
||||
import React from 'react';
|
||||
import { useNavigate, Link } from 'react-router-dom';
|
||||
import { useTranslation } from 'react-i18next';
|
||||
import {
|
||||
ArrowLeft, FileSignature, FileText, Send, CheckCircle, ChevronRight,
|
||||
HelpCircle, Shield, Clock, Users, AlertCircle, Copy, Eye, Download,
|
||||
Plus, Pencil, Trash2, ChevronDown, RefreshCw, Ban, ExternalLink,
|
||||
LayoutGrid, Search, Filter,
|
||||
} from 'lucide-react';
|
||||
|
||||
const HelpContracts: React.FC = () => {
|
||||
const navigate = useNavigate();
|
||||
const { t } = useTranslation();
|
||||
|
||||
return (
|
||||
<div className="max-w-4xl mx-auto py-8 px-4">
|
||||
<button onClick={() => navigate(-1)} className="flex items-center gap-2 text-brand-600 hover:text-brand-700 dark:text-brand-400 mb-6">
|
||||
<ArrowLeft size={20} /> Back
|
||||
<ArrowLeft size={20} /> {t('common.back', 'Back')}
|
||||
</button>
|
||||
|
||||
<div className="mb-8">
|
||||
@@ -44,6 +50,42 @@ const HelpContracts: React.FC = () => {
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<section className="mb-10">
|
||||
<h2 className="text-xl font-semibold text-gray-900 dark:text-white mb-4 flex items-center gap-2">
|
||||
<LayoutGrid size={20} className="text-brand-500" /> Page Layout
|
||||
</h2>
|
||||
<div className="bg-white dark:bg-gray-800 rounded-xl border border-gray-200 dark:border-gray-700 p-6">
|
||||
<p className="text-gray-600 dark:text-gray-300 mb-4">
|
||||
The Contracts page is organized into two collapsible sections for easy management:
|
||||
</p>
|
||||
<div className="space-y-4">
|
||||
<div className="flex items-start gap-3 p-4 bg-blue-50 dark:bg-blue-900/20 rounded-lg">
|
||||
<ChevronDown size={20} className="text-blue-500 mt-0.5" />
|
||||
<div>
|
||||
<h4 className="font-medium text-gray-900 dark:text-white">Templates Section</h4>
|
||||
<p className="text-sm text-gray-500 dark:text-gray-400">
|
||||
Create and manage reusable contract templates. Includes search, status filters (All, Active, Draft, Archived), and actions to create, edit, preview PDF, and delete templates.
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
<div className="flex items-start gap-3 p-4 bg-green-50 dark:bg-green-900/20 rounded-lg">
|
||||
<ChevronDown size={20} className="text-green-500 mt-0.5" />
|
||||
<div>
|
||||
<h4 className="font-medium text-gray-900 dark:text-white">Sent Contracts Section</h4>
|
||||
<p className="text-sm text-gray-500 dark:text-gray-400">
|
||||
Track contracts sent to customers. Includes search, status filters (All, Pending, Signed, Expired, Voided), and actions to view, copy link, resend, or void contracts.
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div className="mt-4 p-4 bg-gray-50 dark:bg-gray-700/50 rounded-lg">
|
||||
<p className="text-sm text-gray-600 dark:text-gray-300">
|
||||
<strong>Tip:</strong> Click the section header to collapse/expand each section. The count badge shows how many items are in each section.
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<section className="mb-10">
|
||||
<h2 className="text-xl font-semibold text-gray-900 dark:text-white mb-4 flex items-center gap-2">
|
||||
<FileText size={20} className="text-brand-500" /> Contract Templates
|
||||
@@ -80,10 +122,10 @@ const HelpContracts: React.FC = () => {
|
||||
<p className="text-sm text-gray-500 dark:text-gray-400">One-time contracts per customer (e.g., privacy policy, terms of service)</p>
|
||||
</div>
|
||||
</div>
|
||||
<div className="flex items-start gap-3 p-3 bg-green-50 dark:bg-green-900/20 rounded-lg">
|
||||
<Clock size={20} className="text-green-500 mt-0.5" />
|
||||
<div className="flex items-start gap-3 p-3 bg-purple-50 dark:bg-purple-900/20 rounded-lg">
|
||||
<Clock size={20} className="text-purple-500 mt-0.5" />
|
||||
<div>
|
||||
<span className="font-medium text-gray-900 dark:text-white">Appointment-Level</span>
|
||||
<span className="font-medium text-gray-900 dark:text-white">Per Appointment</span>
|
||||
<p className="text-sm text-gray-500 dark:text-gray-400">Signed for each booking (e.g., liability waivers, service agreements)</p>
|
||||
</div>
|
||||
</div>
|
||||
@@ -91,11 +133,114 @@ const HelpContracts: React.FC = () => {
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<section className="mb-10">
|
||||
<h2 className="text-xl font-semibold text-gray-900 dark:text-white mb-4 flex items-center gap-2">
|
||||
<Plus size={20} className="text-brand-500" /> Creating Templates
|
||||
</h2>
|
||||
<div className="bg-white dark:bg-gray-800 rounded-xl border border-gray-200 dark:border-gray-700 p-6">
|
||||
<p className="text-gray-600 dark:text-gray-300 mb-4">
|
||||
Click the <strong>"New Template"</strong> button in the Templates section to create a new contract template:
|
||||
</p>
|
||||
<ol className="space-y-4">
|
||||
<li className="flex items-start gap-3">
|
||||
<span className="flex-shrink-0 w-6 h-6 rounded-full bg-brand-600 text-white text-sm flex items-center justify-center">1</span>
|
||||
<div>
|
||||
<h4 className="font-medium text-gray-900 dark:text-white">Enter Template Name</h4>
|
||||
<p className="text-sm text-gray-500 dark:text-gray-400">
|
||||
Give your template a clear, descriptive name (e.g., "Service Agreement", "Liability Waiver").
|
||||
</p>
|
||||
</div>
|
||||
</li>
|
||||
<li className="flex items-start gap-3">
|
||||
<span className="flex-shrink-0 w-6 h-6 rounded-full bg-brand-600 text-white text-sm flex items-center justify-center">2</span>
|
||||
<div>
|
||||
<h4 className="font-medium text-gray-900 dark:text-white">Select Scope</h4>
|
||||
<p className="text-sm text-gray-500 dark:text-gray-400">
|
||||
Choose "Per Appointment" for waivers or "Customer-Level" for one-time agreements.
|
||||
</p>
|
||||
</div>
|
||||
</li>
|
||||
<li className="flex items-start gap-3">
|
||||
<span className="flex-shrink-0 w-6 h-6 rounded-full bg-brand-600 text-white text-sm flex items-center justify-center">3</span>
|
||||
<div>
|
||||
<h4 className="font-medium text-gray-900 dark:text-white">Set Status</h4>
|
||||
<p className="text-sm text-gray-500 dark:text-gray-400">
|
||||
Start as "Draft" while editing. Change to "Active" when ready to send to customers.
|
||||
</p>
|
||||
</div>
|
||||
</li>
|
||||
<li className="flex items-start gap-3">
|
||||
<span className="flex-shrink-0 w-6 h-6 rounded-full bg-brand-600 text-white text-sm flex items-center justify-center">4</span>
|
||||
<div>
|
||||
<h4 className="font-medium text-gray-900 dark:text-white">Set Expiration (Optional)</h4>
|
||||
<p className="text-sm text-gray-500 dark:text-gray-400">
|
||||
Enter days until contracts expire. Leave blank for no expiration.
|
||||
</p>
|
||||
</div>
|
||||
</li>
|
||||
<li className="flex items-start gap-3">
|
||||
<span className="flex-shrink-0 w-6 h-6 rounded-full bg-brand-600 text-white text-sm flex items-center justify-center">5</span>
|
||||
<div>
|
||||
<h4 className="font-medium text-gray-900 dark:text-white">Write Contract Content</h4>
|
||||
<p className="text-sm text-gray-500 dark:text-gray-400">
|
||||
Enter your contract text using HTML formatting. Click variable chips to insert placeholders.
|
||||
</p>
|
||||
</div>
|
||||
</li>
|
||||
</ol>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<section className="mb-10">
|
||||
<h2 className="text-xl font-semibold text-gray-900 dark:text-white mb-4 flex items-center gap-2">
|
||||
<Pencil size={20} className="text-brand-500" /> Managing Templates
|
||||
</h2>
|
||||
<div className="bg-white dark:bg-gray-800 rounded-xl border border-gray-200 dark:border-gray-700 p-6">
|
||||
<p className="text-gray-600 dark:text-gray-300 mb-4">
|
||||
Each template in the list has action buttons on the right:
|
||||
</p>
|
||||
<div className="space-y-3">
|
||||
<div className="flex items-center gap-3 p-3 bg-gray-50 dark:bg-gray-700/50 rounded-lg">
|
||||
<Eye size={18} className="text-blue-500" />
|
||||
<div>
|
||||
<span className="font-medium text-gray-900 dark:text-white">Preview PDF</span>
|
||||
<span className="text-sm text-gray-500 dark:text-gray-400 ml-2">- See how the contract looks as a PDF with sample data</span>
|
||||
</div>
|
||||
</div>
|
||||
<div className="flex items-center gap-3 p-3 bg-gray-50 dark:bg-gray-700/50 rounded-lg">
|
||||
<Pencil size={18} className="text-blue-500" />
|
||||
<div>
|
||||
<span className="font-medium text-gray-900 dark:text-white">Edit</span>
|
||||
<span className="text-sm text-gray-500 dark:text-gray-400 ml-2">- Modify template name, content, scope, or status</span>
|
||||
</div>
|
||||
</div>
|
||||
<div className="flex items-center gap-3 p-3 bg-gray-50 dark:bg-gray-700/50 rounded-lg">
|
||||
<Trash2 size={18} className="text-red-500" />
|
||||
<div>
|
||||
<span className="font-medium text-gray-900 dark:text-white">Delete</span>
|
||||
<span className="text-sm text-gray-500 dark:text-gray-400 ml-2">- Remove the template (requires confirmation)</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div className="mt-4 p-4 bg-amber-50 dark:bg-amber-900/20 rounded-lg border border-amber-200 dark:border-amber-800">
|
||||
<div className="flex items-start gap-2">
|
||||
<AlertCircle size={16} className="text-amber-600 dark:text-amber-400 mt-0.5" />
|
||||
<p className="text-sm text-amber-800 dark:text-amber-200">
|
||||
<strong>Note:</strong> Only "Active" templates can be used to send contracts. Switch templates to Active status when they're ready for use.
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<section className="mb-10">
|
||||
<h2 className="text-xl font-semibold text-gray-900 dark:text-white mb-4 flex items-center gap-2">
|
||||
<Send size={20} className="text-brand-500" /> Sending Contracts
|
||||
</h2>
|
||||
<div className="bg-white dark:bg-gray-800 rounded-xl border border-gray-200 dark:border-gray-700 p-6">
|
||||
<p className="text-gray-600 dark:text-gray-300 mb-4">
|
||||
Click the <strong>"Send Contract"</strong> button in the Sent Contracts section:
|
||||
</p>
|
||||
<div className="space-y-4">
|
||||
<div className="flex items-start gap-3">
|
||||
<div className="w-6 h-6 rounded-full bg-brand-100 dark:bg-brand-900/30 flex items-center justify-center flex-shrink-0">
|
||||
@@ -112,7 +257,7 @@ const HelpContracts: React.FC = () => {
|
||||
</div>
|
||||
<div>
|
||||
<h4 className="font-medium text-gray-900 dark:text-white">Choose a Customer</h4>
|
||||
<p className="text-sm text-gray-500 dark:text-gray-400">Variables are automatically filled with customer data</p>
|
||||
<p className="text-sm text-gray-500 dark:text-gray-400">Search and select a customer. Variables are automatically filled with their data.</p>
|
||||
</div>
|
||||
</div>
|
||||
<div className="flex items-start gap-3">
|
||||
@@ -120,8 +265,8 @@ const HelpContracts: React.FC = () => {
|
||||
<span className="text-sm font-semibold text-brand-600 dark:text-brand-400">3</span>
|
||||
</div>
|
||||
<div>
|
||||
<h4 className="font-medium text-gray-900 dark:text-white">Send for Signature</h4>
|
||||
<p className="text-sm text-gray-500 dark:text-gray-400">Customer receives an email with a secure signing link</p>
|
||||
<h4 className="font-medium text-gray-900 dark:text-white">Send Immediately (Optional)</h4>
|
||||
<p className="text-sm text-gray-500 dark:text-gray-400">Check the box to email the signing request right away, or uncheck to send later.</p>
|
||||
</div>
|
||||
</div>
|
||||
<div className="flex items-start gap-3">
|
||||
@@ -130,7 +275,7 @@ const HelpContracts: React.FC = () => {
|
||||
</div>
|
||||
<div>
|
||||
<h4 className="font-medium text-gray-900 dark:text-white">Track Status</h4>
|
||||
<p className="text-sm text-gray-500 dark:text-gray-400">Monitor pending, signed, expired, or voided contracts</p>
|
||||
<p className="text-sm text-gray-500 dark:text-gray-400">Monitor the contract in the Sent Contracts list</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
@@ -139,10 +284,10 @@ const HelpContracts: React.FC = () => {
|
||||
|
||||
<section className="mb-10">
|
||||
<h2 className="text-xl font-semibold text-gray-900 dark:text-white mb-4 flex items-center gap-2">
|
||||
<Eye size={20} className="text-brand-500" /> Contract Status
|
||||
<Eye size={20} className="text-brand-500" /> Contract Status & Actions
|
||||
</h2>
|
||||
<div className="bg-white dark:bg-gray-800 rounded-xl border border-gray-200 dark:border-gray-700 p-6">
|
||||
<div className="space-y-3">
|
||||
<div className="space-y-3 mb-6">
|
||||
<div className="flex items-center gap-3 p-3 bg-yellow-50 dark:bg-yellow-900/20 rounded-lg">
|
||||
<div className="w-3 h-3 rounded-full bg-yellow-500" />
|
||||
<span className="font-medium text-gray-900 dark:text-white">Pending</span>
|
||||
@@ -164,6 +309,30 @@ const HelpContracts: React.FC = () => {
|
||||
<span className="text-sm text-gray-500 dark:text-gray-400">- Contract was cancelled by business</span>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<h3 className="font-semibold text-gray-900 dark:text-white mb-3">Available Actions</h3>
|
||||
<div className="space-y-2">
|
||||
<div className="flex items-center gap-3 p-2 text-sm">
|
||||
<Eye size={16} className="text-blue-500" />
|
||||
<span className="text-gray-700 dark:text-gray-300"><strong>View Details</strong> - See full contract information</span>
|
||||
</div>
|
||||
<div className="flex items-center gap-3 p-2 text-sm">
|
||||
<Copy size={16} className="text-blue-500" />
|
||||
<span className="text-gray-700 dark:text-gray-300"><strong>Copy Link</strong> - Copy signing URL to clipboard (pending only)</span>
|
||||
</div>
|
||||
<div className="flex items-center gap-3 p-2 text-sm">
|
||||
<Send size={16} className="text-green-500" />
|
||||
<span className="text-gray-700 dark:text-gray-300"><strong>Send/Resend</strong> - Email the signing request (pending only)</span>
|
||||
</div>
|
||||
<div className="flex items-center gap-3 p-2 text-sm">
|
||||
<ExternalLink size={16} className="text-blue-500" />
|
||||
<span className="text-gray-700 dark:text-gray-300"><strong>Open Signing Page</strong> - View the customer signing experience</span>
|
||||
</div>
|
||||
<div className="flex items-center gap-3 p-2 text-sm">
|
||||
<Ban size={16} className="text-red-500" />
|
||||
<span className="text-gray-700 dark:text-gray-300"><strong>Void</strong> - Cancel a pending contract</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
@@ -220,6 +389,10 @@ const HelpContracts: React.FC = () => {
|
||||
Once a contract is signed, a PDF is automatically generated that includes:
|
||||
</p>
|
||||
<ul className="space-y-2">
|
||||
<li className="flex items-start gap-2">
|
||||
<ChevronRight size={16} className="text-brand-500 mt-1 flex-shrink-0" />
|
||||
<span className="text-gray-700 dark:text-gray-300">Your business branding and logo</span>
|
||||
</li>
|
||||
<li className="flex items-start gap-2">
|
||||
<ChevronRight size={16} className="text-brand-500 mt-1 flex-shrink-0" />
|
||||
<span className="text-gray-700 dark:text-gray-300">The full contract content with substituted variables</span>
|
||||
@@ -230,13 +403,18 @@ const HelpContracts: React.FC = () => {
|
||||
</li>
|
||||
<li className="flex items-start gap-2">
|
||||
<ChevronRight size={16} className="text-brand-500 mt-1 flex-shrink-0" />
|
||||
<span className="text-gray-700 dark:text-gray-300">Complete audit trail footer with all verification data</span>
|
||||
<span className="text-gray-700 dark:text-gray-300">Complete audit trail with verification data</span>
|
||||
</li>
|
||||
<li className="flex items-start gap-2">
|
||||
<ChevronRight size={16} className="text-brand-500 mt-1 flex-shrink-0" />
|
||||
<span className="text-gray-700 dark:text-gray-300">Your business branding and logo</span>
|
||||
<span className="text-gray-700 dark:text-gray-300">Legal notice about electronic signatures</span>
|
||||
</li>
|
||||
</ul>
|
||||
<div className="mt-4 p-4 bg-gray-50 dark:bg-gray-700/50 rounded-lg">
|
||||
<p className="text-sm text-gray-600 dark:text-gray-300">
|
||||
<strong>Preview PDFs:</strong> Use the eye icon on any template to preview how the final PDF will look with sample customer data.
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
@@ -252,15 +430,19 @@ const HelpContracts: React.FC = () => {
|
||||
</li>
|
||||
<li className="flex items-start gap-2">
|
||||
<ChevronRight size={16} className="text-brand-500 mt-1 flex-shrink-0" />
|
||||
<span className="text-gray-700 dark:text-gray-300"><strong>Set Expiration Dates:</strong> Use the expiration feature to ensure contracts are signed in a timely manner</span>
|
||||
<span className="text-gray-700 dark:text-gray-300"><strong>Start as Draft:</strong> Create templates in Draft status, test with PDF preview, then activate</span>
|
||||
</li>
|
||||
<li className="flex items-start gap-2">
|
||||
<ChevronRight size={16} className="text-brand-500 mt-1 flex-shrink-0" />
|
||||
<span className="text-gray-700 dark:text-gray-300"><strong>Link to Services:</strong> Associate contracts with specific services for automatic requirement checking</span>
|
||||
<span className="text-gray-700 dark:text-gray-300"><strong>Set Expiration Dates:</strong> Use the expiration feature to ensure contracts are signed promptly</span>
|
||||
</li>
|
||||
<li className="flex items-start gap-2">
|
||||
<ChevronRight size={16} className="text-brand-500 mt-1 flex-shrink-0" />
|
||||
<span className="text-gray-700 dark:text-gray-300"><strong>Version Control:</strong> Create new versions rather than editing existing active templates</span>
|
||||
<span className="text-gray-700 dark:text-gray-300"><strong>Create Customers First:</strong> Ensure customers exist in the system before sending contracts</span>
|
||||
</li>
|
||||
<li className="flex items-start gap-2">
|
||||
<ChevronRight size={16} className="text-brand-500 mt-1 flex-shrink-0" />
|
||||
<span className="text-gray-700 dark:text-gray-300"><strong>Archive Old Templates:</strong> Rather than deleting, archive templates you no longer use</span>
|
||||
</li>
|
||||
<li className="flex items-start gap-2">
|
||||
<ChevronRight size={16} className="text-brand-500 mt-1 flex-shrink-0" />
|
||||
|
||||
Reference in New Issue
Block a user