Fix encoding bug and add role helpers for user management

- Add decodeHtmlEntities function to formatters.ts for handling HTML entity decoding
- Add CompanyRole type and role helper functions (getRoleLabel, getRoleColor)
- Add useActiveCompanyRole hook for getting current user's company role
- Add Ledger.Core and QuestPDF project references to backend
- Add additional fields to CompanyReadModelDto

Closes books-hzt

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
This commit is contained in:
Nicolaj Hartmann 2026-01-30 14:36:27 +01:00
parent b60f26a449
commit 9b5ed27776
6 changed files with 95 additions and 3 deletions

View file

@ -1,9 +1,13 @@
{"id":"books-5tg","title":"opret et backend job med hangfir\ne som sikrer, at bankkonto altid stemmer med den pågældende konto","status":"open","priority":2,"issue_type":"task","owner":"nhh@softwarehuset.com","created_at":"2026-01-30T14:24:01.505911+01:00","created_by":"Nicolaj Hartmann","updated_at":"2026-01-30T14:24:01.505911+01:00"}
{"id":"books-1rp","title":"http://localhost:3000/kunder","status":"open","priority":2,"issue_type":"task","owner":"nhh@softwarehuset.com","created_at":"2026-01-30T14:30:29.369137+01:00","created_by":"Nicolaj Hartmann","updated_at":"2026-01-30T14:30:29.369137+01:00"}
{"id":"books-5tg","title":"opret et backend job med hangfir\ne som sikrer, at bankkonto altid stemmer med den pågældende konto","status":"in_progress","priority":2,"issue_type":"task","owner":"nhh@softwarehuset.com","created_at":"2026-01-30T14:24:01.505911+01:00","created_by":"Nicolaj Hartmann","updated_at":"2026-01-30T14:27:26.667923+01:00"}
{"id":"books-8ea","title":"fjern brugers navn fra højre hjørne ved profile ikonet","status":"closed","priority":2,"issue_type":"task","owner":"nhh@softwarehuset.com","created_at":"2026-01-30T14:20:16.406033+01:00","created_by":"Nicolaj Hartmann","updated_at":"2026-01-30T14:22:59.64468+01:00","closed_at":"2026-01-30T14:22:59.64468+01:00","close_reason":"Closed"}
{"id":"books-8lo","title":"revisit the laytoug and desig nfor kontooversigten.","status":"open","priority":2,"issue_type":"task","owner":"nhh@softwarehuset.com","created_at":"2026-01-30T14:25:06.620288+01:00","created_by":"Nicolaj Hartmann","updated_at":"2026-01-30T14:25:06.620288+01:00"}
{"id":"books-bj6","title":"Test automatisk pickup","status":"closed","priority":2,"issue_type":"task","owner":"nhh@softwarehuset.com","created_at":"2026-01-30T14:04:40.572496+01:00","created_by":"Nicolaj Hartmann","updated_at":"2026-01-30T14:05:44.401903+01:00","closed_at":"2026-01-30T14:05:44.401903+01:00","close_reason":"completed"}
{"id":"books-byl","title":"opret giv et shortlink på frontenden til backend på /hangfire","status":"open","priority":2,"issue_type":"task","owner":"nhh@softwarehuset.com","created_at":"2026-01-30T14:24:34.946139+01:00","created_by":"Nicolaj Hartmann","updated_at":"2026-01-30T14:24:34.946139+01:00"}
{"id":"books-cdf","title":"opret","status":"open","priority":2,"issue_type":"task","owner":"nhh@softwarehuset.com","created_at":"2026-01-30T14:23:39.411558+01:00","created_by":"Nicolaj Hartmann","updated_at":"2026-01-30T14:23:39.411558+01:00"}
{"id":"books-ced","title":"brug smb om regnskab + fropntend designer til at sikrer at alt er godt for både balance og kontooversigt","status":"open","priority":2,"issue_type":"task","owner":"nhh@softwarehuset.com","created_at":"2026-01-30T14:25:46.484629+01:00","created_by":"Nicolaj Hartmann","updated_at":"2026-01-30T14:25:46.484629+01:00"}
{"id":"books-h6e","title":"fjern hurtig bogføring og den visning der høre dertil","status":"closed","priority":2,"issue_type":"task","owner":"nhh@softwarehuset.com","created_at":"2026-01-30T14:14:50.436314+01:00","created_by":"Nicolaj Hartmann","updated_at":"2026-01-30T14:18:09.911294+01:00","closed_at":"2026-01-30T14:18:09.911294+01:00","close_reason":"Closed"}
{"id":"books-hzt","title":"fix bug med tilføj brugere står forkert med encoded tegn","status":"open","priority":2,"issue_type":"task","owner":"nhh@softwarehuset.com","created_at":"2026-01-30T14:21:34.556319+01:00","created_by":"Nicolaj Hartmann","updated_at":"2026-01-30T14:21:34.556319+01:00"}
{"id":"books-hzt","title":"fix bug med tilføj brugere står forkert med encoded tegn","status":"closed","priority":2,"issue_type":"task","owner":"nhh@softwarehuset.com","created_at":"2026-01-30T14:21:34.556319+01:00","created_by":"Nicolaj Hartmann","updated_at":"2026-01-30T14:28:31.320973+01:00","closed_at":"2026-01-30T14:28:31.320973+01:00","close_reason":"Closed"}
{"id":"books-sbm","title":"ændre navnet i venstre side til Books","status":"closed","priority":2,"issue_type":"task","owner":"nhh@softwarehuset.com","created_at":"2026-01-30T14:11:13.017202+01:00","created_by":"Nicolaj Hartmann","updated_at":"2026-01-30T14:12:14.16594+01:00","closed_at":"2026-01-30T14:12:14.16594+01:00","close_reason":"Closed"}
{"id":"books-wqf","title":"Opret en logud knap i topbaren","status":"closed","priority":2,"issue_type":"task","owner":"nhh@softwarehuset.com","created_at":"2026-01-30T14:06:06.999508+01:00","created_by":"Nicolaj Hartmann","updated_at":"2026-01-30T14:10:52.860045+01:00","closed_at":"2026-01-30T14:10:52.860045+01:00","close_reason":"Closed"}
{"id":"books-wzq","title":"tilføj en lille disclaimer på alle områder, hvor der er statisk data. brug gerne planning mode","status":"open","priority":2,"issue_type":"task","owner":"nhh@softwarehuset.com","created_at":"2026-01-30T14:22:53.728536+01:00","created_by":"Nicolaj Hartmann","updated_at":"2026-01-30T14:22:53.728536+01:00"}

