Adds account management and subscription features

Introduces new account pages for managing subscriptions, payment methods, and invoice history

Includes:
- Subscription plan selection view
- Payment method display component
- Invoice history table
- Account page layout and styling

Updates main layout to include new CSS files for account management
This commit is contained in:
Janus C. H. Knudsen 2026-01-11 22:33:21 +01:00
parent 5e0bd9db74
commit 1f400dcc6e
11 changed files with 724 additions and 1 deletions

View file

@ -0,0 +1,108 @@
<swp-invoices-card>
<swp-invoices-header>
<swp-invoices-title>Faktura-historik</swp-invoices-title>
</swp-invoices-header>
<swp-invoice-table>
<swp-invoice-table-header>
<swp-invoice-row>
<swp-invoice-cell>Dato</swp-invoice-cell>
<swp-invoice-cell>Fakturanr.</swp-invoice-cell>
<swp-invoice-cell>Beløb</swp-invoice-cell>
<swp-invoice-cell>Status</swp-invoice-cell>
<swp-invoice-cell></swp-invoice-cell>
</swp-invoice-row>
</swp-invoice-table-header>
<swp-invoice-table-body>
<swp-invoice-row>
<swp-invoice-cell>1. jan 2026</swp-invoice-cell>
<swp-invoice-cell class="mono">INV-2026-0001</swp-invoice-cell>
<swp-invoice-cell>599,00 kr</swp-invoice-cell>
<swp-invoice-cell>
<swp-invoice-status class="paid">
<i class="ph ph-check-circle"></i>
Betalt
</swp-invoice-status>
</swp-invoice-cell>
<swp-invoice-cell>
<swp-download-btn>
<i class="ph ph-download"></i>
PDF
</swp-download-btn>
</swp-invoice-cell>
</swp-invoice-row>
<swp-invoice-row>
<swp-invoice-cell>1. dec 2025</swp-invoice-cell>
<swp-invoice-cell class="mono">INV-2025-0012</swp-invoice-cell>
<swp-invoice-cell>599,00 kr</swp-invoice-cell>
<swp-invoice-cell>
<swp-invoice-status class="paid">
<i class="ph ph-check-circle"></i>
Betalt
</swp-invoice-status>
</swp-invoice-cell>
<swp-invoice-cell>
<swp-download-btn>
<i class="ph ph-download"></i>
PDF
</swp-download-btn>
</swp-invoice-cell>
</swp-invoice-row>
<swp-invoice-row>
<swp-invoice-cell>1. nov 2025</swp-invoice-cell>
<swp-invoice-cell class="mono">INV-2025-0011</swp-invoice-cell>
<swp-invoice-cell>599,00 kr</swp-invoice-cell>
<swp-invoice-cell>
<swp-invoice-status class="paid">
<i class="ph ph-check-circle"></i>
Betalt
</swp-invoice-status>
</swp-invoice-cell>
<swp-invoice-cell>
<swp-download-btn>
<i class="ph ph-download"></i>
PDF
</swp-download-btn>
</swp-invoice-cell>
</swp-invoice-row>
<swp-invoice-row>
<swp-invoice-cell>1. okt 2025</swp-invoice-cell>
<swp-invoice-cell class="mono">INV-2025-0010</swp-invoice-cell>
<swp-invoice-cell>599,00 kr</swp-invoice-cell>
<swp-invoice-cell>
<swp-invoice-status class="paid">
<i class="ph ph-check-circle"></i>
Betalt
</swp-invoice-status>
</swp-invoice-cell>
<swp-invoice-cell>
<swp-download-btn>
<i class="ph ph-download"></i>
PDF
</swp-download-btn>
</swp-invoice-cell>
</swp-invoice-row>
<swp-invoice-row>
<swp-invoice-cell>1. sep 2025</swp-invoice-cell>
<swp-invoice-cell class="mono">INV-2025-0009</swp-invoice-cell>
<swp-invoice-cell>599,00 kr</swp-invoice-cell>
<swp-invoice-cell>
<swp-invoice-status class="paid">
<i class="ph ph-check-circle"></i>
Betalt
</swp-invoice-status>
</swp-invoice-cell>
<swp-invoice-cell>
<swp-download-btn>
<i class="ph ph-download"></i>
PDF
</swp-download-btn>
</swp-invoice-cell>
</swp-invoice-row>
</swp-invoice-table-body>
</swp-invoice-table>
</swp-invoices-card>

View file

@ -0,0 +1,15 @@
using Microsoft.AspNetCore.Mvc;
namespace PlanTempus.Application.Features.Account.Components;
/// <summary>
/// ViewComponent for the invoice history table.
/// Shows past invoices with status and download options.
/// </summary>
public class InvoiceHistoryViewComponent : ViewComponent
{
public IViewComponentResult Invoke()
{
return View();
}
}

View file

@ -0,0 +1,40 @@
<swp-payment-card>
<swp-payment-method>
<swp-payment-icon>
<i class="ph ph-credit-card"></i>
</swp-payment-icon>
<swp-payment-info>
<swp-payment-type>Visa</swp-payment-type>
<swp-payment-number>**** **** **** 4582</swp-payment-number>
</swp-payment-info>
<swp-btn class="secondary sm">
<i class="ph ph-pencil"></i>
Skift
</swp-btn>
</swp-payment-method>
<swp-payment-detail>
<swp-payment-label>Betalingsfrekvens</swp-payment-label>
<swp-payment-value>Månedlig</swp-payment-value>
</swp-payment-detail>
<swp-payment-detail>
<swp-payment-label>Næste betaling</swp-payment-label>
<swp-payment-value class="highlight">1. februar 2026</swp-payment-value>
</swp-payment-detail>
<swp-payment-detail>
<swp-payment-label>Beløb</swp-payment-label>
<swp-payment-value>599,00 kr</swp-payment-value>
</swp-payment-detail>
<swp-payment-detail>
<swp-payment-label>Kortudløb</swp-payment-label>
<swp-payment-value>08/2027</swp-payment-value>
</swp-payment-detail>
<swp-btn class="outline" style="margin-top: var(--spacing-2);">
<i class="ph ph-arrows-clockwise"></i>
Skift til årlig betaling (spar 15%)
</swp-btn>
</swp-payment-card>

View file

@ -0,0 +1,15 @@
using Microsoft.AspNetCore.Mvc;
namespace PlanTempus.Application.Features.Account.Components;
/// <summary>
/// ViewComponent for the payment method display.
/// Shows current card info, payment frequency, and next payment date.
/// </summary>
public class PaymentMethodViewComponent : ViewComponent
{
public IViewComponentResult Invoke()
{
return View();
}
}

View file

@ -0,0 +1,66 @@
@using PlanTempus.Application.Features.Accounts.Models
@model IEnumerable<PlanInfo>
@{
var currentPlanKey = (string)ViewBag.CurrentPlanKey;
}
<swp-plan-grid>
@foreach (var plan in Model)
{
var isCurrent = plan.Key == currentPlanKey;
var cardClass = plan.Key switch
{
"enterprise" => isCurrent ? "enterprise current" : "enterprise",
_ => isCurrent ? "current" : ""
};
var badgeClass = isCurrent ? "current" : plan.BadgeClass;
var badgeText = isCurrent ? "Nuværende plan" : plan.BadgeText;
var badgeIcon = isCurrent ? "ph-check" : plan.BadgeIcon;
var buttonText = isCurrent
? "Nuværende plan"
: plan.IsContactSales
? "Kontakt salg"
: $"Skift til {plan.Name}";
var buttonClass = isCurrent
? "secondary"
: plan.IsContactSales
? "outline"
: "secondary";
<swp-plan-card class="@cardClass">
<swp-plan-badge class="@badgeClass">
<i class="ph @badgeIcon"></i>
@badgeText
</swp-plan-badge>
<swp-plan-name>@plan.Name</swp-plan-name>
<swp-plan-users>@plan.UserRange</swp-plan-users>
<swp-plan-price>
@if (plan.PricePerMonth.HasValue)
{
<swp-plan-price-amount>@plan.PriceDisplay</swp-plan-price-amount>
<swp-plan-price-period>kr/md</swp-plan-price-period>
}
else
{
<swp-plan-price-amount>Kontakt os</swp-plan-price-amount>
}
</swp-plan-price>
<swp-plan-features>
@foreach (var feature in plan.Features)
{
<swp-plan-feature>
<i class="ph ph-check-circle"></i>
@feature
</swp-plan-feature>
}
</swp-plan-features>
<swp-plan-action>
<swp-btn class="@buttonClass">@buttonText</swp-btn>
</swp-plan-action>
</swp-plan-card>
}
</swp-plan-grid>

View file

@ -0,0 +1,21 @@
using Microsoft.AspNetCore.Mvc;
using PlanTempus.Application.Features.Accounts.Models;
namespace PlanTempus.Application.Features.Account.Components;
/// <summary>
/// ViewComponent for the subscription plan selection grid.
/// Shows all available plans with the current plan highlighted.
/// </summary>
public class SubscriptionPlansViewComponent : ViewComponent
{
public IViewComponentResult Invoke()
{
var plans = PlanCatalog.GetAllPlans();
// Mock: current plan is "pro"
var currentPlanKey = "pro";
ViewBag.CurrentPlanKey = currentPlanKey;
return View(plans);
}
}

View file

@ -0,0 +1,45 @@
@page "/konto"
@using PlanTempus.Application.Features.Account.Pages
@model PlanTempus.Application.Features.Account.Pages.IndexModel
@{
ViewData["Title"] = "Abonnement & Konto";
}
<swp-page-container>
<!-- Page Header -->
<swp-page-header>
<swp-page-title>
<h1>Abonnement & Konto</h1>
<p>Administrer dit abonnement og betalingsinfo</p>
</swp-page-title>
</swp-page-header>
<!-- Subscription Section -->
<swp-account-section>
<swp-account-section-header>
<swp-account-section-title>
<i class="ph ph-crown"></i>
Dit abonnement
</swp-account-section-title>
</swp-account-section-header>
@await Component.InvokeAsync("SubscriptionPlans")
</swp-account-section>
<!-- Billing Section -->
<swp-account-section>
<swp-account-section-header>
<swp-account-section-title>
<i class="ph ph-credit-card"></i>
Betaling & Fakturaer
</swp-account-section-title>
</swp-account-section-header>
<swp-billing-grid>
@await Component.InvokeAsync("PaymentMethod")
@await Component.InvokeAsync("InvoiceHistory")
</swp-billing-grid>
</swp-account-section>
</swp-page-container>

View file

@ -0,0 +1,10 @@
using Microsoft.AspNetCore.Mvc.RazorPages;
namespace PlanTempus.Application.Features.Account.Pages;
public class IndexModel : PageModel
{
public void OnGet()
{
}
}