diff --git a/.beads/issues.jsonl b/.beads/issues.jsonl
index 5f3f8b4..1ff27f4 100644
--- a/.beads/issues.jsonl
+++ b/.beads/issues.jsonl
@@ -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"}
diff --git a/backend/Books.Api/Books.Api.csproj b/backend/Books.Api/Books.Api.csproj
index cef3f4f..b2d553f 100644
--- a/backend/Books.Api/Books.Api.csproj
+++ b/backend/Books.Api/Books.Api.csproj
@@ -15,6 +15,9 @@
+
+
+
@@ -38,6 +41,9 @@
+
+
+
diff --git a/backend/Books.Api/EventFlow/ReadModels/CompanyReadModelDto.cs b/backend/Books.Api/EventFlow/ReadModels/CompanyReadModelDto.cs
index 7344090..97394e4 100644
--- a/backend/Books.Api/EventFlow/ReadModels/CompanyReadModelDto.cs
+++ b/backend/Books.Api/EventFlow/ReadModels/CompanyReadModelDto.cs
@@ -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; }
}
diff --git a/frontend/src/lib/formatters.ts b/frontend/src/lib/formatters.ts
index 4ca565e..442e86e 100644
--- a/frontend/src/lib/formatters.ts
+++ b/frontend/src/lib/formatters.ts
@@ -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 &, <, >, ", ', 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;
+}
diff --git a/frontend/src/stores/companyStore.ts b/frontend/src/stores/companyStore.ts
index 1f3c03d..5417cf5 100644
--- a/frontend/src/stores/companyStore.ts
+++ b/frontend/src/stores/companyStore.ts
@@ -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;
+}
diff --git a/frontend/src/types/accounting.ts b/frontend/src/types/accounting.ts
index 3fd4ed9..2f4fb3f 100644
--- a/frontend/src/types/accounting.ts
+++ b/frontend/src/types/accounting.ts
@@ -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;
+}