move to propietary for settings

This commit is contained in:
Anthony Stirling 2025-11-12 17:18:35 +00:00
parent 0da1ae06d9
commit 2239e9cc2d
25 changed files with 279 additions and 187 deletions

View File

@ -2,18 +2,6 @@ import React from 'react';
import { NavKey } from '@app/components/shared/config/types';
import HotkeysSection from '@app/components/shared/config/configSections/HotkeysSection';
import GeneralSection from '@app/components/shared/config/configSections/GeneralSection';
import AdminGeneralSection from '@app/components/shared/config/configSections/AdminGeneralSection';
import AdminSecuritySection from '@app/components/shared/config/configSections/AdminSecuritySection';
import AdminConnectionsSection from '@app/components/shared/config/configSections/AdminConnectionsSection';
import AdminPrivacySection from '@app/components/shared/config/configSections/AdminPrivacySection';
import AdminDatabaseSection from '@app/components/shared/config/configSections/AdminDatabaseSection';
import AdminAdvancedSection from '@app/components/shared/config/configSections/AdminAdvancedSection';
import AdminLegalSection from '@app/components/shared/config/configSections/AdminLegalSection';
import AdminPremiumSection from '@app/components/shared/config/configSections/AdminPremiumSection';
import AdminFeaturesSection from '@app/components/shared/config/configSections/AdminFeaturesSection';
import AdminEndpointsSection from '@app/components/shared/config/configSections/AdminEndpointsSection';
import AdminAuditSection from '@app/components/shared/config/configSections/AdminAuditSection';
import AdminUsageSection from '@app/components/shared/config/configSections/AdminUsageSection';
export interface ConfigNavItem {
key: NavKey;
@ -64,133 +52,5 @@ export const createConfigNavSections = (
},
];
// Add Admin sections if user is admin OR if login is disabled (but mark as disabled)
if (isAdmin || !loginEnabled) {
const requiresLogin = !loginEnabled;
// Configuration
sections.push({
title: 'Configuration',
items: [
{
key: 'adminGeneral',
label: 'System Settings',
icon: 'settings-rounded',
component: <AdminGeneralSection />,
disabled: requiresLogin,
disabledTooltip: requiresLogin ? 'Enable login mode first' : undefined
},
{
key: 'adminFeatures',
label: 'Features',
icon: 'extension-rounded',
component: <AdminFeaturesSection />,
disabled: requiresLogin,
disabledTooltip: requiresLogin ? 'Enable login mode first' : undefined
},
{
key: 'adminEndpoints',
label: 'Endpoints',
icon: 'api-rounded',
component: <AdminEndpointsSection />,
disabled: requiresLogin,
disabledTooltip: requiresLogin ? 'Enable login mode first' : undefined
},
{
key: 'adminDatabase',
label: 'Database',
icon: 'storage-rounded',
component: <AdminDatabaseSection />,
disabled: requiresLogin,
disabledTooltip: requiresLogin ? 'Enable login mode first' : undefined
},
{
key: 'adminAdvanced',
label: 'Advanced',
icon: 'tune-rounded',
component: <AdminAdvancedSection />,
disabled: requiresLogin,
disabledTooltip: requiresLogin ? 'Enable login mode first' : undefined
},
],
});
// Security & Authentication
sections.push({
title: 'Security & Authentication',
items: [
{
key: 'adminSecurity',
label: 'Security',
icon: 'shield-rounded',
component: <AdminSecuritySection />,
disabled: requiresLogin,
disabledTooltip: requiresLogin ? 'Enable login mode first' : undefined
},
{
key: 'adminConnections',
label: 'Connections',
icon: 'link-rounded',
component: <AdminConnectionsSection />,
disabled: requiresLogin,
disabledTooltip: requiresLogin ? 'Enable login mode first' : undefined
},
],
});
// Licensing & Analytics
sections.push({
title: 'Licensing & Analytics',
items: [
{
key: 'adminPremium',
label: 'Premium',
icon: 'star-rounded',
component: <AdminPremiumSection />,
disabled: requiresLogin,
disabledTooltip: requiresLogin ? 'Enable login mode first' : undefined
},
{
key: 'adminAudit',
label: 'Audit',
icon: 'fact-check-rounded',
component: <AdminAuditSection />,
disabled: !runningEE || requiresLogin,
disabledTooltip: requiresLogin ? 'Enable login mode first' : 'Requires Enterprise license'
},
{
key: 'adminUsage',
label: 'Usage Analytics',
icon: 'analytics-rounded',
component: <AdminUsageSection />,
disabled: !runningEE || requiresLogin,
disabledTooltip: requiresLogin ? 'Enable login mode first' : 'Requires Enterprise license'
},
],
});
// Policies & Privacy
sections.push({
title: 'Policies & Privacy',
items: [
{
key: 'adminLegal',
label: 'Legal',
icon: 'gavel-rounded',
component: <AdminLegalSection />,
disabled: requiresLogin,
disabledTooltip: requiresLogin ? 'Enable login mode first' : undefined
},
{
key: 'adminPrivacy',
label: 'Privacy',
icon: 'visibility-rounded',
component: <AdminPrivacySection />,
disabled: requiresLogin,
disabledTooltip: requiresLogin ? 'Enable login mode first' : undefined
},
],
});
}
return sections;
};

View File

@ -2,40 +2,180 @@ import React from 'react';
import { createConfigNavSections as createCoreConfigNavSections, ConfigNavSection } from '@core/components/shared/config/configNavSections';
import PeopleSection from '@app/components/shared/config/configSections/PeopleSection';
import TeamsSection from '@app/components/shared/config/configSections/TeamsSection';
import AdminGeneralSection from '@app/components/shared/config/configSections/AdminGeneralSection';
import AdminSecuritySection from '@app/components/shared/config/configSections/AdminSecuritySection';
import AdminConnectionsSection from '@app/components/shared/config/configSections/AdminConnectionsSection';
import AdminPrivacySection from '@app/components/shared/config/configSections/AdminPrivacySection';
import AdminDatabaseSection from '@app/components/shared/config/configSections/AdminDatabaseSection';
import AdminAdvancedSection from '@app/components/shared/config/configSections/AdminAdvancedSection';
import AdminLegalSection from '@app/components/shared/config/configSections/AdminLegalSection';
import AdminPremiumSection from '@app/components/shared/config/configSections/AdminPremiumSection';
import AdminFeaturesSection from '@app/components/shared/config/configSections/AdminFeaturesSection';
import AdminEndpointsSection from '@app/components/shared/config/configSections/AdminEndpointsSection';
import AdminAuditSection from '@app/components/shared/config/configSections/AdminAuditSection';
import AdminUsageSection from '@app/components/shared/config/configSections/AdminUsageSection';
/**
* Proprietary extension of createConfigNavSections that adds workspace sections
* Proprietary extension of createConfigNavSections that adds all admin and workspace sections
*/
export const createConfigNavSections = (
isAdmin: boolean = false,
runningEE: boolean = false,
loginEnabled: boolean = true
): ConfigNavSection[] => {
// Get the core sections
// Get the core sections (just Preferences)
const sections = createCoreConfigNavSections(isAdmin, runningEE, loginEnabled);
// Add Workspace section if user is admin
if (isAdmin) {
const workspaceSection: ConfigNavSection = {
// Add Admin sections if user is admin OR if login is disabled (but mark as disabled)
if (isAdmin || !loginEnabled) {
const requiresLogin = !loginEnabled;
// Workspace
sections.push({
title: 'Workspace',
items: [
{
key: 'people',
label: 'People',
icon: 'group-rounded',
component: <PeopleSection />
component: <PeopleSection />,
disabled: requiresLogin,
disabledTooltip: requiresLogin ? 'Enable login mode first' : undefined
},
{
key: 'teams',
label: 'Teams',
icon: 'groups-rounded',
component: <TeamsSection />
component: <TeamsSection />,
disabled: requiresLogin,
disabledTooltip: requiresLogin ? 'Enable login mode first' : undefined
},
],
};
});
// Insert workspace section after Preferences (at index 1)
sections.splice(1, 0, workspaceSection);
// Configuration
sections.push({
title: 'Configuration',
items: [
{
key: 'adminGeneral',
label: 'System Settings',
icon: 'settings-rounded',
component: <AdminGeneralSection />,
disabled: requiresLogin,
disabledTooltip: requiresLogin ? 'Enable login mode first' : undefined
},
{
key: 'adminFeatures',
label: 'Features',
icon: 'extension-rounded',
component: <AdminFeaturesSection />,
disabled: requiresLogin,
disabledTooltip: requiresLogin ? 'Enable login mode first' : undefined
},
{
key: 'adminEndpoints',
label: 'Endpoints',
icon: 'api-rounded',
component: <AdminEndpointsSection />,
disabled: requiresLogin,
disabledTooltip: requiresLogin ? 'Enable login mode first' : undefined
},
{
key: 'adminDatabase',
label: 'Database',
icon: 'storage-rounded',
component: <AdminDatabaseSection />,
disabled: requiresLogin,
disabledTooltip: requiresLogin ? 'Enable login mode first' : undefined
},
{
key: 'adminAdvanced',
label: 'Advanced',
icon: 'tune-rounded',
component: <AdminAdvancedSection />,
disabled: requiresLogin,
disabledTooltip: requiresLogin ? 'Enable login mode first' : undefined
},
],
});
// Security & Authentication
sections.push({
title: 'Security & Authentication',
items: [
{
key: 'adminSecurity',
label: 'Security',
icon: 'shield-rounded',
component: <AdminSecuritySection />,
disabled: requiresLogin,
disabledTooltip: requiresLogin ? 'Enable login mode first' : undefined
},
{
key: 'adminConnections',
label: 'Connections',
icon: 'link-rounded',
component: <AdminConnectionsSection />,
disabled: requiresLogin,
disabledTooltip: requiresLogin ? 'Enable login mode first' : undefined
},
],
});
// Licensing & Analytics
sections.push({
title: 'Licensing & Analytics',
items: [
{
key: 'adminPremium',
label: 'Premium',
icon: 'star-rounded',
component: <AdminPremiumSection />,
disabled: requiresLogin,
disabledTooltip: requiresLogin ? 'Enable login mode first' : undefined
},
{
key: 'adminAudit',
label: 'Audit',
icon: 'fact-check-rounded',
component: <AdminAuditSection />,
disabled: !runningEE || requiresLogin,
disabledTooltip: requiresLogin ? 'Enable login mode first' : 'Requires Enterprise license'
},
{
key: 'adminUsage',
label: 'Usage Analytics',
icon: 'analytics-rounded',
component: <AdminUsageSection />,
disabled: !runningEE || requiresLogin,
disabledTooltip: requiresLogin ? 'Enable login mode first' : 'Requires Enterprise license'
},
],
});
// Policies & Privacy
sections.push({
title: 'Policies & Privacy',
items: [
{
key: 'adminLegal',
label: 'Legal',
icon: 'gavel-rounded',
component: <AdminLegalSection />,
disabled: requiresLogin,
disabledTooltip: requiresLogin ? 'Enable login mode first' : undefined
},
{
key: 'adminPrivacy',
label: 'Privacy',
icon: 'visibility-rounded',
component: <AdminPrivacySection />,
disabled: requiresLogin,
disabledTooltip: requiresLogin ? 'Enable login mode first' : undefined
},
],
});
}
return sections;

View File

@ -28,10 +28,13 @@ import { userManagementService, User } from '@app/services/userManagementService
import { teamService, Team } from '@app/services/teamService';
import { Z_INDEX_OVER_CONFIG_MODAL } from '@app/styles/zIndex';
import { useAppConfig } from '@app/contexts/AppConfigContext';
import { useLoginRequired } from '@app/hooks/useLoginRequired';
import LoginRequiredBanner from '@app/components/shared/config/LoginRequiredBanner';
export default function PeopleSection() {
const { t } = useTranslation();
const { config } = useAppConfig();
const { loginEnabled } = useLoginRequired();
const [users, setUsers] = useState<User[]>([]);
const [teams, setTeams] = useState<Team[]>([]);
const [loading, setLoading] = useState(true);
@ -97,30 +100,103 @@ export default function PeopleSection() {
const fetchData = async () => {
try {
setLoading(true);
const [adminData, teamsData] = await Promise.all([
userManagementService.getUsers(),
teamService.getTeams(),
]);
// Enrich users with session data
const enrichedUsers = adminData.users.map(user => ({
...user,
isActive: adminData.userSessions[user.username] || false,
lastRequest: adminData.userLastRequest[user.username] || undefined,
}));
if (loginEnabled) {
const [adminData, teamsData] = await Promise.all([
userManagementService.getUsers(),
teamService.getTeams(),
]);
setUsers(enrichedUsers);
setTeams(teamsData);
// Enrich users with session data
const enrichedUsers = adminData.users.map(user => ({
...user,
isActive: adminData.userSessions[user.username] || false,
lastRequest: adminData.userLastRequest[user.username] || undefined,
}));
// Store license information
setLicenseInfo({
maxAllowedUsers: adminData.maxAllowedUsers,
availableSlots: adminData.availableSlots,
grandfatheredUserCount: adminData.grandfatheredUserCount,
licenseMaxUsers: adminData.licenseMaxUsers,
premiumEnabled: adminData.premiumEnabled,
totalUsers: adminData.totalUsers,
});
setUsers(enrichedUsers);
setTeams(teamsData);
// Store license information
setLicenseInfo({
maxAllowedUsers: adminData.maxAllowedUsers,
availableSlots: adminData.availableSlots,
grandfatheredUserCount: adminData.grandfatheredUserCount,
licenseMaxUsers: adminData.licenseMaxUsers,
premiumEnabled: adminData.premiumEnabled,
totalUsers: adminData.totalUsers,
});
} else {
// Provide example data when login is disabled
const exampleUsers: User[] = [
{
id: 1,
username: 'admin',
email: 'admin@example.com',
enabled: true,
roleName: 'ROLE_ADMIN',
rolesAsString: 'ROLE_ADMIN',
authenticationType: 'password',
isActive: true,
lastRequest: new Date().toISOString(),
team: { id: 1, name: 'Engineering', userCount: 3 }
},
{
id: 2,
username: 'john.doe',
email: 'john.doe@example.com',
enabled: true,
roleName: 'ROLE_USER',
rolesAsString: 'ROLE_USER',
authenticationType: 'password',
isActive: false,
lastRequest: new Date(Date.now() - 86400000).toISOString(),
team: { id: 1, name: 'Engineering', userCount: 3 }
},
{
id: 3,
username: 'jane.smith',
email: 'jane.smith@example.com',
enabled: true,
roleName: 'ROLE_USER',
rolesAsString: 'ROLE_USER',
authenticationType: 'oauth',
isActive: true,
lastRequest: new Date().toISOString(),
team: { id: 2, name: 'Marketing', userCount: 2 }
},
{
id: 4,
username: 'bob.wilson',
email: 'bob.wilson@example.com',
enabled: false,
roleName: 'ROLE_USER',
rolesAsString: 'ROLE_USER',
authenticationType: 'password',
isActive: false,
lastRequest: new Date(Date.now() - 604800000).toISOString(),
team: undefined
}
];
const exampleTeams: Team[] = [
{ id: 1, name: 'Engineering', userCount: 3 },
{ id: 2, name: 'Marketing', userCount: 2 }
];
setUsers(exampleUsers);
setTeams(exampleTeams);
// Example license information
setLicenseInfo({
maxAllowedUsers: 10,
availableSlots: 6,
grandfatheredUserCount: 0,
licenseMaxUsers: 5,
premiumEnabled: true,
totalUsers: 4,
});
}
} catch (error) {
console.error('Failed to fetch people data:', error);
alert({ alertType: 'error', title: 'Failed to load people data' });
@ -405,6 +481,7 @@ export default function PeopleSection() {
return (
<Stack gap="lg">
<LoginRequiredBanner show={!loginEnabled} />
<div>
<Text fw={600} size="lg">
{t('workspace.people.title')}
@ -457,15 +534,15 @@ export default function PeopleSection() {
style={{ maxWidth: 300 }}
/>
<Tooltip
label={t('workspace.people.license.noSlotsAvailable', 'No user slots available')}
disabled={!licenseInfo || licenseInfo.availableSlots > 0}
label={!loginEnabled ? 'Enable login mode first' : t('workspace.people.license.noSlotsAvailable', 'No user slots available')}
disabled={loginEnabled && (!licenseInfo || licenseInfo.availableSlots > 0)}
position="bottom"
withArrow
>
<Button
leftSection={<LocalIcon icon="person-add" width="1rem" height="1rem" />}
onClick={() => setInviteModalOpened(true)}
disabled={licenseInfo ? licenseInfo.availableSlots === 0 : false}
disabled={!loginEnabled || (licenseInfo ? licenseInfo.availableSlots === 0 : false)}
>
{t('workspace.people.addMembers')}
</Button>
@ -616,20 +693,21 @@ export default function PeopleSection() {
{/* Actions menu */}
<Menu position="bottom-end" withinPortal>
<Menu.Target>
<ActionIcon variant="subtle" color="gray">
<ActionIcon variant="subtle" color="gray" disabled={!loginEnabled}>
<LocalIcon icon="more-vert" width="1rem" height="1rem" />
</ActionIcon>
</Menu.Target>
<Menu.Dropdown style={{ zIndex: Z_INDEX_OVER_CONFIG_MODAL }}>
<Menu.Item onClick={() => openEditModal(user)}>{t('workspace.people.editRole')}</Menu.Item>
<Menu.Item onClick={() => openEditModal(user)} disabled={!loginEnabled}>{t('workspace.people.editRole')}</Menu.Item>
<Menu.Item
leftSection={user.enabled ? <LocalIcon icon="person-off" width="1rem" height="1rem" /> : <LocalIcon icon="person-check" width="1rem" height="1rem" />}
onClick={() => handleToggleEnabled(user)}
disabled={!loginEnabled}
>
{user.enabled ? t('workspace.people.disable') : t('workspace.people.enable')}
</Menu.Item>
<Menu.Divider />
<Menu.Item color="red" leftSection={<LocalIcon icon="delete" width="1rem" height="1rem" />} onClick={() => handleDeleteUser(user)}>
<Menu.Item color="red" leftSection={<LocalIcon icon="delete" width="1rem" height="1rem" />} onClick={() => handleDeleteUser(user)} disabled={!loginEnabled}>
{t('workspace.people.deleteUser')}
</Menu.Item>
</Menu.Dropdown>

View File

@ -22,9 +22,12 @@ import { teamService, Team } from '@app/services/teamService';
import { userManagementService, User } from '@app/services/userManagementService';
import { Z_INDEX_OVER_CONFIG_MODAL } from '@app/styles/zIndex';
import TeamDetailsSection from '@app/components/shared/config/configSections/TeamDetailsSection';
import { useLoginRequired } from '@app/hooks/useLoginRequired';
import LoginRequiredBanner from '@app/components/shared/config/LoginRequiredBanner';
export default function TeamsSection() {
const { t } = useTranslation();
const { loginEnabled } = useLoginRequired();
const [teams, setTeams] = useState<Team[]>([]);
const [loading, setLoading] = useState(true);
const [createModalOpened, setCreateModalOpened] = useState(false);
@ -47,8 +50,18 @@ export default function TeamsSection() {
const fetchTeams = async () => {
try {
setLoading(true);
const teamsData = await teamService.getTeams();
setTeams(teamsData);
if (loginEnabled) {
const teamsData = await teamService.getTeams();
setTeams(teamsData);
} else {
// Provide example data when login is disabled
const exampleTeams: Team[] = [
{ id: 1, name: 'Engineering', userCount: 3 },
{ id: 2, name: 'Marketing', userCount: 2 },
{ id: 3, name: 'Internal', userCount: 1 },
];
setTeams(exampleTeams);
}
} catch (error) {
console.error('Failed to fetch teams:', error);
alert({ alertType: 'error', title: 'Failed to load teams' });
@ -207,6 +220,7 @@ export default function TeamsSection() {
return (
<Stack gap="lg">
<LoginRequiredBanner show={!loginEnabled} />
<div>
<Text fw={600} size="lg">
{t('workspace.teams.title')}
@ -218,7 +232,7 @@ export default function TeamsSection() {
{/* Header Actions */}
<Group justify="flex-end">
<Button leftSection={<LocalIcon icon="add" width="1rem" height="1rem" />} onClick={() => setCreateModalOpened(true)}>
<Button leftSection={<LocalIcon icon="add" width="1rem" height="1rem" />} onClick={() => setCreateModalOpened(true)} disabled={!loginEnabled}>
{t('workspace.teams.createNewTeam')}
</Button>
</Group>
@ -257,8 +271,8 @@ export default function TeamsSection() {
teams.map((team) => (
<Table.Tr
key={team.id}
style={{ cursor: 'pointer' }}
onClick={() => setViewingTeamId(team.id)}
style={{ cursor: loginEnabled ? 'pointer' : 'default' }}
onClick={() => loginEnabled && setViewingTeamId(team.id)}
>
<Table.Td>
<Group gap="xs">
@ -290,18 +304,18 @@ export default function TeamsSection() {
<Table.Td onClick={(e) => e.stopPropagation()}>
<Menu position="bottom-end" withinPortal>
<Menu.Target>
<ActionIcon variant="subtle" color="gray">
<ActionIcon variant="subtle" color="gray" disabled={!loginEnabled}>
<LocalIcon icon="more-vert" width="1rem" height="1rem" />
</ActionIcon>
</Menu.Target>
<Menu.Dropdown style={{ zIndex: Z_INDEX_OVER_CONFIG_MODAL }}>
<Menu.Item leftSection={<LocalIcon icon="visibility" width="1rem" height="1rem" />} onClick={() => setViewingTeamId(team.id)}>
<Menu.Item leftSection={<LocalIcon icon="visibility" width="1rem" height="1rem" />} onClick={() => setViewingTeamId(team.id)} disabled={!loginEnabled}>
{t('workspace.teams.viewTeam', 'View Team')}
</Menu.Item>
<Menu.Item leftSection={<LocalIcon icon="group" width="1rem" height="1rem" />} onClick={() => openAddMemberModal(team)}>
<Menu.Item leftSection={<LocalIcon icon="group" width="1rem" height="1rem" />} onClick={() => openAddMemberModal(team)} disabled={!loginEnabled}>
{t('workspace.teams.addMember')}
</Menu.Item>
<Menu.Item leftSection={<LocalIcon icon="edit" width="1rem" height="1rem" />} onClick={() => openRenameModal(team)}>
<Menu.Item leftSection={<LocalIcon icon="edit" width="1rem" height="1rem" />} onClick={() => openRenameModal(team)} disabled={!loginEnabled}>
{t('workspace.teams.renameTeamLabel')}
</Menu.Item>
<Menu.Divider />
@ -309,7 +323,7 @@ export default function TeamsSection() {
color="red"
leftSection={<LocalIcon icon="delete" width="1rem" height="1rem" />}
onClick={() => handleDeleteTeam(team)}
disabled={team.name === 'Internal'}
disabled={!loginEnabled || team.name === 'Internal'}
>
{t('workspace.teams.deleteTeamLabel')}
</Menu.Item>