fixed stricts
This commit is contained in:
parent
97a164f682
commit
92bf11c809
14 changed files with 556 additions and 316 deletions
|
|
@ -2,7 +2,7 @@ import { useState, useEffect } from 'react';
|
||||||
import { IoIosDownload } from 'react-icons/io';
|
import { IoIosDownload } from 'react-icons/io';
|
||||||
import { Popup, PopupAction } from '../UiComponents/Popup/Popup';
|
import { Popup, PopupAction } from '../UiComponents/Popup/Popup';
|
||||||
import { useLanguage } from '../../providers/language/LanguageContext';
|
import { useLanguage } from '../../providers/language/LanguageContext';
|
||||||
import { PdfRenderer, PdfJsRenderer, LoadingRenderer, ErrorRenderer } from './renderers';
|
import { PdfRenderer, LoadingRenderer } from './renderers';
|
||||||
import styles from './ContentPreview.module.css';
|
import styles from './ContentPreview.module.css';
|
||||||
|
|
||||||
export interface UrlContentPreviewProps {
|
export interface UrlContentPreviewProps {
|
||||||
|
|
@ -64,11 +64,8 @@ export function UrlContentPreview({
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
const handlePdfLoad = () => {
|
// PDF load is handled by the PdfRenderer's onError callback;
|
||||||
setIsLoading(false);
|
// successful load is implicit when no error occurs.
|
||||||
setHasLoaded(true);
|
|
||||||
setError(null);
|
|
||||||
};
|
|
||||||
|
|
||||||
const handlePdfError = () => {
|
const handlePdfError = () => {
|
||||||
// Try PDF.js as fallback instead of showing error immediately
|
// Try PDF.js as fallback instead of showing error immediately
|
||||||
|
|
@ -208,7 +205,6 @@ export function UrlContentPreview({
|
||||||
previewUrl={url}
|
previewUrl={url}
|
||||||
fileName={fileName}
|
fileName={fileName}
|
||||||
onError={handlePdfError}
|
onError={handlePdfError}
|
||||||
onLoad={handlePdfLoad}
|
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
|
||||||
|
|
@ -1,3 +1,5 @@
|
||||||
export { ContentPreview } from './ContentPreview';
|
export { ContentPreview } from './ContentPreview';
|
||||||
export type { ContentPreviewProps } from './ContentPreview';
|
export type { ContentPreviewProps } from './ContentPreview';
|
||||||
|
export { UrlContentPreview } from './UrlContentPreview';
|
||||||
|
export type { UrlContentPreviewProps } from './UrlContentPreview';
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -1,4 +1,5 @@
|
||||||
import { useEffect, useRef, useState } from 'react';
|
import { useEffect, useRef, useState } from 'react';
|
||||||
|
// @ts-ignore
|
||||||
import * as pdfjsLib from 'pdfjs-dist';
|
import * as pdfjsLib from 'pdfjs-dist';
|
||||||
import styles from '../ContentPreview.module.css';
|
import styles from '../ContentPreview.module.css';
|
||||||
|
|
||||||
|
|
@ -23,7 +24,7 @@ interface PdfJsRendererProps {
|
||||||
onLoad?: () => void;
|
onLoad?: () => void;
|
||||||
}
|
}
|
||||||
|
|
||||||
export function PdfJsRenderer({ previewUrl, fileName, onError, onLoad }: PdfJsRendererProps) {
|
export function PdfJsRenderer({ previewUrl, fileName: _fileName, onError, onLoad }: PdfJsRendererProps) {
|
||||||
const canvasRef = useRef<HTMLCanvasElement>(null);
|
const canvasRef = useRef<HTMLCanvasElement>(null);
|
||||||
const containerRef = useRef<HTMLDivElement>(null);
|
const containerRef = useRef<HTMLDivElement>(null);
|
||||||
const [isLoading, setIsLoading] = useState(true);
|
const [isLoading, setIsLoading] = useState(true);
|
||||||
|
|
|
||||||
|
|
@ -1,10 +1,12 @@
|
||||||
import React, { useState, useEffect, useRef, useCallback } from 'react';
|
import React, { useState, useEffect, useRef, useCallback } from 'react';
|
||||||
import TextField, { BaseTextFieldProps } from '../TextField/TextField';
|
import TextField from '../TextField/TextField';
|
||||||
|
import { BaseTextFieldProps } from '../TextField/TextFieldTypes';
|
||||||
import { autocompleteAddress, AddressSuggestion } from '../../../api/realEstateApi';
|
import { autocompleteAddress, AddressSuggestion } from '../../../api/realEstateApi';
|
||||||
import styles from './AddressAutocomplete.module.css';
|
import styles from './AddressAutocomplete.module.css';
|
||||||
|
|
||||||
interface AddressAutocompleteProps extends BaseTextFieldProps {
|
interface AddressAutocompleteProps extends BaseTextFieldProps {
|
||||||
onSelect?: (suggestion: AddressSuggestion) => void;
|
onSelect?: (suggestion: AddressSuggestion) => void;
|
||||||
|
onKeyDown?: (e: React.KeyboardEvent<HTMLInputElement | HTMLTextAreaElement>) => void;
|
||||||
debounceMs?: number;
|
debounceMs?: number;
|
||||||
minQueryLength?: number;
|
minQueryLength?: number;
|
||||||
maxSuggestions?: number;
|
maxSuggestions?: number;
|
||||||
|
|
|
||||||
1
src/components/UiComponents/OerebSection/index.ts
Normal file
1
src/components/UiComponents/OerebSection/index.ts
Normal file
|
|
@ -0,0 +1 @@
|
||||||
|
export { OerebSection } from './OerebSection';
|
||||||
|
|
@ -414,7 +414,7 @@ const ParcelInfoPanel: React.FC<ParcelInfoPanelProps> = ({
|
||||||
<div className={styles.documentsSection}>
|
<div className={styles.documentsSection}>
|
||||||
<h4 className={styles.documentsSectionTitle}>Dokumente ({parcelData.documents.length})</h4>
|
<h4 className={styles.documentsSectionTitle}>Dokumente ({parcelData.documents.length})</h4>
|
||||||
<div className={styles.documentsList}>
|
<div className={styles.documentsList}>
|
||||||
{parcelData.documents.map((document) => (
|
{parcelData.documents.map((document: any) => (
|
||||||
<button
|
<button
|
||||||
key={document.id}
|
key={document.id}
|
||||||
className={styles.documentLink}
|
className={styles.documentLink}
|
||||||
|
|
|
||||||
|
|
@ -1,8 +1,8 @@
|
||||||
import { useState, useEffect, useCallback } from 'react';
|
import { useState, useEffect, useCallback } from 'react';
|
||||||
import { useApiRequest } from './useApi';
|
import { useApiRequest } from './useApi';
|
||||||
import { getUserDataCache } from '../utils/userCache';
|
|
||||||
import api from '../api';
|
import api from '../api';
|
||||||
import { usePermissions, type UserPermissions } from './usePermissions';
|
import { usePermissions, type UserPermissions } from './usePermissions';
|
||||||
|
import { useInstanceId } from './useCurrentInstance';
|
||||||
import {
|
import {
|
||||||
fetchAccess as fetchAccessApi,
|
fetchAccess as fetchAccessApi,
|
||||||
fetchAccessById as fetchAccessByIdApi,
|
fetchAccessById as fetchAccessByIdApi,
|
||||||
|
|
@ -10,15 +10,38 @@ import {
|
||||||
updateAccess as updateAccessApi,
|
updateAccess as updateAccessApi,
|
||||||
deleteAccess as deleteAccessApi,
|
deleteAccess as deleteAccessApi,
|
||||||
type TrusteeAccess,
|
type TrusteeAccess,
|
||||||
type AttributeDefinition,
|
|
||||||
type PaginationParams
|
type PaginationParams
|
||||||
} from '../api/trusteeApi';
|
} from '../api/trusteeApi';
|
||||||
|
|
||||||
|
export interface AttributeDefinition {
|
||||||
|
name: string;
|
||||||
|
type: 'text' | 'email' | 'date' | 'checkbox' | 'select' | 'multiselect' | 'number' | 'textarea' | 'timestamp' | 'file';
|
||||||
|
label: string;
|
||||||
|
description?: string;
|
||||||
|
required?: boolean;
|
||||||
|
default?: any;
|
||||||
|
options?: any[] | string;
|
||||||
|
readonly?: boolean;
|
||||||
|
editable?: boolean;
|
||||||
|
visible?: boolean;
|
||||||
|
order?: number;
|
||||||
|
sortable?: boolean;
|
||||||
|
filterable?: boolean;
|
||||||
|
searchable?: boolean;
|
||||||
|
width?: number;
|
||||||
|
minWidth?: number;
|
||||||
|
maxWidth?: number;
|
||||||
|
filterOptions?: string[];
|
||||||
|
dependsOn?: string;
|
||||||
|
}
|
||||||
|
|
||||||
// Re-export types
|
// Re-export types
|
||||||
export type { TrusteeAccess, AttributeDefinition, PaginationParams };
|
export type { TrusteeAccess, PaginationParams };
|
||||||
|
|
||||||
// Access list hook
|
// Access list hook
|
||||||
export function useTrusteeAccess() {
|
export function useTrusteeAccess() {
|
||||||
|
const instanceId = useInstanceId();
|
||||||
|
|
||||||
const [accessRecords, setAccessRecords] = useState<TrusteeAccess[]>([]);
|
const [accessRecords, setAccessRecords] = useState<TrusteeAccess[]>([]);
|
||||||
const [attributes, setAttributes] = useState<AttributeDefinition[]>([]);
|
const [attributes, setAttributes] = useState<AttributeDefinition[]>([]);
|
||||||
const [permissions, setPermissions] = useState<UserPermissions | null>(null);
|
const [permissions, setPermissions] = useState<UserPermissions | null>(null);
|
||||||
|
|
@ -33,8 +56,10 @@ export function useTrusteeAccess() {
|
||||||
|
|
||||||
// Fetch attributes from backend
|
// Fetch attributes from backend
|
||||||
const fetchAttributes = useCallback(async () => {
|
const fetchAttributes = useCallback(async () => {
|
||||||
|
if (!instanceId) return [];
|
||||||
|
|
||||||
try {
|
try {
|
||||||
const response = await api.get('/api/attributes/TrusteeAccess');
|
const response = await api.get(`/api/trustee/${instanceId}/attributes/TrusteeAccess`);
|
||||||
|
|
||||||
let attrs: AttributeDefinition[] = [];
|
let attrs: AttributeDefinition[] = [];
|
||||||
if (response.data?.attributes && Array.isArray(response.data.attributes)) {
|
if (response.data?.attributes && Array.isArray(response.data.attributes)) {
|
||||||
|
|
@ -58,12 +83,13 @@ export function useTrusteeAccess() {
|
||||||
setAttributes([]);
|
setAttributes([]);
|
||||||
return [];
|
return [];
|
||||||
}
|
}
|
||||||
}, []);
|
}, [instanceId]);
|
||||||
|
|
||||||
// Fetch permissions from backend
|
// Fetch permissions from backend
|
||||||
const fetchPermissions = useCallback(async () => {
|
const fetchPermissions = useCallback(async () => {
|
||||||
try {
|
try {
|
||||||
const perms = await checkPermission('DATA', 'trustee.access');
|
const objectKey = 'data.feature.trustee.TrusteeAccess';
|
||||||
|
const perms = await checkPermission('DATA', objectKey);
|
||||||
setPermissions(perms);
|
setPermissions(perms);
|
||||||
return perms;
|
return perms;
|
||||||
} catch (error: any) {
|
} catch (error: any) {
|
||||||
|
|
@ -81,8 +107,13 @@ export function useTrusteeAccess() {
|
||||||
}, [checkPermission]);
|
}, [checkPermission]);
|
||||||
|
|
||||||
const fetchAccess = useCallback(async (params?: PaginationParams) => {
|
const fetchAccess = useCallback(async (params?: PaginationParams) => {
|
||||||
|
if (!instanceId) {
|
||||||
|
setAccessRecords([]);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
try {
|
try {
|
||||||
const data = await fetchAccessApi(request, params);
|
const data = await fetchAccessApi(request, instanceId, params);
|
||||||
|
|
||||||
if (data && typeof data === 'object' && 'items' in data) {
|
if (data && typeof data === 'object' && 'items' in data) {
|
||||||
const items = Array.isArray(data.items) ? data.items : [];
|
const items = Array.isArray(data.items) ? data.items : [];
|
||||||
|
|
@ -99,7 +130,7 @@ export function useTrusteeAccess() {
|
||||||
setAccessRecords([]);
|
setAccessRecords([]);
|
||||||
setPagination(null);
|
setPagination(null);
|
||||||
}
|
}
|
||||||
}, [request]);
|
}, [request, instanceId]);
|
||||||
|
|
||||||
// Optimistically remove an access record
|
// Optimistically remove an access record
|
||||||
const removeOptimistically = (accessId: string) => {
|
const removeOptimistically = (accessId: string) => {
|
||||||
|
|
@ -119,8 +150,9 @@ export function useTrusteeAccess() {
|
||||||
|
|
||||||
// Fetch a single access record by ID
|
// Fetch a single access record by ID
|
||||||
const fetchAccessById = useCallback(async (accessId: string): Promise<TrusteeAccess | null> => {
|
const fetchAccessById = useCallback(async (accessId: string): Promise<TrusteeAccess | null> => {
|
||||||
return await fetchAccessByIdApi(request, accessId);
|
if (!instanceId) return null;
|
||||||
}, [request]);
|
return await fetchAccessByIdApi(request, instanceId, accessId);
|
||||||
|
}, [request, instanceId]);
|
||||||
|
|
||||||
// Generate edit fields from attributes dynamically
|
// Generate edit fields from attributes dynamically
|
||||||
const generateEditFieldsFromAttributes = useCallback((): Array<{
|
const generateEditFieldsFromAttributes = useCallback((): Array<{
|
||||||
|
|
@ -161,7 +193,7 @@ export function useTrusteeAccess() {
|
||||||
} else if (attr.type === 'select') {
|
} else if (attr.type === 'select') {
|
||||||
fieldType = 'enum';
|
fieldType = 'enum';
|
||||||
if (Array.isArray(attr.options)) {
|
if (Array.isArray(attr.options)) {
|
||||||
options = attr.options.map(opt => {
|
options = attr.options.map((opt: any) => {
|
||||||
const labelValue = typeof opt.label === 'string'
|
const labelValue = typeof opt.label === 'string'
|
||||||
? opt.label
|
? opt.label
|
||||||
: opt.label?.en || opt.label?.[Object.keys(opt.label)[0]] || String(opt.value);
|
: opt.label?.en || opt.label?.[Object.keys(opt.label)[0]] || String(opt.value);
|
||||||
|
|
@ -223,16 +255,14 @@ export function useTrusteeAccess() {
|
||||||
return fetchedAttributes;
|
return fetchedAttributes;
|
||||||
}, [attributes, fetchAttributes]);
|
}, [attributes, fetchAttributes]);
|
||||||
|
|
||||||
// Fetch attributes and permissions on mount
|
// Fetch data when instanceId is available
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
fetchAttributes();
|
if (instanceId) {
|
||||||
fetchPermissions();
|
fetchAttributes();
|
||||||
}, [fetchAttributes, fetchPermissions]);
|
fetchPermissions();
|
||||||
|
fetchAccess();
|
||||||
// Initial fetch
|
}
|
||||||
useEffect(() => {
|
}, [instanceId, fetchAttributes, fetchPermissions, fetchAccess]);
|
||||||
fetchAccess();
|
|
||||||
}, [fetchAccess]);
|
|
||||||
|
|
||||||
return {
|
return {
|
||||||
accessRecords,
|
accessRecords,
|
||||||
|
|
@ -246,12 +276,15 @@ export function useTrusteeAccess() {
|
||||||
pagination,
|
pagination,
|
||||||
fetchAccessById,
|
fetchAccessById,
|
||||||
generateEditFieldsFromAttributes,
|
generateEditFieldsFromAttributes,
|
||||||
ensureAttributesLoaded
|
ensureAttributesLoaded,
|
||||||
|
instanceId
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
// Access operations hook
|
// Access operations hook
|
||||||
export function useTrusteeAccessOperations() {
|
export function useTrusteeAccessOperations() {
|
||||||
|
const instanceId = useInstanceId();
|
||||||
|
|
||||||
const [deletingAccess, setDeletingAccess] = useState<Set<string>>(new Set());
|
const [deletingAccess, setDeletingAccess] = useState<Set<string>>(new Set());
|
||||||
const [creatingAccess, setCreatingAccess] = useState(false);
|
const [creatingAccess, setCreatingAccess] = useState(false);
|
||||||
const { request, isLoading } = useApiRequest();
|
const { request, isLoading } = useApiRequest();
|
||||||
|
|
@ -260,11 +293,16 @@ export function useTrusteeAccessOperations() {
|
||||||
const [updateError, setUpdateError] = useState<string | null>(null);
|
const [updateError, setUpdateError] = useState<string | null>(null);
|
||||||
|
|
||||||
const handleAccessDelete = async (accessId: string) => {
|
const handleAccessDelete = async (accessId: string) => {
|
||||||
|
if (!instanceId) {
|
||||||
|
setDeleteError('No instance context');
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
setDeleteError(null);
|
setDeleteError(null);
|
||||||
setDeletingAccess(prev => new Set(prev).add(accessId));
|
setDeletingAccess(prev => new Set(prev).add(accessId));
|
||||||
|
|
||||||
try {
|
try {
|
||||||
await deleteAccessApi(request, accessId);
|
await deleteAccessApi(request, instanceId, accessId);
|
||||||
await new Promise(resolve => setTimeout(resolve, 300));
|
await new Promise(resolve => setTimeout(resolve, 300));
|
||||||
return true;
|
return true;
|
||||||
} catch (error: any) {
|
} catch (error: any) {
|
||||||
|
|
@ -280,20 +318,16 @@ export function useTrusteeAccessOperations() {
|
||||||
};
|
};
|
||||||
|
|
||||||
const handleAccessCreate = async (accessData: Partial<TrusteeAccess>) => {
|
const handleAccessCreate = async (accessData: Partial<TrusteeAccess>) => {
|
||||||
|
if (!instanceId) {
|
||||||
|
setCreateError('No instance context');
|
||||||
|
return { success: false, error: 'No instance context' };
|
||||||
|
}
|
||||||
|
|
||||||
setCreateError(null);
|
setCreateError(null);
|
||||||
setCreatingAccess(true);
|
setCreatingAccess(true);
|
||||||
|
|
||||||
try {
|
try {
|
||||||
const currentUserData = getUserDataCache();
|
const newAccess = await createAccessApi(request, instanceId, accessData);
|
||||||
const mandateId = currentUserData?.mandateId || '';
|
|
||||||
|
|
||||||
const requestBody = {
|
|
||||||
...accessData,
|
|
||||||
mandate: mandateId
|
|
||||||
};
|
|
||||||
|
|
||||||
const newAccess = await createAccessApi(request, requestBody);
|
|
||||||
|
|
||||||
return { success: true, accessData: newAccess };
|
return { success: true, accessData: newAccess };
|
||||||
} catch (error: any) {
|
} catch (error: any) {
|
||||||
setCreateError(error.message);
|
setCreateError(error.message);
|
||||||
|
|
@ -308,19 +342,15 @@ export function useTrusteeAccessOperations() {
|
||||||
updateData: Partial<TrusteeAccess>,
|
updateData: Partial<TrusteeAccess>,
|
||||||
_originalData?: any
|
_originalData?: any
|
||||||
) => {
|
) => {
|
||||||
|
if (!instanceId) {
|
||||||
|
setUpdateError('No instance context');
|
||||||
|
return { success: false, error: 'No instance context' };
|
||||||
|
}
|
||||||
|
|
||||||
setUpdateError(null);
|
setUpdateError(null);
|
||||||
|
|
||||||
try {
|
try {
|
||||||
const currentUserData = getUserDataCache();
|
const updatedAccess = await updateAccessApi(request, instanceId, accessId, updateData);
|
||||||
const mandateId = currentUserData?.mandateId || '';
|
|
||||||
|
|
||||||
const requestBody = {
|
|
||||||
...updateData,
|
|
||||||
mandate: mandateId
|
|
||||||
};
|
|
||||||
|
|
||||||
const updatedAccess = await updateAccessApi(request, accessId, requestBody);
|
|
||||||
|
|
||||||
return { success: true, accessData: updatedAccess };
|
return { success: true, accessData: updatedAccess };
|
||||||
} catch (error: any) {
|
} catch (error: any) {
|
||||||
const errorMessage = error.response?.data?.message || error.message || 'Failed to update access';
|
const errorMessage = error.response?.data?.message || error.message || 'Failed to update access';
|
||||||
|
|
@ -347,6 +377,7 @@ export function useTrusteeAccessOperations() {
|
||||||
handleAccessDelete,
|
handleAccessDelete,
|
||||||
handleAccessCreate,
|
handleAccessCreate,
|
||||||
handleAccessUpdate,
|
handleAccessUpdate,
|
||||||
isLoading
|
isLoading,
|
||||||
|
instanceId
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,8 +1,8 @@
|
||||||
import { useState, useEffect, useCallback } from 'react';
|
import { useState, useEffect, useCallback } from 'react';
|
||||||
import { useApiRequest } from './useApi';
|
import { useApiRequest } from './useApi';
|
||||||
import { getUserDataCache } from '../utils/userCache';
|
|
||||||
import api from '../api';
|
import api from '../api';
|
||||||
import { usePermissions, type UserPermissions } from './usePermissions';
|
import { usePermissions, type UserPermissions } from './usePermissions';
|
||||||
|
import { useInstanceId } from './useCurrentInstance';
|
||||||
import {
|
import {
|
||||||
fetchContracts as fetchContractsApi,
|
fetchContracts as fetchContractsApi,
|
||||||
fetchContractById as fetchContractByIdApi,
|
fetchContractById as fetchContractByIdApi,
|
||||||
|
|
@ -10,15 +10,38 @@ import {
|
||||||
updateContract as updateContractApi,
|
updateContract as updateContractApi,
|
||||||
deleteContract as deleteContractApi,
|
deleteContract as deleteContractApi,
|
||||||
type TrusteeContract,
|
type TrusteeContract,
|
||||||
type AttributeDefinition,
|
|
||||||
type PaginationParams
|
type PaginationParams
|
||||||
} from '../api/trusteeApi';
|
} from '../api/trusteeApi';
|
||||||
|
|
||||||
|
export interface AttributeDefinition {
|
||||||
|
name: string;
|
||||||
|
type: 'text' | 'email' | 'date' | 'checkbox' | 'select' | 'multiselect' | 'number' | 'textarea' | 'timestamp' | 'file';
|
||||||
|
label: string;
|
||||||
|
description?: string;
|
||||||
|
required?: boolean;
|
||||||
|
default?: any;
|
||||||
|
options?: any[] | string;
|
||||||
|
readonly?: boolean;
|
||||||
|
editable?: boolean;
|
||||||
|
visible?: boolean;
|
||||||
|
order?: number;
|
||||||
|
sortable?: boolean;
|
||||||
|
filterable?: boolean;
|
||||||
|
searchable?: boolean;
|
||||||
|
width?: number;
|
||||||
|
minWidth?: number;
|
||||||
|
maxWidth?: number;
|
||||||
|
filterOptions?: string[];
|
||||||
|
dependsOn?: string;
|
||||||
|
}
|
||||||
|
|
||||||
// Re-export types
|
// Re-export types
|
||||||
export type { TrusteeContract, AttributeDefinition, PaginationParams };
|
export type { TrusteeContract, PaginationParams };
|
||||||
|
|
||||||
// Contracts list hook
|
// Contracts list hook
|
||||||
export function useTrusteeContracts() {
|
export function useTrusteeContracts() {
|
||||||
|
const instanceId = useInstanceId();
|
||||||
|
|
||||||
const [contracts, setContracts] = useState<TrusteeContract[]>([]);
|
const [contracts, setContracts] = useState<TrusteeContract[]>([]);
|
||||||
const [attributes, setAttributes] = useState<AttributeDefinition[]>([]);
|
const [attributes, setAttributes] = useState<AttributeDefinition[]>([]);
|
||||||
const [permissions, setPermissions] = useState<UserPermissions | null>(null);
|
const [permissions, setPermissions] = useState<UserPermissions | null>(null);
|
||||||
|
|
@ -33,8 +56,10 @@ export function useTrusteeContracts() {
|
||||||
|
|
||||||
// Fetch attributes from backend
|
// Fetch attributes from backend
|
||||||
const fetchAttributes = useCallback(async () => {
|
const fetchAttributes = useCallback(async () => {
|
||||||
|
if (!instanceId) return [];
|
||||||
|
|
||||||
try {
|
try {
|
||||||
const response = await api.get('/api/attributes/TrusteeContract');
|
const response = await api.get(`/api/trustee/${instanceId}/attributes/TrusteeContract`);
|
||||||
|
|
||||||
let attrs: AttributeDefinition[] = [];
|
let attrs: AttributeDefinition[] = [];
|
||||||
if (response.data?.attributes && Array.isArray(response.data.attributes)) {
|
if (response.data?.attributes && Array.isArray(response.data.attributes)) {
|
||||||
|
|
@ -58,12 +83,13 @@ export function useTrusteeContracts() {
|
||||||
setAttributes([]);
|
setAttributes([]);
|
||||||
return [];
|
return [];
|
||||||
}
|
}
|
||||||
}, []);
|
}, [instanceId]);
|
||||||
|
|
||||||
// Fetch permissions from backend
|
// Fetch permissions from backend
|
||||||
const fetchPermissions = useCallback(async () => {
|
const fetchPermissions = useCallback(async () => {
|
||||||
try {
|
try {
|
||||||
const perms = await checkPermission('DATA', 'trustee.contract');
|
const objectKey = 'data.feature.trustee.TrusteeContract';
|
||||||
|
const perms = await checkPermission('DATA', objectKey);
|
||||||
setPermissions(perms);
|
setPermissions(perms);
|
||||||
return perms;
|
return perms;
|
||||||
} catch (error: any) {
|
} catch (error: any) {
|
||||||
|
|
@ -81,8 +107,13 @@ export function useTrusteeContracts() {
|
||||||
}, [checkPermission]);
|
}, [checkPermission]);
|
||||||
|
|
||||||
const fetchContracts = useCallback(async (params?: PaginationParams) => {
|
const fetchContracts = useCallback(async (params?: PaginationParams) => {
|
||||||
|
if (!instanceId) {
|
||||||
|
setContracts([]);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
try {
|
try {
|
||||||
const data = await fetchContractsApi(request, params);
|
const data = await fetchContractsApi(request, instanceId, params);
|
||||||
|
|
||||||
if (data && typeof data === 'object' && 'items' in data) {
|
if (data && typeof data === 'object' && 'items' in data) {
|
||||||
const items = Array.isArray(data.items) ? data.items : [];
|
const items = Array.isArray(data.items) ? data.items : [];
|
||||||
|
|
@ -99,7 +130,7 @@ export function useTrusteeContracts() {
|
||||||
setContracts([]);
|
setContracts([]);
|
||||||
setPagination(null);
|
setPagination(null);
|
||||||
}
|
}
|
||||||
}, [request]);
|
}, [request, instanceId]);
|
||||||
|
|
||||||
// Optimistically remove a contract
|
// Optimistically remove a contract
|
||||||
const removeOptimistically = (contractId: string) => {
|
const removeOptimistically = (contractId: string) => {
|
||||||
|
|
@ -119,8 +150,9 @@ export function useTrusteeContracts() {
|
||||||
|
|
||||||
// Fetch a single contract by ID
|
// Fetch a single contract by ID
|
||||||
const fetchContractById = useCallback(async (contractId: string): Promise<TrusteeContract | null> => {
|
const fetchContractById = useCallback(async (contractId: string): Promise<TrusteeContract | null> => {
|
||||||
return await fetchContractByIdApi(request, contractId);
|
if (!instanceId) return null;
|
||||||
}, [request]);
|
return await fetchContractByIdApi(request, instanceId, contractId);
|
||||||
|
}, [request, instanceId]);
|
||||||
|
|
||||||
// Generate edit fields from attributes dynamically
|
// Generate edit fields from attributes dynamically
|
||||||
const generateEditFieldsFromAttributes = useCallback((): Array<{
|
const generateEditFieldsFromAttributes = useCallback((): Array<{
|
||||||
|
|
@ -161,7 +193,7 @@ export function useTrusteeContracts() {
|
||||||
} else if (attr.type === 'select') {
|
} else if (attr.type === 'select') {
|
||||||
fieldType = 'enum';
|
fieldType = 'enum';
|
||||||
if (Array.isArray(attr.options)) {
|
if (Array.isArray(attr.options)) {
|
||||||
options = attr.options.map(opt => {
|
options = attr.options.map((opt: any) => {
|
||||||
const labelValue = typeof opt.label === 'string'
|
const labelValue = typeof opt.label === 'string'
|
||||||
? opt.label
|
? opt.label
|
||||||
: opt.label?.en || opt.label?.[Object.keys(opt.label)[0]] || String(opt.value);
|
: opt.label?.en || opt.label?.[Object.keys(opt.label)[0]] || String(opt.value);
|
||||||
|
|
@ -231,16 +263,14 @@ export function useTrusteeContracts() {
|
||||||
return fetchedAttributes;
|
return fetchedAttributes;
|
||||||
}, [attributes, fetchAttributes]);
|
}, [attributes, fetchAttributes]);
|
||||||
|
|
||||||
// Fetch attributes and permissions on mount
|
// Fetch data when instanceId is available
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
fetchAttributes();
|
if (instanceId) {
|
||||||
fetchPermissions();
|
fetchAttributes();
|
||||||
}, [fetchAttributes, fetchPermissions]);
|
fetchPermissions();
|
||||||
|
fetchContracts();
|
||||||
// Initial fetch
|
}
|
||||||
useEffect(() => {
|
}, [instanceId, fetchAttributes, fetchPermissions, fetchContracts]);
|
||||||
fetchContracts();
|
|
||||||
}, [fetchContracts]);
|
|
||||||
|
|
||||||
return {
|
return {
|
||||||
contracts,
|
contracts,
|
||||||
|
|
@ -254,12 +284,15 @@ export function useTrusteeContracts() {
|
||||||
pagination,
|
pagination,
|
||||||
fetchContractById,
|
fetchContractById,
|
||||||
generateEditFieldsFromAttributes,
|
generateEditFieldsFromAttributes,
|
||||||
ensureAttributesLoaded
|
ensureAttributesLoaded,
|
||||||
|
instanceId
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
// Contract operations hook
|
// Contract operations hook
|
||||||
export function useTrusteeContractOperations() {
|
export function useTrusteeContractOperations() {
|
||||||
|
const instanceId = useInstanceId();
|
||||||
|
|
||||||
const [deletingContracts, setDeletingContracts] = useState<Set<string>>(new Set());
|
const [deletingContracts, setDeletingContracts] = useState<Set<string>>(new Set());
|
||||||
const [creatingContract, setCreatingContract] = useState(false);
|
const [creatingContract, setCreatingContract] = useState(false);
|
||||||
const { request, isLoading } = useApiRequest();
|
const { request, isLoading } = useApiRequest();
|
||||||
|
|
@ -268,11 +301,16 @@ export function useTrusteeContractOperations() {
|
||||||
const [updateError, setUpdateError] = useState<string | null>(null);
|
const [updateError, setUpdateError] = useState<string | null>(null);
|
||||||
|
|
||||||
const handleContractDelete = async (contractId: string) => {
|
const handleContractDelete = async (contractId: string) => {
|
||||||
|
if (!instanceId) {
|
||||||
|
setDeleteError('No instance context');
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
setDeleteError(null);
|
setDeleteError(null);
|
||||||
setDeletingContracts(prev => new Set(prev).add(contractId));
|
setDeletingContracts(prev => new Set(prev).add(contractId));
|
||||||
|
|
||||||
try {
|
try {
|
||||||
await deleteContractApi(request, contractId);
|
await deleteContractApi(request, instanceId, contractId);
|
||||||
await new Promise(resolve => setTimeout(resolve, 300));
|
await new Promise(resolve => setTimeout(resolve, 300));
|
||||||
return true;
|
return true;
|
||||||
} catch (error: any) {
|
} catch (error: any) {
|
||||||
|
|
@ -288,20 +326,16 @@ export function useTrusteeContractOperations() {
|
||||||
};
|
};
|
||||||
|
|
||||||
const handleContractCreate = async (contractData: Partial<TrusteeContract>) => {
|
const handleContractCreate = async (contractData: Partial<TrusteeContract>) => {
|
||||||
|
if (!instanceId) {
|
||||||
|
setCreateError('No instance context');
|
||||||
|
return { success: false, error: 'No instance context' };
|
||||||
|
}
|
||||||
|
|
||||||
setCreateError(null);
|
setCreateError(null);
|
||||||
setCreatingContract(true);
|
setCreatingContract(true);
|
||||||
|
|
||||||
try {
|
try {
|
||||||
const currentUserData = getUserDataCache();
|
const newContract = await createContractApi(request, instanceId, contractData);
|
||||||
const mandateId = currentUserData?.mandateId || '';
|
|
||||||
|
|
||||||
const requestBody = {
|
|
||||||
...contractData,
|
|
||||||
mandate: mandateId
|
|
||||||
};
|
|
||||||
|
|
||||||
const newContract = await createContractApi(request, requestBody);
|
|
||||||
|
|
||||||
return { success: true, contractData: newContract };
|
return { success: true, contractData: newContract };
|
||||||
} catch (error: any) {
|
} catch (error: any) {
|
||||||
setCreateError(error.message);
|
setCreateError(error.message);
|
||||||
|
|
@ -316,21 +350,15 @@ export function useTrusteeContractOperations() {
|
||||||
updateData: Partial<TrusteeContract>,
|
updateData: Partial<TrusteeContract>,
|
||||||
_originalData?: any
|
_originalData?: any
|
||||||
) => {
|
) => {
|
||||||
|
if (!instanceId) {
|
||||||
|
setUpdateError('No instance context');
|
||||||
|
return { success: false, error: 'No instance context' };
|
||||||
|
}
|
||||||
|
|
||||||
setUpdateError(null);
|
setUpdateError(null);
|
||||||
|
|
||||||
try {
|
try {
|
||||||
const currentUserData = getUserDataCache();
|
const updatedContract = await updateContractApi(request, instanceId, contractId, updateData);
|
||||||
const mandateId = currentUserData?.mandateId || '';
|
|
||||||
|
|
||||||
// Note: organisationId should NOT be included in update if immutable
|
|
||||||
// Backend will reject if organisationId is changed
|
|
||||||
const requestBody = {
|
|
||||||
...updateData,
|
|
||||||
mandate: mandateId
|
|
||||||
};
|
|
||||||
|
|
||||||
const updatedContract = await updateContractApi(request, contractId, requestBody);
|
|
||||||
|
|
||||||
return { success: true, contractData: updatedContract };
|
return { success: true, contractData: updatedContract };
|
||||||
} catch (error: any) {
|
} catch (error: any) {
|
||||||
const errorMessage = error.response?.data?.message || error.message || 'Failed to update contract';
|
const errorMessage = error.response?.data?.message || error.message || 'Failed to update contract';
|
||||||
|
|
@ -357,6 +385,7 @@ export function useTrusteeContractOperations() {
|
||||||
handleContractDelete,
|
handleContractDelete,
|
||||||
handleContractCreate,
|
handleContractCreate,
|
||||||
handleContractUpdate,
|
handleContractUpdate,
|
||||||
isLoading
|
isLoading,
|
||||||
|
instanceId
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,25 +1,47 @@
|
||||||
import { useState, useEffect, useCallback } from 'react';
|
import { useState, useEffect, useCallback } from 'react';
|
||||||
import { useApiRequest } from './useApi';
|
import { useApiRequest } from './useApi';
|
||||||
import { getUserDataCache } from '../utils/userCache';
|
|
||||||
import api from '../api';
|
import api from '../api';
|
||||||
import { usePermissions, type UserPermissions } from './usePermissions';
|
import { usePermissions, type UserPermissions } from './usePermissions';
|
||||||
|
import { useInstanceId } from './useCurrentInstance';
|
||||||
import {
|
import {
|
||||||
fetchDocuments as fetchDocumentsApi,
|
fetchDocuments as fetchDocumentsApi,
|
||||||
fetchDocumentById as fetchDocumentByIdApi,
|
fetchDocumentById as fetchDocumentByIdApi,
|
||||||
createDocument as createDocumentApi,
|
createDocument as createDocumentApi,
|
||||||
updateDocument as updateDocumentApi,
|
updateDocument as updateDocumentApi,
|
||||||
deleteDocument as deleteDocumentApi,
|
deleteDocument as deleteDocumentApi,
|
||||||
downloadDocumentData as downloadDocumentDataApi,
|
|
||||||
type TrusteeDocument,
|
type TrusteeDocument,
|
||||||
type AttributeDefinition,
|
|
||||||
type PaginationParams
|
type PaginationParams
|
||||||
} from '../api/trusteeApi';
|
} from '../api/trusteeApi';
|
||||||
|
|
||||||
|
export interface AttributeDefinition {
|
||||||
|
name: string;
|
||||||
|
type: 'text' | 'email' | 'date' | 'checkbox' | 'select' | 'multiselect' | 'number' | 'textarea' | 'timestamp' | 'file';
|
||||||
|
label: string;
|
||||||
|
description?: string;
|
||||||
|
required?: boolean;
|
||||||
|
default?: any;
|
||||||
|
options?: any[] | string;
|
||||||
|
readonly?: boolean;
|
||||||
|
editable?: boolean;
|
||||||
|
visible?: boolean;
|
||||||
|
order?: number;
|
||||||
|
sortable?: boolean;
|
||||||
|
filterable?: boolean;
|
||||||
|
searchable?: boolean;
|
||||||
|
width?: number;
|
||||||
|
minWidth?: number;
|
||||||
|
maxWidth?: number;
|
||||||
|
filterOptions?: string[];
|
||||||
|
dependsOn?: string;
|
||||||
|
}
|
||||||
|
|
||||||
// Re-export types
|
// Re-export types
|
||||||
export type { TrusteeDocument, AttributeDefinition, PaginationParams };
|
export type { TrusteeDocument, PaginationParams };
|
||||||
|
|
||||||
// Documents list hook
|
// Documents list hook
|
||||||
export function useTrusteeDocuments() {
|
export function useTrusteeDocuments() {
|
||||||
|
const instanceId = useInstanceId();
|
||||||
|
|
||||||
const [documents, setDocuments] = useState<TrusteeDocument[]>([]);
|
const [documents, setDocuments] = useState<TrusteeDocument[]>([]);
|
||||||
const [attributes, setAttributes] = useState<AttributeDefinition[]>([]);
|
const [attributes, setAttributes] = useState<AttributeDefinition[]>([]);
|
||||||
const [permissions, setPermissions] = useState<UserPermissions | null>(null);
|
const [permissions, setPermissions] = useState<UserPermissions | null>(null);
|
||||||
|
|
@ -34,8 +56,10 @@ export function useTrusteeDocuments() {
|
||||||
|
|
||||||
// Fetch attributes from backend
|
// Fetch attributes from backend
|
||||||
const fetchAttributes = useCallback(async () => {
|
const fetchAttributes = useCallback(async () => {
|
||||||
|
if (!instanceId) return [];
|
||||||
|
|
||||||
try {
|
try {
|
||||||
const response = await api.get('/api/attributes/TrusteeDocument');
|
const response = await api.get(`/api/trustee/${instanceId}/attributes/TrusteeDocument`);
|
||||||
|
|
||||||
let attrs: AttributeDefinition[] = [];
|
let attrs: AttributeDefinition[] = [];
|
||||||
if (response.data?.attributes && Array.isArray(response.data.attributes)) {
|
if (response.data?.attributes && Array.isArray(response.data.attributes)) {
|
||||||
|
|
@ -59,12 +83,13 @@ export function useTrusteeDocuments() {
|
||||||
setAttributes([]);
|
setAttributes([]);
|
||||||
return [];
|
return [];
|
||||||
}
|
}
|
||||||
}, []);
|
}, [instanceId]);
|
||||||
|
|
||||||
// Fetch permissions from backend
|
// Fetch permissions from backend
|
||||||
const fetchPermissions = useCallback(async () => {
|
const fetchPermissions = useCallback(async () => {
|
||||||
try {
|
try {
|
||||||
const perms = await checkPermission('DATA', 'trustee.document');
|
const objectKey = 'data.feature.trustee.TrusteeDocument';
|
||||||
|
const perms = await checkPermission('DATA', objectKey);
|
||||||
setPermissions(perms);
|
setPermissions(perms);
|
||||||
return perms;
|
return perms;
|
||||||
} catch (error: any) {
|
} catch (error: any) {
|
||||||
|
|
@ -82,8 +107,13 @@ export function useTrusteeDocuments() {
|
||||||
}, [checkPermission]);
|
}, [checkPermission]);
|
||||||
|
|
||||||
const fetchDocuments = useCallback(async (params?: PaginationParams) => {
|
const fetchDocuments = useCallback(async (params?: PaginationParams) => {
|
||||||
|
if (!instanceId) {
|
||||||
|
setDocuments([]);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
try {
|
try {
|
||||||
const data = await fetchDocumentsApi(request, params);
|
const data = await fetchDocumentsApi(request, instanceId, params);
|
||||||
|
|
||||||
if (data && typeof data === 'object' && 'items' in data) {
|
if (data && typeof data === 'object' && 'items' in data) {
|
||||||
const items = Array.isArray(data.items) ? data.items : [];
|
const items = Array.isArray(data.items) ? data.items : [];
|
||||||
|
|
@ -100,7 +130,7 @@ export function useTrusteeDocuments() {
|
||||||
setDocuments([]);
|
setDocuments([]);
|
||||||
setPagination(null);
|
setPagination(null);
|
||||||
}
|
}
|
||||||
}, [request]);
|
}, [request, instanceId]);
|
||||||
|
|
||||||
// Optimistically remove a document
|
// Optimistically remove a document
|
||||||
const removeOptimistically = (documentId: string) => {
|
const removeOptimistically = (documentId: string) => {
|
||||||
|
|
@ -120,8 +150,9 @@ export function useTrusteeDocuments() {
|
||||||
|
|
||||||
// Fetch a single document by ID
|
// Fetch a single document by ID
|
||||||
const fetchDocumentById = useCallback(async (documentId: string): Promise<TrusteeDocument | null> => {
|
const fetchDocumentById = useCallback(async (documentId: string): Promise<TrusteeDocument | null> => {
|
||||||
return await fetchDocumentByIdApi(request, documentId);
|
if (!instanceId) return null;
|
||||||
}, [request]);
|
return await fetchDocumentByIdApi(request, instanceId, documentId);
|
||||||
|
}, [request, instanceId]);
|
||||||
|
|
||||||
// Generate edit fields from attributes dynamically
|
// Generate edit fields from attributes dynamically
|
||||||
const generateEditFieldsFromAttributes = useCallback((): Array<{
|
const generateEditFieldsFromAttributes = useCallback((): Array<{
|
||||||
|
|
@ -163,7 +194,7 @@ export function useTrusteeDocuments() {
|
||||||
} else if (attr.type === 'select') {
|
} else if (attr.type === 'select') {
|
||||||
fieldType = 'enum';
|
fieldType = 'enum';
|
||||||
if (Array.isArray(attr.options)) {
|
if (Array.isArray(attr.options)) {
|
||||||
options = attr.options.map(opt => {
|
options = attr.options.map((opt: any) => {
|
||||||
const labelValue = typeof opt.label === 'string'
|
const labelValue = typeof opt.label === 'string'
|
||||||
? opt.label
|
? opt.label
|
||||||
: opt.label?.en || opt.label?.[Object.keys(opt.label)[0]] || String(opt.value);
|
: opt.label?.en || opt.label?.[Object.keys(opt.label)[0]] || String(opt.value);
|
||||||
|
|
@ -222,16 +253,14 @@ export function useTrusteeDocuments() {
|
||||||
return fetchedAttributes;
|
return fetchedAttributes;
|
||||||
}, [attributes, fetchAttributes]);
|
}, [attributes, fetchAttributes]);
|
||||||
|
|
||||||
// Fetch attributes and permissions on mount
|
// Fetch data when instanceId is available
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
fetchAttributes();
|
if (instanceId) {
|
||||||
fetchPermissions();
|
fetchAttributes();
|
||||||
}, [fetchAttributes, fetchPermissions]);
|
fetchPermissions();
|
||||||
|
fetchDocuments();
|
||||||
// Initial fetch
|
}
|
||||||
useEffect(() => {
|
}, [instanceId, fetchAttributes, fetchPermissions, fetchDocuments]);
|
||||||
fetchDocuments();
|
|
||||||
}, [fetchDocuments]);
|
|
||||||
|
|
||||||
return {
|
return {
|
||||||
documents,
|
documents,
|
||||||
|
|
@ -245,12 +274,15 @@ export function useTrusteeDocuments() {
|
||||||
pagination,
|
pagination,
|
||||||
fetchDocumentById,
|
fetchDocumentById,
|
||||||
generateEditFieldsFromAttributes,
|
generateEditFieldsFromAttributes,
|
||||||
ensureAttributesLoaded
|
ensureAttributesLoaded,
|
||||||
|
instanceId
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
// Document operations hook
|
// Document operations hook
|
||||||
export function useTrusteeDocumentOperations() {
|
export function useTrusteeDocumentOperations() {
|
||||||
|
const instanceId = useInstanceId();
|
||||||
|
|
||||||
const [deletingDocuments, setDeletingDocuments] = useState<Set<string>>(new Set());
|
const [deletingDocuments, setDeletingDocuments] = useState<Set<string>>(new Set());
|
||||||
const [creatingDocument, setCreatingDocument] = useState(false);
|
const [creatingDocument, setCreatingDocument] = useState(false);
|
||||||
const [downloadingDocuments, setDownloadingDocuments] = useState<Set<string>>(new Set());
|
const [downloadingDocuments, setDownloadingDocuments] = useState<Set<string>>(new Set());
|
||||||
|
|
@ -261,11 +293,16 @@ export function useTrusteeDocumentOperations() {
|
||||||
const [downloadError, setDownloadError] = useState<string | null>(null);
|
const [downloadError, setDownloadError] = useState<string | null>(null);
|
||||||
|
|
||||||
const handleDocumentDelete = async (documentId: string) => {
|
const handleDocumentDelete = async (documentId: string) => {
|
||||||
|
if (!instanceId) {
|
||||||
|
setDeleteError('No instance context');
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
setDeleteError(null);
|
setDeleteError(null);
|
||||||
setDeletingDocuments(prev => new Set(prev).add(documentId));
|
setDeletingDocuments(prev => new Set(prev).add(documentId));
|
||||||
|
|
||||||
try {
|
try {
|
||||||
await deleteDocumentApi(request, documentId);
|
await deleteDocumentApi(request, instanceId, documentId);
|
||||||
await new Promise(resolve => setTimeout(resolve, 300));
|
await new Promise(resolve => setTimeout(resolve, 300));
|
||||||
return true;
|
return true;
|
||||||
} catch (error: any) {
|
} catch (error: any) {
|
||||||
|
|
@ -280,18 +317,17 @@ export function useTrusteeDocumentOperations() {
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
const handleDocumentCreate = async (documentData: FormData) => {
|
const handleDocumentCreate = async (documentData: Partial<TrusteeDocument>) => {
|
||||||
|
if (!instanceId) {
|
||||||
|
setCreateError('No instance context');
|
||||||
|
return { success: false, error: 'No instance context' };
|
||||||
|
}
|
||||||
|
|
||||||
setCreateError(null);
|
setCreateError(null);
|
||||||
setCreatingDocument(true);
|
setCreatingDocument(true);
|
||||||
|
|
||||||
try {
|
try {
|
||||||
const currentUserData = getUserDataCache();
|
const newDocument = await createDocumentApi(request, instanceId, documentData);
|
||||||
const mandateId = currentUserData?.mandateId || '';
|
|
||||||
|
|
||||||
documentData.append('mandate', mandateId);
|
|
||||||
|
|
||||||
const newDocument = await createDocumentApi(request, documentData);
|
|
||||||
|
|
||||||
return { success: true, documentData: newDocument };
|
return { success: true, documentData: newDocument };
|
||||||
} catch (error: any) {
|
} catch (error: any) {
|
||||||
setCreateError(error.message);
|
setCreateError(error.message);
|
||||||
|
|
@ -306,19 +342,15 @@ export function useTrusteeDocumentOperations() {
|
||||||
updateData: Partial<TrusteeDocument>,
|
updateData: Partial<TrusteeDocument>,
|
||||||
_originalData?: any
|
_originalData?: any
|
||||||
) => {
|
) => {
|
||||||
|
if (!instanceId) {
|
||||||
|
setUpdateError('No instance context');
|
||||||
|
return { success: false, error: 'No instance context' };
|
||||||
|
}
|
||||||
|
|
||||||
setUpdateError(null);
|
setUpdateError(null);
|
||||||
|
|
||||||
try {
|
try {
|
||||||
const currentUserData = getUserDataCache();
|
const updatedDocument = await updateDocumentApi(request, instanceId, documentId, updateData);
|
||||||
const mandateId = currentUserData?.mandateId || '';
|
|
||||||
|
|
||||||
const requestBody = {
|
|
||||||
...updateData,
|
|
||||||
mandate: mandateId
|
|
||||||
};
|
|
||||||
|
|
||||||
const updatedDocument = await updateDocumentApi(request, documentId, requestBody);
|
|
||||||
|
|
||||||
return { success: true, documentData: updatedDocument };
|
return { success: true, documentData: updatedDocument };
|
||||||
} catch (error: any) {
|
} catch (error: any) {
|
||||||
const errorMessage = error.response?.data?.message || error.message || 'Failed to update document';
|
const errorMessage = error.response?.data?.message || error.message || 'Failed to update document';
|
||||||
|
|
@ -337,11 +369,28 @@ export function useTrusteeDocumentOperations() {
|
||||||
};
|
};
|
||||||
|
|
||||||
const handleDocumentDownload = async (documentId: string, documentName: string) => {
|
const handleDocumentDownload = async (documentId: string, documentName: string) => {
|
||||||
|
if (!instanceId) {
|
||||||
|
setDownloadError('No instance context');
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
setDownloadError(null);
|
setDownloadError(null);
|
||||||
setDownloadingDocuments(prev => new Set(prev).add(documentId));
|
setDownloadingDocuments(prev => new Set(prev).add(documentId));
|
||||||
|
|
||||||
try {
|
try {
|
||||||
const blob = await downloadDocumentDataApi(request, documentId);
|
const doc = await fetchDocumentByIdApi(request, instanceId, documentId);
|
||||||
|
if (!doc || !doc.documentData) {
|
||||||
|
throw new Error('Document data not found');
|
||||||
|
}
|
||||||
|
|
||||||
|
// Convert base64 to blob
|
||||||
|
const byteCharacters = atob(doc.documentData);
|
||||||
|
const byteNumbers = new Array(byteCharacters.length);
|
||||||
|
for (let i = 0; i < byteCharacters.length; i++) {
|
||||||
|
byteNumbers[i] = byteCharacters.charCodeAt(i);
|
||||||
|
}
|
||||||
|
const byteArray = new Uint8Array(byteNumbers);
|
||||||
|
const blob = new Blob([byteArray], { type: doc.documentMimeType || 'application/octet-stream' });
|
||||||
|
|
||||||
// Create download link
|
// Create download link
|
||||||
const url = window.URL.createObjectURL(blob);
|
const url = window.URL.createObjectURL(blob);
|
||||||
|
|
@ -379,6 +428,7 @@ export function useTrusteeDocumentOperations() {
|
||||||
handleDocumentCreate,
|
handleDocumentCreate,
|
||||||
handleDocumentUpdate,
|
handleDocumentUpdate,
|
||||||
handleDocumentDownload,
|
handleDocumentDownload,
|
||||||
isLoading
|
isLoading,
|
||||||
|
instanceId
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,8 +1,8 @@
|
||||||
import { useState, useEffect, useCallback } from 'react';
|
import { useState, useEffect, useCallback } from 'react';
|
||||||
import { useApiRequest } from './useApi';
|
import { useApiRequest } from './useApi';
|
||||||
import { getUserDataCache } from '../utils/userCache';
|
|
||||||
import api from '../api';
|
import api from '../api';
|
||||||
import { usePermissions, type UserPermissions } from './usePermissions';
|
import { usePermissions, type UserPermissions } from './usePermissions';
|
||||||
|
import { useInstanceId } from './useCurrentInstance';
|
||||||
import {
|
import {
|
||||||
fetchOrganisations as fetchOrganisationsApi,
|
fetchOrganisations as fetchOrganisationsApi,
|
||||||
fetchOrganisationById as fetchOrganisationByIdApi,
|
fetchOrganisationById as fetchOrganisationByIdApi,
|
||||||
|
|
@ -10,15 +10,38 @@ import {
|
||||||
updateOrganisation as updateOrganisationApi,
|
updateOrganisation as updateOrganisationApi,
|
||||||
deleteOrganisation as deleteOrganisationApi,
|
deleteOrganisation as deleteOrganisationApi,
|
||||||
type TrusteeOrganisation,
|
type TrusteeOrganisation,
|
||||||
type AttributeDefinition,
|
|
||||||
type PaginationParams
|
type PaginationParams
|
||||||
} from '../api/trusteeApi';
|
} from '../api/trusteeApi';
|
||||||
|
|
||||||
|
export interface AttributeDefinition {
|
||||||
|
name: string;
|
||||||
|
type: 'text' | 'email' | 'date' | 'checkbox' | 'select' | 'multiselect' | 'number' | 'textarea' | 'timestamp' | 'file';
|
||||||
|
label: string;
|
||||||
|
description?: string;
|
||||||
|
required?: boolean;
|
||||||
|
default?: any;
|
||||||
|
options?: any[] | string;
|
||||||
|
readonly?: boolean;
|
||||||
|
editable?: boolean;
|
||||||
|
visible?: boolean;
|
||||||
|
order?: number;
|
||||||
|
sortable?: boolean;
|
||||||
|
filterable?: boolean;
|
||||||
|
searchable?: boolean;
|
||||||
|
width?: number;
|
||||||
|
minWidth?: number;
|
||||||
|
maxWidth?: number;
|
||||||
|
filterOptions?: string[];
|
||||||
|
dependsOn?: string;
|
||||||
|
}
|
||||||
|
|
||||||
// Re-export types
|
// Re-export types
|
||||||
export type { TrusteeOrganisation, AttributeDefinition, PaginationParams };
|
export type { TrusteeOrganisation, PaginationParams };
|
||||||
|
|
||||||
// Organisations list hook
|
// Organisations list hook
|
||||||
export function useTrusteeOrganisations() {
|
export function useTrusteeOrganisations() {
|
||||||
|
const instanceId = useInstanceId();
|
||||||
|
|
||||||
const [organisations, setOrganisations] = useState<TrusteeOrganisation[]>([]);
|
const [organisations, setOrganisations] = useState<TrusteeOrganisation[]>([]);
|
||||||
const [attributes, setAttributes] = useState<AttributeDefinition[]>([]);
|
const [attributes, setAttributes] = useState<AttributeDefinition[]>([]);
|
||||||
const [permissions, setPermissions] = useState<UserPermissions | null>(null);
|
const [permissions, setPermissions] = useState<UserPermissions | null>(null);
|
||||||
|
|
@ -33,8 +56,10 @@ export function useTrusteeOrganisations() {
|
||||||
|
|
||||||
// Fetch attributes from backend
|
// Fetch attributes from backend
|
||||||
const fetchAttributes = useCallback(async () => {
|
const fetchAttributes = useCallback(async () => {
|
||||||
|
if (!instanceId) return [];
|
||||||
|
|
||||||
try {
|
try {
|
||||||
const response = await api.get('/api/attributes/TrusteeOrganisation');
|
const response = await api.get(`/api/trustee/${instanceId}/attributes/TrusteeOrganisation`);
|
||||||
|
|
||||||
let attrs: AttributeDefinition[] = [];
|
let attrs: AttributeDefinition[] = [];
|
||||||
if (response.data?.attributes && Array.isArray(response.data.attributes)) {
|
if (response.data?.attributes && Array.isArray(response.data.attributes)) {
|
||||||
|
|
@ -58,12 +83,13 @@ export function useTrusteeOrganisations() {
|
||||||
setAttributes([]);
|
setAttributes([]);
|
||||||
return [];
|
return [];
|
||||||
}
|
}
|
||||||
}, []);
|
}, [instanceId]);
|
||||||
|
|
||||||
// Fetch permissions from backend
|
// Fetch permissions from backend
|
||||||
const fetchPermissions = useCallback(async () => {
|
const fetchPermissions = useCallback(async () => {
|
||||||
try {
|
try {
|
||||||
const perms = await checkPermission('DATA', 'trustee.organisation');
|
const objectKey = 'data.feature.trustee.TrusteeOrganisation';
|
||||||
|
const perms = await checkPermission('DATA', objectKey);
|
||||||
setPermissions(perms);
|
setPermissions(perms);
|
||||||
return perms;
|
return perms;
|
||||||
} catch (error: any) {
|
} catch (error: any) {
|
||||||
|
|
@ -81,8 +107,13 @@ export function useTrusteeOrganisations() {
|
||||||
}, [checkPermission]);
|
}, [checkPermission]);
|
||||||
|
|
||||||
const fetchOrganisations = useCallback(async (params?: PaginationParams) => {
|
const fetchOrganisations = useCallback(async (params?: PaginationParams) => {
|
||||||
|
if (!instanceId) {
|
||||||
|
setOrganisations([]);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
try {
|
try {
|
||||||
const data = await fetchOrganisationsApi(request, params);
|
const data = await fetchOrganisationsApi(request, instanceId, params);
|
||||||
|
|
||||||
if (data && typeof data === 'object' && 'items' in data) {
|
if (data && typeof data === 'object' && 'items' in data) {
|
||||||
const items = Array.isArray(data.items) ? data.items : [];
|
const items = Array.isArray(data.items) ? data.items : [];
|
||||||
|
|
@ -99,7 +130,7 @@ export function useTrusteeOrganisations() {
|
||||||
setOrganisations([]);
|
setOrganisations([]);
|
||||||
setPagination(null);
|
setPagination(null);
|
||||||
}
|
}
|
||||||
}, [request]);
|
}, [request, instanceId]);
|
||||||
|
|
||||||
// Optimistically remove an organisation
|
// Optimistically remove an organisation
|
||||||
const removeOptimistically = (organisationId: string) => {
|
const removeOptimistically = (organisationId: string) => {
|
||||||
|
|
@ -119,8 +150,9 @@ export function useTrusteeOrganisations() {
|
||||||
|
|
||||||
// Fetch a single organisation by ID
|
// Fetch a single organisation by ID
|
||||||
const fetchOrganisationById = useCallback(async (organisationId: string): Promise<TrusteeOrganisation | null> => {
|
const fetchOrganisationById = useCallback(async (organisationId: string): Promise<TrusteeOrganisation | null> => {
|
||||||
return await fetchOrganisationByIdApi(request, organisationId);
|
if (!instanceId) return null;
|
||||||
}, [request]);
|
return await fetchOrganisationByIdApi(request, instanceId, organisationId);
|
||||||
|
}, [request, instanceId]);
|
||||||
|
|
||||||
// Generate edit fields from attributes dynamically
|
// Generate edit fields from attributes dynamically
|
||||||
const generateEditFieldsFromAttributes = useCallback((): Array<{
|
const generateEditFieldsFromAttributes = useCallback((): Array<{
|
||||||
|
|
@ -159,7 +191,7 @@ export function useTrusteeOrganisations() {
|
||||||
} else if (attr.type === 'select') {
|
} else if (attr.type === 'select') {
|
||||||
fieldType = 'enum';
|
fieldType = 'enum';
|
||||||
if (Array.isArray(attr.options)) {
|
if (Array.isArray(attr.options)) {
|
||||||
options = attr.options.map(opt => {
|
options = attr.options.map((opt: any) => {
|
||||||
const labelValue = typeof opt.label === 'string'
|
const labelValue = typeof opt.label === 'string'
|
||||||
? opt.label
|
? opt.label
|
||||||
: opt.label?.en || opt.label?.[Object.keys(opt.label)[0]] || String(opt.value);
|
: opt.label?.en || opt.label?.[Object.keys(opt.label)[0]] || String(opt.value);
|
||||||
|
|
@ -174,7 +206,7 @@ export function useTrusteeOrganisations() {
|
||||||
} else if (attr.type === 'multiselect') {
|
} else if (attr.type === 'multiselect') {
|
||||||
fieldType = 'multiselect';
|
fieldType = 'multiselect';
|
||||||
if (Array.isArray(attr.options)) {
|
if (Array.isArray(attr.options)) {
|
||||||
options = attr.options.map(opt => {
|
options = attr.options.map((opt: any) => {
|
||||||
const labelValue = typeof opt.label === 'string'
|
const labelValue = typeof opt.label === 'string'
|
||||||
? opt.label
|
? opt.label
|
||||||
: opt.label?.en || opt.label?.[Object.keys(opt.label)[0]] || String(opt.value);
|
: opt.label?.en || opt.label?.[Object.keys(opt.label)[0]] || String(opt.value);
|
||||||
|
|
@ -239,16 +271,14 @@ export function useTrusteeOrganisations() {
|
||||||
return fetchedAttributes;
|
return fetchedAttributes;
|
||||||
}, [attributes, fetchAttributes]);
|
}, [attributes, fetchAttributes]);
|
||||||
|
|
||||||
// Fetch attributes and permissions on mount
|
// Fetch data when instanceId is available
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
fetchAttributes();
|
if (instanceId) {
|
||||||
fetchPermissions();
|
fetchAttributes();
|
||||||
}, [fetchAttributes, fetchPermissions]);
|
fetchPermissions();
|
||||||
|
fetchOrganisations();
|
||||||
// Initial fetch
|
}
|
||||||
useEffect(() => {
|
}, [instanceId, fetchAttributes, fetchPermissions, fetchOrganisations]);
|
||||||
fetchOrganisations();
|
|
||||||
}, [fetchOrganisations]);
|
|
||||||
|
|
||||||
return {
|
return {
|
||||||
organisations,
|
organisations,
|
||||||
|
|
@ -262,12 +292,15 @@ export function useTrusteeOrganisations() {
|
||||||
pagination,
|
pagination,
|
||||||
fetchOrganisationById,
|
fetchOrganisationById,
|
||||||
generateEditFieldsFromAttributes,
|
generateEditFieldsFromAttributes,
|
||||||
ensureAttributesLoaded
|
ensureAttributesLoaded,
|
||||||
|
instanceId
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
// Organisation operations hook
|
// Organisation operations hook
|
||||||
export function useTrusteeOrganisationOperations() {
|
export function useTrusteeOrganisationOperations() {
|
||||||
|
const instanceId = useInstanceId();
|
||||||
|
|
||||||
const [deletingOrganisations, setDeletingOrganisations] = useState<Set<string>>(new Set());
|
const [deletingOrganisations, setDeletingOrganisations] = useState<Set<string>>(new Set());
|
||||||
const [creatingOrganisation, setCreatingOrganisation] = useState(false);
|
const [creatingOrganisation, setCreatingOrganisation] = useState(false);
|
||||||
const { request, isLoading } = useApiRequest();
|
const { request, isLoading } = useApiRequest();
|
||||||
|
|
@ -276,11 +309,16 @@ export function useTrusteeOrganisationOperations() {
|
||||||
const [updateError, setUpdateError] = useState<string | null>(null);
|
const [updateError, setUpdateError] = useState<string | null>(null);
|
||||||
|
|
||||||
const handleOrganisationDelete = async (organisationId: string) => {
|
const handleOrganisationDelete = async (organisationId: string) => {
|
||||||
|
if (!instanceId) {
|
||||||
|
setDeleteError('No instance context');
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
setDeleteError(null);
|
setDeleteError(null);
|
||||||
setDeletingOrganisations(prev => new Set(prev).add(organisationId));
|
setDeletingOrganisations(prev => new Set(prev).add(organisationId));
|
||||||
|
|
||||||
try {
|
try {
|
||||||
await deleteOrganisationApi(request, organisationId);
|
await deleteOrganisationApi(request, instanceId, organisationId);
|
||||||
await new Promise(resolve => setTimeout(resolve, 300));
|
await new Promise(resolve => setTimeout(resolve, 300));
|
||||||
return true;
|
return true;
|
||||||
} catch (error: any) {
|
} catch (error: any) {
|
||||||
|
|
@ -296,20 +334,16 @@ export function useTrusteeOrganisationOperations() {
|
||||||
};
|
};
|
||||||
|
|
||||||
const handleOrganisationCreate = async (organisationData: Partial<TrusteeOrganisation>) => {
|
const handleOrganisationCreate = async (organisationData: Partial<TrusteeOrganisation>) => {
|
||||||
|
if (!instanceId) {
|
||||||
|
setCreateError('No instance context');
|
||||||
|
return { success: false, error: 'No instance context' };
|
||||||
|
}
|
||||||
|
|
||||||
setCreateError(null);
|
setCreateError(null);
|
||||||
setCreatingOrganisation(true);
|
setCreatingOrganisation(true);
|
||||||
|
|
||||||
try {
|
try {
|
||||||
const currentUserData = getUserDataCache();
|
const newOrganisation = await createOrganisationApi(request, instanceId, organisationData);
|
||||||
const mandateId = currentUserData?.mandateId || '';
|
|
||||||
|
|
||||||
const requestBody = {
|
|
||||||
...organisationData,
|
|
||||||
mandate: mandateId
|
|
||||||
};
|
|
||||||
|
|
||||||
const newOrganisation = await createOrganisationApi(request, requestBody);
|
|
||||||
|
|
||||||
return { success: true, organisationData: newOrganisation };
|
return { success: true, organisationData: newOrganisation };
|
||||||
} catch (error: any) {
|
} catch (error: any) {
|
||||||
setCreateError(error.message);
|
setCreateError(error.message);
|
||||||
|
|
@ -324,19 +358,15 @@ export function useTrusteeOrganisationOperations() {
|
||||||
updateData: Partial<TrusteeOrganisation>,
|
updateData: Partial<TrusteeOrganisation>,
|
||||||
_originalData?: any
|
_originalData?: any
|
||||||
) => {
|
) => {
|
||||||
|
if (!instanceId) {
|
||||||
|
setUpdateError('No instance context');
|
||||||
|
return { success: false, error: 'No instance context' };
|
||||||
|
}
|
||||||
|
|
||||||
setUpdateError(null);
|
setUpdateError(null);
|
||||||
|
|
||||||
try {
|
try {
|
||||||
const currentUserData = getUserDataCache();
|
const updatedOrganisation = await updateOrganisationApi(request, instanceId, organisationId, updateData);
|
||||||
const mandateId = currentUserData?.mandateId || '';
|
|
||||||
|
|
||||||
const requestBody = {
|
|
||||||
...updateData,
|
|
||||||
mandate: mandateId
|
|
||||||
};
|
|
||||||
|
|
||||||
const updatedOrganisation = await updateOrganisationApi(request, organisationId, requestBody);
|
|
||||||
|
|
||||||
return { success: true, organisationData: updatedOrganisation };
|
return { success: true, organisationData: updatedOrganisation };
|
||||||
} catch (error: any) {
|
} catch (error: any) {
|
||||||
const errorMessage = error.response?.data?.message || error.message || 'Failed to update organisation';
|
const errorMessage = error.response?.data?.message || error.message || 'Failed to update organisation';
|
||||||
|
|
@ -363,6 +393,7 @@ export function useTrusteeOrganisationOperations() {
|
||||||
handleOrganisationDelete,
|
handleOrganisationDelete,
|
||||||
handleOrganisationCreate,
|
handleOrganisationCreate,
|
||||||
handleOrganisationUpdate,
|
handleOrganisationUpdate,
|
||||||
isLoading
|
isLoading,
|
||||||
|
instanceId
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,23 +1,46 @@
|
||||||
import { useState, useEffect, useCallback } from 'react';
|
import { useState, useEffect, useCallback } from 'react';
|
||||||
import { useApiRequest } from './useApi';
|
import { useApiRequest } from './useApi';
|
||||||
import { getUserDataCache } from '../utils/userCache';
|
|
||||||
import api from '../api';
|
import api from '../api';
|
||||||
import { usePermissions, type UserPermissions } from './usePermissions';
|
import { usePermissions, type UserPermissions } from './usePermissions';
|
||||||
|
import { useInstanceId } from './useCurrentInstance';
|
||||||
import {
|
import {
|
||||||
fetchPositionDocuments as fetchPositionDocumentsApi,
|
fetchPositionDocuments as fetchPositionDocumentsApi,
|
||||||
fetchPositionDocumentById as fetchPositionDocumentByIdApi,
|
fetchPositionDocumentById as fetchPositionDocumentByIdApi,
|
||||||
createPositionDocument as createPositionDocumentApi,
|
createPositionDocument as createPositionDocumentApi,
|
||||||
deletePositionDocument as deletePositionDocumentApi,
|
deletePositionDocument as deletePositionDocumentApi,
|
||||||
type TrusteePositionDocument,
|
type TrusteePositionDocument,
|
||||||
type AttributeDefinition,
|
|
||||||
type PaginationParams
|
type PaginationParams
|
||||||
} from '../api/trusteeApi';
|
} from '../api/trusteeApi';
|
||||||
|
|
||||||
|
export interface AttributeDefinition {
|
||||||
|
name: string;
|
||||||
|
type: 'text' | 'email' | 'date' | 'checkbox' | 'select' | 'multiselect' | 'number' | 'textarea' | 'timestamp' | 'file';
|
||||||
|
label: string;
|
||||||
|
description?: string;
|
||||||
|
required?: boolean;
|
||||||
|
default?: any;
|
||||||
|
options?: any[] | string;
|
||||||
|
readonly?: boolean;
|
||||||
|
editable?: boolean;
|
||||||
|
visible?: boolean;
|
||||||
|
order?: number;
|
||||||
|
sortable?: boolean;
|
||||||
|
filterable?: boolean;
|
||||||
|
searchable?: boolean;
|
||||||
|
width?: number;
|
||||||
|
minWidth?: number;
|
||||||
|
maxWidth?: number;
|
||||||
|
filterOptions?: string[];
|
||||||
|
dependsOn?: string;
|
||||||
|
}
|
||||||
|
|
||||||
// Re-export types
|
// Re-export types
|
||||||
export type { TrusteePositionDocument, AttributeDefinition, PaginationParams };
|
export type { TrusteePositionDocument, PaginationParams };
|
||||||
|
|
||||||
// Position-Documents list hook
|
// Position-Documents list hook
|
||||||
export function useTrusteePositionDocuments() {
|
export function useTrusteePositionDocuments() {
|
||||||
|
const instanceId = useInstanceId();
|
||||||
|
|
||||||
const [positionDocuments, setPositionDocuments] = useState<TrusteePositionDocument[]>([]);
|
const [positionDocuments, setPositionDocuments] = useState<TrusteePositionDocument[]>([]);
|
||||||
const [attributes, setAttributes] = useState<AttributeDefinition[]>([]);
|
const [attributes, setAttributes] = useState<AttributeDefinition[]>([]);
|
||||||
const [permissions, setPermissions] = useState<UserPermissions | null>(null);
|
const [permissions, setPermissions] = useState<UserPermissions | null>(null);
|
||||||
|
|
@ -32,8 +55,10 @@ export function useTrusteePositionDocuments() {
|
||||||
|
|
||||||
// Fetch attributes from backend
|
// Fetch attributes from backend
|
||||||
const fetchAttributes = useCallback(async () => {
|
const fetchAttributes = useCallback(async () => {
|
||||||
|
if (!instanceId) return [];
|
||||||
|
|
||||||
try {
|
try {
|
||||||
const response = await api.get('/api/attributes/TrusteePositionDocument');
|
const response = await api.get(`/api/trustee/${instanceId}/attributes/TrusteePositionDocument`);
|
||||||
|
|
||||||
let attrs: AttributeDefinition[] = [];
|
let attrs: AttributeDefinition[] = [];
|
||||||
if (response.data?.attributes && Array.isArray(response.data.attributes)) {
|
if (response.data?.attributes && Array.isArray(response.data.attributes)) {
|
||||||
|
|
@ -57,12 +82,13 @@ export function useTrusteePositionDocuments() {
|
||||||
setAttributes([]);
|
setAttributes([]);
|
||||||
return [];
|
return [];
|
||||||
}
|
}
|
||||||
}, []);
|
}, [instanceId]);
|
||||||
|
|
||||||
// Fetch permissions from backend
|
// Fetch permissions from backend
|
||||||
const fetchPermissions = useCallback(async () => {
|
const fetchPermissions = useCallback(async () => {
|
||||||
try {
|
try {
|
||||||
const perms = await checkPermission('DATA', 'trustee.xpositiondocument');
|
const objectKey = 'data.feature.trustee.TrusteePositionDocument';
|
||||||
|
const perms = await checkPermission('DATA', objectKey);
|
||||||
setPermissions(perms);
|
setPermissions(perms);
|
||||||
return perms;
|
return perms;
|
||||||
} catch (error: any) {
|
} catch (error: any) {
|
||||||
|
|
@ -80,8 +106,13 @@ export function useTrusteePositionDocuments() {
|
||||||
}, [checkPermission]);
|
}, [checkPermission]);
|
||||||
|
|
||||||
const fetchPositionDocuments = useCallback(async (params?: PaginationParams) => {
|
const fetchPositionDocuments = useCallback(async (params?: PaginationParams) => {
|
||||||
|
if (!instanceId) {
|
||||||
|
setPositionDocuments([]);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
try {
|
try {
|
||||||
const data = await fetchPositionDocumentsApi(request, params);
|
const data = await fetchPositionDocumentsApi(request, instanceId, params);
|
||||||
|
|
||||||
if (data && typeof data === 'object' && 'items' in data) {
|
if (data && typeof data === 'object' && 'items' in data) {
|
||||||
const items = Array.isArray(data.items) ? data.items : [];
|
const items = Array.isArray(data.items) ? data.items : [];
|
||||||
|
|
@ -98,7 +129,7 @@ export function useTrusteePositionDocuments() {
|
||||||
setPositionDocuments([]);
|
setPositionDocuments([]);
|
||||||
setPagination(null);
|
setPagination(null);
|
||||||
}
|
}
|
||||||
}, [request]);
|
}, [request, instanceId]);
|
||||||
|
|
||||||
// Optimistically remove a position-document link
|
// Optimistically remove a position-document link
|
||||||
const removeOptimistically = (positionDocumentId: string) => {
|
const removeOptimistically = (positionDocumentId: string) => {
|
||||||
|
|
@ -107,8 +138,9 @@ export function useTrusteePositionDocuments() {
|
||||||
|
|
||||||
// Fetch a single position-document by ID
|
// Fetch a single position-document by ID
|
||||||
const fetchPositionDocumentById = useCallback(async (positionDocumentId: string): Promise<TrusteePositionDocument | null> => {
|
const fetchPositionDocumentById = useCallback(async (positionDocumentId: string): Promise<TrusteePositionDocument | null> => {
|
||||||
return await fetchPositionDocumentByIdApi(request, positionDocumentId);
|
if (!instanceId) return null;
|
||||||
}, [request]);
|
return await fetchPositionDocumentByIdApi(request, instanceId, positionDocumentId);
|
||||||
|
}, [request, instanceId]);
|
||||||
|
|
||||||
// Generate edit fields from attributes dynamically
|
// Generate edit fields from attributes dynamically
|
||||||
const generateEditFieldsFromAttributes = useCallback((): Array<{
|
const generateEditFieldsFromAttributes = useCallback((): Array<{
|
||||||
|
|
@ -149,7 +181,7 @@ export function useTrusteePositionDocuments() {
|
||||||
} else if (attr.type === 'select') {
|
} else if (attr.type === 'select') {
|
||||||
fieldType = 'enum';
|
fieldType = 'enum';
|
||||||
if (Array.isArray(attr.options)) {
|
if (Array.isArray(attr.options)) {
|
||||||
options = attr.options.map(opt => {
|
options = attr.options.map((opt: any) => {
|
||||||
const labelValue = typeof opt.label === 'string'
|
const labelValue = typeof opt.label === 'string'
|
||||||
? opt.label
|
? opt.label
|
||||||
: opt.label?.en || opt.label?.[Object.keys(opt.label)[0]] || String(opt.value);
|
: opt.label?.en || opt.label?.[Object.keys(opt.label)[0]] || String(opt.value);
|
||||||
|
|
@ -212,16 +244,14 @@ export function useTrusteePositionDocuments() {
|
||||||
return fetchedAttributes;
|
return fetchedAttributes;
|
||||||
}, [attributes, fetchAttributes]);
|
}, [attributes, fetchAttributes]);
|
||||||
|
|
||||||
// Fetch attributes and permissions on mount
|
// Fetch data when instanceId is available
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
fetchAttributes();
|
if (instanceId) {
|
||||||
fetchPermissions();
|
fetchAttributes();
|
||||||
}, [fetchAttributes, fetchPermissions]);
|
fetchPermissions();
|
||||||
|
fetchPositionDocuments();
|
||||||
// Initial fetch
|
}
|
||||||
useEffect(() => {
|
}, [instanceId, fetchAttributes, fetchPermissions, fetchPositionDocuments]);
|
||||||
fetchPositionDocuments();
|
|
||||||
}, [fetchPositionDocuments]);
|
|
||||||
|
|
||||||
return {
|
return {
|
||||||
positionDocuments,
|
positionDocuments,
|
||||||
|
|
@ -234,12 +264,15 @@ export function useTrusteePositionDocuments() {
|
||||||
pagination,
|
pagination,
|
||||||
fetchPositionDocumentById,
|
fetchPositionDocumentById,
|
||||||
generateEditFieldsFromAttributes,
|
generateEditFieldsFromAttributes,
|
||||||
ensureAttributesLoaded
|
ensureAttributesLoaded,
|
||||||
|
instanceId
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
// Position-Document operations hook
|
// Position-Document operations hook
|
||||||
export function useTrusteePositionDocumentOperations() {
|
export function useTrusteePositionDocumentOperations() {
|
||||||
|
const instanceId = useInstanceId();
|
||||||
|
|
||||||
const [deletingPositionDocuments, setDeletingPositionDocuments] = useState<Set<string>>(new Set());
|
const [deletingPositionDocuments, setDeletingPositionDocuments] = useState<Set<string>>(new Set());
|
||||||
const [creatingPositionDocument, setCreatingPositionDocument] = useState(false);
|
const [creatingPositionDocument, setCreatingPositionDocument] = useState(false);
|
||||||
const { request, isLoading } = useApiRequest();
|
const { request, isLoading } = useApiRequest();
|
||||||
|
|
@ -247,11 +280,16 @@ export function useTrusteePositionDocumentOperations() {
|
||||||
const [createError, setCreateError] = useState<string | null>(null);
|
const [createError, setCreateError] = useState<string | null>(null);
|
||||||
|
|
||||||
const handlePositionDocumentDelete = async (positionDocumentId: string) => {
|
const handlePositionDocumentDelete = async (positionDocumentId: string) => {
|
||||||
|
if (!instanceId) {
|
||||||
|
setDeleteError('No instance context');
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
setDeleteError(null);
|
setDeleteError(null);
|
||||||
setDeletingPositionDocuments(prev => new Set(prev).add(positionDocumentId));
|
setDeletingPositionDocuments(prev => new Set(prev).add(positionDocumentId));
|
||||||
|
|
||||||
try {
|
try {
|
||||||
await deletePositionDocumentApi(request, positionDocumentId);
|
await deletePositionDocumentApi(request, instanceId, positionDocumentId);
|
||||||
await new Promise(resolve => setTimeout(resolve, 300));
|
await new Promise(resolve => setTimeout(resolve, 300));
|
||||||
return true;
|
return true;
|
||||||
} catch (error: any) {
|
} catch (error: any) {
|
||||||
|
|
@ -267,20 +305,16 @@ export function useTrusteePositionDocumentOperations() {
|
||||||
};
|
};
|
||||||
|
|
||||||
const handlePositionDocumentCreate = async (positionDocumentData: Partial<TrusteePositionDocument>) => {
|
const handlePositionDocumentCreate = async (positionDocumentData: Partial<TrusteePositionDocument>) => {
|
||||||
|
if (!instanceId) {
|
||||||
|
setCreateError('No instance context');
|
||||||
|
return { success: false, error: 'No instance context' };
|
||||||
|
}
|
||||||
|
|
||||||
setCreateError(null);
|
setCreateError(null);
|
||||||
setCreatingPositionDocument(true);
|
setCreatingPositionDocument(true);
|
||||||
|
|
||||||
try {
|
try {
|
||||||
const currentUserData = getUserDataCache();
|
const newPositionDocument = await createPositionDocumentApi(request, instanceId, positionDocumentData);
|
||||||
const mandateId = currentUserData?.mandateId || '';
|
|
||||||
|
|
||||||
const requestBody = {
|
|
||||||
...positionDocumentData,
|
|
||||||
mandate: mandateId
|
|
||||||
};
|
|
||||||
|
|
||||||
const newPositionDocument = await createPositionDocumentApi(request, requestBody);
|
|
||||||
|
|
||||||
return { success: true, positionDocumentData: newPositionDocument };
|
return { success: true, positionDocumentData: newPositionDocument };
|
||||||
} catch (error: any) {
|
} catch (error: any) {
|
||||||
setCreateError(error.message);
|
setCreateError(error.message);
|
||||||
|
|
@ -297,6 +331,7 @@ export function useTrusteePositionDocumentOperations() {
|
||||||
createError,
|
createError,
|
||||||
handlePositionDocumentDelete,
|
handlePositionDocumentDelete,
|
||||||
handlePositionDocumentCreate,
|
handlePositionDocumentCreate,
|
||||||
isLoading
|
isLoading,
|
||||||
|
instanceId
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,8 +1,8 @@
|
||||||
import { useState, useEffect, useCallback } from 'react';
|
import { useState, useEffect, useCallback } from 'react';
|
||||||
import { useApiRequest } from './useApi';
|
import { useApiRequest } from './useApi';
|
||||||
import { getUserDataCache } from '../utils/userCache';
|
|
||||||
import api from '../api';
|
import api from '../api';
|
||||||
import { usePermissions, type UserPermissions } from './usePermissions';
|
import { usePermissions, type UserPermissions } from './usePermissions';
|
||||||
|
import { useInstanceId } from './useCurrentInstance';
|
||||||
import {
|
import {
|
||||||
fetchPositions as fetchPositionsApi,
|
fetchPositions as fetchPositionsApi,
|
||||||
fetchPositionById as fetchPositionByIdApi,
|
fetchPositionById as fetchPositionByIdApi,
|
||||||
|
|
@ -10,15 +10,38 @@ import {
|
||||||
updatePosition as updatePositionApi,
|
updatePosition as updatePositionApi,
|
||||||
deletePosition as deletePositionApi,
|
deletePosition as deletePositionApi,
|
||||||
type TrusteePosition,
|
type TrusteePosition,
|
||||||
type AttributeDefinition,
|
|
||||||
type PaginationParams
|
type PaginationParams
|
||||||
} from '../api/trusteeApi';
|
} from '../api/trusteeApi';
|
||||||
|
|
||||||
|
export interface AttributeDefinition {
|
||||||
|
name: string;
|
||||||
|
type: 'text' | 'email' | 'date' | 'checkbox' | 'select' | 'multiselect' | 'number' | 'textarea' | 'timestamp' | 'file';
|
||||||
|
label: string;
|
||||||
|
description?: string;
|
||||||
|
required?: boolean;
|
||||||
|
default?: any;
|
||||||
|
options?: any[] | string;
|
||||||
|
readonly?: boolean;
|
||||||
|
editable?: boolean;
|
||||||
|
visible?: boolean;
|
||||||
|
order?: number;
|
||||||
|
sortable?: boolean;
|
||||||
|
filterable?: boolean;
|
||||||
|
searchable?: boolean;
|
||||||
|
width?: number;
|
||||||
|
minWidth?: number;
|
||||||
|
maxWidth?: number;
|
||||||
|
filterOptions?: string[];
|
||||||
|
dependsOn?: string;
|
||||||
|
}
|
||||||
|
|
||||||
// Re-export types
|
// Re-export types
|
||||||
export type { TrusteePosition, AttributeDefinition, PaginationParams };
|
export type { TrusteePosition, PaginationParams };
|
||||||
|
|
||||||
// Positions list hook
|
// Positions list hook
|
||||||
export function useTrusteePositions() {
|
export function useTrusteePositions() {
|
||||||
|
const instanceId = useInstanceId();
|
||||||
|
|
||||||
const [positions, setPositions] = useState<TrusteePosition[]>([]);
|
const [positions, setPositions] = useState<TrusteePosition[]>([]);
|
||||||
const [attributes, setAttributes] = useState<AttributeDefinition[]>([]);
|
const [attributes, setAttributes] = useState<AttributeDefinition[]>([]);
|
||||||
const [permissions, setPermissions] = useState<UserPermissions | null>(null);
|
const [permissions, setPermissions] = useState<UserPermissions | null>(null);
|
||||||
|
|
@ -33,8 +56,10 @@ export function useTrusteePositions() {
|
||||||
|
|
||||||
// Fetch attributes from backend
|
// Fetch attributes from backend
|
||||||
const fetchAttributes = useCallback(async () => {
|
const fetchAttributes = useCallback(async () => {
|
||||||
|
if (!instanceId) return [];
|
||||||
|
|
||||||
try {
|
try {
|
||||||
const response = await api.get('/api/attributes/TrusteePosition');
|
const response = await api.get(`/api/trustee/${instanceId}/attributes/TrusteePosition`);
|
||||||
|
|
||||||
let attrs: AttributeDefinition[] = [];
|
let attrs: AttributeDefinition[] = [];
|
||||||
if (response.data?.attributes && Array.isArray(response.data.attributes)) {
|
if (response.data?.attributes && Array.isArray(response.data.attributes)) {
|
||||||
|
|
@ -58,12 +83,13 @@ export function useTrusteePositions() {
|
||||||
setAttributes([]);
|
setAttributes([]);
|
||||||
return [];
|
return [];
|
||||||
}
|
}
|
||||||
}, []);
|
}, [instanceId]);
|
||||||
|
|
||||||
// Fetch permissions from backend
|
// Fetch permissions from backend
|
||||||
const fetchPermissions = useCallback(async () => {
|
const fetchPermissions = useCallback(async () => {
|
||||||
try {
|
try {
|
||||||
const perms = await checkPermission('DATA', 'trustee.position');
|
const objectKey = 'data.feature.trustee.TrusteePosition';
|
||||||
|
const perms = await checkPermission('DATA', objectKey);
|
||||||
setPermissions(perms);
|
setPermissions(perms);
|
||||||
return perms;
|
return perms;
|
||||||
} catch (error: any) {
|
} catch (error: any) {
|
||||||
|
|
@ -81,8 +107,13 @@ export function useTrusteePositions() {
|
||||||
}, [checkPermission]);
|
}, [checkPermission]);
|
||||||
|
|
||||||
const fetchPositions = useCallback(async (params?: PaginationParams) => {
|
const fetchPositions = useCallback(async (params?: PaginationParams) => {
|
||||||
|
if (!instanceId) {
|
||||||
|
setPositions([]);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
try {
|
try {
|
||||||
const data = await fetchPositionsApi(request, params);
|
const data = await fetchPositionsApi(request, instanceId, params);
|
||||||
|
|
||||||
if (data && typeof data === 'object' && 'items' in data) {
|
if (data && typeof data === 'object' && 'items' in data) {
|
||||||
const items = Array.isArray(data.items) ? data.items : [];
|
const items = Array.isArray(data.items) ? data.items : [];
|
||||||
|
|
@ -99,7 +130,7 @@ export function useTrusteePositions() {
|
||||||
setPositions([]);
|
setPositions([]);
|
||||||
setPagination(null);
|
setPagination(null);
|
||||||
}
|
}
|
||||||
}, [request]);
|
}, [request, instanceId]);
|
||||||
|
|
||||||
// Optimistically remove a position
|
// Optimistically remove a position
|
||||||
const removeOptimistically = (positionId: string) => {
|
const removeOptimistically = (positionId: string) => {
|
||||||
|
|
@ -119,8 +150,9 @@ export function useTrusteePositions() {
|
||||||
|
|
||||||
// Fetch a single position by ID
|
// Fetch a single position by ID
|
||||||
const fetchPositionById = useCallback(async (positionId: string): Promise<TrusteePosition | null> => {
|
const fetchPositionById = useCallback(async (positionId: string): Promise<TrusteePosition | null> => {
|
||||||
return await fetchPositionByIdApi(request, positionId);
|
if (!instanceId) return null;
|
||||||
}, [request]);
|
return await fetchPositionByIdApi(request, instanceId, positionId);
|
||||||
|
}, [request, instanceId]);
|
||||||
|
|
||||||
// Generate edit fields from attributes dynamically with MwSt calculation logic
|
// Generate edit fields from attributes dynamically with MwSt calculation logic
|
||||||
const generateEditFieldsFromAttributes = useCallback((): Array<{
|
const generateEditFieldsFromAttributes = useCallback((): Array<{
|
||||||
|
|
@ -175,7 +207,7 @@ export function useTrusteePositions() {
|
||||||
} else if (attr.type === 'select') {
|
} else if (attr.type === 'select') {
|
||||||
fieldType = 'enum';
|
fieldType = 'enum';
|
||||||
if (Array.isArray(attr.options)) {
|
if (Array.isArray(attr.options)) {
|
||||||
options = attr.options.map(opt => {
|
options = attr.options.map((opt: any) => {
|
||||||
const labelValue = typeof opt.label === 'string'
|
const labelValue = typeof opt.label === 'string'
|
||||||
? opt.label
|
? opt.label
|
||||||
: opt.label?.en || opt.label?.[Object.keys(opt.label)[0]] || String(opt.value);
|
: opt.label?.en || opt.label?.[Object.keys(opt.label)[0]] || String(opt.value);
|
||||||
|
|
@ -288,16 +320,14 @@ export function useTrusteePositions() {
|
||||||
return fetchedAttributes;
|
return fetchedAttributes;
|
||||||
}, [attributes, fetchAttributes]);
|
}, [attributes, fetchAttributes]);
|
||||||
|
|
||||||
// Fetch attributes and permissions on mount
|
// Fetch data when instanceId is available
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
fetchAttributes();
|
if (instanceId) {
|
||||||
fetchPermissions();
|
fetchAttributes();
|
||||||
}, [fetchAttributes, fetchPermissions]);
|
fetchPermissions();
|
||||||
|
fetchPositions();
|
||||||
// Initial fetch
|
}
|
||||||
useEffect(() => {
|
}, [instanceId, fetchAttributes, fetchPermissions, fetchPositions]);
|
||||||
fetchPositions();
|
|
||||||
}, [fetchPositions]);
|
|
||||||
|
|
||||||
return {
|
return {
|
||||||
positions,
|
positions,
|
||||||
|
|
@ -311,12 +341,15 @@ export function useTrusteePositions() {
|
||||||
pagination,
|
pagination,
|
||||||
fetchPositionById,
|
fetchPositionById,
|
||||||
generateEditFieldsFromAttributes,
|
generateEditFieldsFromAttributes,
|
||||||
ensureAttributesLoaded
|
ensureAttributesLoaded,
|
||||||
|
instanceId
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
// Position operations hook
|
// Position operations hook
|
||||||
export function useTrusteePositionOperations() {
|
export function useTrusteePositionOperations() {
|
||||||
|
const instanceId = useInstanceId();
|
||||||
|
|
||||||
const [deletingPositions, setDeletingPositions] = useState<Set<string>>(new Set());
|
const [deletingPositions, setDeletingPositions] = useState<Set<string>>(new Set());
|
||||||
const [creatingPosition, setCreatingPosition] = useState(false);
|
const [creatingPosition, setCreatingPosition] = useState(false);
|
||||||
const { request, isLoading } = useApiRequest();
|
const { request, isLoading } = useApiRequest();
|
||||||
|
|
@ -325,11 +358,16 @@ export function useTrusteePositionOperations() {
|
||||||
const [updateError, setUpdateError] = useState<string | null>(null);
|
const [updateError, setUpdateError] = useState<string | null>(null);
|
||||||
|
|
||||||
const handlePositionDelete = async (positionId: string) => {
|
const handlePositionDelete = async (positionId: string) => {
|
||||||
|
if (!instanceId) {
|
||||||
|
setDeleteError('No instance context');
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
setDeleteError(null);
|
setDeleteError(null);
|
||||||
setDeletingPositions(prev => new Set(prev).add(positionId));
|
setDeletingPositions(prev => new Set(prev).add(positionId));
|
||||||
|
|
||||||
try {
|
try {
|
||||||
await deletePositionApi(request, positionId);
|
await deletePositionApi(request, instanceId, positionId);
|
||||||
await new Promise(resolve => setTimeout(resolve, 300));
|
await new Promise(resolve => setTimeout(resolve, 300));
|
||||||
return true;
|
return true;
|
||||||
} catch (error: any) {
|
} catch (error: any) {
|
||||||
|
|
@ -345,20 +383,16 @@ export function useTrusteePositionOperations() {
|
||||||
};
|
};
|
||||||
|
|
||||||
const handlePositionCreate = async (positionData: Partial<TrusteePosition>) => {
|
const handlePositionCreate = async (positionData: Partial<TrusteePosition>) => {
|
||||||
|
if (!instanceId) {
|
||||||
|
setCreateError('No instance context');
|
||||||
|
return { success: false, error: 'No instance context' };
|
||||||
|
}
|
||||||
|
|
||||||
setCreateError(null);
|
setCreateError(null);
|
||||||
setCreatingPosition(true);
|
setCreatingPosition(true);
|
||||||
|
|
||||||
try {
|
try {
|
||||||
const currentUserData = getUserDataCache();
|
const newPosition = await createPositionApi(request, instanceId, positionData);
|
||||||
const mandateId = currentUserData?.mandateId || '';
|
|
||||||
|
|
||||||
const requestBody = {
|
|
||||||
...positionData,
|
|
||||||
mandate: mandateId
|
|
||||||
};
|
|
||||||
|
|
||||||
const newPosition = await createPositionApi(request, requestBody);
|
|
||||||
|
|
||||||
return { success: true, positionData: newPosition };
|
return { success: true, positionData: newPosition };
|
||||||
} catch (error: any) {
|
} catch (error: any) {
|
||||||
setCreateError(error.message);
|
setCreateError(error.message);
|
||||||
|
|
@ -373,19 +407,15 @@ export function useTrusteePositionOperations() {
|
||||||
updateData: Partial<TrusteePosition>,
|
updateData: Partial<TrusteePosition>,
|
||||||
_originalData?: any
|
_originalData?: any
|
||||||
) => {
|
) => {
|
||||||
|
if (!instanceId) {
|
||||||
|
setUpdateError('No instance context');
|
||||||
|
return { success: false, error: 'No instance context' };
|
||||||
|
}
|
||||||
|
|
||||||
setUpdateError(null);
|
setUpdateError(null);
|
||||||
|
|
||||||
try {
|
try {
|
||||||
const currentUserData = getUserDataCache();
|
const updatedPosition = await updatePositionApi(request, instanceId, positionId, updateData);
|
||||||
const mandateId = currentUserData?.mandateId || '';
|
|
||||||
|
|
||||||
const requestBody = {
|
|
||||||
...updateData,
|
|
||||||
mandate: mandateId
|
|
||||||
};
|
|
||||||
|
|
||||||
const updatedPosition = await updatePositionApi(request, positionId, requestBody);
|
|
||||||
|
|
||||||
return { success: true, positionData: updatedPosition };
|
return { success: true, positionData: updatedPosition };
|
||||||
} catch (error: any) {
|
} catch (error: any) {
|
||||||
const errorMessage = error.response?.data?.message || error.message || 'Failed to update position';
|
const errorMessage = error.response?.data?.message || error.message || 'Failed to update position';
|
||||||
|
|
@ -412,6 +442,7 @@ export function useTrusteePositionOperations() {
|
||||||
handlePositionDelete,
|
handlePositionDelete,
|
||||||
handlePositionCreate,
|
handlePositionCreate,
|
||||||
handlePositionUpdate,
|
handlePositionUpdate,
|
||||||
isLoading
|
isLoading,
|
||||||
|
instanceId
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,8 +1,8 @@
|
||||||
import { useState, useEffect, useCallback } from 'react';
|
import { useState, useEffect, useCallback } from 'react';
|
||||||
import { useApiRequest } from './useApi';
|
import { useApiRequest } from './useApi';
|
||||||
import { getUserDataCache } from '../utils/userCache';
|
|
||||||
import api from '../api';
|
import api from '../api';
|
||||||
import { usePermissions, type UserPermissions } from './usePermissions';
|
import { usePermissions, type UserPermissions } from './usePermissions';
|
||||||
|
import { useInstanceId } from './useCurrentInstance';
|
||||||
import {
|
import {
|
||||||
fetchRoles as fetchRolesApi,
|
fetchRoles as fetchRolesApi,
|
||||||
fetchRoleById as fetchRoleByIdApi,
|
fetchRoleById as fetchRoleByIdApi,
|
||||||
|
|
@ -10,15 +10,38 @@ import {
|
||||||
updateRole as updateRoleApi,
|
updateRole as updateRoleApi,
|
||||||
deleteRole as deleteRoleApi,
|
deleteRole as deleteRoleApi,
|
||||||
type TrusteeRole,
|
type TrusteeRole,
|
||||||
type AttributeDefinition,
|
|
||||||
type PaginationParams
|
type PaginationParams
|
||||||
} from '../api/trusteeApi';
|
} from '../api/trusteeApi';
|
||||||
|
|
||||||
|
export interface AttributeDefinition {
|
||||||
|
name: string;
|
||||||
|
type: 'text' | 'email' | 'date' | 'checkbox' | 'select' | 'multiselect' | 'number' | 'textarea' | 'timestamp' | 'file';
|
||||||
|
label: string;
|
||||||
|
description?: string;
|
||||||
|
required?: boolean;
|
||||||
|
default?: any;
|
||||||
|
options?: any[] | string;
|
||||||
|
readonly?: boolean;
|
||||||
|
editable?: boolean;
|
||||||
|
visible?: boolean;
|
||||||
|
order?: number;
|
||||||
|
sortable?: boolean;
|
||||||
|
filterable?: boolean;
|
||||||
|
searchable?: boolean;
|
||||||
|
width?: number;
|
||||||
|
minWidth?: number;
|
||||||
|
maxWidth?: number;
|
||||||
|
filterOptions?: string[];
|
||||||
|
dependsOn?: string;
|
||||||
|
}
|
||||||
|
|
||||||
// Re-export types
|
// Re-export types
|
||||||
export type { TrusteeRole, AttributeDefinition, PaginationParams };
|
export type { TrusteeRole, PaginationParams };
|
||||||
|
|
||||||
// Roles list hook
|
// Roles list hook
|
||||||
export function useTrusteeRoles() {
|
export function useTrusteeRoles() {
|
||||||
|
const instanceId = useInstanceId();
|
||||||
|
|
||||||
const [roles, setRoles] = useState<TrusteeRole[]>([]);
|
const [roles, setRoles] = useState<TrusteeRole[]>([]);
|
||||||
const [attributes, setAttributes] = useState<AttributeDefinition[]>([]);
|
const [attributes, setAttributes] = useState<AttributeDefinition[]>([]);
|
||||||
const [permissions, setPermissions] = useState<UserPermissions | null>(null);
|
const [permissions, setPermissions] = useState<UserPermissions | null>(null);
|
||||||
|
|
@ -33,8 +56,10 @@ export function useTrusteeRoles() {
|
||||||
|
|
||||||
// Fetch attributes from backend
|
// Fetch attributes from backend
|
||||||
const fetchAttributes = useCallback(async () => {
|
const fetchAttributes = useCallback(async () => {
|
||||||
|
if (!instanceId) return [];
|
||||||
|
|
||||||
try {
|
try {
|
||||||
const response = await api.get('/api/attributes/TrusteeRole');
|
const response = await api.get(`/api/trustee/${instanceId}/attributes/TrusteeRole`);
|
||||||
|
|
||||||
let attrs: AttributeDefinition[] = [];
|
let attrs: AttributeDefinition[] = [];
|
||||||
if (response.data?.attributes && Array.isArray(response.data.attributes)) {
|
if (response.data?.attributes && Array.isArray(response.data.attributes)) {
|
||||||
|
|
@ -58,12 +83,13 @@ export function useTrusteeRoles() {
|
||||||
setAttributes([]);
|
setAttributes([]);
|
||||||
return [];
|
return [];
|
||||||
}
|
}
|
||||||
}, []);
|
}, [instanceId]);
|
||||||
|
|
||||||
// Fetch permissions from backend
|
// Fetch permissions from backend
|
||||||
const fetchPermissions = useCallback(async () => {
|
const fetchPermissions = useCallback(async () => {
|
||||||
try {
|
try {
|
||||||
const perms = await checkPermission('DATA', 'trustee.role');
|
const objectKey = 'data.feature.trustee.TrusteeRole';
|
||||||
|
const perms = await checkPermission('DATA', objectKey);
|
||||||
setPermissions(perms);
|
setPermissions(perms);
|
||||||
return perms;
|
return perms;
|
||||||
} catch (error: any) {
|
} catch (error: any) {
|
||||||
|
|
@ -81,8 +107,13 @@ export function useTrusteeRoles() {
|
||||||
}, [checkPermission]);
|
}, [checkPermission]);
|
||||||
|
|
||||||
const fetchRoles = useCallback(async (params?: PaginationParams) => {
|
const fetchRoles = useCallback(async (params?: PaginationParams) => {
|
||||||
|
if (!instanceId) {
|
||||||
|
setRoles([]);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
try {
|
try {
|
||||||
const data = await fetchRolesApi(request, params);
|
const data = await fetchRolesApi(request, instanceId, params);
|
||||||
|
|
||||||
if (data && typeof data === 'object' && 'items' in data) {
|
if (data && typeof data === 'object' && 'items' in data) {
|
||||||
const items = Array.isArray(data.items) ? data.items : [];
|
const items = Array.isArray(data.items) ? data.items : [];
|
||||||
|
|
@ -99,7 +130,7 @@ export function useTrusteeRoles() {
|
||||||
setRoles([]);
|
setRoles([]);
|
||||||
setPagination(null);
|
setPagination(null);
|
||||||
}
|
}
|
||||||
}, [request]);
|
}, [request, instanceId]);
|
||||||
|
|
||||||
// Optimistically remove a role
|
// Optimistically remove a role
|
||||||
const removeOptimistically = (roleId: string) => {
|
const removeOptimistically = (roleId: string) => {
|
||||||
|
|
@ -119,30 +150,11 @@ export function useTrusteeRoles() {
|
||||||
|
|
||||||
// Fetch a single role by ID
|
// Fetch a single role by ID
|
||||||
const fetchRoleById = useCallback(async (roleId: string): Promise<TrusteeRole | null> => {
|
const fetchRoleById = useCallback(async (roleId: string): Promise<TrusteeRole | null> => {
|
||||||
return await fetchRoleByIdApi(request, roleId);
|
if (!instanceId) return null;
|
||||||
}, [request]);
|
return await fetchRoleByIdApi(request, instanceId, roleId);
|
||||||
|
}, [request, instanceId]);
|
||||||
|
|
||||||
// Generate edit fields from attributes dynamically
|
// Generate edit fields from attributes dynamically
|
||||||
// Ensure attributes are loaded
|
|
||||||
const ensureAttributesLoaded = useCallback(async () => {
|
|
||||||
if (attributes && attributes.length > 0) {
|
|
||||||
return attributes;
|
|
||||||
}
|
|
||||||
const fetchedAttributes = await fetchAttributes();
|
|
||||||
return fetchedAttributes;
|
|
||||||
}, [attributes, fetchAttributes]);
|
|
||||||
|
|
||||||
// Fetch attributes and permissions on mount
|
|
||||||
useEffect(() => {
|
|
||||||
fetchAttributes();
|
|
||||||
fetchPermissions();
|
|
||||||
}, [fetchAttributes, fetchPermissions]);
|
|
||||||
|
|
||||||
// Initial fetch
|
|
||||||
useEffect(() => {
|
|
||||||
fetchRoles();
|
|
||||||
}, [fetchRoles]);
|
|
||||||
|
|
||||||
const generateEditFieldsFromAttributes = useCallback((): Array<{
|
const generateEditFieldsFromAttributes = useCallback((): Array<{
|
||||||
key: string;
|
key: string;
|
||||||
label: string;
|
label: string;
|
||||||
|
|
@ -189,7 +201,7 @@ export function useTrusteeRoles() {
|
||||||
} else if (attr.type === 'select') {
|
} else if (attr.type === 'select') {
|
||||||
fieldType = 'enum';
|
fieldType = 'enum';
|
||||||
if (Array.isArray(attr.options)) {
|
if (Array.isArray(attr.options)) {
|
||||||
options = attr.options.map(opt => {
|
options = attr.options.map((opt: any) => {
|
||||||
const labelValue = typeof opt.label === 'string'
|
const labelValue = typeof opt.label === 'string'
|
||||||
? opt.label
|
? opt.label
|
||||||
: opt.label?.en || opt.label?.[Object.keys(opt.label)[0]] || String(opt.value);
|
: opt.label?.en || opt.label?.[Object.keys(opt.label)[0]] || String(opt.value);
|
||||||
|
|
@ -248,6 +260,24 @@ export function useTrusteeRoles() {
|
||||||
return editableFields;
|
return editableFields;
|
||||||
}, [attributes]);
|
}, [attributes]);
|
||||||
|
|
||||||
|
// Ensure attributes are loaded
|
||||||
|
const ensureAttributesLoaded = useCallback(async () => {
|
||||||
|
if (attributes && attributes.length > 0) {
|
||||||
|
return attributes;
|
||||||
|
}
|
||||||
|
const fetchedAttributes = await fetchAttributes();
|
||||||
|
return fetchedAttributes;
|
||||||
|
}, [attributes, fetchAttributes]);
|
||||||
|
|
||||||
|
// Fetch data when instanceId is available
|
||||||
|
useEffect(() => {
|
||||||
|
if (instanceId) {
|
||||||
|
fetchAttributes();
|
||||||
|
fetchPermissions();
|
||||||
|
fetchRoles();
|
||||||
|
}
|
||||||
|
}, [instanceId, fetchAttributes, fetchPermissions, fetchRoles]);
|
||||||
|
|
||||||
return {
|
return {
|
||||||
roles,
|
roles,
|
||||||
loading,
|
loading,
|
||||||
|
|
@ -260,12 +290,15 @@ export function useTrusteeRoles() {
|
||||||
pagination,
|
pagination,
|
||||||
fetchRoleById,
|
fetchRoleById,
|
||||||
generateEditFieldsFromAttributes,
|
generateEditFieldsFromAttributes,
|
||||||
ensureAttributesLoaded
|
ensureAttributesLoaded,
|
||||||
|
instanceId
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
// Role operations hook
|
// Role operations hook
|
||||||
export function useTrusteeRoleOperations() {
|
export function useTrusteeRoleOperations() {
|
||||||
|
const instanceId = useInstanceId();
|
||||||
|
|
||||||
const [deletingRoles, setDeletingRoles] = useState<Set<string>>(new Set());
|
const [deletingRoles, setDeletingRoles] = useState<Set<string>>(new Set());
|
||||||
const [creatingRole, setCreatingRole] = useState(false);
|
const [creatingRole, setCreatingRole] = useState(false);
|
||||||
const { request, isLoading } = useApiRequest();
|
const { request, isLoading } = useApiRequest();
|
||||||
|
|
@ -274,11 +307,16 @@ export function useTrusteeRoleOperations() {
|
||||||
const [updateError, setUpdateError] = useState<string | null>(null);
|
const [updateError, setUpdateError] = useState<string | null>(null);
|
||||||
|
|
||||||
const handleRoleDelete = async (roleId: string) => {
|
const handleRoleDelete = async (roleId: string) => {
|
||||||
|
if (!instanceId) {
|
||||||
|
setDeleteError('No instance context');
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
setDeleteError(null);
|
setDeleteError(null);
|
||||||
setDeletingRoles(prev => new Set(prev).add(roleId));
|
setDeletingRoles(prev => new Set(prev).add(roleId));
|
||||||
|
|
||||||
try {
|
try {
|
||||||
await deleteRoleApi(request, roleId);
|
await deleteRoleApi(request, instanceId, roleId);
|
||||||
await new Promise(resolve => setTimeout(resolve, 300));
|
await new Promise(resolve => setTimeout(resolve, 300));
|
||||||
return true;
|
return true;
|
||||||
} catch (error: any) {
|
} catch (error: any) {
|
||||||
|
|
@ -296,20 +334,16 @@ export function useTrusteeRoleOperations() {
|
||||||
};
|
};
|
||||||
|
|
||||||
const handleRoleCreate = async (roleData: Partial<TrusteeRole>) => {
|
const handleRoleCreate = async (roleData: Partial<TrusteeRole>) => {
|
||||||
|
if (!instanceId) {
|
||||||
|
setCreateError('No instance context');
|
||||||
|
return { success: false, error: 'No instance context' };
|
||||||
|
}
|
||||||
|
|
||||||
setCreateError(null);
|
setCreateError(null);
|
||||||
setCreatingRole(true);
|
setCreatingRole(true);
|
||||||
|
|
||||||
try {
|
try {
|
||||||
const currentUserData = getUserDataCache();
|
const newRole = await createRoleApi(request, instanceId, roleData);
|
||||||
const mandateId = currentUserData?.mandateId || '';
|
|
||||||
|
|
||||||
const requestBody = {
|
|
||||||
...roleData,
|
|
||||||
mandate: mandateId
|
|
||||||
};
|
|
||||||
|
|
||||||
const newRole = await createRoleApi(request, requestBody);
|
|
||||||
|
|
||||||
return { success: true, roleData: newRole };
|
return { success: true, roleData: newRole };
|
||||||
} catch (error: any) {
|
} catch (error: any) {
|
||||||
setCreateError(error.message);
|
setCreateError(error.message);
|
||||||
|
|
@ -324,19 +358,15 @@ export function useTrusteeRoleOperations() {
|
||||||
updateData: Partial<TrusteeRole>,
|
updateData: Partial<TrusteeRole>,
|
||||||
_originalData?: any
|
_originalData?: any
|
||||||
) => {
|
) => {
|
||||||
|
if (!instanceId) {
|
||||||
|
setUpdateError('No instance context');
|
||||||
|
return { success: false, error: 'No instance context' };
|
||||||
|
}
|
||||||
|
|
||||||
setUpdateError(null);
|
setUpdateError(null);
|
||||||
|
|
||||||
try {
|
try {
|
||||||
const currentUserData = getUserDataCache();
|
const updatedRole = await updateRoleApi(request, instanceId, roleId, updateData);
|
||||||
const mandateId = currentUserData?.mandateId || '';
|
|
||||||
|
|
||||||
const requestBody = {
|
|
||||||
...updateData,
|
|
||||||
mandate: mandateId
|
|
||||||
};
|
|
||||||
|
|
||||||
const updatedRole = await updateRoleApi(request, roleId, requestBody);
|
|
||||||
|
|
||||||
return { success: true, roleData: updatedRole };
|
return { success: true, roleData: updatedRole };
|
||||||
} catch (error: any) {
|
} catch (error: any) {
|
||||||
const errorMessage = error.response?.data?.message || error.message || 'Failed to update role';
|
const errorMessage = error.response?.data?.message || error.message || 'Failed to update role';
|
||||||
|
|
@ -363,6 +393,7 @@ export function useTrusteeRoleOperations() {
|
||||||
handleRoleDelete,
|
handleRoleDelete,
|
||||||
handleRoleCreate,
|
handleRoleCreate,
|
||||||
handleRoleUpdate,
|
handleRoleUpdate,
|
||||||
isLoading
|
isLoading,
|
||||||
|
instanceId
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -11,7 +11,7 @@ export const TeamsbotSettingsView: React.FC = () => {
|
||||||
const { instance } = useCurrentInstance();
|
const { instance } = useCurrentInstance();
|
||||||
const instanceId = instance?.id || '';
|
const instanceId = instance?.id || '';
|
||||||
|
|
||||||
const [config, setConfig] = useState<TeamsbotConfig | null>(null);
|
const [_config, setConfig] = useState<TeamsbotConfig | null>(null);
|
||||||
const [loading, setLoading] = useState(true);
|
const [loading, setLoading] = useState(true);
|
||||||
const [saving, setSaving] = useState(false);
|
const [saving, setSaving] = useState(false);
|
||||||
const [error, setError] = useState<string | null>(null);
|
const [error, setError] = useState<string | null>(null);
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue