Files
twenty/packages/twenty-front/src/modules/settings/admin-panel/components/SettingsAdminWorkspaceContent.tsx
T
Félix Malfait 09806d7d8c Add admin panel workspace detail page with chat viewer (#19579)
## Overview
Adds comprehensive admin panel functionality for viewing workspace
details and AI chat threads.

## Changes

### Frontend
- **New Routes**: Added `AdminPanelWorkspaceDetail` and
`AdminPanelWorkspaceChatThread` pages with lazy loading
- **New Queries**: 
  - `getAdminWorkspaceChatThreads` - fetch chat threads for a workspace
  - `getAdminChatThreadMessages` - fetch messages for a specific thread
  - `workspaceLookupAdminPanel` - lookup workspace info and users
- **New Components**:
- `SettingsAdminWorkspaceDetail` - displays workspace info and chat
sessions tabs
- `SettingsAdminWorkspaceChatThread` - renders chat conversation with
message bubbles
- **Navigation**: Updated AI admin panel to link to workspace detail
pages
- **Settings Paths**: Added `AdminPanelWorkspaceDetail` and
`AdminPanelWorkspaceChatThread` paths

### Backend
- **New DTOs**:
  - `AdminWorkspaceChatThreadDTO` - workspace chat thread data
  - `AdminChatThreadMessagesDTO` - thread with messages
  - `AdminChatMessageDTO` - individual message with parts
- **New Resolvers**: Added three queries to `AdminPanelResolver`
- **New Service Methods**:
  - `workspaceLookup()` - fetch workspace info
  - `getWorkspaceChatThreads()` - list chat threads
  - `getChatThreadMessages()` - fetch thread messages with validation
- **Module Updates**: Added entity imports for workspace, user, AI chat,
and feature flag data

### Security
- Added `allowImpersonation` check before accessing chat data
- Validates workspace ownership and access permissions

---------

Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com>
2026-04-13 09:47:34 +02:00

131 lines
3.5 KiB
TypeScript

import { SettingsTableCard } from '@/settings/components/SettingsTableCard';
import { type WorkspaceInfo } from '@/settings/admin-panel/types/WorkspaceInfo';
import { getWorkspaceSchemaName } from '@/settings/admin-panel/utils/getWorkspaceSchemaName';
import { DEFAULT_WORKSPACE_LOGO } from '@/ui/navigation/navigation-drawer/constants/DefaultWorkspaceLogo';
import { styled } from '@linaria/react';
import { useLingui } from '@lingui/react/macro';
import { isNonEmptyString } from '@sniptt/guards';
import { SettingsPath } from 'twenty-shared/types';
import {
getImageAbsoluteURI,
getSettingsPath,
isDefined,
} from 'twenty-shared/utils';
import { AvatarOrIcon, LinkChip } from 'twenty-ui/components';
import {
H2Title,
IconCalendar,
IconHome,
IconId,
IconLink,
IconStatusChange,
IconUser,
} from 'twenty-ui/display';
import { Section } from 'twenty-ui/layout';
import { themeCssVariables } from 'twenty-ui/theme-constants';
import { REACT_APP_SERVER_BASE_URL } from '~/config';
type SettingsAdminWorkspaceContentProps = {
activeWorkspace: WorkspaceInfo | undefined;
};
const StyledContainer = styled.div`
display: flex;
flex-direction: column;
gap: ${themeCssVariables.spacing[3]};
margin-top: ${themeCssVariables.spacing[6]};
`;
export const SettingsAdminWorkspaceContent = ({
activeWorkspace,
}: SettingsAdminWorkspaceContentProps) => {
const { t } = useLingui();
const getWorkspaceUrl = (workspaceUrls: WorkspaceInfo['workspaceUrls']) => {
return workspaceUrls.customUrl ?? workspaceUrls.subdomainUrl;
};
const workspaceInfoItems = [
{
Icon: IconHome,
label: t`Name`,
value: activeWorkspace?.id ? (
<LinkChip
label={activeWorkspace?.name ?? ''}
emptyLabel={t`Untitled`}
to={getSettingsPath(SettingsPath.AdminPanelWorkspaceDetail, {
workspaceId: activeWorkspace.id,
})}
leftComponent={
<AvatarOrIcon
avatarUrl={
getImageAbsoluteURI({
imageUrl: isNonEmptyString(activeWorkspace?.logo)
? activeWorkspace?.logo
: DEFAULT_WORKSPACE_LOGO,
baseUrl: REACT_APP_SERVER_BASE_URL,
}) ?? ''
}
/>
}
/>
) : (
(activeWorkspace?.name ?? '')
),
},
{
Icon: IconId,
label: t`ID`,
value: activeWorkspace?.id,
},
{
Icon: IconId,
label: t`Schema name`,
value: isDefined(activeWorkspace?.id)
? getWorkspaceSchemaName(activeWorkspace.id)
: '',
},
{
Icon: IconLink,
label: t`URL`,
value: activeWorkspace?.workspaceUrls
? getWorkspaceUrl(activeWorkspace.workspaceUrls)
: '',
},
{
Icon: IconUser,
label: t`Members`,
value: activeWorkspace?.totalUsers,
},
{
Icon: IconStatusChange,
label: t`Status`,
value: activeWorkspace?.activationStatus,
},
{
Icon: IconCalendar,
label: t`Created`,
value: activeWorkspace?.createdAt
? new Date(activeWorkspace.createdAt).toLocaleDateString()
: '',
},
];
if (!activeWorkspace) return null;
return (
<StyledContainer>
<Section>
<H2Title
title={t`Workspace Info`}
description={t`About this workspace`}
/>
<SettingsTableCard
items={workspaceInfoItems}
gridAutoColumns="1fr 4fr"
/>
</Section>
</StyledContainer>
);
};