diff --git a/src/frontend/components/_features/[workspace]/editor/server/opcua-server/components/certificate-modal.tsx b/src/frontend/components/_features/[workspace]/editor/server/opcua-server/components/certificate-modal.tsx index bf4712671..825ce7b66 100644 --- a/src/frontend/components/_features/[workspace]/editor/server/opcua-server/components/certificate-modal.tsx +++ b/src/frontend/components/_features/[workspace]/editor/server/opcua-server/components/certificate-modal.tsx @@ -226,10 +226,10 @@ MIIEpDCCAowCCQC7... {/* Validation Errors */} {validationErrors.length > 0 && ( -
+
)}
@@ -415,19 +430,6 @@ export const UserModal = ({ ))} - - {/* Validation Errors */} - {validationErrors.length > 0 && ( -
- -
- )} diff --git a/src/frontend/components/_features/[workspace]/editor/server/opcua-server/components/users-tab.tsx b/src/frontend/components/_features/[workspace]/editor/server/opcua-server/components/users-tab.tsx index 41d1340c7..9db6c7291 100644 --- a/src/frontend/components/_features/[workspace]/editor/server/opcua-server/components/users-tab.tsx +++ b/src/frontend/components/_features/[workspace]/editor/server/opcua-server/components/users-tab.tsx @@ -1,5 +1,5 @@ +import { Pencil1Icon, PlusIcon, TrashIcon } from '@radix-ui/react-icons' import { useOpenPLCStore } from '@root/frontend/store' -import { cn } from '@root/frontend/utils/cn' import type { OpcUaServerConfig, OpcUaUser } from '@root/middleware/shared/ports/types' import { useCallback, useMemo, useState } from 'react' @@ -11,28 +11,10 @@ interface UsersTabProps { onConfigChange: () => void } -// Helper to get role display info -const getRoleInfo = (role: OpcUaUser['role']): { label: string; description: string; color: string } => { - switch (role) { - case 'viewer': - return { - label: 'Viewer', - description: 'Read-only access', - color: 'bg-blue-100 text-blue-700 dark:bg-blue-900 dark:text-blue-300', - } - case 'operator': - return { - label: 'Operator', - description: 'Read/Write per variable permissions', - color: 'bg-amber-100 text-amber-700 dark:bg-amber-900 dark:text-amber-300', - } - case 'engineer': - return { - label: 'Engineer', - description: 'Full access', - color: 'bg-green-100 text-green-700 dark:bg-green-900 dark:text-green-300', - } - } +const ROLE_LABELS: Record = { + viewer: 'Viewer', + operator: 'Operator', + engineer: 'Engineer', } // Helper to get user display name @@ -117,8 +99,8 @@ export const UsersTab = ({ config, serverName, onConfigChange }: UsersTabProps) {/* Warning if username auth is enabled but no users */} {usernameAuthEnabled && passwordUserCount === 0 && ( -
-

+

+

Warning: Username authentication is enabled but no password users exist. Add at least one user for clients to authenticate.

@@ -129,80 +111,64 @@ export const UsersTab = ({ config, serverName, onConfigChange }: UsersTabProps) - {/* Users List */} + {/* Users Table */} {config.users.length === 0 ? ( -
-

- No users configured. Add users for password or certificate authentication. -

-
+

+ No users configured. Add users for password or certificate authentication. +

) : ( -
- {config.users.map((user) => { - const roleInfo = getRoleInfo(user.role) - return ( -
- {/* Header row with icon, name, and actions */} -
-
- {/* User Icon */} - {user.type === 'password' ? '👤' : '🔐'} - - {/* User Name */} - - {getUserDisplayName(user)} - - - {/* Role Badge */} - - {roleInfo.label} - -
- - {/* Actions */} -
- - -
-
- - {/* User Details */} -
-

- Type:{' '} - {user.type === 'password' ? 'Password Authentication' : 'Certificate Authentication'} -

- {user.type === 'certificate' && user.certificateId && ( -

- Certificate: {user.certificateId} -

- )} -

{roleInfo.description}

-
-
- ) - })} +
+ + + + + + + + + + + {config.users.map((user) => ( + + + + + + + ))} + +
NameTypeRole + Actions +
+ {getUserDisplayName(user)} + + {user.type === 'password' ? 'Password' : 'Certificate'} + {ROLE_LABELS[user.role]} +
+ + +
+
)} diff --git a/src/frontend/components/_features/[workspace]/editor/server/opcua-server/components/variable-config-modal.tsx b/src/frontend/components/_features/[workspace]/editor/server/opcua-server/components/variable-config-modal.tsx index 2b0ecbbb5..412e56bc0 100644 --- a/src/frontend/components/_features/[workspace]/editor/server/opcua-server/components/variable-config-modal.tsx +++ b/src/frontend/components/_features/[workspace]/editor/server/opcua-server/components/variable-config-modal.tsx @@ -656,7 +656,7 @@ export const VariableConfigModal = ({ {/* Structure Info */} {isStructureOrFb && variable?.structureInfo && ( -
+

Structure Information

@@ -751,10 +751,10 @@ export const VariableConfigModal = ({ {/* Validation Errors */} {validationErrors.length > 0 && ( -
+
    {validationErrors.map((error, index) => ( -
  • +
  • {error}
  • ))} diff --git a/src/frontend/components/_features/[workspace]/editor/server/opcua-server/components/variable-tree.tsx b/src/frontend/components/_features/[workspace]/editor/server/opcua-server/components/variable-tree.tsx index 05fc036a6..df28ddf71 100644 --- a/src/frontend/components/_features/[workspace]/editor/server/opcua-server/components/variable-tree.tsx +++ b/src/frontend/components/_features/[workspace]/editor/server/opcua-server/components/variable-tree.tsx @@ -1,3 +1,4 @@ +import { ChevronDownIcon, ChevronRightIcon } from '@radix-ui/react-icons' import { cn } from '@root/frontend/utils/cn' import { useCallback, useState } from 'react' @@ -10,60 +11,37 @@ interface VariableTreeProps { filter?: string } -// Icons for different node types (using text abbreviations) +const NODE_TYPE_LABEL: Record = { + program: 'P', + function_block: 'FB', + global: 'G', + structure: 'S', + array: '[]', + variable: 'V', +} + const NodeIcon = ({ type }: { type: VariableTreeNode['type'] }) => { - switch (type) { - case 'program': - return ( - - P - - ) - case 'function_block': - return ( - - FB - - ) - case 'global': - return ( - - G - - ) - case 'structure': - return ( - - S - - ) - case 'array': - return ( - - [] - - ) - case 'variable': - return ( - - V - - ) - default: - return null - } + const label = NODE_TYPE_LABEL[type] + if (!label) return null + return ( + + {label} + + ) } -// Expand/collapse icon -const ExpandIcon = ({ expanded, onClick }: { expanded: boolean; onClick: (e: React.MouseEvent) => void }) => ( - -) +const ExpandIcon = ({ expanded, onClick }: { expanded: boolean; onClick: (e: React.MouseEvent) => void }) => { + const Icon = expanded ? ChevronDownIcon : ChevronRightIcon + return ( + + ) +} // Checkbox for selection const Checkbox = ({