From 71666d28916e7f8a33871206f80413371f3b569a Mon Sep 17 00:00:00 2001
From: ValueOn AG
Date: Tue, 13 Jan 2026 21:17:27 +0100
Subject: [PATCH] hotfixes
---
src/api/connectionApi.ts | 14 ++--
.../FormGeneratorControls.tsx | 1 -
.../PageManager/data/pages/connections.ts | 70 ++++++++++++++-----
src/core/PageManager/data/pages/files.ts | 2 +-
src/core/PageManager/pageInterface.ts | 15 ++--
src/hooks/useTrustee.ts | 1 -
6 files changed, 72 insertions(+), 31 deletions(-)
diff --git a/src/api/connectionApi.ts b/src/api/connectionApi.ts
index 83e6dfc..cb1b83e 100644
--- a/src/api/connectionApi.ts
+++ b/src/api/connectionApi.ts
@@ -193,29 +193,31 @@ export async function updateConnection(
/**
* Refresh Microsoft token
- * Endpoint: POST /api/connections/{connectionId}/refresh-microsoft-token
+ * Endpoint: POST /api/msft/refresh
*/
export async function refreshMicrosoftToken(
request: ApiRequestFunction,
connectionId: string
): Promise {
return await request({
- url: `/api/connections/${connectionId}/refresh-microsoft-token`,
- method: 'post'
+ url: '/api/msft/refresh',
+ method: 'post',
+ data: { connectionId }
});
}
/**
* Refresh Google token
- * Endpoint: POST /api/connections/{connectionId}/refresh-google-token
+ * Endpoint: POST /api/google/refresh
*/
export async function refreshGoogleToken(
request: ApiRequestFunction,
connectionId: string
): Promise {
return await request({
- url: `/api/connections/${connectionId}/refresh-google-token`,
- method: 'post'
+ url: '/api/google/refresh',
+ method: 'post',
+ data: { connectionId }
});
}
diff --git a/src/components/FormGenerator/FormGeneratorControls/FormGeneratorControls.tsx b/src/components/FormGenerator/FormGeneratorControls/FormGeneratorControls.tsx
index 92b14a7..4439c77 100644
--- a/src/components/FormGenerator/FormGeneratorControls/FormGeneratorControls.tsx
+++ b/src/components/FormGenerator/FormGeneratorControls/FormGeneratorControls.tsx
@@ -1,4 +1,3 @@
-import React from 'react';
import { useLanguage } from '../../../providers/language/LanguageContext';
import styles from './FormGeneratorControls.module.css';
import { Button } from '../../UiComponents/Button';
diff --git a/src/core/PageManager/data/pages/connections.ts b/src/core/PageManager/data/pages/connections.ts
index cc2811d..3af8f48 100644
--- a/src/core/PageManager/data/pages/connections.ts
+++ b/src/core/PageManager/data/pages/connections.ts
@@ -1,6 +1,6 @@
-import { useCallback } from 'react';
+import React, { useCallback } from 'react';
import { GenericPageData } from '../../pageInterface';
-import { FaGoogle, FaMicrosoft, FaLink } from 'react-icons/fa';
+import { FaGoogle, FaMicrosoft, FaLink, FaSync, FaPlug } from 'react-icons/fa';
import { useConnections } from '../../../../hooks/useConnections';
// Helper function to convert attribute definitions to column config
@@ -29,6 +29,8 @@ const createConnectionsHook = () => {
createGoogleConnectionAndAuth,
createMicrosoftConnectionAndAuth,
connectWithPopup,
+ refreshMicrosoftToken,
+ refreshGoogleToken,
isConnecting,
isLoading,
error,
@@ -92,9 +94,13 @@ const createConnectionsHook = () => {
connectWithPopup,
createGoogleConnectionAndAuth,
createMicrosoftConnectionAndAuth,
+ // Token refresh operations
+ refreshMicrosoftToken,
+ refreshGoogleToken,
// Loading states
isConnecting,
deletingConnections: new Set(), // Placeholder for consistency with other pages
+ refreshingConnections: new Set(), // Track which connections are refreshing
// Attributes and permissions for dynamic column/button generation
attributes,
permissions,
@@ -192,28 +198,14 @@ export const connectionsPageData: GenericPageData = {
tableConfig: {
hookFactory: createConnectionsHook,
// Columns are generated dynamically from attributes via hookData.columns
+ // Standard action buttons
actionButtons: [
- {
- type: 'connect',
- title: 'connections.action.connect',
- idField: 'id',
- statusField: 'status',
- operationName: 'connectWithPopup',
- loadingStateName: 'isConnecting',
- // Only show if user has update permission (connect = update operation)
- disabled: (hookData: any) => {
- if (!hookData?.permissions) return { disabled: false };
- const hasUpdate = hookData.permissions.update !== 'n' && hookData.permissions.view;
- return { disabled: !hasUpdate, message: 'No permission to connect' };
- }
- },
{
type: 'delete',
title: 'connections.action.delete',
idField: 'id',
operationName: 'handleDelete',
loadingStateName: 'deletingConnections',
- // Only show if user has delete permission
disabled: (hookData: any) => {
if (!hookData?.permissions) return { disabled: false };
const hasDelete = hookData.permissions.delete !== 'n' && hookData.permissions.view;
@@ -221,6 +213,50 @@ export const connectionsPageData: GenericPageData = {
}
}
],
+ // Custom action buttons (entity-specific)
+ customActions: [
+ {
+ id: 'connect',
+ icon: React.createElement(FaPlug),
+ title: 'connections.action.connect',
+ onClick: async (row: any, hookData: any) => {
+ if (hookData?.connectWithPopup) {
+ await hookData.connectWithPopup(row.id);
+ }
+ },
+ // Only show connect button if status is not 'active'
+ visible: (row: any) => row.status !== 'active',
+ disabled: (_row: any, hookData: any) => {
+ if (!hookData?.permissions) return { disabled: false, message: '' };
+ const hasUpdate = hookData.permissions.update !== 'n' && hookData.permissions.view;
+ return { disabled: !hasUpdate, message: 'No permission to connect' };
+ },
+ loading: (_row: any, hookData: any) => hookData?.isConnecting || false
+ },
+ {
+ id: 'refresh',
+ icon: React.createElement(FaSync),
+ title: 'connections.action.refresh',
+ onClick: async (row: any, hookData: any) => {
+ // Determine which refresh function to use based on authority
+ if (row.authority === 'msft' && hookData?.refreshMicrosoftToken) {
+ await hookData.refreshMicrosoftToken(row.id);
+ if (hookData?.refetch) await hookData.refetch();
+ } else if (row.authority === 'google' && hookData?.refreshGoogleToken) {
+ await hookData.refreshGoogleToken(row.id);
+ if (hookData?.refetch) await hookData.refetch();
+ }
+ },
+ // Only show refresh button if status is 'active' (already connected)
+ visible: (row: any) => row.status === 'active',
+ disabled: (_row: any, hookData: any) => {
+ if (!hookData?.permissions) return { disabled: false, message: '' };
+ const hasUpdate = hookData.permissions.update !== 'n' && hookData.permissions.view;
+ return { disabled: !hasUpdate, message: 'No permission to refresh token' };
+ },
+ loading: (row: any, hookData: any) => hookData?.refreshingConnections?.has(row.id) || false
+ }
+ ],
searchable: true,
filterable: true,
sortable: true,
diff --git a/src/core/PageManager/data/pages/files.ts b/src/core/PageManager/data/pages/files.ts
index ba494d8..1526e05 100644
--- a/src/core/PageManager/data/pages/files.ts
+++ b/src/core/PageManager/data/pages/files.ts
@@ -257,7 +257,7 @@ export const filesPageData: GenericPageData = {
await hookData.handleDownload(row.id, row.fileName, row.mimeType);
}
},
- disabled: (row: any, hookData: any) => {
+ disabled: (_row: any, hookData: any) => {
if (!hookData?.permissions) return { disabled: false, message: '' };
const hasRead = hookData.permissions.read !== 'n' && hookData.permissions.view;
return { disabled: !hasRead, message: 'No permission to download files' };
diff --git a/src/core/PageManager/pageInterface.ts b/src/core/PageManager/pageInterface.ts
index 147f853..18d8447 100644
--- a/src/core/PageManager/pageInterface.ts
+++ b/src/core/PageManager/pageInterface.ts
@@ -9,7 +9,7 @@ export type PrivilegeChecker = () => boolean | Promise;
export interface ButtonFormField {
key: string;
label: string | LanguageText;
- type: 'string' | 'boolean' | 'email' | 'textarea' | 'date' | 'enum' | 'readonly' | 'multiselect';
+ type: 'string' | 'boolean' | 'email' | 'textarea' | 'date' | 'enum' | 'readonly' | 'multiselect' | 'number';
required?: boolean;
placeholder?: string | LanguageText;
minRows?: number;
@@ -17,6 +17,7 @@ export interface ButtonFormField {
validator?: (value: any) => string | null;
defaultValue?: any;
options?: string[] | Array<{ value: string | number; label: string }>; // For enum/multiselect fields
+ optionsReference?: string; // Reference to a data source for dynamic options (e.g., 'TrusteeOrganisation', 'User')
}
// Dropdown configuration for header dropdown buttons
@@ -262,22 +263,26 @@ export interface GenericDataHook {
[key: string]: any; // Allow additional properties for dynamic data sources
}
-// Standard action button configuration (built-in actions: edit, delete, view, copy)
+// Standard action button configuration (built-in actions: edit, delete, view, copy, connect, play)
export interface ActionButtonConfig {
- type: 'view' | 'edit' | 'delete' | 'copy';
+ type: 'view' | 'edit' | 'delete' | 'copy' | 'connect' | 'play';
onAction?: (row: any) => Promise | void; // Optional for delete buttons since they handle their own logic
title?: string | LanguageText;
- disabled?: (row: any) => boolean | { disabled: boolean; message?: string };
- loading?: (row: any) => boolean;
+ disabled?: (row: any, hookData?: any) => boolean | { disabled: boolean; message?: string };
+ loading?: (row: any, hookData?: any) => boolean;
// Field mappings for flexible data access
idField?: string; // Field name for the unique identifier (default: 'id')
nameField?: string; // Field name for display name (default: 'name' or 'file_name')
typeField?: string; // Field name for type/mime type (default: 'type' or 'mime_type')
contentField?: string; // Field name for content (default: 'content')
+ statusField?: string; // Field name for status (used by connect action)
// Operation and loading state names
operationName?: string; // Name of the operation function in hookData
loadingStateName?: string; // Name of the loading state in hookData
fetchItemFunctionName?: string; // Name of the function in hookData to fetch a single item by ID (for edit button)
+ // Navigation and mode (for play action)
+ navigateTo?: string; // Path to navigate to after action
+ mode?: string; // Mode to set (e.g., 'prompt', 'workflow')
}
// Custom action button configuration (for entity-specific actions like download, connect, play, sendPasswordLink)
diff --git a/src/hooks/useTrustee.ts b/src/hooks/useTrustee.ts
index 7aff5a1..9d68038 100644
--- a/src/hooks/useTrustee.ts
+++ b/src/hooks/useTrustee.ts
@@ -1,6 +1,5 @@
import { useState, useEffect, useCallback } from 'react';
import { useApiRequest } from './useApi';
-import { getUserDataCache } from '../utils/userCache';
import api from '../api';
import { usePermissions, type UserPermissions } from './usePermissions';
import {