Adds authentication and pricing pages for user onboarding

Introduces comprehensive user authentication flow with plan selection and registration

Includes:
- Pricing page with plan details and selection
- Payment page with plan summary and card information
- Signup page for different plan tiers
- Shared authentication layout and design system improvements

Enhances user onboarding experience with clear plan information and streamlined signup process
This commit is contained in:
Janus C. H. Knudsen 2026-01-11 01:30:32 +01:00
parent cd092f8290
commit 217a9cd95c
18 changed files with 2312 additions and 165 deletions

View file

@ -0,0 +1,283 @@
@page "/signup"
@model PlanTempus.Application.Features.Accounts.Pages.SignupModel
@{
ViewData["Title"] = Model.SelectedPlan.IsContactSales ? "Kontakt salg" : "Opret konto";
Layout = "/Features/_Shared/Pages/_AuthLayout.cshtml";
var plan = Model.SelectedPlan;
var badgeClass = plan.IsContactSales ? plan.BadgeClass : (plan.IsFree ? plan.BadgeClass : "selected");
var badgeIcon = plan.IsContactSales ? plan.BadgeIcon : (plan.IsFree ? plan.BadgeIcon : "ph-check");
var badgeText = plan.IsContactSales ? plan.BadgeText : (plan.IsFree ? plan.BadgeText : "Valgt");
}
<swp-auth-layout>
<!-- Selected Plan Panel -->
<swp-auth-plan-panel>
<swp-auth-logo>
<i class="ph ph-calendar-check"></i>
<span>PlanTempus</span>
</swp-auth-logo>
<swp-selected-plan-header>@(plan.IsContactSales ? "Enterprise løsning" : "Dit valgte abonnement")</swp-selected-plan-header>
<swp-plan-card class="selected">
<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>
@if (plan.PricePerMonth.HasValue)
{
<swp-plan-price>
<swp-plan-price-amount>@plan.PriceDisplay</swp-plan-price-amount>
<swp-plan-price-period>kr/md</swp-plan-price-period>
</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-card>
<swp-change-plan-link>
<a href="/pricing">
<i class="ph ph-arrows-clockwise"></i>
Skift abonnement
</a>
</swp-change-plan-link>
</swp-auth-plan-panel>
<!-- Form Panel -->
<swp-auth-form-panel>
<swp-auth-form-container>
@if (plan.IsContactSales)
{
<!-- Enterprise Contact Form -->
<swp-auth-header>
<swp-auth-title>Kontakt vores salgsteam</swp-auth-title>
<swp-auth-description>Vi vender tilbage inden for 24 timer</swp-auth-description>
</swp-auth-header>
<swp-auth-form>
<swp-form-group>
<swp-form-label>Virksomhedsnavn</swp-form-label>
<swp-form-input>
<input type="text"
name="companyName"
placeholder="F.eks. Salon Group ApS"
autocomplete="organization" />
</swp-form-input>
</swp-form-group>
<swp-form-group>
<swp-form-label>Antal ansatte</swp-form-label>
<swp-form-input>
<select name="employeeCount">
<option value="">Vælg antal</option>
<option value="8-15">8-15 ansatte</option>
<option value="16-30">16-30 ansatte</option>
<option value="31-50">31-50 ansatte</option>
<option value="50+">Mere end 50 ansatte</option>
</select>
</swp-form-input>
</swp-form-group>
<swp-form-group>
<swp-form-label>Kontaktperson</swp-form-label>
<swp-form-input>
<input type="text"
name="contactName"
placeholder="Dit fulde navn"
autocomplete="name" />
</swp-form-input>
</swp-form-group>
<swp-form-row>
<swp-form-group>
<swp-form-label>Email</swp-form-label>
<swp-form-input>
<input type="email"
name="email"
placeholder="din@email.dk"
autocomplete="email" />
</swp-form-input>
</swp-form-group>
<swp-form-group>
<swp-form-label>Telefon</swp-form-label>
<swp-form-input>
<input type="tel"
name="phone"
placeholder="+45 12 34 56 78"
autocomplete="tel" />
</swp-form-input>
</swp-form-group>
</swp-form-row>
<swp-form-group>
<swp-form-label>Besked <span class="optional">(valgfrit)</span></swp-form-label>
<swp-form-input>
<textarea name="message"
rows="3"
placeholder="Fortæl os om jeres behov..."></textarea>
</swp-form-input>
</swp-form-group>
<swp-btn class="primary full-width lg">
<span>Send forespørgsel</span>
<i class="ph ph-paper-plane-tilt"></i>
</swp-btn>
</swp-auth-form>
<swp-auth-footer>
<swp-auth-footer-text>
Eller ring til os på <a href="tel:+4570123456">70 12 34 56</a>
</swp-auth-footer-text>
</swp-auth-footer>
}
else
{
<!-- Standard Signup Form -->
<swp-auth-header>
<swp-auth-title>Opret din konto</swp-auth-title>
<swp-auth-description>
@(plan.IsFree ? "Kom i gang gratis - ingen kreditkort påkrævet" : "Start din gratis 14-dages prøveperiode")
</swp-auth-description>
</swp-auth-header>
<!-- User Info (already known) -->
<swp-user-info-card>
<swp-user-avatar>MJ</swp-user-avatar>
<swp-user-details>
<swp-user-name>Maria Jensen</swp-user-name>
<swp-user-email>maria@example.dk</swp-user-email>
</swp-user-details>
<swp-user-verified>
<i class="ph ph-check-circle"></i>
</swp-user-verified>
</swp-user-info-card>
<swp-auth-form>
<swp-form-group>
<swp-form-label>Virksomhedsnavn</swp-form-label>
<swp-form-input>
<input type="text"
name="companyName"
placeholder="F.eks. Salon Copenhagen"
autocomplete="organization" />
</swp-form-input>
</swp-form-group>
<swp-form-group>
<swp-form-label>Vælg adgangskode</swp-form-label>
<swp-form-input class="has-icon">
<input type="password"
name="password"
id="password"
placeholder="Mindst 8 tegn"
autocomplete="new-password" />
<span class="input-icon" id="togglePassword">
<i class="ph ph-eye"></i>
</span>
</swp-form-input>
<swp-password-strength>
<swp-strength-bar>
<swp-strength-fill id="strengthFill"></swp-strength-fill>
</swp-strength-bar>
<swp-strength-text id="strengthText"></swp-strength-text>
</swp-password-strength>
</swp-form-group>
<swp-form-checkbox>
<input type="checkbox" name="acceptTerms" id="acceptTerms" />
<span>
Jeg accepterer <a href="/terms">vilkår og betingelser</a>
samt <a href="/privacy">privatlivspolitikken</a>
</span>
</swp-form-checkbox>
<swp-btn class="primary full-width lg">
<span>Opret konto</span>
<i class="ph ph-arrow-right"></i>
</swp-btn>
</swp-auth-form>
<swp-auth-footer>
<swp-auth-footer-text>
Har du allerede en konto? <a href="/login">Log ind</a>
</swp-auth-footer-text>
</swp-auth-footer>
}
</swp-auth-form-container>
</swp-auth-form-panel>
</swp-auth-layout>
@if (!Model.SelectedPlan.IsContactSales)
{
@section Scripts {
<script>
// Toggle password visibility
const togglePassword = document.getElementById('togglePassword');
const passwordInput = document.getElementById('password');
const toggleIcon = togglePassword.querySelector('i');
togglePassword.addEventListener('click', function() {
const type = passwordInput.getAttribute('type') === 'password' ? 'text' : 'password';
passwordInput.setAttribute('type', type);
toggleIcon.classList.toggle('ph-eye');
toggleIcon.classList.toggle('ph-eye-slash');
});
// Password strength indicator
const strengthFill = document.getElementById('strengthFill');
const strengthText = document.getElementById('strengthText');
passwordInput.addEventListener('input', function() {
const password = this.value;
const strength = calculateStrength(password);
strengthFill.className = '';
strengthText.className = '';
if (password.length === 0) {
strengthText.textContent = '';
return;
}
if (strength < 2) {
strengthFill.classList.add('weak');
strengthText.classList.add('weak');
strengthText.textContent = 'Svag';
} else if (strength < 3) {
strengthFill.classList.add('fair');
strengthText.classList.add('fair');
strengthText.textContent = 'Middel';
} else if (strength < 4) {
strengthFill.classList.add('good');
strengthText.classList.add('good');
strengthText.textContent = 'God';
} else {
strengthFill.classList.add('strong');
strengthText.classList.add('strong');
strengthText.textContent = 'Stærk';
}
});
function calculateStrength(password) {
let strength = 0;
if (password.length >= 8) strength++;
if (password.length >= 12) strength++;
if (/[a-z]/.test(password) && /[A-Z]/.test(password)) strength++;
if (/\d/.test(password)) strength++;
if (/[^a-zA-Z0-9]/.test(password)) strength++;
return strength;
}
</script>
}
}