feat(sms): implement SMS messaging and metered billing
CI / Frontend (push) Successful in 9m50s
CI / Go - apps/auth-service (push) Failing after 4s
CI / Go - apps/backend (push) Successful in 10m18s
CI / Docker publish - auth-service (push) Has been skipped
CI / Docker publish - backend (push) Has been skipped

Implement a complete SMS messaging system including:
- Integration with SMS Manager.cz API for sending messages.
- Metered billing via Stripe using monthly aggregate invoice items.
- Backend services for managing SMS settings, usage logging, and monthly reporting.
- Database migrations for tenant settings, usage logs, and monthly reports.
- Frontend dashboard components for SMS configuration, usage tracking, and history.
- Support for customer phone numbers in the booking flow.

Includes new migrations, backend services, and frontend UI components.
This commit is contained in:
Tomas Dvorak
2026-05-10 11:40:53 +02:00
parent 164a37e997
commit 7d3e3448cf
28 changed files with 3633 additions and 3190 deletions
@@ -322,6 +322,88 @@ const dictionaries = {
"dashboard.onboarding.timezone": "Časové pásmo",
"dashboard.onboarding.submit": "Vytvořit prostor",
"dashboard.onboarding.pending": "Vytvářím prostor...",
"dashboard.revenueTitle": "Trend rezervací",
"dashboard.revenueSubtitle": "Počet rezervací za posledních 7 dní",
"dashboard.noNotifications": "Žádná nová oznámení",
"dashboard.markAllRead": "Označit vše jako přečtené",
"dashboard.notifications.title": "Oznámení",
"dashboard.demoBanner": "Demo režim",
"dashboard.demoBannerDesc": "Prozkoumáte Bookra s ukázkovými daty. Pro plnou funkčnost se zaregistrujte.",
"dashboard.demoBannerCTA": "Vytvořit účet",
"dashboard.tryDemo": "Vyzkoušet demo",
"dashboard.language": "Jazyk",
"dashboard.calendar.noBookings": "Tento den nemáte žádné rezervace.",
"dashboard.calendar.bookingsCount": "rezervací",
"dashboard.chart.bookingsTrend": "Rezervace",
"dashboard.chart.revenueTrend": "Obrat",
"dashboard.recentActivity.empty": "Zatím žádná aktivita. Rezervace se zobrazí zde.",
"dashboard.filter.all": "Vše",
"dashboard.filter.today": "Dnes",
"dashboard.filter.week": "Týden",
"dashboard.filter.month": "Měsíc",
"dashboard.search.placeholder": "Hledat...",
"dashboard.search.bookings": "Hledat podle jména, služby nebo reference...",
"dashboard.search.customers": "Hledat podle jména, emailu nebo telefonu...",
"dashboard.actions": "Akce",
"dashboard.customer.email": "E-mail",
"dashboard.customer.phone": "Telefon",
"dashboard.customer.totalBookings": "Celkem rezervací",
"dashboard.customer.lastVisit": "Poslední návštěva",
"dashboard.customer.status": "Stav",
"dashboard.customer.notes": "Poznámky",
"dashboard.customer.noNotes": "Žádné poznámky",
"dashboard.customer.bookings": "Rezervace zákazníka",
"dashboard.customer.noBookings": "Žádné rezervace",
"dashboard.zone.add": "Přidat zónu",
"dashboard.zone.name": "Název",
"dashboard.zone.type": "Typ",
"dashboard.zone.capacity": "Kapacita",
"dashboard.zone.limitReached": "Dosáhli jste limitu",
"dashboard.zone.rooms": "Místnost",
"dashboard.zone.private": "Soukromá místnost",
"dashboard.zone.hall": "Sál",
"dashboard.zone.blockedDays": "Blokované dny",
"dashboard.zone.workingHours": "Pracovní doba",
"dashboard.zone.open": "Otevřeno",
"dashboard.zone.close": "Zavřeno",
"dashboard.zone.addBlocked": "Přidat blokovaný den",
"dashboard.zone.reason": "Důvod",
"dashboard.zone.noZones": "Zatím nemáte žádné zóny. Přidejte první.",
"dashboard.billing.planUsage": "Využití plánu",
"dashboard.billing.locations": "Lokace",
"dashboard.billing.bookings": "Rezervace",
"dashboard.billing.staff": "Zaměstnanci",
"dashboard.billing.period": "Období",
"dashboard.billing.currentPlan": "Aktuální plán",
"dashboard.billing.nextBilling": "Další fakturace",
"dashboard.settings.businessInfo": "Informace o podniku",
"dashboard.settings.branding": "Branding a vzhled",
"dashboard.settings.emailNotifications": "E-mailová oznámení",
"dashboard.settings.emailSubject": "Předmět",
"dashboard.settings.emailBody": "Obsah",
"dashboard.settings.save": "Uložit",
"dashboard.bookingModal.customer": "Zákazník",
"dashboard.bookingModal.service": "Služba",
"dashboard.bookingModal.location": "Místo",
"dashboard.bookingModal.dateTime": "Datum a čas",
"dashboard.bookingModal.duration": "Délka",
"dashboard.bookingModal.status": "Stav",
"dashboard.bookingModal.reference": "Reference",
"dashboard.bookingModal.notes": "Poznámky",
"dashboard.bookingModal.reschedule": "Přeplánovat",
"dashboard.bookingModal.confirmCancel": "Opravdu chcete zrušit tuto rezervaci?",
"dashboard.bookingModal.createdAt": "Vytvořeno",
"dashboard.bookingModal.assignedTo": "Přiřazeno",
"dashboard.bookingModal.noAssign": "Nepřiřazeno",
"dashboard.bookingModal.email": "E-mail",
"dashboard.bookingModal.phone": "Telefon",
"dashboard.empty.title": "Zatím prázdné",
"dashboard.empty.bookingsDesc": "Žádné rezervace neodpovídají vašemu filtru.",
"dashboard.empty.customersDesc": "Zatím nemáte žádné zákazníky.",
"dashboard.notification.newBooking": "Nová rezervace od",
"dashboard.notification.reminder": "Připomínka: rezervace u",
"dashboard.notification.upgrade": "Blížíte se limitu plánu",
"dashboard.notification.trialEnding": "Vaše zkušební doba končí za 3 dny",
"booking.title": "Rezervace",
"booking.body": "Vyberte dostupný termín, doplňte kontaktní údaje a potvrzení přijde e-mailem.",
"booking.slots": "Dostupné termíny",
@@ -340,6 +422,7 @@ const dictionaries = {
"booking.customer.body": "Tyto údaje použijeme pro potvrzení rezervace a připomenutí.",
"booking.customer.name": "Jméno",
"booking.customer.email": "E-mail",
"booking.customer.phone": "Telefon",
"booking.customer.notes": "Poznámka",
"booking.customerRequired": "Před rezervací vyplňte jméno a e-mail.",
"booking.failed": "Rezervaci se nepodařilo vytvořit",
@@ -719,6 +802,88 @@ const dictionaries = {
"dashboard.onboarding.timezone": "Timezone",
"dashboard.onboarding.submit": "Create workspace",
"dashboard.onboarding.pending": "Creating workspace...",
"dashboard.revenueTitle": "Booking trend",
"dashboard.revenueSubtitle": "Bookings over the last 7 days",
"dashboard.noNotifications": "No new notifications",
"dashboard.markAllRead": "Mark all as read",
"dashboard.notifications.title": "Notifications",
"dashboard.demoBanner": "Demo mode",
"dashboard.demoBannerDesc": "You're exploring Bookra with sample data. Register for full functionality.",
"dashboard.demoBannerCTA": "Create account",
"dashboard.tryDemo": "Try demo",
"dashboard.language": "Language",
"dashboard.calendar.noBookings": "No bookings for this day.",
"dashboard.calendar.bookingsCount": "bookings",
"dashboard.chart.bookingsTrend": "Bookings",
"dashboard.chart.revenueTrend": "Revenue",
"dashboard.recentActivity.empty": "No activity yet. Bookings will appear here.",
"dashboard.filter.all": "All",
"dashboard.filter.today": "Today",
"dashboard.filter.week": "Week",
"dashboard.filter.month": "Month",
"dashboard.search.placeholder": "Search...",
"dashboard.search.bookings": "Search by name, service or reference...",
"dashboard.search.customers": "Search by name, email or phone...",
"dashboard.actions": "Actions",
"dashboard.customer.email": "Email",
"dashboard.customer.phone": "Phone",
"dashboard.customer.totalBookings": "Total bookings",
"dashboard.customer.lastVisit": "Last visit",
"dashboard.customer.status": "Status",
"dashboard.customer.notes": "Notes",
"dashboard.customer.noNotes": "No notes",
"dashboard.customer.bookings": "Customer bookings",
"dashboard.customer.noBookings": "No bookings",
"dashboard.zone.add": "Add zone",
"dashboard.zone.name": "Name",
"dashboard.zone.type": "Type",
"dashboard.zone.capacity": "Capacity",
"dashboard.zone.limitReached": "Limit reached",
"dashboard.zone.rooms": "Room",
"dashboard.zone.private": "Private room",
"dashboard.zone.hall": "Hall",
"dashboard.zone.blockedDays": "Blocked days",
"dashboard.zone.workingHours": "Working hours",
"dashboard.zone.open": "Open",
"dashboard.zone.close": "Close",
"dashboard.zone.addBlocked": "Add blocked day",
"dashboard.zone.reason": "Reason",
"dashboard.zone.noZones": "No zones yet. Add your first one.",
"dashboard.billing.planUsage": "Plan usage",
"dashboard.billing.locations": "Locations",
"dashboard.billing.bookings": "Bookings",
"dashboard.billing.staff": "Staff",
"dashboard.billing.period": "Period",
"dashboard.billing.currentPlan": "Current plan",
"dashboard.billing.nextBilling": "Next billing",
"dashboard.settings.businessInfo": "Business information",
"dashboard.settings.branding": "Branding & appearance",
"dashboard.settings.emailNotifications": "Email notifications",
"dashboard.settings.emailSubject": "Subject",
"dashboard.settings.emailBody": "Body",
"dashboard.settings.save": "Save",
"dashboard.bookingModal.customer": "Customer",
"dashboard.bookingModal.service": "Service",
"dashboard.bookingModal.location": "Location",
"dashboard.bookingModal.dateTime": "Date & time",
"dashboard.bookingModal.duration": "Duration",
"dashboard.bookingModal.status": "Status",
"dashboard.bookingModal.reference": "Reference",
"dashboard.bookingModal.notes": "Notes",
"dashboard.bookingModal.reschedule": "Reschedule",
"dashboard.bookingModal.confirmCancel": "Are you sure you want to cancel this booking?",
"dashboard.bookingModal.createdAt": "Created",
"dashboard.bookingModal.assignedTo": "Assigned to",
"dashboard.bookingModal.noAssign": "Not assigned",
"dashboard.bookingModal.email": "Email",
"dashboard.bookingModal.phone": "Phone",
"dashboard.empty.title": "Nothing here yet",
"dashboard.empty.bookingsDesc": "No bookings match your filter.",
"dashboard.empty.customersDesc": "You don't have any customers yet.",
"dashboard.notification.newBooking": "New booking from",
"dashboard.notification.reminder": "Reminder: booking at",
"dashboard.notification.upgrade": "You're nearing your plan limit",
"dashboard.notification.trialEnding": "Your trial ends in 3 days",
"booking.title": "Book a visit",
"booking.body": "Choose an available time, add your contact details, and receive confirmation by email.",
"booking.slots": "Available times",
@@ -737,6 +902,7 @@ const dictionaries = {
"booking.customer.body": "These details are used for confirmation and reminders.",
"booking.customer.name": "Name",
"booking.customer.email": "Email",
"booking.customer.phone": "Phone",
"booking.customer.notes": "Note",
"booking.customerRequired": "Add your name and email before booking.",
"booking.failed": "Booking failed",