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

@ -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;
}