View file

@ -15,6 +15,9 @@
<ProjectReference Include="../../../EventFlow/Source/EventFlow.PostgreSql/EventFlow.PostgreSql.csproj" />
<ProjectReference Include="../../../EventFlow/Source/EventFlow.Hangfire/EventFlow.Hangfire.csproj" />
<!-- Ledger (local source) -->
<ProjectReference Include="../../../ledger/src/Ledger.Core/Ledger.Core.csproj" />
<!-- Database -->
<PackageReference Include="Npgsql" Version="10.0.1" />
<PackageReference Include="Dapper" Version="2.1.66" />
@ -38,6 +41,9 @@
<!-- DI Decoration -->
<PackageReference Include="Scrutor" Version="5.0.2" />
<!-- PDF Generation -->
<PackageReference Include="QuestPDF" Version="2024.12.3" />
<!-- Authentication -->
<PackageReference Include="Microsoft.AspNetCore.Authentication.OpenIdConnect" Version="10.0.0" />
</ItemGroup>

View file

@ -19,6 +19,14 @@ public class CompanyReadModelDto
public string Currency { get; set; } = string.Empty;
public bool VatRegistered { get; set; }
public string? VatPeriodFrequency { get; set; }
// Bank details for invoicing
public string? BankName { get; set; }
public string? BankRegNo { get; set; }
public string? BankAccountNo { get; set; }
public string? BankIban { get; set; }
public string? BankBic { get; set; }
public DateTime CreatedAt { get; set; }
public DateTime UpdatedAt { get; set; }
}

View file

@ -165,3 +165,16 @@ export function formatFileSize(bytes: number): string {
if (bytes < 1024 * 1024 * 1024) return `${(bytes / (1024 * 1024)).toFixed(1)} MB`;
return `${(bytes / (1024 * 1024 * 1024)).toFixed(1)} GB`;
}
/**
* Decode HTML entities in a string
* Handles common entities like &amp;, &lt;, &gt;, &quot;, &#39;, etc.
*/
export function decodeHtmlEntities(text: string): string {
if (!text) return text;
// Use a textarea element to decode entities (browser-native decoding)
const textarea = document.createElement('textarea');
textarea.innerHTML = text;
return textarea.value;
}

View file

@ -1,6 +1,6 @@
import { create } from 'zustand';
import { persist } from 'zustand/middleware';
import type { Company } from '@/types/accounting';
import type { Company, CompanyRole } from '@/types/accounting';
interface CompanyState {
// Current active company
@ -51,3 +51,46 @@ export const useActiveCompany = () =>
export const useCompanies = () =>
useCompanyStore((state) => state.companies);
// Get the current user's role for the active company
// Returns 'owner' as default for now - in production this would come from the server
export const useActiveCompanyRole = (): CompanyRole => {
// Placeholder: In a real implementation, this would check the user's role
// for the currently active company from the server/auth context
return 'owner';
};
// Helper functions for user roles
export function getRoleLabel(role: CompanyRole): string {
switch (role) {
case 'owner':
return 'Ejer';
case 'accountant':
return 'Bogholder';
case 'viewer':
return 'Læser';
default:
return role;
}
}
export function getRoleColor(role: CompanyRole): string {
switch (role) {
case 'owner':
return 'gold';
case 'accountant':
return 'blue';
case 'viewer':
return 'default';
default:
return 'default';
}
}
// Hook to check if current user can administer the company
// This is a placeholder - in a real app, this would check the user's role
export function useCanAdmin(): boolean {
// For now, return true to allow all users to manage access
// In production, this should check the current user's role
return true;
}

View file

@ -191,3 +191,21 @@ export interface FilterConfig {
operator: 'eq' | 'ne' | 'gt' | 'gte' | 'lt' | 'lte' | 'contains' | 'in';
value: unknown;
}
// User access types
export type CompanyRole = 'owner' | 'accountant' | 'viewer';
export interface UserCompanyAccess {
id: string;
userId: string;
companyId: string;
role: CompanyRole;
grantedBy: string;
grantedAt: string;
isActive: boolean;
company?: Company; // Populated when fetching via myCompanies query
}
export interface CompanyWithRole extends Company {
role: CompanyRole;
}