This commit is contained in:
Anthony Stirling 2025-10-31 12:52:25 +00:00
parent 28c70cbae7
commit a256fd9593
7 changed files with 64 additions and 90 deletions

View File

@ -3726,7 +3726,7 @@
"enableAnalytics": "Enable Analytics",
"enableAnalytics.description": "Collect anonymous usage analytics to help improve the application",
"metricsEnabled": "Enable Metrics",
"metricsEnabled.description": "Enable collection of performance and usage metrics",
"metricsEnabled.description": "Enable collection of performance and usage metrics. Provides API endpoint for admins to access metrics data",
"searchEngine": "Search Engine Visibility",
"googleVisibility": "Google Visibility",
"googleVisibility.description": "Allow search engines to index this application"

View File

@ -1,6 +1,6 @@
import { useEffect } from 'react';
import { useTranslation } from 'react-i18next';
import { TextInput, Switch, Button, Stack, Paper, Text, Loader, Group, Alert } from '@mantine/core';
import { TextInput, Switch, Button, Stack, Paper, Text, Loader, Group, Alert, List } from '@mantine/core';
import { alert } from '@app/components/toast';
import LocalIcon from '@app/components/shared/LocalIcon';
import RestartConfirmationModal from '@app/components/shared/config/RestartConfirmationModal';
@ -73,12 +73,12 @@ export default function AdminPremiumSection() {
<Text size="sm">
{t('admin.settings.premium.movedFeatures.message', 'Premium and Enterprise features are now organized in their respective sections:')}
</Text>
<ul style={{ marginTop: '8px', marginBottom: 0, paddingLeft: '20px' }}>
<li><Text size="sm" component="span"><strong>SSO Auto Login</strong> (PRO) - Connections</Text></li>
<li><Text size="sm" component="span"><strong>Custom Metadata</strong> (PRO) - General</Text></li>
<li><Text size="sm" component="span"><strong>Audit Logging</strong> (ENTERPRISE) - Security</Text></li>
<li><Text size="sm" component="span"><strong>Database Configuration</strong> (ENTERPRISE) - Database</Text></li>
</ul>
<List mt="xs" size="sm">
<List.Item><Text size="sm" component="span"><strong>SSO Auto Login</strong> (PRO) - Connections</Text></List.Item>
<List.Item><Text size="sm" component="span"><strong>Custom Metadata</strong> (PRO) - General</Text></List.Item>
<List.Item><Text size="sm" component="span"><strong>Audit Logging</strong> (ENTERPRISE) - Security</Text></List.Item>
<List.Item><Text size="sm" component="span"><strong>Database Configuration</strong> (ENTERPRISE) - Database</Text></List.Item>
</List>
</Alert>
{/* License Configuration */}

View File

@ -1,5 +1,5 @@
import React, { useState, useEffect } from 'react';
import { Card, Text, Group, Stack, SegmentedControl, Loader, Alert } from '@mantine/core';
import { Card, Text, Group, Stack, SegmentedControl, Loader, Alert, Box, SimpleGrid } from '@mantine/core';
import { useTranslation } from 'react-i18next';
import auditService, { AuditChartsData } from '@app/services/auditService';
@ -17,9 +17,9 @@ const SimpleBarChart: React.FC<SimpleBarChartProps> = ({ data, title, color = 'b
<Text size="sm" fw={600}>
{title}
</Text>
<div style={{ display: 'flex', flexDirection: 'column', gap: '0.5rem' }}>
<Stack gap="sm">
{data.map((item, index) => (
<div key={index}>
<Box key={index}>
<Group justify="space-between" mb={4}>
<Text size="xs" c="dimmed" maw={200} style={{ overflow: 'hidden', textOverflow: 'ellipsis' }}>
{item.label}
@ -28,16 +28,16 @@ const SimpleBarChart: React.FC<SimpleBarChartProps> = ({ data, title, color = 'b
{item.value}
</Text>
</Group>
<div
<Box
style={{
width: '100%',
height: 8,
height: '0.5rem',
backgroundColor: 'var(--mantine-color-gray-2)',
borderRadius: 4,
borderRadius: 'var(--mantine-radius-sm)',
overflow: 'hidden',
}}
>
<div
<Box
style={{
width: `${(item.value / maxValue) * 100}%`,
height: '100%',
@ -45,10 +45,10 @@ const SimpleBarChart: React.FC<SimpleBarChartProps> = ({ data, title, color = 'b
transition: 'width 0.3s ease',
}}
/>
</div>
</div>
</Box>
</Box>
))}
</div>
</Stack>
</Stack>
);
};
@ -82,9 +82,9 @@ const AuditChartsSection: React.FC<AuditChartsSectionProps> = () => {
if (loading) {
return (
<Card padding="lg" radius="md" withBorder>
<div style={{ display: 'flex', justifyContent: 'center' }}>
<Group justify="center">
<Loader size="lg" my="xl" />
</div>
</Group>
</Card>
);
}
@ -134,13 +134,7 @@ const AuditChartsSection: React.FC<AuditChartsSectionProps> = () => {
/>
</Group>
<div
style={{
display: 'grid',
gridTemplateColumns: 'repeat(3, 1fr)',
gap: '1.5rem',
}}
>
<SimpleGrid cols={3} spacing="xl">
<SimpleBarChart
data={eventsByTypeData}
title={t('audit.charts.byType', 'Events by Type')}
@ -156,7 +150,7 @@ const AuditChartsSection: React.FC<AuditChartsSectionProps> = () => {
title={t('audit.charts.overTime', 'Events Over Time')}
color="purple"
/>
</div>
</SimpleGrid>
</Stack>
</Card>
);

View File

@ -1,5 +1,5 @@
import React from 'react';
import { Card, Text, Stack } from '@mantine/core';
import { Card, Text, Stack, Group, Box } from '@mantine/core';
import { useTranslation } from 'react-i18next';
interface SimpleBarChartProps {
@ -11,20 +11,20 @@ const SimpleBarChart: React.FC<SimpleBarChartProps> = ({ data, maxValue }) => {
const { t } = useTranslation();
return (
<div style={{ display: 'flex', flexDirection: 'column', gap: '0.5rem' }}>
<Stack gap="sm">
{data.length === 0 ? (
<Text c="dimmed" ta="center" py="xl">
{t('usage.noData', 'No data available')}
</Text>
) : (
data.map((item, index) => (
<div key={index}>
<div style={{ display: 'flex', justifyContent: 'space-between', marginBottom: '4px' }}>
<Box key={index}>
<Group justify="space-between" mb={4}>
<Text
size="xs"
c="dimmed"
maw="60%"
style={{
maxWidth: '60%',
overflow: 'hidden',
textOverflow: 'ellipsis',
whiteSpace: 'nowrap',
@ -32,25 +32,25 @@ const SimpleBarChart: React.FC<SimpleBarChartProps> = ({ data, maxValue }) => {
>
{item.label}
</Text>
<div style={{ display: 'flex', alignItems: 'center', gap: '0.5rem' }}>
<Group gap="xs">
<Text size="xs" fw={600}>
{item.value}
</Text>
<Text size="xs" c="dimmed">
({((item.value / maxValue) * 100).toFixed(1)}%)
</Text>
</div>
</div>
<div
</Group>
</Group>
<Box
style={{
width: '100%',
height: 8,
height: '0.5rem',
backgroundColor: 'var(--mantine-color-gray-2)',
borderRadius: 4,
borderRadius: 'var(--mantine-radius-sm)',
overflow: 'hidden',
}}
>
<div
<Box
style={{
width: `${(item.value / maxValue) * 100}%`,
height: '100%',
@ -58,11 +58,11 @@ const SimpleBarChart: React.FC<SimpleBarChartProps> = ({ data, maxValue }) => {
transition: 'width 0.3s ease',
}}
/>
</div>
</div>
</Box>
</Box>
))
)}
</div>
</Stack>
);
};

View File

@ -476,6 +476,7 @@ export default function PeopleSection() {
<Table
horizontalSpacing="md"
verticalSpacing="sm"
withRowBorders
style={{
'--table-border-color': 'var(--mantine-color-gray-3)',
} as React.CSSProperties}
@ -505,12 +506,7 @@ export default function PeopleSection() {
</Table.Tr>
) : (
filteredUsers.map((user) => (
<Table.Tr
key={user.id}
style={{
borderBottom: '1px solid var(--mantine-color-gray-3)',
}}
>
<Table.Tr key={user.id}>
<Table.Td>
<Group gap="xs" wrap="nowrap">
<Tooltip

View File

@ -15,6 +15,8 @@ import {
CloseButton,
Tooltip,
Menu,
Avatar,
Box,
} from '@mantine/core';
import LocalIcon from '@app/components/shared/LocalIcon';
import { alert } from '@app/components/toast';
@ -261,6 +263,7 @@ export default function TeamDetailsSection({ teamId, onBack }: TeamDetailsSectio
<Table
horizontalSpacing="md"
verticalSpacing="sm"
withRowBorders
style={{
'--table-border-color': 'var(--mantine-color-gray-3)',
} as React.CSSProperties}
@ -291,45 +294,33 @@ export default function TeamDetailsSection({ teamId, onBack }: TeamDetailsSectio
(Date.now() - userLastRequest[user.username]) < 5 * 60 * 1000; // Active within last 5 minutes
return (
<Table.Tr
key={user.id}
style={{
borderBottom: '1px solid var(--mantine-color-gray-3)',
}}
>
<Table.Tr key={user.id}>
<Table.Td>
<Group gap="xs" wrap="nowrap">
<div
style={{
width: 32,
height: 32,
borderRadius: '50%',
backgroundColor: user.enabled
? 'var(--mantine-color-blue-1)'
: 'var(--mantine-color-gray-2)',
display: 'flex',
alignItems: 'center',
justifyContent: 'center',
fontWeight: 600,
fontSize: '0.875rem',
color: user.enabled
? 'var(--mantine-color-blue-7)'
: 'var(--mantine-color-gray-6)',
flexShrink: 0,
border: isActive ? '2px solid var(--mantine-color-green-6)' : 'none',
opacity: user.enabled ? 1 : 0.5,
}}
title={
<Tooltip
label={
!user.enabled
? t('workspace.people.disabled', 'Disabled')
: isActive
? t('workspace.people.activeSession', 'Active session')
: t('workspace.people.active', 'Active')
}
zIndex={Z_INDEX_OVER_CONFIG_MODAL}
>
{user.username.charAt(0).toUpperCase()}
</div>
<div style={{ minWidth: 0, flex: 1 }}>
<Avatar
size={32}
color={user.enabled ? 'blue' : 'gray'}
styles={{
root: {
border: isActive ? '2px solid var(--mantine-color-green-6)' : 'none',
opacity: user.enabled ? 1 : 0.5,
}
}}
>
{user.username.charAt(0).toUpperCase()}
</Avatar>
</Tooltip>
<Box style={{ minWidth: 0, flex: 1 }}>
<Tooltip label={user.username} disabled={user.username.length <= 20} zIndex={Z_INDEX_OVER_CONFIG_MODAL}>
<Text
size="sm"
@ -351,7 +342,7 @@ export default function TeamDetailsSection({ teamId, onBack }: TeamDetailsSectio
{user.email}
</Text>
)}
</div>
</Box>
</Group>
</Table.Td>
<Table.Td w={100}>

View File

@ -228,6 +228,8 @@ export default function TeamsSection() {
<Table
horizontalSpacing="md"
verticalSpacing="sm"
withRowBorders
highlightOnHover
style={{
'--table-border-color': 'var(--mantine-color-gray-3)',
} as React.CSSProperties}
@ -256,17 +258,8 @@ export default function TeamsSection() {
teams.map((team) => (
<Table.Tr
key={team.id}
style={{
cursor: 'pointer',
borderBottom: '1px solid var(--mantine-color-gray-3)',
}}
style={{ cursor: 'pointer' }}
onClick={() => setViewingTeamId(team.id)}
onMouseEnter={(e) => {
e.currentTarget.style.backgroundColor = 'var(--mantine-color-gray-0)';
}}
onMouseLeave={(e) => {
e.currentTarget.style.backgroundColor = 'transparent';
}}
>
<Table.Td>
<Group gap="xs">