Adds localization support across application views
Implements localization for dashboard, cash register, account, and profile sections Adds localization keys for various UI elements, improving internationalization support Refactors view components to use ILocalizationService for dynamic text rendering Prepares ground for multi-language support with translation-ready markup
This commit is contained in:
parent
1f400dcc6e
commit
ef174af0e1
36 changed files with 821 additions and 263 deletions
|
|
@ -1,15 +1,15 @@
|
|||
<swp-invoices-card>
|
||||
<swp-invoices-header>
|
||||
<swp-invoices-title>Faktura-historik</swp-invoices-title>
|
||||
<swp-invoices-title localize="account.invoices.title">Faktura-historik</swp-invoices-title>
|
||||
</swp-invoices-header>
|
||||
|
||||
<swp-invoice-table>
|
||||
<swp-invoice-table-header>
|
||||
<swp-invoice-row>
|
||||
<swp-invoice-cell>Dato</swp-invoice-cell>
|
||||
<swp-invoice-cell>Fakturanr.</swp-invoice-cell>
|
||||
<swp-invoice-cell>Beløb</swp-invoice-cell>
|
||||
<swp-invoice-cell>Status</swp-invoice-cell>
|
||||
<swp-invoice-cell localize="account.invoices.date">Dato</swp-invoice-cell>
|
||||
<swp-invoice-cell localize="account.invoices.invoiceNumber">Fakturanr.</swp-invoice-cell>
|
||||
<swp-invoice-cell localize="account.invoices.amount">Beløb</swp-invoice-cell>
|
||||
<swp-invoice-cell localize="common.status">Status</swp-invoice-cell>
|
||||
<swp-invoice-cell></swp-invoice-cell>
|
||||
</swp-invoice-row>
|
||||
</swp-invoice-table-header>
|
||||
|
|
@ -21,13 +21,13 @@
|
|||
<swp-invoice-cell>
|
||||
<swp-invoice-status class="paid">
|
||||
<i class="ph ph-check-circle"></i>
|
||||
Betalt
|
||||
<span localize="account.invoices.paid">Betalt</span>
|
||||
</swp-invoice-status>
|
||||
</swp-invoice-cell>
|
||||
<swp-invoice-cell>
|
||||
<swp-download-btn>
|
||||
<i class="ph ph-download"></i>
|
||||
PDF
|
||||
<span localize="account.invoices.download">PDF</span>
|
||||
</swp-download-btn>
|
||||
</swp-invoice-cell>
|
||||
</swp-invoice-row>
|
||||
|
|
@ -39,13 +39,13 @@
|
|||
<swp-invoice-cell>
|
||||
<swp-invoice-status class="paid">
|
||||
<i class="ph ph-check-circle"></i>
|
||||
Betalt
|
||||
<span localize="account.invoices.paid">Betalt</span>
|
||||
</swp-invoice-status>
|
||||
</swp-invoice-cell>
|
||||
<swp-invoice-cell>
|
||||
<swp-download-btn>
|
||||
<i class="ph ph-download"></i>
|
||||
PDF
|
||||
<span localize="account.invoices.download">PDF</span>
|
||||
</swp-download-btn>
|
||||
</swp-invoice-cell>
|
||||
</swp-invoice-row>
|
||||
|
|
@ -57,13 +57,13 @@
|
|||
<swp-invoice-cell>
|
||||
<swp-invoice-status class="paid">
|
||||
<i class="ph ph-check-circle"></i>
|
||||
Betalt
|
||||
<span localize="account.invoices.paid">Betalt</span>
|
||||
</swp-invoice-status>
|
||||
</swp-invoice-cell>
|
||||
<swp-invoice-cell>
|
||||
<swp-download-btn>
|
||||
<i class="ph ph-download"></i>
|
||||
PDF
|
||||
<span localize="account.invoices.download">PDF</span>
|
||||
</swp-download-btn>
|
||||
</swp-invoice-cell>
|
||||
</swp-invoice-row>
|
||||
|
|
@ -75,13 +75,13 @@
|
|||
<swp-invoice-cell>
|
||||
<swp-invoice-status class="paid">
|
||||
<i class="ph ph-check-circle"></i>
|
||||
Betalt
|
||||
<span localize="account.invoices.paid">Betalt</span>
|
||||
</swp-invoice-status>
|
||||
</swp-invoice-cell>
|
||||
<swp-invoice-cell>
|
||||
<swp-download-btn>
|
||||
<i class="ph ph-download"></i>
|
||||
PDF
|
||||
<span localize="account.invoices.download">PDF</span>
|
||||
</swp-download-btn>
|
||||
</swp-invoice-cell>
|
||||
</swp-invoice-row>
|
||||
|
|
@ -93,13 +93,13 @@
|
|||
<swp-invoice-cell>
|
||||
<swp-invoice-status class="paid">
|
||||
<i class="ph ph-check-circle"></i>
|
||||
Betalt
|
||||
<span localize="account.invoices.paid">Betalt</span>
|
||||
</swp-invoice-status>
|
||||
</swp-invoice-cell>
|
||||
<swp-invoice-cell>
|
||||
<swp-download-btn>
|
||||
<i class="ph ph-download"></i>
|
||||
PDF
|
||||
<span localize="account.invoices.download">PDF</span>
|
||||
</swp-download-btn>
|
||||
</swp-invoice-cell>
|
||||
</swp-invoice-row>
|
||||
|
|
|
|||
|
|
@ -9,32 +9,32 @@
|
|||
</swp-payment-info>
|
||||
<swp-btn class="secondary sm">
|
||||
<i class="ph ph-pencil"></i>
|
||||
Skift
|
||||
<span localize="account.payment.change">Skift</span>
|
||||
</swp-btn>
|
||||
</swp-payment-method>
|
||||
|
||||
<swp-payment-detail>
|
||||
<swp-payment-label>Betalingsfrekvens</swp-payment-label>
|
||||
<swp-payment-value>Månedlig</swp-payment-value>
|
||||
<swp-payment-label localize="account.payment.frequency">Betalingsfrekvens</swp-payment-label>
|
||||
<swp-payment-value localize="account.payment.monthly">Månedlig</swp-payment-value>
|
||||
</swp-payment-detail>
|
||||
|
||||
<swp-payment-detail>
|
||||
<swp-payment-label>Næste betaling</swp-payment-label>
|
||||
<swp-payment-label localize="account.payment.nextPayment">Næste betaling</swp-payment-label>
|
||||
<swp-payment-value class="highlight">1. februar 2026</swp-payment-value>
|
||||
</swp-payment-detail>
|
||||
|
||||
<swp-payment-detail>
|
||||
<swp-payment-label>Beløb</swp-payment-label>
|
||||
<swp-payment-label localize="account.payment.amount">Beløb</swp-payment-label>
|
||||
<swp-payment-value>599,00 kr</swp-payment-value>
|
||||
</swp-payment-detail>
|
||||
|
||||
<swp-payment-detail>
|
||||
<swp-payment-label>Kortudløb</swp-payment-label>
|
||||
<swp-payment-label localize="account.payment.cardExpiry">Kortudløb</swp-payment-label>
|
||||
<swp-payment-value>08/2027</swp-payment-value>
|
||||
</swp-payment-detail>
|
||||
|
||||
<swp-btn class="outline" style="margin-top: var(--spacing-2);">
|
||||
<i class="ph ph-arrows-clockwise"></i>
|
||||
Skift til årlig betaling (spar 15%)
|
||||
<span localize="account.payment.switchToYearly">Skift til årlig betaling (spar 15%)</span>
|
||||
</swp-btn>
|
||||
</swp-payment-card>
|
||||
|
|
|
|||
|
|
@ -1,8 +1,10 @@
|
|||
@using PlanTempus.Application.Features.Accounts.Models
|
||||
@using PlanTempus.Application.Features.Localization.Services
|
||||
@model IEnumerable<PlanInfo>
|
||||
|
||||
@{
|
||||
var currentPlanKey = (string)ViewBag.CurrentPlanKey;
|
||||
var L = (ILocalizationService)ViewBag.Localization;
|
||||
}
|
||||
|
||||
<swp-plan-grid>
|
||||
|
|
@ -16,14 +18,14 @@
|
|||
};
|
||||
|
||||
var badgeClass = isCurrent ? "current" : plan.BadgeClass;
|
||||
var badgeText = isCurrent ? "Nuværende plan" : plan.BadgeText;
|
||||
var badgeText = isCurrent ? L.Get("account.subscription.currentPlan") : plan.BadgeText;
|
||||
var badgeIcon = isCurrent ? "ph-check" : plan.BadgeIcon;
|
||||
|
||||
var buttonText = isCurrent
|
||||
? "Nuværende plan"
|
||||
? L.Get("account.subscription.currentPlan")
|
||||
: plan.IsContactSales
|
||||
? "Kontakt salg"
|
||||
: $"Skift til {plan.Name}";
|
||||
? L.Get("account.subscription.contactSales")
|
||||
: L.Get("account.subscription.switchTo").Replace("{plan}", plan.Name);
|
||||
|
||||
var buttonClass = isCurrent
|
||||
? "secondary"
|
||||
|
|
@ -42,11 +44,11 @@
|
|||
@if (plan.PricePerMonth.HasValue)
|
||||
{
|
||||
<swp-plan-price-amount>@plan.PriceDisplay</swp-plan-price-amount>
|
||||
<swp-plan-price-period>kr/md</swp-plan-price-period>
|
||||
<swp-plan-price-period localize="account.subscription.pricePerMonth">kr/md</swp-plan-price-period>
|
||||
}
|
||||
else
|
||||
{
|
||||
<swp-plan-price-amount>Kontakt os</swp-plan-price-amount>
|
||||
<swp-plan-price-amount localize="account.subscription.contactUs">Kontakt os</swp-plan-price-amount>
|
||||
}
|
||||
</swp-plan-price>
|
||||
<swp-plan-features>
|
||||
|
|
|
|||
|
|
@ -1,5 +1,6 @@
|
|||
using Microsoft.AspNetCore.Mvc;
|
||||
using PlanTempus.Application.Features.Accounts.Models;
|
||||
using PlanTempus.Application.Features.Localization.Services;
|
||||
|
||||
namespace PlanTempus.Application.Features.Account.Components;
|
||||
|
||||
|
|
@ -9,6 +10,13 @@ namespace PlanTempus.Application.Features.Account.Components;
|
|||
/// </summary>
|
||||
public class SubscriptionPlansViewComponent : ViewComponent
|
||||
{
|
||||
private readonly ILocalizationService _localization;
|
||||
|
||||
public SubscriptionPlansViewComponent(ILocalizationService localization)
|
||||
{
|
||||
_localization = localization;
|
||||
}
|
||||
|
||||
public IViewComponentResult Invoke()
|
||||
{
|
||||
var plans = PlanCatalog.GetAllPlans();
|
||||
|
|
@ -16,6 +24,7 @@ public class SubscriptionPlansViewComponent : ViewComponent
|
|||
var currentPlanKey = "pro";
|
||||
|
||||
ViewBag.CurrentPlanKey = currentPlanKey;
|
||||
ViewBag.Localization = _localization;
|
||||
return View(plans);
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -10,8 +10,8 @@
|
|||
<!-- Page Header -->
|
||||
<swp-page-header>
|
||||
<swp-page-title>
|
||||
<h1>Abonnement & Konto</h1>
|
||||
<p>Administrer dit abonnement og betalingsinfo</p>
|
||||
<h1 localize="account.title">Abonnement & Konto</h1>
|
||||
<p localize="account.subtitle">Administrer dit abonnement og betalingsinfo</p>
|
||||
</swp-page-title>
|
||||
</swp-page-header>
|
||||
|
||||
|
|
@ -20,7 +20,7 @@
|
|||
<swp-account-section-header>
|
||||
<swp-account-section-title>
|
||||
<i class="ph ph-crown"></i>
|
||||
Dit abonnement
|
||||
<span localize="account.subscription.title">Dit abonnement</span>
|
||||
</swp-account-section-title>
|
||||
</swp-account-section-header>
|
||||
|
||||
|
|
@ -32,7 +32,7 @@
|
|||
<swp-account-section-header>
|
||||
<swp-account-section-title>
|
||||
<i class="ph ph-credit-card"></i>
|
||||
Betaling & Fakturaer
|
||||
<span localize="account.billing.title">Betaling & Fakturaer</span>
|
||||
</swp-account-section-title>
|
||||
</swp-account-section-header>
|
||||
|
||||
|
|
|
|||
|
|
@ -1,21 +1,21 @@
|
|||
<swp-card>
|
||||
<swp-card-header>
|
||||
<swp-card-title>Afslut dagen</swp-card-title>
|
||||
<swp-card-title localize="cash.approval.title">Afslut dagen</swp-card-title>
|
||||
</swp-card-header>
|
||||
<swp-card-content>
|
||||
<swp-approval-grid>
|
||||
<swp-form-field>
|
||||
<swp-form-label>Status</swp-form-label>
|
||||
<swp-form-label localize="common.status">Status</swp-form-label>
|
||||
<swp-status-row>
|
||||
<swp-status-badge class="draft">Kladde</swp-status-badge>
|
||||
<swp-status-badge class="draft" localize="cash.status.draft">Kladde</swp-status-badge>
|
||||
</swp-status-row>
|
||||
</swp-form-field>
|
||||
|
||||
<swp-form-field>
|
||||
<swp-form-label>Godkendt af (valgfrit)</swp-form-label>
|
||||
<swp-form-label localize="cash.approval.approvedBy">Godkendt af (valgfrit)</swp-form-label>
|
||||
<swp-form-input>
|
||||
<select id="approver">
|
||||
<option value="">Vælg...</option>
|
||||
<option value="" localize="cash.approval.selectPlaceholder">Vælg...</option>
|
||||
<option>Karina Knudsen</option>
|
||||
<option>Butikschef</option>
|
||||
</select>
|
||||
|
|
@ -24,15 +24,15 @@
|
|||
|
||||
<swp-checkbox-field>
|
||||
<input type="checkbox" id="confirmCheckbox" />
|
||||
<label for="confirmCheckbox">Jeg bekræfter, at kassen er talt op, og at tallene er indtastet efter bedste evne.</label>
|
||||
<label for="confirmCheckbox" localize="cash.approval.confirmation">Jeg bekræfter, at kassen er talt op, og at tallene er indtastet efter bedste evne.</label>
|
||||
</swp-checkbox-field>
|
||||
</swp-approval-grid>
|
||||
</swp-card-content>
|
||||
<swp-card-footer>
|
||||
<swp-btn class="secondary">Gem som kladde</swp-btn>
|
||||
<swp-btn class="secondary" localize="cash.approval.saveDraft">Gem som kladde</swp-btn>
|
||||
<swp-actions-right>
|
||||
<swp-btn class="ghost">Fortryd</swp-btn>
|
||||
<swp-btn class="primary" id="approveBtn" disabled>Godkend & lås</swp-btn>
|
||||
<swp-btn class="ghost" localize="common.cancel">Fortryd</swp-btn>
|
||||
<swp-btn class="primary" id="approveBtn" disabled localize="cash.approval.approve">Godkend & lås</swp-btn>
|
||||
</swp-actions-right>
|
||||
</swp-card-footer>
|
||||
</swp-card>
|
||||
|
|
|
|||
|
|
@ -1,20 +1,20 @@
|
|||
<swp-card>
|
||||
<swp-card-header>
|
||||
<swp-card-title>Kontanter i kassen</swp-card-title>
|
||||
<swp-card-title localize="cash.balance.title">Kontanter i kassen</swp-card-title>
|
||||
</swp-card-header>
|
||||
<swp-card-content>
|
||||
<swp-calc-row>
|
||||
<swp-calc-label>
|
||||
<span>Startbeholdning</span>
|
||||
<small>Overført fra sidste afstemning</small>
|
||||
<span localize="cash.balance.startBalance">Startbeholdning</span>
|
||||
<small localize="cash.balance.startHint">Overført fra sidste afstemning</small>
|
||||
</swp-calc-label>
|
||||
<swp-calc-value class="muted">2.000,00</swp-calc-value>
|
||||
</swp-calc-row>
|
||||
|
||||
<swp-calc-row>
|
||||
<swp-calc-label>
|
||||
<span>Udbetalinger / Bilag</span>
|
||||
<small>Sammentæl bilag betalt kontant</small>
|
||||
<span localize="cash.balance.payouts">Udbetalinger / Bilag</span>
|
||||
<small localize="cash.balance.payoutsHint">Sammentæl bilag betalt kontant</small>
|
||||
</swp-calc-label>
|
||||
<swp-calc-input>
|
||||
<input type="text" id="payouts" placeholder="0,00" />
|
||||
|
|
@ -23,8 +23,8 @@
|
|||
|
||||
<swp-calc-row>
|
||||
<swp-calc-label>
|
||||
<span>Udtaget til bank</span>
|
||||
<small>Kontanter lagt til side</small>
|
||||
<span localize="cash.balance.toBank">Udtaget til bank</span>
|
||||
<small localize="cash.balance.toBankHint">Kontanter lagt til side</small>
|
||||
</swp-calc-label>
|
||||
<swp-calc-input>
|
||||
<input type="text" id="toBank" placeholder="0,00" />
|
||||
|
|
@ -33,15 +33,15 @@
|
|||
|
||||
<swp-calc-row>
|
||||
<swp-calc-label>
|
||||
<span>Forventet kontantbeholdning</span>
|
||||
<span localize="cash.balance.expected">Forventet kontantbeholdning</span>
|
||||
</swp-calc-label>
|
||||
<swp-calc-value id="expectedCash">5.220,00</swp-calc-value>
|
||||
</swp-calc-row>
|
||||
|
||||
<swp-calc-row class="input-row">
|
||||
<swp-calc-label>
|
||||
<span>Optalt kontantbeholdning <span style="color: var(--color-red)">*</span></span>
|
||||
<small>Hvad ligger der faktisk i kassen?</small>
|
||||
<span><span localize="cash.balance.counted">Optalt kontantbeholdning</span> <span style="color: var(--color-red)">*</span></span>
|
||||
<small localize="cash.balance.countedHint">Hvad ligger der faktisk i kassen?</small>
|
||||
</swp-calc-label>
|
||||
<swp-calc-input>
|
||||
<input type="text" id="actualCash" placeholder="0,00" />
|
||||
|
|
|
|||
|
|
@ -1,6 +1,6 @@
|
|||
<swp-difference-box id="differenceBox" class="neutral">
|
||||
<swp-difference-label>
|
||||
Kontant difference
|
||||
<span localize="cash.difference.title">Kassedifference</span>
|
||||
<small>Optalt minus forventet</small>
|
||||
</swp-difference-label>
|
||||
<swp-difference-value id="differenceValue">– kr</swp-difference-value>
|
||||
|
|
|
|||
|
|
@ -1,11 +1,10 @@
|
|||
<swp-card>
|
||||
<swp-card-header>
|
||||
<swp-card-title>Periodeoplysninger</swp-card-title>
|
||||
<swp-card-action>Identificér afstemningen</swp-card-action>
|
||||
<swp-card-title localize="cash.period.title">Periodeoplysninger</swp-card-title>
|
||||
</swp-card-header>
|
||||
<swp-card-content>
|
||||
<swp-period-display>
|
||||
<swp-period-label>Periode</swp-period-label>
|
||||
<swp-period-label localize="cash.period.dateRange">Periode</swp-period-label>
|
||||
<swp-period-value>
|
||||
<span class="from">28. dec 2025 kl. 18:00</span>
|
||||
<span class="arrow">→</span>
|
||||
|
|
@ -15,7 +14,7 @@
|
|||
|
||||
<swp-form-grid style="margin-top: var(--spacing-10);">
|
||||
<swp-form-field>
|
||||
<swp-form-label>Kassepunkt</swp-form-label>
|
||||
<swp-form-label localize="cash.period.register">Kassepunkt</swp-form-label>
|
||||
<swp-form-input>
|
||||
<select id="register">
|
||||
<option>Kasse 1 – Reception</option>
|
||||
|
|
@ -25,7 +24,7 @@
|
|||
</swp-form-field>
|
||||
|
||||
<swp-form-field>
|
||||
<swp-form-label>Afsluttet af</swp-form-label>
|
||||
<swp-form-label localize="cash.period.employee">Medarbejder</swp-form-label>
|
||||
<swp-form-input>
|
||||
<select id="employee">
|
||||
<option>Anna Jensen</option>
|
||||
|
|
|
|||
|
|
@ -1,7 +1,7 @@
|
|||
<swp-card>
|
||||
<swp-card-header>
|
||||
<swp-card-title>Periodens omsætning</swp-card-title>
|
||||
<swp-card-action>Systemtal vs. kontrol</swp-card-action>
|
||||
<swp-card-title localize="cash.revenue.title">Periodens omsætning</swp-card-title>
|
||||
<swp-card-action localize="cash.revenue.subtitle">Systemtal vs. kontrol</swp-card-action>
|
||||
</swp-card-header>
|
||||
<swp-card-content>
|
||||
<swp-data-table>
|
||||
|
|
@ -12,7 +12,7 @@
|
|||
</swp-data-header>
|
||||
|
||||
<swp-data-row>
|
||||
<swp-data-label>Kortbetalinger</swp-data-label>
|
||||
<swp-data-label localize="cash.revenue.cardPayments">Kortbetalinger</swp-data-label>
|
||||
<swp-data-system>12.875,50</swp-data-system>
|
||||
<swp-data-input>
|
||||
<input type="text" placeholder="Valgfrit" />
|
||||
|
|
@ -20,7 +20,7 @@
|
|||
</swp-data-row>
|
||||
|
||||
<swp-data-row>
|
||||
<swp-data-label>MobilePay / Online</swp-data-label>
|
||||
<swp-data-label localize="cash.revenue.mobilePay">MobilePay / Online</swp-data-label>
|
||||
<swp-data-system>2.450,00</swp-data-system>
|
||||
<swp-data-input>
|
||||
<input type="text" placeholder="Valgfrit" />
|
||||
|
|
@ -28,12 +28,12 @@
|
|||
</swp-data-row>
|
||||
|
||||
<swp-data-row>
|
||||
<swp-data-label>Kontantsalg</swp-data-label>
|
||||
<swp-data-label localize="cash.revenue.cashSales">Kontantsalg</swp-data-label>
|
||||
<swp-data-system>3.540,00</swp-data-system>
|
||||
<swp-data-value class="muted">..</swp-data-value>
|
||||
</swp-data-row>
|
||||
</swp-data-table>
|
||||
|
||||
<swp-table-note>Kort og MobilePay afstemmes mod bank/indløser. Kontanter tælles op nedenfor.</swp-table-note>
|
||||
<swp-table-note localize="cash.revenue.hint">Kort og MobilePay afstemmes mod bank/indløser. Kontanter tælles op nedenfor.</swp-table-note>
|
||||
</swp-card-content>
|
||||
</swp-card>
|
||||
|
|
|
|||
|
|
@ -1,12 +1,10 @@
|
|||
<swp-card>
|
||||
<swp-card-header>
|
||||
<swp-card-title>Note til difference</swp-card-title>
|
||||
<swp-card-action>Valgfrit</swp-card-action>
|
||||
<swp-card-title localize="cash.note.title">Note til afstemning</swp-card-title>
|
||||
</swp-card-header>
|
||||
<swp-card-content>
|
||||
<swp-note-field>
|
||||
<textarea placeholder="Fx kassedifference, fejlslag, runding osv."></textarea>
|
||||
<textarea placeholder="Beskriv evt. årsag til difference..."></textarea>
|
||||
</swp-note-field>
|
||||
<swp-note-hint>Kan gøres obligatorisk ved difference over 100 kr.</swp-note-hint>
|
||||
</swp-card-content>
|
||||
</swp-card>
|
||||
|
|
|
|||
|
|
@ -1,11 +1,11 @@
|
|||
<!-- Action Bar -->
|
||||
<swp-action-bar>
|
||||
<swp-selection-info>
|
||||
<span id="selectionCount">0 valgt</span>
|
||||
<span id="selectionCount" localize="cash.table.noneSelected">0 valgt</span>
|
||||
</swp-selection-info>
|
||||
<swp-btn class="primary" id="exportBtn" disabled>
|
||||
<i class="ph ph-download"></i>
|
||||
Eksporter SAF-T
|
||||
<span localize="cash.table.exportSaft">Eksporter SAF-T</span>
|
||||
</swp-btn>
|
||||
</swp-action-bar>
|
||||
|
||||
|
|
@ -13,14 +13,14 @@
|
|||
<swp-cash-table>
|
||||
<swp-cash-table-header>
|
||||
<swp-cash-th class="checkbox"><input type="checkbox" id="selectAll" /></swp-cash-th>
|
||||
<swp-cash-th>Dato</swp-cash-th>
|
||||
<swp-cash-th>ID</swp-cash-th>
|
||||
<swp-cash-th>Periode</swp-cash-th>
|
||||
<swp-cash-th>Kassepunkt</swp-cash-th>
|
||||
<swp-cash-th>Afsluttet af</swp-cash-th>
|
||||
<swp-cash-th class="right">Omsætning</swp-cash-th>
|
||||
<swp-cash-th class="right">Difference</swp-cash-th>
|
||||
<swp-cash-th>Status</swp-cash-th>
|
||||
<swp-cash-th localize="cash.table.date">Dato</swp-cash-th>
|
||||
<swp-cash-th localize="cash.table.id">ID</swp-cash-th>
|
||||
<swp-cash-th localize="cash.table.period">Periode</swp-cash-th>
|
||||
<swp-cash-th localize="cash.table.register">Kassepunkt</swp-cash-th>
|
||||
<swp-cash-th localize="cash.table.closedBy">Afsluttet af</swp-cash-th>
|
||||
<swp-cash-th class="right" localize="cash.table.revenue">Omsætning</swp-cash-th>
|
||||
<swp-cash-th class="right" localize="cash.table.difference">Difference</swp-cash-th>
|
||||
<swp-cash-th localize="common.status">Status</swp-cash-th>
|
||||
<swp-cash-th></swp-cash-th>
|
||||
</swp-cash-table-header>
|
||||
|
||||
|
|
@ -39,7 +39,7 @@
|
|||
<swp-cash-td class="muted">–</swp-cash-td>
|
||||
<swp-cash-td class="right mono muted">4.250 kr</swp-cash-td>
|
||||
<swp-cash-td class="right mono muted">–</swp-cash-td>
|
||||
<swp-cash-td><swp-status-badge class="draft">Kladde</swp-status-badge></swp-cash-td>
|
||||
<swp-cash-td><swp-status-badge class="draft" localize="cash.status.draft">Kladde</swp-status-badge></swp-cash-td>
|
||||
<swp-cash-td><swp-row-arrow><i class="ph ph-caret-right"></i></swp-row-arrow></swp-cash-td>
|
||||
</swp-cash-table-row>
|
||||
|
||||
|
|
@ -56,7 +56,7 @@
|
|||
<swp-cash-td>Anna Jensen</swp-cash-td>
|
||||
<swp-cash-td class="right mono">18.865 kr</swp-cash-td>
|
||||
<swp-cash-td class="right mono">0 kr</swp-cash-td>
|
||||
<swp-cash-td><swp-status-badge class="approved">Godkendt</swp-status-badge></swp-cash-td>
|
||||
<swp-cash-td><swp-status-badge class="approved" localize="cash.status.approved">Godkendt</swp-status-badge></swp-cash-td>
|
||||
<swp-cash-td><swp-row-toggle><i class="ph ph-caret-right"></i></swp-row-toggle></swp-cash-td>
|
||||
</swp-cash-table-row>
|
||||
<swp-cash-row-detail data-for="043">
|
||||
|
|
@ -64,15 +64,15 @@
|
|||
<swp-row-detail-actions>
|
||||
<swp-btn class="secondary">
|
||||
<i class="ph ph-file-csv"></i>
|
||||
Download CSV
|
||||
<span localize="cash.table.downloadCsv">Download CSV</span>
|
||||
</swp-btn>
|
||||
<swp-btn class="secondary">
|
||||
<i class="ph ph-file-pdf"></i>
|
||||
Download PDF
|
||||
<span localize="cash.table.downloadPdf">Download PDF</span>
|
||||
</swp-btn>
|
||||
<swp-btn class="primary">
|
||||
<i class="ph ph-list-bullets"></i>
|
||||
Se transaktioner
|
||||
<span localize="cash.table.viewTransactions">Se transaktioner</span>
|
||||
</swp-btn>
|
||||
</swp-row-detail-actions>
|
||||
</swp-row-detail-content>
|
||||
|
|
@ -91,7 +91,7 @@
|
|||
<swp-cash-td>Karina Knudsen</swp-cash-td>
|
||||
<swp-cash-td class="right mono">12.450 kr</swp-cash-td>
|
||||
<swp-cash-td class="right mono negative">-25 kr</swp-cash-td>
|
||||
<swp-cash-td><swp-status-badge class="approved">Godkendt</swp-status-badge></swp-cash-td>
|
||||
<swp-cash-td><swp-status-badge class="approved" localize="cash.status.approved">Godkendt</swp-status-badge></swp-cash-td>
|
||||
<swp-cash-td><swp-row-toggle><i class="ph ph-caret-right"></i></swp-row-toggle></swp-cash-td>
|
||||
</swp-cash-table-row>
|
||||
<swp-cash-row-detail data-for="042">
|
||||
|
|
@ -99,15 +99,15 @@
|
|||
<swp-row-detail-actions>
|
||||
<swp-btn class="secondary">
|
||||
<i class="ph ph-file-csv"></i>
|
||||
Download CSV
|
||||
<span localize="cash.table.downloadCsv">Download CSV</span>
|
||||
</swp-btn>
|
||||
<swp-btn class="secondary">
|
||||
<i class="ph ph-file-pdf"></i>
|
||||
Download PDF
|
||||
<span localize="cash.table.downloadPdf">Download PDF</span>
|
||||
</swp-btn>
|
||||
<swp-btn class="primary">
|
||||
<i class="ph ph-list-bullets"></i>
|
||||
Se transaktioner
|
||||
<span localize="cash.table.viewTransactions">Se transaktioner</span>
|
||||
</swp-btn>
|
||||
</swp-row-detail-actions>
|
||||
</swp-row-detail-content>
|
||||
|
|
@ -126,7 +126,7 @@
|
|||
<swp-cash-td>Martin Nielsen</swp-cash-td>
|
||||
<swp-cash-td class="right mono">21.340 kr</swp-cash-td>
|
||||
<swp-cash-td class="right mono">0 kr</swp-cash-td>
|
||||
<swp-cash-td><swp-status-badge class="approved">Godkendt</swp-status-badge></swp-cash-td>
|
||||
<swp-cash-td><swp-status-badge class="approved" localize="cash.status.approved">Godkendt</swp-status-badge></swp-cash-td>
|
||||
<swp-cash-td><swp-row-toggle><i class="ph ph-caret-right"></i></swp-row-toggle></swp-cash-td>
|
||||
</swp-cash-table-row>
|
||||
<swp-cash-row-detail data-for="041">
|
||||
|
|
@ -134,15 +134,15 @@
|
|||
<swp-row-detail-actions>
|
||||
<swp-btn class="secondary">
|
||||
<i class="ph ph-file-csv"></i>
|
||||
Download CSV
|
||||
<span localize="cash.table.downloadCsv">Download CSV</span>
|
||||
</swp-btn>
|
||||
<swp-btn class="secondary">
|
||||
<i class="ph ph-file-pdf"></i>
|
||||
Download PDF
|
||||
<span localize="cash.table.downloadPdf">Download PDF</span>
|
||||
</swp-btn>
|
||||
<swp-btn class="primary">
|
||||
<i class="ph ph-list-bullets"></i>
|
||||
Se transaktioner
|
||||
<span localize="cash.table.viewTransactions">Se transaktioner</span>
|
||||
</swp-btn>
|
||||
</swp-row-detail-actions>
|
||||
</swp-row-detail-content>
|
||||
|
|
@ -161,7 +161,7 @@
|
|||
<swp-cash-td>Anna Jensen</swp-cash-td>
|
||||
<swp-cash-td class="right mono">28.750 kr</swp-cash-td>
|
||||
<swp-cash-td class="right mono negative">-50 kr</swp-cash-td>
|
||||
<swp-cash-td><swp-status-badge class="approved">Godkendt</swp-status-badge></swp-cash-td>
|
||||
<swp-cash-td><swp-status-badge class="approved" localize="cash.status.approved">Godkendt</swp-status-badge></swp-cash-td>
|
||||
<swp-cash-td><swp-row-toggle><i class="ph ph-caret-right"></i></swp-row-toggle></swp-cash-td>
|
||||
</swp-cash-table-row>
|
||||
<swp-cash-row-detail data-for="040">
|
||||
|
|
@ -169,15 +169,15 @@
|
|||
<swp-row-detail-actions>
|
||||
<swp-btn class="secondary">
|
||||
<i class="ph ph-file-csv"></i>
|
||||
Download CSV
|
||||
<span localize="cash.table.downloadCsv">Download CSV</span>
|
||||
</swp-btn>
|
||||
<swp-btn class="secondary">
|
||||
<i class="ph ph-file-pdf"></i>
|
||||
Download PDF
|
||||
<span localize="cash.table.downloadPdf">Download PDF</span>
|
||||
</swp-btn>
|
||||
<swp-btn class="primary">
|
||||
<i class="ph ph-list-bullets"></i>
|
||||
Se transaktioner
|
||||
<span localize="cash.table.viewTransactions">Se transaktioner</span>
|
||||
</swp-btn>
|
||||
</swp-row-detail-actions>
|
||||
</swp-row-detail-content>
|
||||
|
|
|
|||
|
|
@ -1,28 +1,28 @@
|
|||
<swp-filter-bar>
|
||||
<swp-filter-group>
|
||||
<swp-filter-label>Fra</swp-filter-label>
|
||||
<swp-filter-label localize="common.from">Fra</swp-filter-label>
|
||||
<input type="date" id="dateFrom" />
|
||||
</swp-filter-group>
|
||||
<swp-filter-group>
|
||||
<swp-filter-label>Til</swp-filter-label>
|
||||
<swp-filter-label localize="common.to">Til</swp-filter-label>
|
||||
<input type="date" id="dateTo" />
|
||||
</swp-filter-group>
|
||||
<swp-filter-group>
|
||||
<swp-filter-label>Kassepunkt</swp-filter-label>
|
||||
<swp-filter-label localize="cash.filter.register">Kassepunkt</swp-filter-label>
|
||||
<select id="register">
|
||||
<option>Alle</option>
|
||||
<option localize="common.all">Alle</option>
|
||||
<option>Kasse 1 – Reception</option>
|
||||
<option>Kasse 2 – Salon</option>
|
||||
</select>
|
||||
</swp-filter-group>
|
||||
<swp-filter-group>
|
||||
<swp-filter-label>Status</swp-filter-label>
|
||||
<swp-filter-label localize="common.status">Status</swp-filter-label>
|
||||
<select id="status">
|
||||
<option>Alle</option>
|
||||
<option>Godkendt</option>
|
||||
<option>Kladde</option>
|
||||
<option localize="common.all">Alle</option>
|
||||
<option localize="cash.filter.approved">Godkendt</option>
|
||||
<option localize="cash.filter.draft">Kladde</option>
|
||||
</select>
|
||||
</swp-filter-group>
|
||||
<swp-filter-spacer></swp-filter-spacer>
|
||||
<swp-btn class="secondary">Nulstil</swp-btn>
|
||||
<swp-btn class="secondary" localize="common.reset">Nulstil</swp-btn>
|
||||
</swp-filter-bar>
|
||||
|
|
|
|||
|
|
@ -13,19 +13,19 @@
|
|||
<swp-cash-stats data-for-tab="oversigt" class="active">
|
||||
<swp-cash-stat>
|
||||
<swp-cash-stat-value>12</swp-cash-stat-value>
|
||||
<swp-cash-stat-label>Afstemninger i periode</swp-cash-stat-label>
|
||||
<swp-cash-stat-label localize="cash.stats.reconciliationsInPeriod">Afstemninger i periode</swp-cash-stat-label>
|
||||
</swp-cash-stat>
|
||||
<swp-cash-stat class="highlight">
|
||||
<swp-cash-stat-value>186.450 kr</swp-cash-stat-value>
|
||||
<swp-cash-stat-label>Total omsætning</swp-cash-stat-label>
|
||||
<swp-cash-stat-label localize="cash.stats.totalRevenue">Total omsætning</swp-cash-stat-label>
|
||||
</swp-cash-stat>
|
||||
<swp-cash-stat>
|
||||
<swp-cash-stat-value>42.340 kr</swp-cash-stat-value>
|
||||
<swp-cash-stat-label>Kontantsalg</swp-cash-stat-label>
|
||||
<swp-cash-stat-label localize="cash.stats.cashSales">Kontantsalg</swp-cash-stat-label>
|
||||
</swp-cash-stat>
|
||||
<swp-cash-stat class="warning">
|
||||
<swp-cash-stat-value>-75 kr</swp-cash-stat-value>
|
||||
<swp-cash-stat-label>Samlet difference</swp-cash-stat-label>
|
||||
<swp-cash-stat-label localize="cash.stats.totalDifference">Samlet difference</swp-cash-stat-label>
|
||||
</swp-cash-stat>
|
||||
</swp-cash-stats>
|
||||
|
||||
|
|
@ -33,19 +33,19 @@
|
|||
<swp-cash-stats data-for-tab="afstemning">
|
||||
<swp-cash-stat>
|
||||
<swp-cash-stat-value>47</swp-cash-stat-value>
|
||||
<swp-cash-stat-label>Transaktioner i dag</swp-cash-stat-label>
|
||||
<swp-cash-stat-label localize="cash.stats.transactionsToday">Transaktioner i dag</swp-cash-stat-label>
|
||||
</swp-cash-stat>
|
||||
<swp-cash-stat class="highlight">
|
||||
<swp-cash-stat-value>18.865 kr</swp-cash-stat-value>
|
||||
<swp-cash-stat-label>Omsætning i dag</swp-cash-stat-label>
|
||||
<swp-cash-stat-label localize="cash.stats.revenueToday">Omsætning i dag</swp-cash-stat-label>
|
||||
</swp-cash-stat>
|
||||
<swp-cash-stat>
|
||||
<swp-cash-stat-value>29. dec 17:45</swp-cash-stat-value>
|
||||
<swp-cash-stat-label>Sidste afstemning</swp-cash-stat-label>
|
||||
<swp-cash-stat-label localize="cash.stats.lastReconciliation">Sidste afstemning</swp-cash-stat-label>
|
||||
</swp-cash-stat>
|
||||
<swp-cash-stat>
|
||||
<swp-cash-stat-value>Anna J.</swp-cash-stat-value>
|
||||
<swp-cash-stat-label>Åbnede kassen 29. dec 09:05</swp-cash-stat-label>
|
||||
<swp-cash-stat-label localize="cash.stats.openedRegister">Åbnede kassen 29. dec 09:05</swp-cash-stat-label>
|
||||
</swp-cash-stat>
|
||||
</swp-cash-stats>
|
||||
</swp-cash-header>
|
||||
|
|
@ -54,11 +54,11 @@
|
|||
<swp-tab-bar>
|
||||
<swp-tab class="active" data-tab="oversigt">
|
||||
<i class="ph ph-list-checks"></i>
|
||||
Oversigt
|
||||
<span localize="cash.tabs.overview">Oversigt</span>
|
||||
</swp-tab>
|
||||
<swp-tab data-tab="afstemning">
|
||||
<i class="ph ph-cash-register"></i>
|
||||
Kasseafstemning
|
||||
<span localize="cash.tabs.reconciliation">Kasseafstemning</span>
|
||||
</swp-tab>
|
||||
</swp-tab-bar>
|
||||
</swp-cash-sticky-header>
|
||||
|
|
@ -88,6 +88,6 @@
|
|||
</swp-cash-column>
|
||||
</swp-cash-grid>
|
||||
|
||||
<swp-system-note>Systemet gemmer hvornår og af hvem der er godkendt – enkelt kontrolspor.</swp-system-note>
|
||||
<swp-system-note localize="cash.systemNote">Systemet gemmer hvornår og af hvem der er godkendt – enkelt kontrolspor.</swp-system-note>
|
||||
</swp-page-container>
|
||||
</swp-tab-content>
|
||||
|
|
|
|||
|
|
@ -1,12 +1,20 @@
|
|||
using Microsoft.AspNetCore.Mvc;
|
||||
using PlanTempus.Application.Features.Localization.Services;
|
||||
|
||||
namespace PlanTempus.Application.Features.Dashboard.Components;
|
||||
|
||||
public class AttentionListViewComponent : ViewComponent
|
||||
{
|
||||
private readonly ILocalizationService _localization;
|
||||
|
||||
public AttentionListViewComponent(ILocalizationService localization)
|
||||
{
|
||||
_localization = localization;
|
||||
}
|
||||
|
||||
public IViewComponentResult Invoke(string key)
|
||||
{
|
||||
var model = AttentionListCatalog.Get(key);
|
||||
var model = AttentionListCatalog.Get(key, _localization);
|
||||
return View(model);
|
||||
}
|
||||
}
|
||||
|
|
@ -18,23 +26,35 @@ public class AttentionListViewModel
|
|||
public required IReadOnlyList<string> AttentionKeys { get; init; }
|
||||
}
|
||||
|
||||
internal class AttentionListData
|
||||
{
|
||||
public required string Key { get; init; }
|
||||
public required string TitleKey { get; init; }
|
||||
public required IReadOnlyList<string> AttentionKeys { get; init; }
|
||||
}
|
||||
|
||||
public static class AttentionListCatalog
|
||||
{
|
||||
private static readonly Dictionary<string, AttentionListViewModel> Lists = new()
|
||||
private static readonly Dictionary<string, AttentionListData> Lists = new()
|
||||
{
|
||||
["current-attentions"] = new AttentionListViewModel
|
||||
["current-attentions"] = new AttentionListData
|
||||
{
|
||||
Key = "current-attentions",
|
||||
Title = "Opmærksomheder",
|
||||
TitleKey = "dashboard.attentions.title",
|
||||
AttentionKeys = ["attention-1", "attention-2", "attention-3"]
|
||||
}
|
||||
};
|
||||
|
||||
public static AttentionListViewModel Get(string key)
|
||||
public static AttentionListViewModel Get(string key, ILocalizationService localization)
|
||||
{
|
||||
if (Lists.TryGetValue(key, out var list))
|
||||
return list;
|
||||
if (!Lists.TryGetValue(key, out var list))
|
||||
throw new KeyNotFoundException($"AttentionList with key '{key}' not found");
|
||||
|
||||
throw new KeyNotFoundException($"AttentionList with key '{key}' not found");
|
||||
return new AttentionListViewModel
|
||||
{
|
||||
Key = list.Key,
|
||||
Title = localization.Get(list.TitleKey),
|
||||
AttentionKeys = list.AttentionKeys
|
||||
};
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,12 +1,20 @@
|
|||
using Microsoft.AspNetCore.Mvc;
|
||||
using PlanTempus.Application.Features.Localization.Services;
|
||||
|
||||
namespace PlanTempus.Application.Features.Dashboard.Components;
|
||||
|
||||
public class BookingItemViewComponent : ViewComponent
|
||||
{
|
||||
private readonly ILocalizationService _localization;
|
||||
|
||||
public BookingItemViewComponent(ILocalizationService localization)
|
||||
{
|
||||
_localization = localization;
|
||||
}
|
||||
|
||||
public IViewComponentResult Invoke(string key)
|
||||
{
|
||||
var model = BookingItemCatalog.Get(key);
|
||||
var model = BookingItemCatalog.Get(key, _localization);
|
||||
return View(model);
|
||||
}
|
||||
}
|
||||
|
|
@ -21,23 +29,36 @@ public class BookingItemViewModel
|
|||
public required string EmployeeInitials { get; init; }
|
||||
public required string EmployeeName { get; init; }
|
||||
public required string Status { get; init; }
|
||||
public required string StatusText { get; init; }
|
||||
public string? IndicatorColor { get; init; }
|
||||
}
|
||||
|
||||
public string StatusText => Status switch
|
||||
{
|
||||
"completed" => "Gennemført",
|
||||
"inprogress" => "I gang",
|
||||
"confirmed" => "Bekræftet",
|
||||
"pending" => "Afventer",
|
||||
_ => Status
|
||||
};
|
||||
internal class BookingItemData
|
||||
{
|
||||
public required string Key { get; init; }
|
||||
public required string TimeStart { get; init; }
|
||||
public required string TimeEnd { get; init; }
|
||||
public required string Service { get; init; }
|
||||
public required string CustomerName { get; init; }
|
||||
public required string EmployeeInitials { get; init; }
|
||||
public required string EmployeeName { get; init; }
|
||||
public required string Status { get; init; }
|
||||
public string? IndicatorColor { get; init; }
|
||||
}
|
||||
|
||||
public static class BookingItemCatalog
|
||||
{
|
||||
private static readonly Dictionary<string, BookingItemViewModel> Bookings = new()
|
||||
private static readonly Dictionary<string, string> StatusKeys = new()
|
||||
{
|
||||
["booking-1"] = new BookingItemViewModel
|
||||
["completed"] = "dashboard.bookings.status.completed",
|
||||
["inprogress"] = "dashboard.bookings.status.inProgress",
|
||||
["confirmed"] = "dashboard.bookings.status.confirmed",
|
||||
["pending"] = "dashboard.bookings.status.pending"
|
||||
};
|
||||
|
||||
private static readonly Dictionary<string, BookingItemData> Bookings = new()
|
||||
{
|
||||
["booking-1"] = new BookingItemData
|
||||
{
|
||||
Key = "booking-1",
|
||||
TimeStart = "08:00",
|
||||
|
|
@ -48,7 +69,7 @@ public static class BookingItemCatalog
|
|||
EmployeeName = "Maria Hansen",
|
||||
Status = "completed"
|
||||
},
|
||||
["booking-2"] = new BookingItemViewModel
|
||||
["booking-2"] = new BookingItemData
|
||||
{
|
||||
Key = "booking-2",
|
||||
TimeStart = "08:30",
|
||||
|
|
@ -59,7 +80,7 @@ public static class BookingItemCatalog
|
|||
EmployeeName = "Anna Sørensen",
|
||||
Status = "completed"
|
||||
},
|
||||
["booking-3"] = new BookingItemViewModel
|
||||
["booking-3"] = new BookingItemData
|
||||
{
|
||||
Key = "booking-3",
|
||||
TimeStart = "09:00",
|
||||
|
|
@ -70,7 +91,7 @@ public static class BookingItemCatalog
|
|||
EmployeeName = "Peter Kristensen",
|
||||
Status = "completed"
|
||||
},
|
||||
["booking-4"] = new BookingItemViewModel
|
||||
["booking-4"] = new BookingItemData
|
||||
{
|
||||
Key = "booking-4",
|
||||
TimeStart = "10:30",
|
||||
|
|
@ -82,7 +103,7 @@ public static class BookingItemCatalog
|
|||
Status = "inprogress",
|
||||
IndicatorColor = "blue"
|
||||
},
|
||||
["booking-5"] = new BookingItemViewModel
|
||||
["booking-5"] = new BookingItemData
|
||||
{
|
||||
Key = "booking-5",
|
||||
TimeStart = "10:00",
|
||||
|
|
@ -94,7 +115,7 @@ public static class BookingItemCatalog
|
|||
Status = "inprogress",
|
||||
IndicatorColor = "purple"
|
||||
},
|
||||
["booking-6"] = new BookingItemViewModel
|
||||
["booking-6"] = new BookingItemData
|
||||
{
|
||||
Key = "booking-6",
|
||||
TimeStart = "11:00",
|
||||
|
|
@ -108,12 +129,28 @@ public static class BookingItemCatalog
|
|||
}
|
||||
};
|
||||
|
||||
public static BookingItemViewModel Get(string key)
|
||||
public static BookingItemViewModel Get(string key, ILocalizationService localization)
|
||||
{
|
||||
if (Bookings.TryGetValue(key, out var booking))
|
||||
return booking;
|
||||
if (!Bookings.TryGetValue(key, out var booking))
|
||||
throw new KeyNotFoundException($"BookingItem with key '{key}' not found");
|
||||
|
||||
throw new KeyNotFoundException($"BookingItem with key '{key}' not found");
|
||||
var statusText = StatusKeys.TryGetValue(booking.Status, out var statusKey)
|
||||
? localization.Get(statusKey)
|
||||
: booking.Status;
|
||||
|
||||
return new BookingItemViewModel
|
||||
{
|
||||
Key = booking.Key,
|
||||
TimeStart = booking.TimeStart,
|
||||
TimeEnd = booking.TimeEnd,
|
||||
Service = booking.Service,
|
||||
CustomerName = booking.CustomerName,
|
||||
EmployeeInitials = booking.EmployeeInitials,
|
||||
EmployeeName = booking.EmployeeName,
|
||||
Status = booking.Status,
|
||||
StatusText = statusText,
|
||||
IndicatorColor = booking.IndicatorColor
|
||||
};
|
||||
}
|
||||
|
||||
public static IEnumerable<string> AllKeys => Bookings.Keys;
|
||||
|
|
|
|||
|
|
@ -1,12 +1,20 @@
|
|||
using Microsoft.AspNetCore.Mvc;
|
||||
using PlanTempus.Application.Features.Localization.Services;
|
||||
|
||||
namespace PlanTempus.Application.Features.Dashboard.Components;
|
||||
|
||||
public class BookingListViewComponent : ViewComponent
|
||||
{
|
||||
private readonly ILocalizationService _localization;
|
||||
|
||||
public BookingListViewComponent(ILocalizationService localization)
|
||||
{
|
||||
_localization = localization;
|
||||
}
|
||||
|
||||
public IViewComponentResult Invoke(string key)
|
||||
{
|
||||
var model = BookingListCatalog.Get(key);
|
||||
var model = BookingListCatalog.Get(key, _localization);
|
||||
return View(model);
|
||||
}
|
||||
}
|
||||
|
|
@ -19,24 +27,38 @@ public class BookingListViewModel
|
|||
public required IReadOnlyList<string> BookingKeys { get; init; }
|
||||
}
|
||||
|
||||
internal class BookingListData
|
||||
{
|
||||
public required string Key { get; init; }
|
||||
public required string TitleKey { get; init; }
|
||||
public required string CurrentTime { get; init; }
|
||||
public required IReadOnlyList<string> BookingKeys { get; init; }
|
||||
}
|
||||
|
||||
public static class BookingListCatalog
|
||||
{
|
||||
private static readonly Dictionary<string, BookingListViewModel> Lists = new()
|
||||
private static readonly Dictionary<string, BookingListData> Lists = new()
|
||||
{
|
||||
["todays-bookings"] = new BookingListViewModel
|
||||
["todays-bookings"] = new BookingListData
|
||||
{
|
||||
Key = "todays-bookings",
|
||||
Title = "Dagens bookinger",
|
||||
TitleKey = "dashboard.bookings.title",
|
||||
CurrentTime = "10:45",
|
||||
BookingKeys = ["booking-1", "booking-2", "booking-3", "booking-4", "booking-5", "booking-6"]
|
||||
}
|
||||
};
|
||||
|
||||
public static BookingListViewModel Get(string key)
|
||||
public static BookingListViewModel Get(string key, ILocalizationService localization)
|
||||
{
|
||||
if (Lists.TryGetValue(key, out var list))
|
||||
return list;
|
||||
if (!Lists.TryGetValue(key, out var list))
|
||||
throw new KeyNotFoundException($"BookingList with key '{key}' not found");
|
||||
|
||||
throw new KeyNotFoundException($"BookingList with key '{key}' not found");
|
||||
return new BookingListViewModel
|
||||
{
|
||||
Key = list.Key,
|
||||
Title = localization.Get(list.TitleKey),
|
||||
CurrentTime = list.CurrentTime,
|
||||
BookingKeys = list.BookingKeys
|
||||
};
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -6,11 +6,11 @@
|
|||
<i class="ph ph-calendar-check"></i>
|
||||
@Model.Title
|
||||
</swp-card-title>
|
||||
<swp-card-action>Se alle</swp-card-action>
|
||||
<swp-card-action localize="dashboard.bookings.viewAll">Se alle</swp-card-action>
|
||||
</swp-card-header>
|
||||
<swp-current-time>
|
||||
<i class="ph ph-clock"></i>
|
||||
<span>Nu: <swp-time>@Model.CurrentTime</swp-time></span>
|
||||
<span><span localize="dashboard.bookings.currentTime">Nu:</span> <swp-time>@Model.CurrentTime</swp-time></span>
|
||||
</swp-current-time>
|
||||
<swp-card-content>
|
||||
<swp-booking-list>
|
||||
|
|
|
|||
|
|
@ -1,12 +1,20 @@
|
|||
using Microsoft.AspNetCore.Mvc;
|
||||
using PlanTempus.Application.Features.Localization.Services;
|
||||
|
||||
namespace PlanTempus.Application.Features.Dashboard.Components;
|
||||
|
||||
public class NotificationListViewComponent : ViewComponent
|
||||
{
|
||||
private readonly ILocalizationService _localization;
|
||||
|
||||
public NotificationListViewComponent(ILocalizationService localization)
|
||||
{
|
||||
_localization = localization;
|
||||
}
|
||||
|
||||
public IViewComponentResult Invoke(string key)
|
||||
{
|
||||
var model = NotificationListCatalog.Get(key);
|
||||
var model = NotificationListCatalog.Get(key, _localization);
|
||||
return View(model);
|
||||
}
|
||||
}
|
||||
|
|
@ -19,24 +27,38 @@ public class NotificationListViewModel
|
|||
public required IReadOnlyList<string> NotificationKeys { get; init; }
|
||||
}
|
||||
|
||||
internal class NotificationListData
|
||||
{
|
||||
public required string Key { get; init; }
|
||||
public required string TitleKey { get; init; }
|
||||
public required string ActionTextKey { get; init; }
|
||||
public required IReadOnlyList<string> NotificationKeys { get; init; }
|
||||
}
|
||||
|
||||
public static class NotificationListCatalog
|
||||
{
|
||||
private static readonly Dictionary<string, NotificationListViewModel> Lists = new()
|
||||
private static readonly Dictionary<string, NotificationListData> Lists = new()
|
||||
{
|
||||
["recent-notifications"] = new NotificationListViewModel
|
||||
["recent-notifications"] = new NotificationListData
|
||||
{
|
||||
Key = "recent-notifications",
|
||||
Title = "Notifikationer",
|
||||
ActionText = "Marker alle som læst",
|
||||
TitleKey = "dashboard.notifications.title",
|
||||
ActionTextKey = "dashboard.notifications.markAllRead",
|
||||
NotificationKeys = ["notif-1", "notif-2", "notif-3", "notif-4"]
|
||||
}
|
||||
};
|
||||
|
||||
public static NotificationListViewModel Get(string key)
|
||||
public static NotificationListViewModel Get(string key, ILocalizationService localization)
|
||||
{
|
||||
if (Lists.TryGetValue(key, out var list))
|
||||
return list;
|
||||
if (!Lists.TryGetValue(key, out var list))
|
||||
throw new KeyNotFoundException($"NotificationList with key '{key}' not found");
|
||||
|
||||
throw new KeyNotFoundException($"NotificationList with key '{key}' not found");
|
||||
return new NotificationListViewModel
|
||||
{
|
||||
Key = list.Key,
|
||||
Title = localization.Get(list.TitleKey),
|
||||
ActionText = localization.Get(list.ActionTextKey),
|
||||
NotificationKeys = list.NotificationKeys
|
||||
};
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,4 +1,5 @@
|
|||
using Microsoft.AspNetCore.Mvc;
|
||||
using PlanTempus.Application.Features.Localization.Services;
|
||||
|
||||
namespace PlanTempus.Application.Features.Dashboard.Components;
|
||||
|
||||
|
|
@ -7,9 +8,16 @@ namespace PlanTempus.Application.Features.Dashboard.Components;
|
|||
/// </summary>
|
||||
public class QuickStatViewComponent : ViewComponent
|
||||
{
|
||||
private readonly ILocalizationService _localization;
|
||||
|
||||
public QuickStatViewComponent(ILocalizationService localization)
|
||||
{
|
||||
_localization = localization;
|
||||
}
|
||||
|
||||
public IViewComponentResult Invoke(string key)
|
||||
{
|
||||
var model = QuickStatCatalog.Get(key);
|
||||
var model = QuickStatCatalog.Get(key, _localization);
|
||||
return View(model);
|
||||
}
|
||||
}
|
||||
|
|
@ -24,45 +32,57 @@ public class QuickStatViewModel
|
|||
public required string Label { get; init; }
|
||||
}
|
||||
|
||||
internal class QuickStatData
|
||||
{
|
||||
public required string Key { get; init; }
|
||||
public required string Value { get; init; }
|
||||
public required string LabelKey { get; init; }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Catalog of available quick stats with their data.
|
||||
/// </summary>
|
||||
public static class QuickStatCatalog
|
||||
{
|
||||
private static readonly Dictionary<string, QuickStatViewModel> Stats = new()
|
||||
private static readonly Dictionary<string, QuickStatData> Stats = new()
|
||||
{
|
||||
["bookings-week"] = new QuickStatViewModel
|
||||
["bookings-week"] = new QuickStatData
|
||||
{
|
||||
Key = "bookings-week",
|
||||
Value = "47",
|
||||
Label = "Bookinger"
|
||||
LabelKey = "dashboard.quickStats.bookings"
|
||||
},
|
||||
["revenue-week"] = new QuickStatViewModel
|
||||
["revenue-week"] = new QuickStatData
|
||||
{
|
||||
Key = "revenue-week",
|
||||
Value = "38.200 kr",
|
||||
Label = "Omsætning"
|
||||
LabelKey = "dashboard.quickStats.revenue"
|
||||
},
|
||||
["new-customers"] = new QuickStatViewModel
|
||||
["new-customers"] = new QuickStatData
|
||||
{
|
||||
Key = "new-customers",
|
||||
Value = "8",
|
||||
Label = "Nye kunder"
|
||||
LabelKey = "dashboard.quickStats.newCustomers"
|
||||
},
|
||||
["avg-occupancy"] = new QuickStatViewModel
|
||||
["avg-occupancy"] = new QuickStatData
|
||||
{
|
||||
Key = "avg-occupancy",
|
||||
Value = "72%",
|
||||
Label = "Gns. belægning"
|
||||
LabelKey = "dashboard.quickStats.avgOccupancy"
|
||||
}
|
||||
};
|
||||
|
||||
public static QuickStatViewModel Get(string key)
|
||||
public static QuickStatViewModel Get(string key, ILocalizationService localization)
|
||||
{
|
||||
if (Stats.TryGetValue(key, out var stat))
|
||||
return stat;
|
||||
if (!Stats.TryGetValue(key, out var stat))
|
||||
throw new KeyNotFoundException($"QuickStat with key '{key}' not found");
|
||||
|
||||
throw new KeyNotFoundException($"QuickStat with key '{key}' not found");
|
||||
return new QuickStatViewModel
|
||||
{
|
||||
Key = stat.Key,
|
||||
Value = stat.Value,
|
||||
Label = localization.Get(stat.LabelKey)
|
||||
};
|
||||
}
|
||||
|
||||
public static IEnumerable<string> AllKeys => Stats.Keys;
|
||||
|
|
|
|||
|
|
@ -1,12 +1,20 @@
|
|||
using Microsoft.AspNetCore.Mvc;
|
||||
using PlanTempus.Application.Features.Localization.Services;
|
||||
|
||||
namespace PlanTempus.Application.Features.Dashboard.Components;
|
||||
|
||||
public class QuickStatListViewComponent : ViewComponent
|
||||
{
|
||||
private readonly ILocalizationService _localization;
|
||||
|
||||
public QuickStatListViewComponent(ILocalizationService localization)
|
||||
{
|
||||
_localization = localization;
|
||||
}
|
||||
|
||||
public IViewComponentResult Invoke(string key)
|
||||
{
|
||||
var model = QuickStatListCatalog.Get(key);
|
||||
var model = QuickStatListCatalog.Get(key, _localization);
|
||||
return View(model);
|
||||
}
|
||||
}
|
||||
|
|
@ -19,24 +27,38 @@ public class QuickStatListViewModel
|
|||
public required IReadOnlyList<string> StatKeys { get; init; }
|
||||
}
|
||||
|
||||
internal class QuickStatListData
|
||||
{
|
||||
public required string Key { get; init; }
|
||||
public required string TitleKey { get; init; }
|
||||
public required string Icon { get; init; }
|
||||
public required IReadOnlyList<string> StatKeys { get; init; }
|
||||
}
|
||||
|
||||
public static class QuickStatListCatalog
|
||||
{
|
||||
private static readonly Dictionary<string, QuickStatListViewModel> Lists = new()
|
||||
private static readonly Dictionary<string, QuickStatListData> Lists = new()
|
||||
{
|
||||
["this-week"] = new QuickStatListViewModel
|
||||
["this-week"] = new QuickStatListData
|
||||
{
|
||||
Key = "this-week",
|
||||
Title = "Denne uge",
|
||||
TitleKey = "dashboard.quickStats.title",
|
||||
Icon = "chart-line-up",
|
||||
StatKeys = ["bookings-week", "revenue-week", "new-customers", "avg-occupancy"]
|
||||
}
|
||||
};
|
||||
|
||||
public static QuickStatListViewModel Get(string key)
|
||||
public static QuickStatListViewModel Get(string key, ILocalizationService localization)
|
||||
{
|
||||
if (Lists.TryGetValue(key, out var list))
|
||||
return list;
|
||||
if (!Lists.TryGetValue(key, out var list))
|
||||
throw new KeyNotFoundException($"QuickStatList with key '{key}' not found");
|
||||
|
||||
throw new KeyNotFoundException($"QuickStatList with key '{key}' not found");
|
||||
return new QuickStatListViewModel
|
||||
{
|
||||
Key = list.Key,
|
||||
Title = localization.Get(list.TitleKey),
|
||||
Icon = list.Icon,
|
||||
StatKeys = list.StatKeys
|
||||
};
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,4 +1,5 @@
|
|||
using Microsoft.AspNetCore.Mvc;
|
||||
using PlanTempus.Application.Features.Localization.Services;
|
||||
|
||||
namespace PlanTempus.Application.Features.Dashboard.Components;
|
||||
|
||||
|
|
@ -7,9 +8,16 @@ namespace PlanTempus.Application.Features.Dashboard.Components;
|
|||
/// </summary>
|
||||
public class StatCardViewComponent : ViewComponent
|
||||
{
|
||||
private readonly ILocalizationService _localization;
|
||||
|
||||
public StatCardViewComponent(ILocalizationService localization)
|
||||
{
|
||||
_localization = localization;
|
||||
}
|
||||
|
||||
public IViewComponentResult Invoke(string key)
|
||||
{
|
||||
var model = StatCardCatalog.Get(key);
|
||||
var model = StatCardCatalog.Get(key, _localization);
|
||||
return View(model);
|
||||
}
|
||||
}
|
||||
|
|
@ -29,61 +37,84 @@ public class StatCardViewModel
|
|||
public bool HasTrend => !string.IsNullOrEmpty(TrendText);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Internal data for stat cards (uses localization keys).
|
||||
/// </summary>
|
||||
internal class StatCardData
|
||||
{
|
||||
public required string Key { get; init; }
|
||||
public required string Value { get; init; }
|
||||
public required string LabelKey { get; init; }
|
||||
public string? TrendTextKey { get; init; }
|
||||
public string? TrendIcon { get; init; }
|
||||
public string? TrendDirection { get; init; }
|
||||
public string? Variant { get; init; }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Catalog of available stat cards with their data.
|
||||
/// </summary>
|
||||
public static class StatCardCatalog
|
||||
{
|
||||
private static readonly Dictionary<string, StatCardViewModel> Cards = new()
|
||||
private static readonly Dictionary<string, StatCardData> Cards = new()
|
||||
{
|
||||
["bookings-today"] = new StatCardViewModel
|
||||
["bookings-today"] = new StatCardData
|
||||
{
|
||||
Key = "bookings-today",
|
||||
Value = "12",
|
||||
Label = "Bookinger i dag",
|
||||
TrendText = "4 gennemført, 2 i gang",
|
||||
LabelKey = "dashboard.stats.bookingsToday",
|
||||
TrendTextKey = "dashboard.stats.bookingsTrend",
|
||||
TrendIcon = "ph-check-circle",
|
||||
TrendDirection = "up",
|
||||
Variant = "highlight"
|
||||
},
|
||||
["expected-revenue"] = new StatCardViewModel
|
||||
["expected-revenue"] = new StatCardData
|
||||
{
|
||||
Key = "expected-revenue",
|
||||
Value = "8.450 kr",
|
||||
Label = "Forventet omsætning",
|
||||
TrendText = "+12% vs. gennemsnit",
|
||||
LabelKey = "dashboard.stats.expectedRevenue",
|
||||
TrendTextKey = "dashboard.stats.revenueTrend",
|
||||
TrendIcon = "ph-trend-up",
|
||||
TrendDirection = "up",
|
||||
Variant = "success"
|
||||
},
|
||||
["occupancy-rate"] = new StatCardViewModel
|
||||
["occupancy-rate"] = new StatCardData
|
||||
{
|
||||
Key = "occupancy-rate",
|
||||
Value = "78%",
|
||||
Label = "Belægningsgrad",
|
||||
TrendText = "God kapacitet",
|
||||
LabelKey = "dashboard.stats.occupancyRate",
|
||||
TrendTextKey = "dashboard.stats.occupancyTrend",
|
||||
TrendIcon = "ph-trend-up",
|
||||
TrendDirection = "up",
|
||||
Variant = null
|
||||
},
|
||||
["needs-attention"] = new StatCardViewModel
|
||||
["needs-attention"] = new StatCardData
|
||||
{
|
||||
Key = "needs-attention",
|
||||
Value = "4",
|
||||
Label = "Kræver opmærksomhed",
|
||||
TrendText = null,
|
||||
LabelKey = "dashboard.stats.needsAttention",
|
||||
TrendTextKey = null,
|
||||
TrendIcon = null,
|
||||
TrendDirection = null,
|
||||
Variant = "warning"
|
||||
}
|
||||
};
|
||||
|
||||
public static StatCardViewModel Get(string key)
|
||||
public static StatCardViewModel Get(string key, ILocalizationService localization)
|
||||
{
|
||||
if (Cards.TryGetValue(key, out var card))
|
||||
return card;
|
||||
if (!Cards.TryGetValue(key, out var card))
|
||||
throw new KeyNotFoundException($"StatCard with key '{key}' not found");
|
||||
|
||||
throw new KeyNotFoundException($"StatCard with key '{key}' not found");
|
||||
return new StatCardViewModel
|
||||
{
|
||||
Key = card.Key,
|
||||
Value = card.Value,
|
||||
Label = localization.Get(card.LabelKey),
|
||||
TrendText = card.TrendTextKey != null ? localization.Get(card.TrendTextKey) : null,
|
||||
TrendIcon = card.TrendIcon,
|
||||
TrendDirection = card.TrendDirection,
|
||||
Variant = card.Variant
|
||||
};
|
||||
}
|
||||
|
||||
public static IEnumerable<string> AllKeys => Cards.Keys;
|
||||
|
|
|
|||
|
|
@ -22,7 +22,7 @@
|
|||
<swp-ai-insight>
|
||||
<swp-ai-header>
|
||||
<i class="ph ph-sparkle"></i>
|
||||
<span>AI Analyse</span>
|
||||
<span localize="dashboard.ai.header">AI Analyse</span>
|
||||
</swp-ai-header>
|
||||
<swp-ai-text>
|
||||
<strong>Godt i gang!</strong> 4 af 12 bookinger er gennemført. 2 er i gang nu, og 6 venter.
|
||||
|
|
@ -51,17 +51,17 @@
|
|||
<!-- Quick Actions -->
|
||||
<swp-card>
|
||||
<swp-card-header>
|
||||
<swp-card-title>Hurtige handlinger</swp-card-title>
|
||||
<swp-card-title localize="dashboard.quickActions.title">Hurtige handlinger</swp-card-title>
|
||||
</swp-card-header>
|
||||
<swp-card-content>
|
||||
<swp-quick-actions>
|
||||
<swp-quick-action-btn>
|
||||
<i class="ph ph-plus"></i>
|
||||
Ny booking
|
||||
<span localize="dashboard.quickActions.newBooking">Ny booking</span>
|
||||
</swp-quick-action-btn>
|
||||
<swp-quick-action-btn>
|
||||
<i class="ph ph-user-plus"></i>
|
||||
Ny kunde
|
||||
<span localize="dashboard.quickActions.newCustomer">Ny kunde</span>
|
||||
</swp-quick-action-btn>
|
||||
</swp-quick-actions>
|
||||
</swp-card-content>
|
||||
|
|
|
|||
|
|
@ -24,10 +24,197 @@
|
|||
"close": "Luk",
|
||||
"delete": "Slet",
|
||||
"edit": "Rediger",
|
||||
"add": "Tilføj"
|
||||
"add": "Tilføj",
|
||||
"from": "Fra",
|
||||
"to": "Til",
|
||||
"all": "Alle",
|
||||
"reset": "Nulstil",
|
||||
"status": "Status"
|
||||
},
|
||||
"sidebar": {
|
||||
"lockScreen": "Lås skærm",
|
||||
"appName": "Salon OS"
|
||||
},
|
||||
"dashboard": {
|
||||
"title": "Dashboard",
|
||||
"subtitle": "Overblik over dagens aktivitet",
|
||||
"stats": {
|
||||
"bookingsToday": "Bookinger i dag",
|
||||
"bookingsTrend": "4 gennemført, 2 i gang",
|
||||
"expectedRevenue": "Forventet omsætning",
|
||||
"revenueTrend": "+12% vs. gennemsnit",
|
||||
"occupancyRate": "Belægningsgrad",
|
||||
"occupancyTrend": "God kapacitet",
|
||||
"needsAttention": "Kræver handling"
|
||||
},
|
||||
"ai": {
|
||||
"header": "AI Analyse"
|
||||
},
|
||||
"bookings": {
|
||||
"title": "Dagens bookinger",
|
||||
"viewAll": "Se alle",
|
||||
"currentTime": "Nu:",
|
||||
"status": {
|
||||
"confirmed": "Bekræftet",
|
||||
"pending": "Afventer",
|
||||
"inProgress": "I gang",
|
||||
"completed": "Gennemført"
|
||||
}
|
||||
},
|
||||
"notifications": {
|
||||
"title": "Notifikationer",
|
||||
"viewAll": "Se alle",
|
||||
"markAllRead": "Marker alle som læst"
|
||||
},
|
||||
"attentions": {
|
||||
"title": "Kræver opmærksomhed",
|
||||
"viewAll": "Se alle"
|
||||
},
|
||||
"quickStats": {
|
||||
"title": "Denne uge",
|
||||
"bookings": "Bookinger",
|
||||
"revenue": "Omsætning",
|
||||
"newCustomers": "Nye kunder",
|
||||
"cancellations": "Aflysninger",
|
||||
"avgOccupancy": "Gns. belægning"
|
||||
},
|
||||
"quickActions": {
|
||||
"title": "Hurtige handlinger",
|
||||
"newBooking": "Ny booking",
|
||||
"newCustomer": "Ny kunde"
|
||||
},
|
||||
"waitlist": {
|
||||
"title": "Venteliste",
|
||||
"count": "{count} venter"
|
||||
}
|
||||
},
|
||||
"cash": {
|
||||
"title": "Kasse",
|
||||
"tabs": {
|
||||
"overview": "Oversigt",
|
||||
"reconciliation": "Kasseafstemning"
|
||||
},
|
||||
"stats": {
|
||||
"reconciliationsInPeriod": "Afstemninger i periode",
|
||||
"totalRevenue": "Total omsætning",
|
||||
"cashSales": "Kontantsalg",
|
||||
"totalDifference": "Samlet difference",
|
||||
"transactionsToday": "Transaktioner i dag",
|
||||
"revenueToday": "Omsætning i dag",
|
||||
"lastReconciliation": "Sidste afstemning",
|
||||
"openedRegister": "Åbnede kassen"
|
||||
},
|
||||
"filter": {
|
||||
"register": "Kassepunkt",
|
||||
"approved": "Godkendt",
|
||||
"draft": "Kladde"
|
||||
},
|
||||
"table": {
|
||||
"date": "Dato",
|
||||
"id": "ID",
|
||||
"period": "Periode",
|
||||
"register": "Kassepunkt",
|
||||
"closedBy": "Afsluttet af",
|
||||
"revenue": "Omsætning",
|
||||
"difference": "Difference",
|
||||
"selected": "{count} valgt",
|
||||
"noneSelected": "0 valgt",
|
||||
"showingCount": "Viser {count} afstemninger",
|
||||
"exportSaft": "Eksporter SAF-T",
|
||||
"downloadCsv": "Download CSV",
|
||||
"downloadPdf": "Download PDF",
|
||||
"viewTransactions": "Se transaktioner"
|
||||
},
|
||||
"revenue": {
|
||||
"title": "Periodens omsætning",
|
||||
"subtitle": "Systemtal vs. kontrol",
|
||||
"cardPayments": "Kortbetalinger",
|
||||
"mobilePay": "MobilePay / Online",
|
||||
"cashSales": "Kontantsalg",
|
||||
"hint": "Kort og MobilePay afstemmes mod bank/indløser. Kontanter tælles op nedenfor."
|
||||
},
|
||||
"balance": {
|
||||
"title": "Kontanter i kassen",
|
||||
"startBalance": "Startbeholdning",
|
||||
"startHint": "Overført fra sidste afstemning",
|
||||
"payouts": "Udbetalinger / Bilag",
|
||||
"payoutsHint": "Sammentæl bilag betalt kontant",
|
||||
"toBank": "Udtaget til bank",
|
||||
"toBankHint": "Kontanter lagt til side",
|
||||
"expected": "Forventet kontantbeholdning",
|
||||
"counted": "Optalt kontantbeholdning",
|
||||
"countedHint": "Hvad ligger der faktisk i kassen?"
|
||||
},
|
||||
"difference": {
|
||||
"title": "Kassedifference",
|
||||
"match": "Kassen stemmer",
|
||||
"over": "Overskud",
|
||||
"under": "Underskud"
|
||||
},
|
||||
"period": {
|
||||
"title": "Periodeoplysninger",
|
||||
"dateRange": "Periode",
|
||||
"register": "Kassepunkt",
|
||||
"employee": "Medarbejder"
|
||||
},
|
||||
"note": {
|
||||
"title": "Note til afstemning",
|
||||
"placeholder": "Beskriv evt. årsag til difference..."
|
||||
},
|
||||
"approval": {
|
||||
"title": "Afslut dagen",
|
||||
"approvedBy": "Godkendt af (valgfrit)",
|
||||
"selectPlaceholder": "Vælg...",
|
||||
"confirmation": "Jeg bekræfter, at kassen er talt op, og at tallene er indtastet efter bedste evne.",
|
||||
"saveDraft": "Gem som kladde",
|
||||
"approve": "Godkend & lås"
|
||||
},
|
||||
"status": {
|
||||
"draft": "Kladde",
|
||||
"approved": "Godkendt"
|
||||
},
|
||||
"systemNote": "Systemet gemmer hvornår og af hvem der er godkendt – enkelt kontrolspor."
|
||||
},
|
||||
"profile": {
|
||||
"title": "Profil",
|
||||
"myProfile": "Min profil",
|
||||
"settings": "Indstillinger",
|
||||
"darkMode": "Mørk tilstand",
|
||||
"logout": "Log ud"
|
||||
},
|
||||
"account": {
|
||||
"title": "Abonnement & Konto",
|
||||
"subtitle": "Administrer dit abonnement og betalingsinfo",
|
||||
"subscription": {
|
||||
"title": "Dit abonnement",
|
||||
"currentPlan": "Nuværende plan",
|
||||
"switchTo": "Skift til {plan}",
|
||||
"contactSales": "Kontakt salg",
|
||||
"contactUs": "Kontakt os",
|
||||
"pricePerMonth": "kr/md"
|
||||
},
|
||||
"billing": {
|
||||
"title": "Betaling & Fakturaer"
|
||||
},
|
||||
"payment": {
|
||||
"frequency": "Betalingsfrekvens",
|
||||
"monthly": "Månedlig",
|
||||
"yearly": "Årlig",
|
||||
"nextPayment": "Næste betaling",
|
||||
"amount": "Beløb",
|
||||
"cardExpiry": "Kortudløb",
|
||||
"change": "Skift",
|
||||
"switchToYearly": "Skift til årlig betaling (spar 15%)"
|
||||
},
|
||||
"invoices": {
|
||||
"title": "Faktura-historik",
|
||||
"date": "Dato",
|
||||
"invoiceNumber": "Fakturanr.",
|
||||
"amount": "Beløb",
|
||||
"download": "PDF",
|
||||
"paid": "Betalt",
|
||||
"pending": "Afventer",
|
||||
"overdue": "Forfalden"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -24,10 +24,197 @@
|
|||
"close": "Close",
|
||||
"delete": "Delete",
|
||||
"edit": "Edit",
|
||||
"add": "Add"
|
||||
"add": "Add",
|
||||
"from": "From",
|
||||
"to": "To",
|
||||
"all": "All",
|
||||
"reset": "Reset",
|
||||
"status": "Status"
|
||||
},
|
||||
"sidebar": {
|
||||
"lockScreen": "Lock screen",
|
||||
"appName": "Salon OS"
|
||||
},
|
||||
"dashboard": {
|
||||
"title": "Dashboard",
|
||||
"subtitle": "Overview of today's activity",
|
||||
"stats": {
|
||||
"bookingsToday": "Bookings today",
|
||||
"bookingsTrend": "4 completed, 2 in progress",
|
||||
"expectedRevenue": "Expected revenue",
|
||||
"revenueTrend": "+12% vs. average",
|
||||
"occupancyRate": "Occupancy rate",
|
||||
"occupancyTrend": "Good capacity",
|
||||
"needsAttention": "Needs attention"
|
||||
},
|
||||
"ai": {
|
||||
"header": "AI Analysis"
|
||||
},
|
||||
"bookings": {
|
||||
"title": "Today's bookings",
|
||||
"viewAll": "View all",
|
||||
"currentTime": "Now:",
|
||||
"status": {
|
||||
"confirmed": "Confirmed",
|
||||
"pending": "Pending",
|
||||
"inProgress": "In progress",
|
||||
"completed": "Completed"
|
||||
}
|
||||
},
|
||||
"notifications": {
|
||||
"title": "Notifications",
|
||||
"viewAll": "View all",
|
||||
"markAllRead": "Mark all as read"
|
||||
},
|
||||
"attentions": {
|
||||
"title": "Needs attention",
|
||||
"viewAll": "View all"
|
||||
},
|
||||
"quickStats": {
|
||||
"title": "This week",
|
||||
"bookings": "Bookings",
|
||||
"revenue": "Revenue",
|
||||
"newCustomers": "New customers",
|
||||
"cancellations": "Cancellations",
|
||||
"avgOccupancy": "Avg. occupancy"
|
||||
},
|
||||
"quickActions": {
|
||||
"title": "Quick actions",
|
||||
"newBooking": "New booking",
|
||||
"newCustomer": "New customer"
|
||||
},
|
||||
"waitlist": {
|
||||
"title": "Waitlist",
|
||||
"count": "{count} waiting"
|
||||
}
|
||||
},
|
||||
"cash": {
|
||||
"title": "Cash Register",
|
||||
"tabs": {
|
||||
"overview": "Overview",
|
||||
"reconciliation": "Reconciliation"
|
||||
},
|
||||
"stats": {
|
||||
"reconciliationsInPeriod": "Reconciliations in period",
|
||||
"totalRevenue": "Total revenue",
|
||||
"cashSales": "Cash sales",
|
||||
"totalDifference": "Total difference",
|
||||
"transactionsToday": "Transactions today",
|
||||
"revenueToday": "Revenue today",
|
||||
"lastReconciliation": "Last reconciliation",
|
||||
"openedRegister": "Opened register"
|
||||
},
|
||||
"filter": {
|
||||
"register": "Register",
|
||||
"approved": "Approved",
|
||||
"draft": "Draft"
|
||||
},
|
||||
"table": {
|
||||
"date": "Date",
|
||||
"id": "ID",
|
||||
"period": "Period",
|
||||
"register": "Register",
|
||||
"closedBy": "Closed by",
|
||||
"revenue": "Revenue",
|
||||
"difference": "Difference",
|
||||
"selected": "{count} selected",
|
||||
"noneSelected": "0 selected",
|
||||
"showingCount": "Showing {count} reconciliations",
|
||||
"exportSaft": "Export SAF-T",
|
||||
"downloadCsv": "Download CSV",
|
||||
"downloadPdf": "Download PDF",
|
||||
"viewTransactions": "View transactions"
|
||||
},
|
||||
"revenue": {
|
||||
"title": "Period revenue",
|
||||
"subtitle": "System vs. control",
|
||||
"cardPayments": "Card payments",
|
||||
"mobilePay": "MobilePay / Online",
|
||||
"cashSales": "Cash sales",
|
||||
"hint": "Card and MobilePay are reconciled against bank/acquirer. Cash is counted below."
|
||||
},
|
||||
"balance": {
|
||||
"title": "Cash in register",
|
||||
"startBalance": "Starting balance",
|
||||
"startHint": "Carried over from last reconciliation",
|
||||
"payouts": "Payouts / Receipts",
|
||||
"payoutsHint": "Total receipts paid in cash",
|
||||
"toBank": "Withdrawn to bank",
|
||||
"toBankHint": "Cash set aside",
|
||||
"expected": "Expected cash balance",
|
||||
"counted": "Counted cash balance",
|
||||
"countedHint": "What is actually in the register?"
|
||||
},
|
||||
"difference": {
|
||||
"title": "Cash difference",
|
||||
"match": "Register balanced",
|
||||
"over": "Overage",
|
||||
"under": "Shortage"
|
||||
},
|
||||
"period": {
|
||||
"title": "Period information",
|
||||
"dateRange": "Period",
|
||||
"register": "Register",
|
||||
"employee": "Employee"
|
||||
},
|
||||
"note": {
|
||||
"title": "Reconciliation note",
|
||||
"placeholder": "Describe reason for difference..."
|
||||
},
|
||||
"approval": {
|
||||
"title": "Close the day",
|
||||
"approvedBy": "Approved by (optional)",
|
||||
"selectPlaceholder": "Select...",
|
||||
"confirmation": "I confirm that the register has been counted and the figures have been entered to the best of my ability.",
|
||||
"saveDraft": "Save as draft",
|
||||
"approve": "Approve & lock"
|
||||
},
|
||||
"status": {
|
||||
"draft": "Draft",
|
||||
"approved": "Approved"
|
||||
},
|
||||
"systemNote": "The system records when and by whom approval was made – simple audit trail."
|
||||
},
|
||||
"profile": {
|
||||
"title": "Profile",
|
||||
"myProfile": "My profile",
|
||||
"settings": "Settings",
|
||||
"darkMode": "Dark mode",
|
||||
"logout": "Log out"
|
||||
},
|
||||
"account": {
|
||||
"title": "Subscription & Account",
|
||||
"subtitle": "Manage your subscription and payment info",
|
||||
"subscription": {
|
||||
"title": "Your subscription",
|
||||
"currentPlan": "Current plan",
|
||||
"switchTo": "Switch to {plan}",
|
||||
"contactSales": "Contact sales",
|
||||
"contactUs": "Contact us",
|
||||
"pricePerMonth": "/mo"
|
||||
},
|
||||
"billing": {
|
||||
"title": "Payment & Invoices"
|
||||
},
|
||||
"payment": {
|
||||
"frequency": "Payment frequency",
|
||||
"monthly": "Monthly",
|
||||
"yearly": "Yearly",
|
||||
"nextPayment": "Next payment",
|
||||
"amount": "Amount",
|
||||
"cardExpiry": "Card expiry",
|
||||
"change": "Change",
|
||||
"switchToYearly": "Switch to yearly billing (save 15%)"
|
||||
},
|
||||
"invoices": {
|
||||
"title": "Invoice history",
|
||||
"date": "Date",
|
||||
"invoiceNumber": "Invoice no.",
|
||||
"amount": "Amount",
|
||||
"download": "PDF",
|
||||
"paid": "Paid",
|
||||
"pending": "Pending",
|
||||
"overdue": "Overdue"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -3,7 +3,7 @@
|
|||
<swp-waitlist-drawer data-drawer="lg" id="waitlist-drawer">
|
||||
<swp-drawer-header>
|
||||
<swp-drawer-title>
|
||||
Venteliste <swp-count>(@WaitlistItemCatalog.AllKeys.Count())</swp-count>
|
||||
<span localize="dashboard.waitlist.title">Venteliste</span> <swp-count>(@WaitlistItemCatalog.AllKeys.Count())</swp-count>
|
||||
</swp-drawer-title>
|
||||
<swp-drawer-close data-drawer-close>
|
||||
<i class="ph ph-x"></i>
|
||||
|
|
|
|||
|
|
@ -1,6 +1,6 @@
|
|||
<swp-profile-drawer id="profileDrawer">
|
||||
<swp-drawer-header>
|
||||
<swp-drawer-title>Profil</swp-drawer-title>
|
||||
<swp-drawer-title localize="profile.title">Profil</swp-drawer-title>
|
||||
<swp-drawer-close id="closeProfileDrawer">
|
||||
<i class="ph ph-x"></i>
|
||||
</swp-drawer-close>
|
||||
|
|
@ -18,11 +18,11 @@
|
|||
<swp-drawer-menu>
|
||||
<swp-drawer-menu-item>
|
||||
<i class="ph ph-user"></i>
|
||||
<span>Min profil</span>
|
||||
<span localize="profile.myProfile">Min profil</span>
|
||||
</swp-drawer-menu-item>
|
||||
<swp-drawer-menu-item>
|
||||
<i class="ph ph-gear"></i>
|
||||
<span>Indstillinger</span>
|
||||
<span localize="profile.settings">Indstillinger</span>
|
||||
</swp-drawer-menu-item>
|
||||
</swp-drawer-menu>
|
||||
|
||||
|
|
@ -31,7 +31,7 @@
|
|||
<swp-theme-toggle>
|
||||
<swp-theme-label>
|
||||
<i class="ph ph-moon"></i>
|
||||
<span>Mørk tilstand</span>
|
||||
<span localize="profile.darkMode">Mørk tilstand</span>
|
||||
</swp-theme-label>
|
||||
<swp-toggle-switch id="themeToggle">
|
||||
<input type="checkbox" id="themeCheckbox">
|
||||
|
|
@ -43,7 +43,7 @@
|
|||
<swp-drawer-footer>
|
||||
<swp-drawer-action class="logout" id="logoutBtn">
|
||||
<i class="ph ph-sign-out"></i>
|
||||
<span>Log ud</span>
|
||||
<span localize="profile.logout">Log ud</span>
|
||||
</swp-drawer-action>
|
||||
</swp-drawer-footer>
|
||||
</swp-profile-drawer>
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue