This commit is contained in:
Janus C. H. Knudsen 2026-01-10 20:39:17 +01:00
parent 54b057886c
commit 7fc1ae0650
204 changed files with 4345 additions and 134 deletions

View file

@ -0,0 +1,39 @@
@model PlanTempus.Application.Features.Menu.SideMenuViewModel
<swp-side-menu>
<swp-side-menu-header>
<i class="ph ph-squares-four"></i>
<swp-side-menu-logo localize="sidebar.appName">Salon OS</swp-side-menu-logo>
<swp-menu-toggle id="menuToggle">
<i class="ph ph-caret-left"></i>
</swp-menu-toggle>
</swp-side-menu-header>
<swp-side-menu-nav>
@foreach (var group in Model.Groups)
{
<swp-side-menu-group>
<swp-side-menu-label>@group.Label</swp-side-menu-label>
@foreach (var item in group.Items)
{
<a href="@item.Url" is="swp-side-menu-item"
data-active="@(item.IsActive ? "true" : "false")"
data-tooltip="@item.Label">
<i class="ph @item.Icon"></i>
<span>@item.Label</span>
</a>
}
</swp-side-menu-group>
}
</swp-side-menu-nav>
<swp-side-menu-footer>
<swp-side-menu-action class="lock" id="lockScreen" title="Lås skærm">
<i class="ph ph-lock"></i>
<span localize="sidebar.lockScreen">Lås skærm</span>
</swp-side-menu-action>
</swp-side-menu-footer>
</swp-side-menu>
<!-- Tooltip for collapsed menu -->
<span id="menuTooltip" class="swp-menu-tooltip" popover="manual"></span>

View file

@ -0,0 +1,38 @@
<!DOCTYPE html>
<html lang="da">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>@ViewData["Title"] - Salon OS</title>
<link href="https://fonts.googleapis.com/css2?family=JetBrains+Mono:wght@400;500;600;700&display=swap" rel="stylesheet">
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/@@phosphor-icons/web@@2.1.2/src/regular/style.css" />
<!-- Design System -->
<link rel="stylesheet" href="~/css/design-system.css">
<link rel="stylesheet" href="~/css/base.css">
<!-- Layout Components -->
<link rel="stylesheet" href="~/css/app-layout.css">
<link rel="stylesheet" href="~/css/sidebar.css">
<link rel="stylesheet" href="~/css/topbar.css">
<link rel="stylesheet" href="~/css/drawers.css">
<!-- Page Components -->
<link rel="stylesheet" href="~/css/page.css">
<link rel="stylesheet" href="~/css/stats.css">
@await RenderSectionAsync("Styles", required: false)
</head>
<body>
<swp-app-layout id="appLayout">
@await Component.InvokeAsync("SideMenu")
<partial name="_TopBar" />
<swp-main-content>
@RenderBody()
</swp-main-content>
</swp-app-layout>
<partial name="_ProfileDrawer" />
<swp-drawer-overlay id="drawerOverlay"></swp-drawer-overlay>
<script type="module" src="~/js/app.js"></script>
@await RenderSectionAsync("Scripts", required: false)
</body>
</html>

View file

@ -0,0 +1,49 @@
<swp-profile-drawer id="profileDrawer">
<swp-drawer-header>
<swp-drawer-title>Profil</swp-drawer-title>
<swp-drawer-close id="closeProfileDrawer">
<i class="ph ph-x"></i>
</swp-drawer-close>
</swp-drawer-header>
<swp-drawer-content>
<swp-profile-section>
<swp-profile-avatar-large>MJ</swp-profile-avatar-large>
<swp-profile-name-large>Maria Jensen</swp-profile-name-large>
<swp-profile-email>maria@salon.dk</swp-profile-email>
</swp-profile-section>
<swp-drawer-divider></swp-drawer-divider>
<swp-drawer-menu>
<swp-drawer-menu-item>
<i class="ph ph-user"></i>
<span>Min profil</span>
</swp-drawer-menu-item>
<swp-drawer-menu-item>
<i class="ph ph-gear"></i>
<span>Indstillinger</span>
</swp-drawer-menu-item>
</swp-drawer-menu>
<swp-drawer-divider></swp-drawer-divider>
<swp-theme-toggle>
<swp-theme-label>
<i class="ph ph-moon"></i>
<span>Mørk tilstand</span>
</swp-theme-label>
<swp-toggle-switch id="themeToggle">
<input type="checkbox" id="themeCheckbox">
<swp-toggle-slider></swp-toggle-slider>
</swp-toggle-switch>
</swp-theme-toggle>
</swp-drawer-content>
<swp-drawer-footer>
<swp-drawer-action class="logout" id="logoutBtn">
<i class="ph ph-sign-out"></i>
<span>Log ud</span>
</swp-drawer-action>
</swp-drawer-footer>
</swp-profile-drawer>

View file

@ -0,0 +1,26 @@
<swp-app-topbar>
<swp-topbar-search>
<i class="ph ph-magnifying-glass"></i>
<input type="text" placeholder="Søg i Salon OS..." id="globalSearch">
<kbd>⌘K</kbd>
</swp-topbar-search>
<swp-topbar-actions>
<!-- Notifications -->
<swp-topbar-btn id="notificationsBtn" title="Notifikationer">
<i class="ph ph-bell"></i>
<swp-notification-badge>3</swp-notification-badge>
</swp-topbar-btn>
<swp-topbar-divider></swp-topbar-divider>
<!-- Profile (opens drawer) -->
<swp-topbar-profile id="profileTrigger">
<swp-profile-avatar>MJ</swp-profile-avatar>
<swp-profile-info>
<swp-profile-name>Maria Jensen</swp-profile-name>
<swp-profile-role>Administrator</swp-profile-role>
</swp-profile-info>
</swp-topbar-profile>
</swp-topbar-actions>
</swp-app-topbar>

View file

@ -0,0 +1,4 @@
@using PlanTempus.Application
@namespace PlanTempus.Application.Features
@addTagHelper *, Microsoft.AspNetCore.Mvc.TagHelpers
@addTagHelper *, PlanTempus.Application

View file

@ -0,0 +1,3 @@
@{
Layout = "/Features/_Shared/Pages/_Layout.cshtml";
}

View file

@ -0,0 +1,28 @@
using Microsoft.AspNetCore.Razor.TagHelpers;
using PlanTempus.Application.Features.Localization.Services;
namespace PlanTempus.Application.Features._Shared.TagHelpers;
[HtmlTargetElement(Attributes = "localize")]
public class LocalizeTagHelper : TagHelper
{
private readonly ILocalizationService _localize;
public LocalizeTagHelper(ILocalizationService localize)
{
_localize = localize;
}
[HtmlAttributeName("localize")]
public string Key { get; set; } = string.Empty;
public override void Process(TagHelperContext context, TagHelperOutput output)
{
var translated = _localize.Get(Key);
if (!string.IsNullOrEmpty(translated) && translated != Key)
output.Content.SetContent(translated);
output.Attributes.RemoveAll("localize");
}
}