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-card>
|
||||||
<swp-invoices-header>
|
<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-invoices-header>
|
||||||
|
|
||||||
<swp-invoice-table>
|
<swp-invoice-table>
|
||||||
<swp-invoice-table-header>
|
<swp-invoice-table-header>
|
||||||
<swp-invoice-row>
|
<swp-invoice-row>
|
||||||
<swp-invoice-cell>Dato</swp-invoice-cell>
|
<swp-invoice-cell localize="account.invoices.date">Dato</swp-invoice-cell>
|
||||||
<swp-invoice-cell>Fakturanr.</swp-invoice-cell>
|
<swp-invoice-cell localize="account.invoices.invoiceNumber">Fakturanr.</swp-invoice-cell>
|
||||||
<swp-invoice-cell>Beløb</swp-invoice-cell>
|
<swp-invoice-cell localize="account.invoices.amount">Beløb</swp-invoice-cell>
|
||||||
<swp-invoice-cell>Status</swp-invoice-cell>
|
<swp-invoice-cell localize="common.status">Status</swp-invoice-cell>
|
||||||
<swp-invoice-cell></swp-invoice-cell>
|
<swp-invoice-cell></swp-invoice-cell>
|
||||||
</swp-invoice-row>
|
</swp-invoice-row>
|
||||||
</swp-invoice-table-header>
|
</swp-invoice-table-header>
|
||||||
|
|
@ -21,13 +21,13 @@
|
||||||
<swp-invoice-cell>
|
<swp-invoice-cell>
|
||||||
<swp-invoice-status class="paid">
|
<swp-invoice-status class="paid">
|
||||||
<i class="ph ph-check-circle"></i>
|
<i class="ph ph-check-circle"></i>
|
||||||
Betalt
|
<span localize="account.invoices.paid">Betalt</span>
|
||||||
</swp-invoice-status>
|
</swp-invoice-status>
|
||||||
</swp-invoice-cell>
|
</swp-invoice-cell>
|
||||||
<swp-invoice-cell>
|
<swp-invoice-cell>
|
||||||
<swp-download-btn>
|
<swp-download-btn>
|
||||||
<i class="ph ph-download"></i>
|
<i class="ph ph-download"></i>
|
||||||
PDF
|
<span localize="account.invoices.download">PDF</span>
|
||||||
</swp-download-btn>
|
</swp-download-btn>
|
||||||
</swp-invoice-cell>
|
</swp-invoice-cell>
|
||||||
</swp-invoice-row>
|
</swp-invoice-row>
|
||||||
|
|
@ -39,13 +39,13 @@
|
||||||
<swp-invoice-cell>
|
<swp-invoice-cell>
|
||||||
<swp-invoice-status class="paid">
|
<swp-invoice-status class="paid">
|
||||||
<i class="ph ph-check-circle"></i>
|
<i class="ph ph-check-circle"></i>
|
||||||
Betalt
|
<span localize="account.invoices.paid">Betalt</span>
|
||||||
</swp-invoice-status>
|
</swp-invoice-status>
|
||||||
</swp-invoice-cell>
|
</swp-invoice-cell>
|
||||||
<swp-invoice-cell>
|
<swp-invoice-cell>
|
||||||
<swp-download-btn>
|
<swp-download-btn>
|
||||||
<i class="ph ph-download"></i>
|
<i class="ph ph-download"></i>
|
||||||
PDF
|
<span localize="account.invoices.download">PDF</span>
|
||||||
</swp-download-btn>
|
</swp-download-btn>
|
||||||
</swp-invoice-cell>
|
</swp-invoice-cell>
|
||||||
</swp-invoice-row>
|
</swp-invoice-row>
|
||||||
|
|
@ -57,13 +57,13 @@
|
||||||
<swp-invoice-cell>
|
<swp-invoice-cell>
|
||||||
<swp-invoice-status class="paid">
|
<swp-invoice-status class="paid">
|
||||||
<i class="ph ph-check-circle"></i>
|
<i class="ph ph-check-circle"></i>
|
||||||
Betalt
|
<span localize="account.invoices.paid">Betalt</span>
|
||||||
</swp-invoice-status>
|
</swp-invoice-status>
|
||||||
</swp-invoice-cell>
|
</swp-invoice-cell>
|
||||||
<swp-invoice-cell>
|
<swp-invoice-cell>
|
||||||
<swp-download-btn>
|
<swp-download-btn>
|
||||||
<i class="ph ph-download"></i>
|
<i class="ph ph-download"></i>
|
||||||
PDF
|
<span localize="account.invoices.download">PDF</span>
|
||||||
</swp-download-btn>
|
</swp-download-btn>
|
||||||
</swp-invoice-cell>
|
</swp-invoice-cell>
|
||||||
</swp-invoice-row>
|
</swp-invoice-row>
|
||||||
|
|
@ -75,13 +75,13 @@
|
||||||
<swp-invoice-cell>
|
<swp-invoice-cell>
|
||||||
<swp-invoice-status class="paid">
|
<swp-invoice-status class="paid">
|
||||||
<i class="ph ph-check-circle"></i>
|
<i class="ph ph-check-circle"></i>
|
||||||
Betalt
|
<span localize="account.invoices.paid">Betalt</span>
|
||||||
</swp-invoice-status>
|
</swp-invoice-status>
|
||||||
</swp-invoice-cell>
|
</swp-invoice-cell>
|
||||||
<swp-invoice-cell>
|
<swp-invoice-cell>
|
||||||
<swp-download-btn>
|
<swp-download-btn>
|
||||||
<i class="ph ph-download"></i>
|
<i class="ph ph-download"></i>
|
||||||
PDF
|
<span localize="account.invoices.download">PDF</span>
|
||||||
</swp-download-btn>
|
</swp-download-btn>
|
||||||
</swp-invoice-cell>
|
</swp-invoice-cell>
|
||||||
</swp-invoice-row>
|
</swp-invoice-row>
|
||||||
|
|
@ -93,13 +93,13 @@
|
||||||
<swp-invoice-cell>
|
<swp-invoice-cell>
|
||||||
<swp-invoice-status class="paid">
|
<swp-invoice-status class="paid">
|
||||||
<i class="ph ph-check-circle"></i>
|
<i class="ph ph-check-circle"></i>
|
||||||
Betalt
|
<span localize="account.invoices.paid">Betalt</span>
|
||||||
</swp-invoice-status>
|
</swp-invoice-status>
|
||||||
</swp-invoice-cell>
|
</swp-invoice-cell>
|
||||||
<swp-invoice-cell>
|
<swp-invoice-cell>
|
||||||
<swp-download-btn>
|
<swp-download-btn>
|
||||||
<i class="ph ph-download"></i>
|
<i class="ph ph-download"></i>
|
||||||
PDF
|
<span localize="account.invoices.download">PDF</span>
|
||||||
</swp-download-btn>
|
</swp-download-btn>
|
||||||
</swp-invoice-cell>
|
</swp-invoice-cell>
|
||||||
</swp-invoice-row>
|
</swp-invoice-row>
|
||||||
|
|
|
||||||
|
|
@ -9,32 +9,32 @@
|
||||||
</swp-payment-info>
|
</swp-payment-info>
|
||||||
<swp-btn class="secondary sm">
|
<swp-btn class="secondary sm">
|
||||||
<i class="ph ph-pencil"></i>
|
<i class="ph ph-pencil"></i>
|
||||||
Skift
|
<span localize="account.payment.change">Skift</span>
|
||||||
</swp-btn>
|
</swp-btn>
|
||||||
</swp-payment-method>
|
</swp-payment-method>
|
||||||
|
|
||||||
<swp-payment-detail>
|
<swp-payment-detail>
|
||||||
<swp-payment-label>Betalingsfrekvens</swp-payment-label>
|
<swp-payment-label localize="account.payment.frequency">Betalingsfrekvens</swp-payment-label>
|
||||||
<swp-payment-value>Månedlig</swp-payment-value>
|
<swp-payment-value localize="account.payment.monthly">Månedlig</swp-payment-value>
|
||||||
</swp-payment-detail>
|
</swp-payment-detail>
|
||||||
|
|
||||||
<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-value class="highlight">1. februar 2026</swp-payment-value>
|
||||||
</swp-payment-detail>
|
</swp-payment-detail>
|
||||||
|
|
||||||
<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-value>599,00 kr</swp-payment-value>
|
||||||
</swp-payment-detail>
|
</swp-payment-detail>
|
||||||
|
|
||||||
<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-value>08/2027</swp-payment-value>
|
||||||
</swp-payment-detail>
|
</swp-payment-detail>
|
||||||
|
|
||||||
<swp-btn class="outline" style="margin-top: var(--spacing-2);">
|
<swp-btn class="outline" style="margin-top: var(--spacing-2);">
|
||||||
<i class="ph ph-arrows-clockwise"></i>
|
<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-btn>
|
||||||
</swp-payment-card>
|
</swp-payment-card>
|
||||||
|
|
|
||||||
|
|
@ -1,8 +1,10 @@
|
||||||
@using PlanTempus.Application.Features.Accounts.Models
|
@using PlanTempus.Application.Features.Accounts.Models
|
||||||
|
@using PlanTempus.Application.Features.Localization.Services
|
||||||
@model IEnumerable<PlanInfo>
|
@model IEnumerable<PlanInfo>
|
||||||
|
|
||||||
@{
|
@{
|
||||||
var currentPlanKey = (string)ViewBag.CurrentPlanKey;
|
var currentPlanKey = (string)ViewBag.CurrentPlanKey;
|
||||||
|
var L = (ILocalizationService)ViewBag.Localization;
|
||||||
}
|
}
|
||||||
|
|
||||||
<swp-plan-grid>
|
<swp-plan-grid>
|
||||||
|
|
@ -16,14 +18,14 @@
|
||||||
};
|
};
|
||||||
|
|
||||||
var badgeClass = isCurrent ? "current" : plan.BadgeClass;
|
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 badgeIcon = isCurrent ? "ph-check" : plan.BadgeIcon;
|
||||||
|
|
||||||
var buttonText = isCurrent
|
var buttonText = isCurrent
|
||||||
? "Nuværende plan"
|
? L.Get("account.subscription.currentPlan")
|
||||||
: plan.IsContactSales
|
: plan.IsContactSales
|
||||||
? "Kontakt salg"
|
? L.Get("account.subscription.contactSales")
|
||||||
: $"Skift til {plan.Name}";
|
: L.Get("account.subscription.switchTo").Replace("{plan}", plan.Name);
|
||||||
|
|
||||||
var buttonClass = isCurrent
|
var buttonClass = isCurrent
|
||||||
? "secondary"
|
? "secondary"
|
||||||
|
|
@ -42,11 +44,11 @@
|
||||||
@if (plan.PricePerMonth.HasValue)
|
@if (plan.PricePerMonth.HasValue)
|
||||||
{
|
{
|
||||||
<swp-plan-price-amount>@plan.PriceDisplay</swp-plan-price-amount>
|
<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
|
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-price>
|
||||||
<swp-plan-features>
|
<swp-plan-features>
|
||||||
|
|
|
||||||
|
|
@ -1,5 +1,6 @@
|
||||||
using Microsoft.AspNetCore.Mvc;
|
using Microsoft.AspNetCore.Mvc;
|
||||||
using PlanTempus.Application.Features.Accounts.Models;
|
using PlanTempus.Application.Features.Accounts.Models;
|
||||||
|
using PlanTempus.Application.Features.Localization.Services;
|
||||||
|
|
||||||
namespace PlanTempus.Application.Features.Account.Components;
|
namespace PlanTempus.Application.Features.Account.Components;
|
||||||
|
|
||||||
|
|
@ -9,6 +10,13 @@ namespace PlanTempus.Application.Features.Account.Components;
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public class SubscriptionPlansViewComponent : ViewComponent
|
public class SubscriptionPlansViewComponent : ViewComponent
|
||||||
{
|
{
|
||||||
|
private readonly ILocalizationService _localization;
|
||||||
|
|
||||||
|
public SubscriptionPlansViewComponent(ILocalizationService localization)
|
||||||
|
{
|
||||||
|
_localization = localization;
|
||||||
|
}
|
||||||
|
|
||||||
public IViewComponentResult Invoke()
|
public IViewComponentResult Invoke()
|
||||||
{
|
{
|
||||||
var plans = PlanCatalog.GetAllPlans();
|
var plans = PlanCatalog.GetAllPlans();
|
||||||
|
|
@ -16,6 +24,7 @@ public class SubscriptionPlansViewComponent : ViewComponent
|
||||||
var currentPlanKey = "pro";
|
var currentPlanKey = "pro";
|
||||||
|
|
||||||
ViewBag.CurrentPlanKey = currentPlanKey;
|
ViewBag.CurrentPlanKey = currentPlanKey;
|
||||||
|
ViewBag.Localization = _localization;
|
||||||
return View(plans);
|
return View(plans);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -10,8 +10,8 @@
|
||||||
<!-- Page Header -->
|
<!-- Page Header -->
|
||||||
<swp-page-header>
|
<swp-page-header>
|
||||||
<swp-page-title>
|
<swp-page-title>
|
||||||
<h1>Abonnement & Konto</h1>
|
<h1 localize="account.title">Abonnement & Konto</h1>
|
||||||
<p>Administrer dit abonnement og betalingsinfo</p>
|
<p localize="account.subtitle">Administrer dit abonnement og betalingsinfo</p>
|
||||||
</swp-page-title>
|
</swp-page-title>
|
||||||
</swp-page-header>
|
</swp-page-header>
|
||||||
|
|
||||||
|
|
@ -20,7 +20,7 @@
|
||||||
<swp-account-section-header>
|
<swp-account-section-header>
|
||||||
<swp-account-section-title>
|
<swp-account-section-title>
|
||||||
<i class="ph ph-crown"></i>
|
<i class="ph ph-crown"></i>
|
||||||
Dit abonnement
|
<span localize="account.subscription.title">Dit abonnement</span>
|
||||||
</swp-account-section-title>
|
</swp-account-section-title>
|
||||||
</swp-account-section-header>
|
</swp-account-section-header>
|
||||||
|
|
||||||
|
|
@ -32,7 +32,7 @@
|
||||||
<swp-account-section-header>
|
<swp-account-section-header>
|
||||||
<swp-account-section-title>
|
<swp-account-section-title>
|
||||||
<i class="ph ph-credit-card"></i>
|
<i class="ph ph-credit-card"></i>
|
||||||
Betaling & Fakturaer
|
<span localize="account.billing.title">Betaling & Fakturaer</span>
|
||||||
</swp-account-section-title>
|
</swp-account-section-title>
|
||||||
</swp-account-section-header>
|
</swp-account-section-header>
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -1,21 +1,21 @@
|
||||||
<swp-card>
|
<swp-card>
|
||||||
<swp-card-header>
|
<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-header>
|
||||||
<swp-card-content>
|
<swp-card-content>
|
||||||
<swp-approval-grid>
|
<swp-approval-grid>
|
||||||
<swp-form-field>
|
<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-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-status-row>
|
||||||
</swp-form-field>
|
</swp-form-field>
|
||||||
|
|
||||||
<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>
|
<swp-form-input>
|
||||||
<select id="approver">
|
<select id="approver">
|
||||||
<option value="">Vælg...</option>
|
<option value="" localize="cash.approval.selectPlaceholder">Vælg...</option>
|
||||||
<option>Karina Knudsen</option>
|
<option>Karina Knudsen</option>
|
||||||
<option>Butikschef</option>
|
<option>Butikschef</option>
|
||||||
</select>
|
</select>
|
||||||
|
|
@ -24,15 +24,15 @@
|
||||||
|
|
||||||
<swp-checkbox-field>
|
<swp-checkbox-field>
|
||||||
<input type="checkbox" id="confirmCheckbox" />
|
<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-checkbox-field>
|
||||||
</swp-approval-grid>
|
</swp-approval-grid>
|
||||||
</swp-card-content>
|
</swp-card-content>
|
||||||
<swp-card-footer>
|
<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-actions-right>
|
||||||
<swp-btn class="ghost">Fortryd</swp-btn>
|
<swp-btn class="ghost" localize="common.cancel">Fortryd</swp-btn>
|
||||||
<swp-btn class="primary" id="approveBtn" disabled>Godkend & lås</swp-btn>
|
<swp-btn class="primary" id="approveBtn" disabled localize="cash.approval.approve">Godkend & lås</swp-btn>
|
||||||
</swp-actions-right>
|
</swp-actions-right>
|
||||||
</swp-card-footer>
|
</swp-card-footer>
|
||||||
</swp-card>
|
</swp-card>
|
||||||
|
|
|
||||||
|
|
@ -1,20 +1,20 @@
|
||||||
<swp-card>
|
<swp-card>
|
||||||
<swp-card-header>
|
<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-header>
|
||||||
<swp-card-content>
|
<swp-card-content>
|
||||||
<swp-calc-row>
|
<swp-calc-row>
|
||||||
<swp-calc-label>
|
<swp-calc-label>
|
||||||
<span>Startbeholdning</span>
|
<span localize="cash.balance.startBalance">Startbeholdning</span>
|
||||||
<small>Overført fra sidste afstemning</small>
|
<small localize="cash.balance.startHint">Overført fra sidste afstemning</small>
|
||||||
</swp-calc-label>
|
</swp-calc-label>
|
||||||
<swp-calc-value class="muted">2.000,00</swp-calc-value>
|
<swp-calc-value class="muted">2.000,00</swp-calc-value>
|
||||||
</swp-calc-row>
|
</swp-calc-row>
|
||||||
|
|
||||||
<swp-calc-row>
|
<swp-calc-row>
|
||||||
<swp-calc-label>
|
<swp-calc-label>
|
||||||
<span>Udbetalinger / Bilag</span>
|
<span localize="cash.balance.payouts">Udbetalinger / Bilag</span>
|
||||||
<small>Sammentæl bilag betalt kontant</small>
|
<small localize="cash.balance.payoutsHint">Sammentæl bilag betalt kontant</small>
|
||||||
</swp-calc-label>
|
</swp-calc-label>
|
||||||
<swp-calc-input>
|
<swp-calc-input>
|
||||||
<input type="text" id="payouts" placeholder="0,00" />
|
<input type="text" id="payouts" placeholder="0,00" />
|
||||||
|
|
@ -23,8 +23,8 @@
|
||||||
|
|
||||||
<swp-calc-row>
|
<swp-calc-row>
|
||||||
<swp-calc-label>
|
<swp-calc-label>
|
||||||
<span>Udtaget til bank</span>
|
<span localize="cash.balance.toBank">Udtaget til bank</span>
|
||||||
<small>Kontanter lagt til side</small>
|
<small localize="cash.balance.toBankHint">Kontanter lagt til side</small>
|
||||||
</swp-calc-label>
|
</swp-calc-label>
|
||||||
<swp-calc-input>
|
<swp-calc-input>
|
||||||
<input type="text" id="toBank" placeholder="0,00" />
|
<input type="text" id="toBank" placeholder="0,00" />
|
||||||
|
|
@ -33,15 +33,15 @@
|
||||||
|
|
||||||
<swp-calc-row>
|
<swp-calc-row>
|
||||||
<swp-calc-label>
|
<swp-calc-label>
|
||||||
<span>Forventet kontantbeholdning</span>
|
<span localize="cash.balance.expected">Forventet kontantbeholdning</span>
|
||||||
</swp-calc-label>
|
</swp-calc-label>
|
||||||
<swp-calc-value id="expectedCash">5.220,00</swp-calc-value>
|
<swp-calc-value id="expectedCash">5.220,00</swp-calc-value>
|
||||||
</swp-calc-row>
|
</swp-calc-row>
|
||||||
|
|
||||||
<swp-calc-row class="input-row">
|
<swp-calc-row class="input-row">
|
||||||
<swp-calc-label>
|
<swp-calc-label>
|
||||||
<span>Optalt kontantbeholdning <span style="color: var(--color-red)">*</span></span>
|
<span><span localize="cash.balance.counted">Optalt kontantbeholdning</span> <span style="color: var(--color-red)">*</span></span>
|
||||||
<small>Hvad ligger der faktisk i kassen?</small>
|
<small localize="cash.balance.countedHint">Hvad ligger der faktisk i kassen?</small>
|
||||||
</swp-calc-label>
|
</swp-calc-label>
|
||||||
<swp-calc-input>
|
<swp-calc-input>
|
||||||
<input type="text" id="actualCash" placeholder="0,00" />
|
<input type="text" id="actualCash" placeholder="0,00" />
|
||||||
|
|
|
||||||
|
|
@ -1,6 +1,6 @@
|
||||||
<swp-difference-box id="differenceBox" class="neutral">
|
<swp-difference-box id="differenceBox" class="neutral">
|
||||||
<swp-difference-label>
|
<swp-difference-label>
|
||||||
Kontant difference
|
<span localize="cash.difference.title">Kassedifference</span>
|
||||||
<small>Optalt minus forventet</small>
|
<small>Optalt minus forventet</small>
|
||||||
</swp-difference-label>
|
</swp-difference-label>
|
||||||
<swp-difference-value id="differenceValue">– kr</swp-difference-value>
|
<swp-difference-value id="differenceValue">– kr</swp-difference-value>
|
||||||
|
|
|
||||||
|
|
@ -1,11 +1,10 @@
|
||||||
<swp-card>
|
<swp-card>
|
||||||
<swp-card-header>
|
<swp-card-header>
|
||||||
<swp-card-title>Periodeoplysninger</swp-card-title>
|
<swp-card-title localize="cash.period.title">Periodeoplysninger</swp-card-title>
|
||||||
<swp-card-action>Identificér afstemningen</swp-card-action>
|
|
||||||
</swp-card-header>
|
</swp-card-header>
|
||||||
<swp-card-content>
|
<swp-card-content>
|
||||||
<swp-period-display>
|
<swp-period-display>
|
||||||
<swp-period-label>Periode</swp-period-label>
|
<swp-period-label localize="cash.period.dateRange">Periode</swp-period-label>
|
||||||
<swp-period-value>
|
<swp-period-value>
|
||||||
<span class="from">28. dec 2025 kl. 18:00</span>
|
<span class="from">28. dec 2025 kl. 18:00</span>
|
||||||
<span class="arrow">→</span>
|
<span class="arrow">→</span>
|
||||||
|
|
@ -15,7 +14,7 @@
|
||||||
|
|
||||||
<swp-form-grid style="margin-top: var(--spacing-10);">
|
<swp-form-grid style="margin-top: var(--spacing-10);">
|
||||||
<swp-form-field>
|
<swp-form-field>
|
||||||
<swp-form-label>Kassepunkt</swp-form-label>
|
<swp-form-label localize="cash.period.register">Kassepunkt</swp-form-label>
|
||||||
<swp-form-input>
|
<swp-form-input>
|
||||||
<select id="register">
|
<select id="register">
|
||||||
<option>Kasse 1 – Reception</option>
|
<option>Kasse 1 – Reception</option>
|
||||||
|
|
@ -25,7 +24,7 @@
|
||||||
</swp-form-field>
|
</swp-form-field>
|
||||||
|
|
||||||
<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>
|
<swp-form-input>
|
||||||
<select id="employee">
|
<select id="employee">
|
||||||
<option>Anna Jensen</option>
|
<option>Anna Jensen</option>
|
||||||
|
|
|
||||||
|
|
@ -1,7 +1,7 @@
|
||||||
<swp-card>
|
<swp-card>
|
||||||
<swp-card-header>
|
<swp-card-header>
|
||||||
<swp-card-title>Periodens omsætning</swp-card-title>
|
<swp-card-title localize="cash.revenue.title">Periodens omsætning</swp-card-title>
|
||||||
<swp-card-action>Systemtal vs. kontrol</swp-card-action>
|
<swp-card-action localize="cash.revenue.subtitle">Systemtal vs. kontrol</swp-card-action>
|
||||||
</swp-card-header>
|
</swp-card-header>
|
||||||
<swp-card-content>
|
<swp-card-content>
|
||||||
<swp-data-table>
|
<swp-data-table>
|
||||||
|
|
@ -12,7 +12,7 @@
|
||||||
</swp-data-header>
|
</swp-data-header>
|
||||||
|
|
||||||
<swp-data-row>
|
<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-system>12.875,50</swp-data-system>
|
||||||
<swp-data-input>
|
<swp-data-input>
|
||||||
<input type="text" placeholder="Valgfrit" />
|
<input type="text" placeholder="Valgfrit" />
|
||||||
|
|
@ -20,7 +20,7 @@
|
||||||
</swp-data-row>
|
</swp-data-row>
|
||||||
|
|
||||||
<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-system>2.450,00</swp-data-system>
|
||||||
<swp-data-input>
|
<swp-data-input>
|
||||||
<input type="text" placeholder="Valgfrit" />
|
<input type="text" placeholder="Valgfrit" />
|
||||||
|
|
@ -28,12 +28,12 @@
|
||||||
</swp-data-row>
|
</swp-data-row>
|
||||||
|
|
||||||
<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-system>3.540,00</swp-data-system>
|
||||||
<swp-data-value class="muted">..</swp-data-value>
|
<swp-data-value class="muted">..</swp-data-value>
|
||||||
</swp-data-row>
|
</swp-data-row>
|
||||||
</swp-data-table>
|
</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-content>
|
||||||
</swp-card>
|
</swp-card>
|
||||||
|
|
|
||||||
|
|
@ -1,12 +1,10 @@
|
||||||
<swp-card>
|
<swp-card>
|
||||||
<swp-card-header>
|
<swp-card-header>
|
||||||
<swp-card-title>Note til difference</swp-card-title>
|
<swp-card-title localize="cash.note.title">Note til afstemning</swp-card-title>
|
||||||
<swp-card-action>Valgfrit</swp-card-action>
|
|
||||||
</swp-card-header>
|
</swp-card-header>
|
||||||
<swp-card-content>
|
<swp-card-content>
|
||||||
<swp-note-field>
|
<swp-note-field>
|
||||||
<textarea placeholder="Fx kassedifference, fejlslag, runding osv."></textarea>
|
<textarea placeholder="Beskriv evt. årsag til difference..."></textarea>
|
||||||
</swp-note-field>
|
</swp-note-field>
|
||||||
<swp-note-hint>Kan gøres obligatorisk ved difference over 100 kr.</swp-note-hint>
|
|
||||||
</swp-card-content>
|
</swp-card-content>
|
||||||
</swp-card>
|
</swp-card>
|
||||||
|
|
|
||||||
|
|
@ -1,11 +1,11 @@
|
||||||
<!-- Action Bar -->
|
<!-- Action Bar -->
|
||||||
<swp-action-bar>
|
<swp-action-bar>
|
||||||
<swp-selection-info>
|
<swp-selection-info>
|
||||||
<span id="selectionCount">0 valgt</span>
|
<span id="selectionCount" localize="cash.table.noneSelected">0 valgt</span>
|
||||||
</swp-selection-info>
|
</swp-selection-info>
|
||||||
<swp-btn class="primary" id="exportBtn" disabled>
|
<swp-btn class="primary" id="exportBtn" disabled>
|
||||||
<i class="ph ph-download"></i>
|
<i class="ph ph-download"></i>
|
||||||
Eksporter SAF-T
|
<span localize="cash.table.exportSaft">Eksporter SAF-T</span>
|
||||||
</swp-btn>
|
</swp-btn>
|
||||||
</swp-action-bar>
|
</swp-action-bar>
|
||||||
|
|
||||||
|
|
@ -13,14 +13,14 @@
|
||||||
<swp-cash-table>
|
<swp-cash-table>
|
||||||
<swp-cash-table-header>
|
<swp-cash-table-header>
|
||||||
<swp-cash-th class="checkbox"><input type="checkbox" id="selectAll" /></swp-cash-th>
|
<swp-cash-th class="checkbox"><input type="checkbox" id="selectAll" /></swp-cash-th>
|
||||||
<swp-cash-th>Dato</swp-cash-th>
|
<swp-cash-th localize="cash.table.date">Dato</swp-cash-th>
|
||||||
<swp-cash-th>ID</swp-cash-th>
|
<swp-cash-th localize="cash.table.id">ID</swp-cash-th>
|
||||||
<swp-cash-th>Periode</swp-cash-th>
|
<swp-cash-th localize="cash.table.period">Periode</swp-cash-th>
|
||||||
<swp-cash-th>Kassepunkt</swp-cash-th>
|
<swp-cash-th localize="cash.table.register">Kassepunkt</swp-cash-th>
|
||||||
<swp-cash-th>Afsluttet af</swp-cash-th>
|
<swp-cash-th localize="cash.table.closedBy">Afsluttet af</swp-cash-th>
|
||||||
<swp-cash-th class="right">Omsætning</swp-cash-th>
|
<swp-cash-th class="right" localize="cash.table.revenue">Omsætning</swp-cash-th>
|
||||||
<swp-cash-th class="right">Difference</swp-cash-th>
|
<swp-cash-th class="right" localize="cash.table.difference">Difference</swp-cash-th>
|
||||||
<swp-cash-th>Status</swp-cash-th>
|
<swp-cash-th localize="common.status">Status</swp-cash-th>
|
||||||
<swp-cash-th></swp-cash-th>
|
<swp-cash-th></swp-cash-th>
|
||||||
</swp-cash-table-header>
|
</swp-cash-table-header>
|
||||||
|
|
||||||
|
|
@ -39,7 +39,7 @@
|
||||||
<swp-cash-td class="muted">–</swp-cash-td>
|
<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">4.250 kr</swp-cash-td>
|
||||||
<swp-cash-td class="right mono muted">–</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-td><swp-row-arrow><i class="ph ph-caret-right"></i></swp-row-arrow></swp-cash-td>
|
||||||
</swp-cash-table-row>
|
</swp-cash-table-row>
|
||||||
|
|
||||||
|
|
@ -56,7 +56,7 @@
|
||||||
<swp-cash-td>Anna Jensen</swp-cash-td>
|
<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">18.865 kr</swp-cash-td>
|
||||||
<swp-cash-td class="right mono">0 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-td><swp-row-toggle><i class="ph ph-caret-right"></i></swp-row-toggle></swp-cash-td>
|
||||||
</swp-cash-table-row>
|
</swp-cash-table-row>
|
||||||
<swp-cash-row-detail data-for="043">
|
<swp-cash-row-detail data-for="043">
|
||||||
|
|
@ -64,15 +64,15 @@
|
||||||
<swp-row-detail-actions>
|
<swp-row-detail-actions>
|
||||||
<swp-btn class="secondary">
|
<swp-btn class="secondary">
|
||||||
<i class="ph ph-file-csv"></i>
|
<i class="ph ph-file-csv"></i>
|
||||||
Download CSV
|
<span localize="cash.table.downloadCsv">Download CSV</span>
|
||||||
</swp-btn>
|
</swp-btn>
|
||||||
<swp-btn class="secondary">
|
<swp-btn class="secondary">
|
||||||
<i class="ph ph-file-pdf"></i>
|
<i class="ph ph-file-pdf"></i>
|
||||||
Download PDF
|
<span localize="cash.table.downloadPdf">Download PDF</span>
|
||||||
</swp-btn>
|
</swp-btn>
|
||||||
<swp-btn class="primary">
|
<swp-btn class="primary">
|
||||||
<i class="ph ph-list-bullets"></i>
|
<i class="ph ph-list-bullets"></i>
|
||||||
Se transaktioner
|
<span localize="cash.table.viewTransactions">Se transaktioner</span>
|
||||||
</swp-btn>
|
</swp-btn>
|
||||||
</swp-row-detail-actions>
|
</swp-row-detail-actions>
|
||||||
</swp-row-detail-content>
|
</swp-row-detail-content>
|
||||||
|
|
@ -91,7 +91,7 @@
|
||||||
<swp-cash-td>Karina Knudsen</swp-cash-td>
|
<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">12.450 kr</swp-cash-td>
|
||||||
<swp-cash-td class="right mono negative">-25 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-td><swp-row-toggle><i class="ph ph-caret-right"></i></swp-row-toggle></swp-cash-td>
|
||||||
</swp-cash-table-row>
|
</swp-cash-table-row>
|
||||||
<swp-cash-row-detail data-for="042">
|
<swp-cash-row-detail data-for="042">
|
||||||
|
|
@ -99,15 +99,15 @@
|
||||||
<swp-row-detail-actions>
|
<swp-row-detail-actions>
|
||||||
<swp-btn class="secondary">
|
<swp-btn class="secondary">
|
||||||
<i class="ph ph-file-csv"></i>
|
<i class="ph ph-file-csv"></i>
|
||||||
Download CSV
|
<span localize="cash.table.downloadCsv">Download CSV</span>
|
||||||
</swp-btn>
|
</swp-btn>
|
||||||
<swp-btn class="secondary">
|
<swp-btn class="secondary">
|
||||||
<i class="ph ph-file-pdf"></i>
|
<i class="ph ph-file-pdf"></i>
|
||||||
Download PDF
|
<span localize="cash.table.downloadPdf">Download PDF</span>
|
||||||
</swp-btn>
|
</swp-btn>
|
||||||
<swp-btn class="primary">
|
<swp-btn class="primary">
|
||||||
<i class="ph ph-list-bullets"></i>
|
<i class="ph ph-list-bullets"></i>
|
||||||
Se transaktioner
|
<span localize="cash.table.viewTransactions">Se transaktioner</span>
|
||||||
</swp-btn>
|
</swp-btn>
|
||||||
</swp-row-detail-actions>
|
</swp-row-detail-actions>
|
||||||
</swp-row-detail-content>
|
</swp-row-detail-content>
|
||||||
|
|
@ -126,7 +126,7 @@
|
||||||
<swp-cash-td>Martin Nielsen</swp-cash-td>
|
<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">21.340 kr</swp-cash-td>
|
||||||
<swp-cash-td class="right mono">0 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-td><swp-row-toggle><i class="ph ph-caret-right"></i></swp-row-toggle></swp-cash-td>
|
||||||
</swp-cash-table-row>
|
</swp-cash-table-row>
|
||||||
<swp-cash-row-detail data-for="041">
|
<swp-cash-row-detail data-for="041">
|
||||||
|
|
@ -134,15 +134,15 @@
|
||||||
<swp-row-detail-actions>
|
<swp-row-detail-actions>
|
||||||
<swp-btn class="secondary">
|
<swp-btn class="secondary">
|
||||||
<i class="ph ph-file-csv"></i>
|
<i class="ph ph-file-csv"></i>
|
||||||
Download CSV
|
<span localize="cash.table.downloadCsv">Download CSV</span>
|
||||||
</swp-btn>
|
</swp-btn>
|
||||||
<swp-btn class="secondary">
|
<swp-btn class="secondary">
|
||||||
<i class="ph ph-file-pdf"></i>
|
<i class="ph ph-file-pdf"></i>
|
||||||
Download PDF
|
<span localize="cash.table.downloadPdf">Download PDF</span>
|
||||||
</swp-btn>
|
</swp-btn>
|
||||||
<swp-btn class="primary">
|
<swp-btn class="primary">
|
||||||
<i class="ph ph-list-bullets"></i>
|
<i class="ph ph-list-bullets"></i>
|
||||||
Se transaktioner
|
<span localize="cash.table.viewTransactions">Se transaktioner</span>
|
||||||
</swp-btn>
|
</swp-btn>
|
||||||
</swp-row-detail-actions>
|
</swp-row-detail-actions>
|
||||||
</swp-row-detail-content>
|
</swp-row-detail-content>
|
||||||
|
|
@ -161,7 +161,7 @@
|
||||||
<swp-cash-td>Anna Jensen</swp-cash-td>
|
<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">28.750 kr</swp-cash-td>
|
||||||
<swp-cash-td class="right mono negative">-50 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-td><swp-row-toggle><i class="ph ph-caret-right"></i></swp-row-toggle></swp-cash-td>
|
||||||
</swp-cash-table-row>
|
</swp-cash-table-row>
|
||||||
<swp-cash-row-detail data-for="040">
|
<swp-cash-row-detail data-for="040">
|
||||||
|
|
@ -169,15 +169,15 @@
|
||||||
<swp-row-detail-actions>
|
<swp-row-detail-actions>
|
||||||
<swp-btn class="secondary">
|
<swp-btn class="secondary">
|
||||||
<i class="ph ph-file-csv"></i>
|
<i class="ph ph-file-csv"></i>
|
||||||
Download CSV
|
<span localize="cash.table.downloadCsv">Download CSV</span>
|
||||||
</swp-btn>
|
</swp-btn>
|
||||||
<swp-btn class="secondary">
|
<swp-btn class="secondary">
|
||||||
<i class="ph ph-file-pdf"></i>
|
<i class="ph ph-file-pdf"></i>
|
||||||
Download PDF
|
<span localize="cash.table.downloadPdf">Download PDF</span>
|
||||||
</swp-btn>
|
</swp-btn>
|
||||||
<swp-btn class="primary">
|
<swp-btn class="primary">
|
||||||
<i class="ph ph-list-bullets"></i>
|
<i class="ph ph-list-bullets"></i>
|
||||||
Se transaktioner
|
<span localize="cash.table.viewTransactions">Se transaktioner</span>
|
||||||
</swp-btn>
|
</swp-btn>
|
||||||
</swp-row-detail-actions>
|
</swp-row-detail-actions>
|
||||||
</swp-row-detail-content>
|
</swp-row-detail-content>
|
||||||
|
|
|
||||||
|
|
@ -1,28 +1,28 @@
|
||||||
<swp-filter-bar>
|
<swp-filter-bar>
|
||||||
<swp-filter-group>
|
<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" />
|
<input type="date" id="dateFrom" />
|
||||||
</swp-filter-group>
|
</swp-filter-group>
|
||||||
<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" />
|
<input type="date" id="dateTo" />
|
||||||
</swp-filter-group>
|
</swp-filter-group>
|
||||||
<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">
|
<select id="register">
|
||||||
<option>Alle</option>
|
<option localize="common.all">Alle</option>
|
||||||
<option>Kasse 1 – Reception</option>
|
<option>Kasse 1 – Reception</option>
|
||||||
<option>Kasse 2 – Salon</option>
|
<option>Kasse 2 – Salon</option>
|
||||||
</select>
|
</select>
|
||||||
</swp-filter-group>
|
</swp-filter-group>
|
||||||
<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">
|
<select id="status">
|
||||||
<option>Alle</option>
|
<option localize="common.all">Alle</option>
|
||||||
<option>Godkendt</option>
|
<option localize="cash.filter.approved">Godkendt</option>
|
||||||
<option>Kladde</option>
|
<option localize="cash.filter.draft">Kladde</option>
|
||||||
</select>
|
</select>
|
||||||
</swp-filter-group>
|
</swp-filter-group>
|
||||||
<swp-filter-spacer></swp-filter-spacer>
|
<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>
|
</swp-filter-bar>
|
||||||
|
|
|
||||||
|
|
@ -13,19 +13,19 @@
|
||||||
<swp-cash-stats data-for-tab="oversigt" class="active">
|
<swp-cash-stats data-for-tab="oversigt" class="active">
|
||||||
<swp-cash-stat>
|
<swp-cash-stat>
|
||||||
<swp-cash-stat-value>12</swp-cash-stat-value>
|
<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>
|
||||||
<swp-cash-stat class="highlight">
|
<swp-cash-stat class="highlight">
|
||||||
<swp-cash-stat-value>186.450 kr</swp-cash-stat-value>
|
<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>
|
<swp-cash-stat>
|
||||||
<swp-cash-stat-value>42.340 kr</swp-cash-stat-value>
|
<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>
|
||||||
<swp-cash-stat class="warning">
|
<swp-cash-stat class="warning">
|
||||||
<swp-cash-stat-value>-75 kr</swp-cash-stat-value>
|
<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-stat>
|
||||||
</swp-cash-stats>
|
</swp-cash-stats>
|
||||||
|
|
||||||
|
|
@ -33,19 +33,19 @@
|
||||||
<swp-cash-stats data-for-tab="afstemning">
|
<swp-cash-stats data-for-tab="afstemning">
|
||||||
<swp-cash-stat>
|
<swp-cash-stat>
|
||||||
<swp-cash-stat-value>47</swp-cash-stat-value>
|
<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>
|
||||||
<swp-cash-stat class="highlight">
|
<swp-cash-stat class="highlight">
|
||||||
<swp-cash-stat-value>18.865 kr</swp-cash-stat-value>
|
<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>
|
<swp-cash-stat>
|
||||||
<swp-cash-stat-value>29. dec 17:45</swp-cash-stat-value>
|
<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>
|
<swp-cash-stat>
|
||||||
<swp-cash-stat-value>Anna J.</swp-cash-stat-value>
|
<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-stat>
|
||||||
</swp-cash-stats>
|
</swp-cash-stats>
|
||||||
</swp-cash-header>
|
</swp-cash-header>
|
||||||
|
|
@ -54,11 +54,11 @@
|
||||||
<swp-tab-bar>
|
<swp-tab-bar>
|
||||||
<swp-tab class="active" data-tab="oversigt">
|
<swp-tab class="active" data-tab="oversigt">
|
||||||
<i class="ph ph-list-checks"></i>
|
<i class="ph ph-list-checks"></i>
|
||||||
Oversigt
|
<span localize="cash.tabs.overview">Oversigt</span>
|
||||||
</swp-tab>
|
</swp-tab>
|
||||||
<swp-tab data-tab="afstemning">
|
<swp-tab data-tab="afstemning">
|
||||||
<i class="ph ph-cash-register"></i>
|
<i class="ph ph-cash-register"></i>
|
||||||
Kasseafstemning
|
<span localize="cash.tabs.reconciliation">Kasseafstemning</span>
|
||||||
</swp-tab>
|
</swp-tab>
|
||||||
</swp-tab-bar>
|
</swp-tab-bar>
|
||||||
</swp-cash-sticky-header>
|
</swp-cash-sticky-header>
|
||||||
|
|
@ -88,6 +88,6 @@
|
||||||
</swp-cash-column>
|
</swp-cash-column>
|
||||||
</swp-cash-grid>
|
</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-page-container>
|
||||||
</swp-tab-content>
|
</swp-tab-content>
|
||||||
|
|
|
||||||
|
|
@ -1,12 +1,20 @@
|
||||||
using Microsoft.AspNetCore.Mvc;
|
using Microsoft.AspNetCore.Mvc;
|
||||||
|
using PlanTempus.Application.Features.Localization.Services;
|
||||||
|
|
||||||
namespace PlanTempus.Application.Features.Dashboard.Components;
|
namespace PlanTempus.Application.Features.Dashboard.Components;
|
||||||
|
|
||||||
public class AttentionListViewComponent : ViewComponent
|
public class AttentionListViewComponent : ViewComponent
|
||||||
{
|
{
|
||||||
|
private readonly ILocalizationService _localization;
|
||||||
|
|
||||||
|
public AttentionListViewComponent(ILocalizationService localization)
|
||||||
|
{
|
||||||
|
_localization = localization;
|
||||||
|
}
|
||||||
|
|
||||||
public IViewComponentResult Invoke(string key)
|
public IViewComponentResult Invoke(string key)
|
||||||
{
|
{
|
||||||
var model = AttentionListCatalog.Get(key);
|
var model = AttentionListCatalog.Get(key, _localization);
|
||||||
return View(model);
|
return View(model);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -18,23 +26,35 @@ public class AttentionListViewModel
|
||||||
public required IReadOnlyList<string> AttentionKeys { get; init; }
|
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
|
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",
|
Key = "current-attentions",
|
||||||
Title = "Opmærksomheder",
|
TitleKey = "dashboard.attentions.title",
|
||||||
AttentionKeys = ["attention-1", "attention-2", "attention-3"]
|
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))
|
if (!Lists.TryGetValue(key, out var list))
|
||||||
return 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 Microsoft.AspNetCore.Mvc;
|
||||||
|
using PlanTempus.Application.Features.Localization.Services;
|
||||||
|
|
||||||
namespace PlanTempus.Application.Features.Dashboard.Components;
|
namespace PlanTempus.Application.Features.Dashboard.Components;
|
||||||
|
|
||||||
public class BookingItemViewComponent : ViewComponent
|
public class BookingItemViewComponent : ViewComponent
|
||||||
{
|
{
|
||||||
|
private readonly ILocalizationService _localization;
|
||||||
|
|
||||||
|
public BookingItemViewComponent(ILocalizationService localization)
|
||||||
|
{
|
||||||
|
_localization = localization;
|
||||||
|
}
|
||||||
|
|
||||||
public IViewComponentResult Invoke(string key)
|
public IViewComponentResult Invoke(string key)
|
||||||
{
|
{
|
||||||
var model = BookingItemCatalog.Get(key);
|
var model = BookingItemCatalog.Get(key, _localization);
|
||||||
return View(model);
|
return View(model);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -21,23 +29,36 @@ public class BookingItemViewModel
|
||||||
public required string EmployeeInitials { get; init; }
|
public required string EmployeeInitials { get; init; }
|
||||||
public required string EmployeeName { get; init; }
|
public required string EmployeeName { get; init; }
|
||||||
public required string Status { get; init; }
|
public required string Status { get; init; }
|
||||||
|
public required string StatusText { get; init; }
|
||||||
public string? IndicatorColor { get; init; }
|
public string? IndicatorColor { get; init; }
|
||||||
|
}
|
||||||
|
|
||||||
public string StatusText => Status switch
|
internal class BookingItemData
|
||||||
{
|
{
|
||||||
"completed" => "Gennemført",
|
public required string Key { get; init; }
|
||||||
"inprogress" => "I gang",
|
public required string TimeStart { get; init; }
|
||||||
"confirmed" => "Bekræftet",
|
public required string TimeEnd { get; init; }
|
||||||
"pending" => "Afventer",
|
public required string Service { get; init; }
|
||||||
_ => Status
|
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
|
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",
|
Key = "booking-1",
|
||||||
TimeStart = "08:00",
|
TimeStart = "08:00",
|
||||||
|
|
@ -48,7 +69,7 @@ public static class BookingItemCatalog
|
||||||
EmployeeName = "Maria Hansen",
|
EmployeeName = "Maria Hansen",
|
||||||
Status = "completed"
|
Status = "completed"
|
||||||
},
|
},
|
||||||
["booking-2"] = new BookingItemViewModel
|
["booking-2"] = new BookingItemData
|
||||||
{
|
{
|
||||||
Key = "booking-2",
|
Key = "booking-2",
|
||||||
TimeStart = "08:30",
|
TimeStart = "08:30",
|
||||||
|
|
@ -59,7 +80,7 @@ public static class BookingItemCatalog
|
||||||
EmployeeName = "Anna Sørensen",
|
EmployeeName = "Anna Sørensen",
|
||||||
Status = "completed"
|
Status = "completed"
|
||||||
},
|
},
|
||||||
["booking-3"] = new BookingItemViewModel
|
["booking-3"] = new BookingItemData
|
||||||
{
|
{
|
||||||
Key = "booking-3",
|
Key = "booking-3",
|
||||||
TimeStart = "09:00",
|
TimeStart = "09:00",
|
||||||
|
|
@ -70,7 +91,7 @@ public static class BookingItemCatalog
|
||||||
EmployeeName = "Peter Kristensen",
|
EmployeeName = "Peter Kristensen",
|
||||||
Status = "completed"
|
Status = "completed"
|
||||||
},
|
},
|
||||||
["booking-4"] = new BookingItemViewModel
|
["booking-4"] = new BookingItemData
|
||||||
{
|
{
|
||||||
Key = "booking-4",
|
Key = "booking-4",
|
||||||
TimeStart = "10:30",
|
TimeStart = "10:30",
|
||||||
|
|
@ -82,7 +103,7 @@ public static class BookingItemCatalog
|
||||||
Status = "inprogress",
|
Status = "inprogress",
|
||||||
IndicatorColor = "blue"
|
IndicatorColor = "blue"
|
||||||
},
|
},
|
||||||
["booking-5"] = new BookingItemViewModel
|
["booking-5"] = new BookingItemData
|
||||||
{
|
{
|
||||||
Key = "booking-5",
|
Key = "booking-5",
|
||||||
TimeStart = "10:00",
|
TimeStart = "10:00",
|
||||||
|
|
@ -94,7 +115,7 @@ public static class BookingItemCatalog
|
||||||
Status = "inprogress",
|
Status = "inprogress",
|
||||||
IndicatorColor = "purple"
|
IndicatorColor = "purple"
|
||||||
},
|
},
|
||||||
["booking-6"] = new BookingItemViewModel
|
["booking-6"] = new BookingItemData
|
||||||
{
|
{
|
||||||
Key = "booking-6",
|
Key = "booking-6",
|
||||||
TimeStart = "11:00",
|
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))
|
if (!Bookings.TryGetValue(key, out var booking))
|
||||||
return 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;
|
public static IEnumerable<string> AllKeys => Bookings.Keys;
|
||||||
|
|
|
||||||
|
|
@ -1,12 +1,20 @@
|
||||||
using Microsoft.AspNetCore.Mvc;
|
using Microsoft.AspNetCore.Mvc;
|
||||||
|
using PlanTempus.Application.Features.Localization.Services;
|
||||||
|
|
||||||
namespace PlanTempus.Application.Features.Dashboard.Components;
|
namespace PlanTempus.Application.Features.Dashboard.Components;
|
||||||
|
|
||||||
public class BookingListViewComponent : ViewComponent
|
public class BookingListViewComponent : ViewComponent
|
||||||
{
|
{
|
||||||
|
private readonly ILocalizationService _localization;
|
||||||
|
|
||||||
|
public BookingListViewComponent(ILocalizationService localization)
|
||||||
|
{
|
||||||
|
_localization = localization;
|
||||||
|
}
|
||||||
|
|
||||||
public IViewComponentResult Invoke(string key)
|
public IViewComponentResult Invoke(string key)
|
||||||
{
|
{
|
||||||
var model = BookingListCatalog.Get(key);
|
var model = BookingListCatalog.Get(key, _localization);
|
||||||
return View(model);
|
return View(model);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -19,24 +27,38 @@ public class BookingListViewModel
|
||||||
public required IReadOnlyList<string> BookingKeys { get; init; }
|
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
|
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",
|
Key = "todays-bookings",
|
||||||
Title = "Dagens bookinger",
|
TitleKey = "dashboard.bookings.title",
|
||||||
CurrentTime = "10:45",
|
CurrentTime = "10:45",
|
||||||
BookingKeys = ["booking-1", "booking-2", "booking-3", "booking-4", "booking-5", "booking-6"]
|
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))
|
if (!Lists.TryGetValue(key, out var list))
|
||||||
return 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>
|
<i class="ph ph-calendar-check"></i>
|
||||||
@Model.Title
|
@Model.Title
|
||||||
</swp-card-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-card-header>
|
||||||
<swp-current-time>
|
<swp-current-time>
|
||||||
<i class="ph ph-clock"></i>
|
<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-current-time>
|
||||||
<swp-card-content>
|
<swp-card-content>
|
||||||
<swp-booking-list>
|
<swp-booking-list>
|
||||||
|
|
|
||||||
|
|
@ -1,12 +1,20 @@
|
||||||
using Microsoft.AspNetCore.Mvc;
|
using Microsoft.AspNetCore.Mvc;
|
||||||
|
using PlanTempus.Application.Features.Localization.Services;
|
||||||
|
|
||||||
namespace PlanTempus.Application.Features.Dashboard.Components;
|
namespace PlanTempus.Application.Features.Dashboard.Components;
|
||||||
|
|
||||||
public class NotificationListViewComponent : ViewComponent
|
public class NotificationListViewComponent : ViewComponent
|
||||||
{
|
{
|
||||||
|
private readonly ILocalizationService _localization;
|
||||||
|
|
||||||
|
public NotificationListViewComponent(ILocalizationService localization)
|
||||||
|
{
|
||||||
|
_localization = localization;
|
||||||
|
}
|
||||||
|
|
||||||
public IViewComponentResult Invoke(string key)
|
public IViewComponentResult Invoke(string key)
|
||||||
{
|
{
|
||||||
var model = NotificationListCatalog.Get(key);
|
var model = NotificationListCatalog.Get(key, _localization);
|
||||||
return View(model);
|
return View(model);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -19,24 +27,38 @@ public class NotificationListViewModel
|
||||||
public required IReadOnlyList<string> NotificationKeys { get; init; }
|
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
|
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",
|
Key = "recent-notifications",
|
||||||
Title = "Notifikationer",
|
TitleKey = "dashboard.notifications.title",
|
||||||
ActionText = "Marker alle som læst",
|
ActionTextKey = "dashboard.notifications.markAllRead",
|
||||||
NotificationKeys = ["notif-1", "notif-2", "notif-3", "notif-4"]
|
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))
|
if (!Lists.TryGetValue(key, out var list))
|
||||||
return 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 Microsoft.AspNetCore.Mvc;
|
||||||
|
using PlanTempus.Application.Features.Localization.Services;
|
||||||
|
|
||||||
namespace PlanTempus.Application.Features.Dashboard.Components;
|
namespace PlanTempus.Application.Features.Dashboard.Components;
|
||||||
|
|
||||||
|
|
@ -7,9 +8,16 @@ namespace PlanTempus.Application.Features.Dashboard.Components;
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public class QuickStatViewComponent : ViewComponent
|
public class QuickStatViewComponent : ViewComponent
|
||||||
{
|
{
|
||||||
|
private readonly ILocalizationService _localization;
|
||||||
|
|
||||||
|
public QuickStatViewComponent(ILocalizationService localization)
|
||||||
|
{
|
||||||
|
_localization = localization;
|
||||||
|
}
|
||||||
|
|
||||||
public IViewComponentResult Invoke(string key)
|
public IViewComponentResult Invoke(string key)
|
||||||
{
|
{
|
||||||
var model = QuickStatCatalog.Get(key);
|
var model = QuickStatCatalog.Get(key, _localization);
|
||||||
return View(model);
|
return View(model);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -24,45 +32,57 @@ public class QuickStatViewModel
|
||||||
public required string Label { get; init; }
|
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>
|
/// <summary>
|
||||||
/// Catalog of available quick stats with their data.
|
/// Catalog of available quick stats with their data.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public static class QuickStatCatalog
|
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",
|
Key = "bookings-week",
|
||||||
Value = "47",
|
Value = "47",
|
||||||
Label = "Bookinger"
|
LabelKey = "dashboard.quickStats.bookings"
|
||||||
},
|
},
|
||||||
["revenue-week"] = new QuickStatViewModel
|
["revenue-week"] = new QuickStatData
|
||||||
{
|
{
|
||||||
Key = "revenue-week",
|
Key = "revenue-week",
|
||||||
Value = "38.200 kr",
|
Value = "38.200 kr",
|
||||||
Label = "Omsætning"
|
LabelKey = "dashboard.quickStats.revenue"
|
||||||
},
|
},
|
||||||
["new-customers"] = new QuickStatViewModel
|
["new-customers"] = new QuickStatData
|
||||||
{
|
{
|
||||||
Key = "new-customers",
|
Key = "new-customers",
|
||||||
Value = "8",
|
Value = "8",
|
||||||
Label = "Nye kunder"
|
LabelKey = "dashboard.quickStats.newCustomers"
|
||||||
},
|
},
|
||||||
["avg-occupancy"] = new QuickStatViewModel
|
["avg-occupancy"] = new QuickStatData
|
||||||
{
|
{
|
||||||
Key = "avg-occupancy",
|
Key = "avg-occupancy",
|
||||||
Value = "72%",
|
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))
|
if (!Stats.TryGetValue(key, out var stat))
|
||||||
return 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;
|
public static IEnumerable<string> AllKeys => Stats.Keys;
|
||||||
|
|
|
||||||
|
|
@ -1,12 +1,20 @@
|
||||||
using Microsoft.AspNetCore.Mvc;
|
using Microsoft.AspNetCore.Mvc;
|
||||||
|
using PlanTempus.Application.Features.Localization.Services;
|
||||||
|
|
||||||
namespace PlanTempus.Application.Features.Dashboard.Components;
|
namespace PlanTempus.Application.Features.Dashboard.Components;
|
||||||
|
|
||||||
public class QuickStatListViewComponent : ViewComponent
|
public class QuickStatListViewComponent : ViewComponent
|
||||||
{
|
{
|
||||||
|
private readonly ILocalizationService _localization;
|
||||||
|
|
||||||
|
public QuickStatListViewComponent(ILocalizationService localization)
|
||||||
|
{
|
||||||
|
_localization = localization;
|
||||||
|
}
|
||||||
|
|
||||||
public IViewComponentResult Invoke(string key)
|
public IViewComponentResult Invoke(string key)
|
||||||
{
|
{
|
||||||
var model = QuickStatListCatalog.Get(key);
|
var model = QuickStatListCatalog.Get(key, _localization);
|
||||||
return View(model);
|
return View(model);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -19,24 +27,38 @@ public class QuickStatListViewModel
|
||||||
public required IReadOnlyList<string> StatKeys { get; init; }
|
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
|
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",
|
Key = "this-week",
|
||||||
Title = "Denne uge",
|
TitleKey = "dashboard.quickStats.title",
|
||||||
Icon = "chart-line-up",
|
Icon = "chart-line-up",
|
||||||
StatKeys = ["bookings-week", "revenue-week", "new-customers", "avg-occupancy"]
|
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))
|
if (!Lists.TryGetValue(key, out var list))
|
||||||
return 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 Microsoft.AspNetCore.Mvc;
|
||||||
|
using PlanTempus.Application.Features.Localization.Services;
|
||||||
|
|
||||||
namespace PlanTempus.Application.Features.Dashboard.Components;
|
namespace PlanTempus.Application.Features.Dashboard.Components;
|
||||||
|
|
||||||
|
|
@ -7,9 +8,16 @@ namespace PlanTempus.Application.Features.Dashboard.Components;
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public class StatCardViewComponent : ViewComponent
|
public class StatCardViewComponent : ViewComponent
|
||||||
{
|
{
|
||||||
|
private readonly ILocalizationService _localization;
|
||||||
|
|
||||||
|
public StatCardViewComponent(ILocalizationService localization)
|
||||||
|
{
|
||||||
|
_localization = localization;
|
||||||
|
}
|
||||||
|
|
||||||
public IViewComponentResult Invoke(string key)
|
public IViewComponentResult Invoke(string key)
|
||||||
{
|
{
|
||||||
var model = StatCardCatalog.Get(key);
|
var model = StatCardCatalog.Get(key, _localization);
|
||||||
return View(model);
|
return View(model);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -29,61 +37,84 @@ public class StatCardViewModel
|
||||||
public bool HasTrend => !string.IsNullOrEmpty(TrendText);
|
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>
|
/// <summary>
|
||||||
/// Catalog of available stat cards with their data.
|
/// Catalog of available stat cards with their data.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public static class StatCardCatalog
|
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",
|
Key = "bookings-today",
|
||||||
Value = "12",
|
Value = "12",
|
||||||
Label = "Bookinger i dag",
|
LabelKey = "dashboard.stats.bookingsToday",
|
||||||
TrendText = "4 gennemført, 2 i gang",
|
TrendTextKey = "dashboard.stats.bookingsTrend",
|
||||||
TrendIcon = "ph-check-circle",
|
TrendIcon = "ph-check-circle",
|
||||||
TrendDirection = "up",
|
TrendDirection = "up",
|
||||||
Variant = "highlight"
|
Variant = "highlight"
|
||||||
},
|
},
|
||||||
["expected-revenue"] = new StatCardViewModel
|
["expected-revenue"] = new StatCardData
|
||||||
{
|
{
|
||||||
Key = "expected-revenue",
|
Key = "expected-revenue",
|
||||||
Value = "8.450 kr",
|
Value = "8.450 kr",
|
||||||
Label = "Forventet omsætning",
|
LabelKey = "dashboard.stats.expectedRevenue",
|
||||||
TrendText = "+12% vs. gennemsnit",
|
TrendTextKey = "dashboard.stats.revenueTrend",
|
||||||
TrendIcon = "ph-trend-up",
|
TrendIcon = "ph-trend-up",
|
||||||
TrendDirection = "up",
|
TrendDirection = "up",
|
||||||
Variant = "success"
|
Variant = "success"
|
||||||
},
|
},
|
||||||
["occupancy-rate"] = new StatCardViewModel
|
["occupancy-rate"] = new StatCardData
|
||||||
{
|
{
|
||||||
Key = "occupancy-rate",
|
Key = "occupancy-rate",
|
||||||
Value = "78%",
|
Value = "78%",
|
||||||
Label = "Belægningsgrad",
|
LabelKey = "dashboard.stats.occupancyRate",
|
||||||
TrendText = "God kapacitet",
|
TrendTextKey = "dashboard.stats.occupancyTrend",
|
||||||
TrendIcon = "ph-trend-up",
|
TrendIcon = "ph-trend-up",
|
||||||
TrendDirection = "up",
|
TrendDirection = "up",
|
||||||
Variant = null
|
Variant = null
|
||||||
},
|
},
|
||||||
["needs-attention"] = new StatCardViewModel
|
["needs-attention"] = new StatCardData
|
||||||
{
|
{
|
||||||
Key = "needs-attention",
|
Key = "needs-attention",
|
||||||
Value = "4",
|
Value = "4",
|
||||||
Label = "Kræver opmærksomhed",
|
LabelKey = "dashboard.stats.needsAttention",
|
||||||
TrendText = null,
|
TrendTextKey = null,
|
||||||
TrendIcon = null,
|
TrendIcon = null,
|
||||||
TrendDirection = null,
|
TrendDirection = null,
|
||||||
Variant = "warning"
|
Variant = "warning"
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
public static StatCardViewModel Get(string key)
|
public static StatCardViewModel Get(string key, ILocalizationService localization)
|
||||||
{
|
{
|
||||||
if (Cards.TryGetValue(key, out var card))
|
if (!Cards.TryGetValue(key, out var card))
|
||||||
return 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;
|
public static IEnumerable<string> AllKeys => Cards.Keys;
|
||||||
|
|
|
||||||
|
|
@ -22,7 +22,7 @@
|
||||||
<swp-ai-insight>
|
<swp-ai-insight>
|
||||||
<swp-ai-header>
|
<swp-ai-header>
|
||||||
<i class="ph ph-sparkle"></i>
|
<i class="ph ph-sparkle"></i>
|
||||||
<span>AI Analyse</span>
|
<span localize="dashboard.ai.header">AI Analyse</span>
|
||||||
</swp-ai-header>
|
</swp-ai-header>
|
||||||
<swp-ai-text>
|
<swp-ai-text>
|
||||||
<strong>Godt i gang!</strong> 4 af 12 bookinger er gennemført. 2 er i gang nu, og 6 venter.
|
<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 -->
|
<!-- Quick Actions -->
|
||||||
<swp-card>
|
<swp-card>
|
||||||
<swp-card-header>
|
<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-header>
|
||||||
<swp-card-content>
|
<swp-card-content>
|
||||||
<swp-quick-actions>
|
<swp-quick-actions>
|
||||||
<swp-quick-action-btn>
|
<swp-quick-action-btn>
|
||||||
<i class="ph ph-plus"></i>
|
<i class="ph ph-plus"></i>
|
||||||
Ny booking
|
<span localize="dashboard.quickActions.newBooking">Ny booking</span>
|
||||||
</swp-quick-action-btn>
|
</swp-quick-action-btn>
|
||||||
<swp-quick-action-btn>
|
<swp-quick-action-btn>
|
||||||
<i class="ph ph-user-plus"></i>
|
<i class="ph ph-user-plus"></i>
|
||||||
Ny kunde
|
<span localize="dashboard.quickActions.newCustomer">Ny kunde</span>
|
||||||
</swp-quick-action-btn>
|
</swp-quick-action-btn>
|
||||||
</swp-quick-actions>
|
</swp-quick-actions>
|
||||||
</swp-card-content>
|
</swp-card-content>
|
||||||
|
|
|
||||||
|
|
@ -24,10 +24,197 @@
|
||||||
"close": "Luk",
|
"close": "Luk",
|
||||||
"delete": "Slet",
|
"delete": "Slet",
|
||||||
"edit": "Rediger",
|
"edit": "Rediger",
|
||||||
"add": "Tilføj"
|
"add": "Tilføj",
|
||||||
|
"from": "Fra",
|
||||||
|
"to": "Til",
|
||||||
|
"all": "Alle",
|
||||||
|
"reset": "Nulstil",
|
||||||
|
"status": "Status"
|
||||||
},
|
},
|
||||||
"sidebar": {
|
"sidebar": {
|
||||||
"lockScreen": "Lås skærm",
|
"lockScreen": "Lås skærm",
|
||||||
"appName": "Salon OS"
|
"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",
|
"close": "Close",
|
||||||
"delete": "Delete",
|
"delete": "Delete",
|
||||||
"edit": "Edit",
|
"edit": "Edit",
|
||||||
"add": "Add"
|
"add": "Add",
|
||||||
|
"from": "From",
|
||||||
|
"to": "To",
|
||||||
|
"all": "All",
|
||||||
|
"reset": "Reset",
|
||||||
|
"status": "Status"
|
||||||
},
|
},
|
||||||
"sidebar": {
|
"sidebar": {
|
||||||
"lockScreen": "Lock screen",
|
"lockScreen": "Lock screen",
|
||||||
"appName": "Salon OS"
|
"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-waitlist-drawer data-drawer="lg" id="waitlist-drawer">
|
||||||
<swp-drawer-header>
|
<swp-drawer-header>
|
||||||
<swp-drawer-title>
|
<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-title>
|
||||||
<swp-drawer-close data-drawer-close>
|
<swp-drawer-close data-drawer-close>
|
||||||
<i class="ph ph-x"></i>
|
<i class="ph ph-x"></i>
|
||||||
|
|
|
||||||
|
|
@ -1,6 +1,6 @@
|
||||||
<swp-profile-drawer id="profileDrawer">
|
<swp-profile-drawer id="profileDrawer">
|
||||||
<swp-drawer-header>
|
<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">
|
<swp-drawer-close id="closeProfileDrawer">
|
||||||
<i class="ph ph-x"></i>
|
<i class="ph ph-x"></i>
|
||||||
</swp-drawer-close>
|
</swp-drawer-close>
|
||||||
|
|
@ -18,11 +18,11 @@
|
||||||
<swp-drawer-menu>
|
<swp-drawer-menu>
|
||||||
<swp-drawer-menu-item>
|
<swp-drawer-menu-item>
|
||||||
<i class="ph ph-user"></i>
|
<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>
|
||||||
<swp-drawer-menu-item>
|
<swp-drawer-menu-item>
|
||||||
<i class="ph ph-gear"></i>
|
<i class="ph ph-gear"></i>
|
||||||
<span>Indstillinger</span>
|
<span localize="profile.settings">Indstillinger</span>
|
||||||
</swp-drawer-menu-item>
|
</swp-drawer-menu-item>
|
||||||
</swp-drawer-menu>
|
</swp-drawer-menu>
|
||||||
|
|
||||||
|
|
@ -31,7 +31,7 @@
|
||||||
<swp-theme-toggle>
|
<swp-theme-toggle>
|
||||||
<swp-theme-label>
|
<swp-theme-label>
|
||||||
<i class="ph ph-moon"></i>
|
<i class="ph ph-moon"></i>
|
||||||
<span>Mørk tilstand</span>
|
<span localize="profile.darkMode">Mørk tilstand</span>
|
||||||
</swp-theme-label>
|
</swp-theme-label>
|
||||||
<swp-toggle-switch id="themeToggle">
|
<swp-toggle-switch id="themeToggle">
|
||||||
<input type="checkbox" id="themeCheckbox">
|
<input type="checkbox" id="themeCheckbox">
|
||||||
|
|
@ -43,7 +43,7 @@
|
||||||
<swp-drawer-footer>
|
<swp-drawer-footer>
|
||||||
<swp-drawer-action class="logout" id="logoutBtn">
|
<swp-drawer-action class="logout" id="logoutBtn">
|
||||||
<i class="ph ph-sign-out"></i>
|
<i class="ph ph-sign-out"></i>
|
||||||
<span>Log ud</span>
|
<span localize="profile.logout">Log ud</span>
|
||||||
</swp-drawer-action>
|
</swp-drawer-action>
|
||||||
</swp-drawer-footer>
|
</swp-drawer-footer>
|
||||||
</swp-profile-drawer>
|
</swp-profile-drawer>
|
||||||
|
|
|
||||||
|
|
@ -10,14 +10,14 @@
|
||||||
=========================================== */
|
=========================================== */
|
||||||
swp-account-section {
|
swp-account-section {
|
||||||
display: block;
|
display: block;
|
||||||
margin-bottom: var(--spacing-8);
|
margin-bottom: var(--section-gap);
|
||||||
}
|
}
|
||||||
|
|
||||||
swp-account-section-header {
|
swp-account-section-header {
|
||||||
display: flex;
|
display: flex;
|
||||||
align-items: center;
|
align-items: center;
|
||||||
justify-content: space-between;
|
justify-content: space-between;
|
||||||
margin-bottom: var(--spacing-4);
|
margin-bottom: var(--section-gap);
|
||||||
}
|
}
|
||||||
|
|
||||||
swp-account-section-title {
|
swp-account-section-title {
|
||||||
|
|
@ -40,7 +40,7 @@ swp-account-section-title i {
|
||||||
swp-plan-grid {
|
swp-plan-grid {
|
||||||
display: grid;
|
display: grid;
|
||||||
grid-template-columns: repeat(3, 1fr);
|
grid-template-columns: repeat(3, 1fr);
|
||||||
gap: var(--spacing-5);
|
gap: var(--card-gap);
|
||||||
}
|
}
|
||||||
|
|
||||||
@media (max-width: 1024px) {
|
@media (max-width: 1024px) {
|
||||||
|
|
@ -82,7 +82,7 @@ swp-plan-badge.popular {
|
||||||
/* Plan action buttons */
|
/* Plan action buttons */
|
||||||
swp-plan-action {
|
swp-plan-action {
|
||||||
margin-top: auto;
|
margin-top: auto;
|
||||||
padding-top: var(--spacing-5);
|
padding-top: var(--card-padding);
|
||||||
}
|
}
|
||||||
|
|
||||||
swp-plan-action swp-btn {
|
swp-plan-action swp-btn {
|
||||||
|
|
@ -96,7 +96,7 @@ swp-plan-action swp-btn {
|
||||||
swp-billing-grid {
|
swp-billing-grid {
|
||||||
display: grid;
|
display: grid;
|
||||||
grid-template-columns: 380px 1fr;
|
grid-template-columns: 380px 1fr;
|
||||||
gap: var(--spacing-6);
|
gap: var(--card-gap);
|
||||||
}
|
}
|
||||||
|
|
||||||
@media (max-width: 1024px) {
|
@media (max-width: 1024px) {
|
||||||
|
|
@ -111,18 +111,18 @@ swp-billing-grid {
|
||||||
swp-payment-card {
|
swp-payment-card {
|
||||||
display: flex;
|
display: flex;
|
||||||
flex-direction: column;
|
flex-direction: column;
|
||||||
gap: var(--spacing-5);
|
gap: var(--card-padding);
|
||||||
background: var(--color-surface);
|
background: var(--color-surface);
|
||||||
border-radius: var(--radius-lg);
|
border-radius: var(--radius-lg);
|
||||||
box-shadow: 0 1px 3px rgba(0, 0, 0, 0.08);
|
box-shadow: 0 1px 3px rgba(0, 0, 0, 0.08);
|
||||||
padding: var(--spacing-6);
|
padding: var(--card-padding);
|
||||||
}
|
}
|
||||||
|
|
||||||
swp-payment-method {
|
swp-payment-method {
|
||||||
display: flex;
|
display: flex;
|
||||||
align-items: center;
|
align-items: center;
|
||||||
gap: var(--spacing-4);
|
gap: var(--spacing-4);
|
||||||
padding: var(--spacing-4);
|
padding: var(--card-padding);
|
||||||
background: var(--color-background-alt);
|
background: var(--color-background-alt);
|
||||||
border-radius: var(--radius-md);
|
border-radius: var(--radius-md);
|
||||||
}
|
}
|
||||||
|
|
@ -168,7 +168,7 @@ swp-payment-detail {
|
||||||
display: flex;
|
display: flex;
|
||||||
justify-content: space-between;
|
justify-content: space-between;
|
||||||
align-items: center;
|
align-items: center;
|
||||||
padding: var(--spacing-3) 0;
|
padding: var(--spacing-4) 0;
|
||||||
border-bottom: 1px solid var(--color-border);
|
border-bottom: 1px solid var(--color-border);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -206,7 +206,7 @@ swp-invoices-header {
|
||||||
display: flex;
|
display: flex;
|
||||||
align-items: center;
|
align-items: center;
|
||||||
justify-content: space-between;
|
justify-content: space-between;
|
||||||
padding: var(--spacing-4) var(--spacing-5);
|
padding: var(--card-padding);
|
||||||
border-bottom: 1px solid var(--color-border);
|
border-bottom: 1px solid var(--color-border);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -255,17 +255,17 @@ swp-invoice-row:last-child {
|
||||||
}
|
}
|
||||||
|
|
||||||
swp-invoice-cell {
|
swp-invoice-cell {
|
||||||
padding: var(--spacing-3) var(--spacing-4);
|
padding: var(--spacing-4) var(--spacing-4);
|
||||||
font-size: var(--font-size-sm);
|
font-size: var(--font-size-sm);
|
||||||
color: var(--color-text);
|
color: var(--color-text);
|
||||||
}
|
}
|
||||||
|
|
||||||
swp-invoice-cell:first-child {
|
swp-invoice-cell:first-child {
|
||||||
padding-left: var(--spacing-5);
|
padding-left: var(--card-padding);
|
||||||
}
|
}
|
||||||
|
|
||||||
swp-invoice-cell:last-child {
|
swp-invoice-cell:last-child {
|
||||||
padding-right: var(--spacing-5);
|
padding-right: var(--card-padding);
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Header cells */
|
/* Header cells */
|
||||||
|
|
@ -275,8 +275,8 @@ swp-invoice-table-header swp-invoice-cell {
|
||||||
text-transform: uppercase;
|
text-transform: uppercase;
|
||||||
letter-spacing: 0.5px;
|
letter-spacing: 0.5px;
|
||||||
color: var(--color-text-secondary);
|
color: var(--color-text-secondary);
|
||||||
padding-top: var(--spacing-3);
|
padding-top: var(--spacing-4);
|
||||||
padding-bottom: var(--spacing-3);
|
padding-bottom: var(--spacing-4);
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Invoice number mono font */
|
/* Invoice number mono font */
|
||||||
|
|
@ -384,18 +384,18 @@ swp-btn.sm i {
|
||||||
}
|
}
|
||||||
|
|
||||||
swp-invoice-cell {
|
swp-invoice-cell {
|
||||||
padding: var(--spacing-2) var(--spacing-3);
|
padding: var(--spacing-3) var(--spacing-3);
|
||||||
}
|
}
|
||||||
|
|
||||||
swp-invoice-cell:first-child {
|
swp-invoice-cell:first-child {
|
||||||
padding-left: var(--spacing-4);
|
padding-left: var(--spacing-5);
|
||||||
}
|
}
|
||||||
|
|
||||||
swp-invoice-cell:last-child {
|
swp-invoice-cell:last-child {
|
||||||
padding-right: var(--spacing-4);
|
padding-right: var(--spacing-5);
|
||||||
}
|
}
|
||||||
|
|
||||||
swp-payment-card {
|
swp-payment-card {
|
||||||
padding: var(--spacing-4);
|
padding: var(--spacing-5);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -20,7 +20,7 @@ swp-attention-item {
|
||||||
grid-template-columns: subgrid;
|
grid-template-columns: subgrid;
|
||||||
align-items: center;
|
align-items: center;
|
||||||
gap: var(--spacing-8);
|
gap: var(--spacing-8);
|
||||||
padding: var(--spacing-5) var(--spacing-6);
|
padding: var(--card-padding);
|
||||||
background: var(--color-background-alt);
|
background: var(--color-background-alt);
|
||||||
border-radius: var(--radius-xl);
|
border-radius: var(--radius-xl);
|
||||||
border-left: 3px solid var(--color-border);
|
border-left: 3px solid var(--color-border);
|
||||||
|
|
|
||||||
|
|
@ -19,7 +19,7 @@ swp-booking-item {
|
||||||
grid-column: 1 / -1;
|
grid-column: 1 / -1;
|
||||||
grid-template-columns: subgrid;
|
grid-template-columns: subgrid;
|
||||||
align-items: center;
|
align-items: center;
|
||||||
padding: var(--spacing-4);
|
padding: var(--card-padding);
|
||||||
background: var(--color-background-alt);
|
background: var(--color-background-alt);
|
||||||
border-radius: var(--radius-lg);
|
border-radius: var(--radius-lg);
|
||||||
cursor: pointer;
|
cursor: pointer;
|
||||||
|
|
|
||||||
|
|
@ -190,7 +190,10 @@
|
||||||
--container-max-width-lg: 1400px;
|
--container-max-width-lg: 1400px;
|
||||||
|
|
||||||
/* -------- Card Spacing -------- */
|
/* -------- Card Spacing -------- */
|
||||||
--card-body-padding: var(--spacing-5);
|
--card-padding: 12px;
|
||||||
|
--card-gap: 24px;
|
||||||
|
--section-gap: 24px;
|
||||||
|
--page-padding: 24px;
|
||||||
|
|
||||||
/* -------- Calendar Grid -------- */
|
/* -------- Calendar Grid -------- */
|
||||||
--hour-height: 64px;
|
--hour-height: 64px;
|
||||||
|
|
|
||||||
|
|
@ -19,7 +19,7 @@ swp-notification-item {
|
||||||
grid-column: 1 / -1;
|
grid-column: 1 / -1;
|
||||||
grid-template-columns: subgrid;
|
grid-template-columns: subgrid;
|
||||||
align-items: center;
|
align-items: center;
|
||||||
padding: var(--spacing-5) var(--spacing-6);
|
padding: var(--card-padding);
|
||||||
background: var(--color-background-alt);
|
background: var(--color-background-alt);
|
||||||
border-radius: var(--radius-xl);
|
border-radius: var(--radius-xl);
|
||||||
cursor: pointer;
|
cursor: pointer;
|
||||||
|
|
|
||||||
|
|
@ -11,7 +11,7 @@ swp-page-container {
|
||||||
display: block;
|
display: block;
|
||||||
max-width: var(--page-max-width);
|
max-width: var(--page-max-width);
|
||||||
margin: 0 auto;
|
margin: 0 auto;
|
||||||
padding: var(--spacing-6);
|
padding: var(--page-padding);
|
||||||
}
|
}
|
||||||
|
|
||||||
/* ===========================================
|
/* ===========================================
|
||||||
|
|
@ -21,7 +21,7 @@ swp-page-header {
|
||||||
display: flex;
|
display: flex;
|
||||||
align-items: flex-start;
|
align-items: flex-start;
|
||||||
justify-content: space-between;
|
justify-content: space-between;
|
||||||
margin-bottom: var(--spacing-6);
|
margin-bottom: var(--section-gap);
|
||||||
}
|
}
|
||||||
|
|
||||||
swp-page-title h1 {
|
swp-page-title h1 {
|
||||||
|
|
@ -49,16 +49,15 @@ swp-card {
|
||||||
background: var(--color-surface);
|
background: var(--color-surface);
|
||||||
border: 1px solid var(--color-border);
|
border: 1px solid var(--color-border);
|
||||||
border-radius: var(--border-radius-lg);
|
border-radius: var(--border-radius-lg);
|
||||||
padding: var(--spacing-5);
|
padding: var(--card-padding);
|
||||||
margin-bottom: var(--spacing-5);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
swp-card-header {
|
swp-card-header {
|
||||||
display: flex;
|
display: flex;
|
||||||
align-items: center;
|
align-items: center;
|
||||||
justify-content: space-between;
|
justify-content: space-between;
|
||||||
margin: calc(-1 * var(--spacing-5)) calc(-1 * var(--spacing-5)) var(--spacing-4) calc(-1 * var(--spacing-5));
|
margin: calc(-1 * var(--card-padding)) calc(-1 * var(--card-padding)) var(--spacing-5) calc(-1 * var(--card-padding));
|
||||||
padding: var(--spacing-4) var(--spacing-5);
|
padding: var(--spacing-4) var(--card-padding);
|
||||||
border-bottom: 1px solid var(--color-border);
|
border-bottom: 1px solid var(--color-border);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -96,7 +95,7 @@ swp-card-content:has(> swp-booking-list),
|
||||||
swp-card-content:has(> swp-notification-list),
|
swp-card-content:has(> swp-notification-list),
|
||||||
swp-card-content:has(> swp-attention-list) {
|
swp-card-content:has(> swp-attention-list) {
|
||||||
display: grid;
|
display: grid;
|
||||||
gap: var(--spacing-4);
|
gap: var(--spacing-5);
|
||||||
}
|
}
|
||||||
|
|
||||||
swp-card-content:has(> swp-booking-list) {
|
swp-card-content:has(> swp-booking-list) {
|
||||||
|
|
@ -134,13 +133,13 @@ swp-item-desc {
|
||||||
swp-dashboard-grid {
|
swp-dashboard-grid {
|
||||||
display: grid;
|
display: grid;
|
||||||
grid-template-columns: 1fr 380px;
|
grid-template-columns: 1fr 380px;
|
||||||
gap: var(--spacing-5);
|
gap: var(--card-gap);
|
||||||
}
|
}
|
||||||
|
|
||||||
swp-main-column,
|
swp-main-column,
|
||||||
swp-side-column {
|
swp-side-column {
|
||||||
display: grid;
|
display: grid;
|
||||||
gap: var(--spacing-5);
|
gap: var(--card-gap);
|
||||||
align-content: start;
|
align-content: start;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -149,7 +148,7 @@ swp-side-column {
|
||||||
=========================================== */
|
=========================================== */
|
||||||
swp-ai-insight {
|
swp-ai-insight {
|
||||||
display: block;
|
display: block;
|
||||||
padding: var(--spacing-4) var(--spacing-5);
|
padding: var(--card-padding);
|
||||||
background: linear-gradient(135deg,
|
background: linear-gradient(135deg,
|
||||||
color-mix(in srgb, var(--color-purple) 8%, transparent),
|
color-mix(in srgb, var(--color-purple) 8%, transparent),
|
||||||
color-mix(in srgb, var(--color-teal) 8%, transparent)
|
color-mix(in srgb, var(--color-teal) 8%, transparent)
|
||||||
|
|
@ -183,14 +182,14 @@ swp-ai-text {
|
||||||
swp-quick-actions {
|
swp-quick-actions {
|
||||||
display: flex;
|
display: flex;
|
||||||
flex-direction: column;
|
flex-direction: column;
|
||||||
gap: var(--spacing-2);
|
gap: var(--spacing-3);
|
||||||
}
|
}
|
||||||
|
|
||||||
swp-quick-action-btn {
|
swp-quick-action-btn {
|
||||||
display: flex;
|
display: flex;
|
||||||
align-items: center;
|
align-items: center;
|
||||||
gap: var(--spacing-2);
|
gap: var(--spacing-3);
|
||||||
padding: var(--spacing-3);
|
padding: var(--card-padding);
|
||||||
font-size: var(--font-size-base);
|
font-size: var(--font-size-base);
|
||||||
font-family: var(--font-family);
|
font-family: var(--font-family);
|
||||||
color: var(--color-text);
|
color: var(--color-text);
|
||||||
|
|
@ -226,7 +225,7 @@ swp-quick-action-btn i {
|
||||||
|
|
||||||
@media (max-width: 768px) {
|
@media (max-width: 768px) {
|
||||||
swp-page-container {
|
swp-page-container {
|
||||||
padding: var(--spacing-4);
|
padding: var(--spacing-5);
|
||||||
}
|
}
|
||||||
|
|
||||||
swp-page-header {
|
swp-page-header {
|
||||||
|
|
|
||||||
|
|
@ -10,7 +10,7 @@
|
||||||
swp-quick-stats {
|
swp-quick-stats {
|
||||||
display: grid;
|
display: grid;
|
||||||
grid-template-columns: repeat(2, 1fr);
|
grid-template-columns: repeat(2, 1fr);
|
||||||
gap: var(--spacing-3);
|
gap: var(--card-gap);
|
||||||
}
|
}
|
||||||
|
|
||||||
/* ===========================================
|
/* ===========================================
|
||||||
|
|
@ -19,8 +19,8 @@ swp-quick-stats {
|
||||||
swp-quick-stat {
|
swp-quick-stat {
|
||||||
display: flex;
|
display: flex;
|
||||||
flex-direction: column;
|
flex-direction: column;
|
||||||
gap: var(--spacing-1);
|
gap: var(--spacing-2);
|
||||||
padding: var(--spacing-3);
|
padding: var(--card-padding);
|
||||||
background: var(--color-background-alt);
|
background: var(--color-background-alt);
|
||||||
border-radius: var(--radius-md);
|
border-radius: var(--radius-md);
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -11,15 +11,15 @@ swp-stats-bar,
|
||||||
swp-stats-grid {
|
swp-stats-grid {
|
||||||
display: grid;
|
display: grid;
|
||||||
grid-template-columns: repeat(4, 1fr);
|
grid-template-columns: repeat(4, 1fr);
|
||||||
gap: var(--spacing-4);
|
gap: var(--card-gap);
|
||||||
margin-bottom: var(--spacing-5);
|
margin-bottom: var(--section-gap);
|
||||||
}
|
}
|
||||||
|
|
||||||
swp-stats-row {
|
swp-stats-row {
|
||||||
display: grid;
|
display: grid;
|
||||||
grid-template-columns: repeat(3, 1fr);
|
grid-template-columns: repeat(3, 1fr);
|
||||||
gap: var(--spacing-4);
|
gap: var(--card-gap);
|
||||||
margin-bottom: var(--spacing-5);
|
margin-bottom: var(--section-gap);
|
||||||
}
|
}
|
||||||
|
|
||||||
/* ===========================================
|
/* ===========================================
|
||||||
|
|
@ -30,15 +30,15 @@ swp-stat-card {
|
||||||
flex-direction: column;
|
flex-direction: column;
|
||||||
background: var(--color-surface);
|
background: var(--color-surface);
|
||||||
border-radius: var(--border-radius-lg);
|
border-radius: var(--border-radius-lg);
|
||||||
padding: var(--spacing-4) var(--spacing-5);
|
padding: var(--card-padding);
|
||||||
border: 1px solid var(--color-border);
|
border: 1px solid var(--color-border);
|
||||||
}
|
}
|
||||||
|
|
||||||
swp-stat-box {
|
swp-stat-box {
|
||||||
display: flex;
|
display: flex;
|
||||||
flex-direction: column;
|
flex-direction: column;
|
||||||
gap: var(--spacing-1);
|
gap: var(--spacing-2);
|
||||||
padding: var(--spacing-4);
|
padding: var(--card-padding);
|
||||||
background: var(--color-surface);
|
background: var(--color-surface);
|
||||||
border-radius: var(--border-radius);
|
border-radius: var(--border-radius);
|
||||||
border: 1px solid var(--color-border);
|
border: 1px solid var(--color-border);
|
||||||
|
|
@ -72,7 +72,7 @@ swp-stat-label {
|
||||||
display: block;
|
display: block;
|
||||||
font-size: var(--font-size-sm);
|
font-size: var(--font-size-sm);
|
||||||
color: var(--color-text-secondary);
|
color: var(--color-text-secondary);
|
||||||
margin-top: var(--spacing-1);
|
margin-top: var(--spacing-2);
|
||||||
}
|
}
|
||||||
|
|
||||||
swp-stat-box swp-stat-label {
|
swp-stat-box swp-stat-label {
|
||||||
|
|
|
||||||
|
|
@ -10,8 +10,8 @@
|
||||||
swp-waitlist-card {
|
swp-waitlist-card {
|
||||||
display: flex;
|
display: flex;
|
||||||
align-items: center;
|
align-items: center;
|
||||||
gap: var(--spacing-3);
|
gap: var(--spacing-4);
|
||||||
padding: var(--spacing-4);
|
padding: var(--card-padding);
|
||||||
background: var(--color-surface);
|
background: var(--color-surface);
|
||||||
border: 1px solid var(--color-border);
|
border: 1px solid var(--color-border);
|
||||||
border-radius: var(--radius-lg);
|
border-radius: var(--radius-lg);
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue