chore: test deploy snapshot
This commit is contained in:
@@ -1,10 +1,12 @@
|
||||
import { DeleteOutlined, EditOutlined, PlusOutlined, SaveOutlined, UnorderedListOutlined } from '@ant-design/icons';
|
||||
import { Alert, App, Button, Card, Checkbox, Descriptions, Empty, Form, Input, InputNumber, List, Modal, Space, Switch, Tabs, Tag, Typography } from 'antd';
|
||||
import { Alert, App, Button, Card, Checkbox, Descriptions, Empty, Form, Input, InputNumber, List, Modal, Space, Switch, Table, Tabs, Tag, Typography } from 'antd';
|
||||
import { useEffect, useMemo, useRef, useState } from 'react';
|
||||
import { getReadyPlayAppEntries } from '../../views/play/apps/apps/appsRegistry';
|
||||
import { confirmWithKeyboard } from './modalKeyboard';
|
||||
import {
|
||||
deleteTokenSetting,
|
||||
fetchTokenSettingActivities,
|
||||
type TokenSettingActivityRecord,
|
||||
type TokenSettingRecord,
|
||||
upsertTokenSetting,
|
||||
useTokenSettingRegistry,
|
||||
@@ -54,6 +56,7 @@ type SharedTokenSettingAccess = {
|
||||
const MANAGEMENT_APP_OPTIONS: AppOption[] = [
|
||||
{ value: 'chat-live', label: 'Codex Live', description: '채팅방 조회와 응답 흐름 진입', category: '관리' },
|
||||
{ value: 'chat-room-settings', label: '채팅방 설정', description: 'Codex Live 채팅방 Context 설정 편집 접근', category: '관리' },
|
||||
{ value: 'text-memo-widget', label: '메모', description: '공유채팅 Apps에서 메모 컴포넌트 실행', category: '관리' },
|
||||
{ value: 'token-setting', label: '토큰관리 설정', description: '토큰 설정 목록과 상세 편집 접근', category: '관리' },
|
||||
{ value: 'shared-resource', label: '공유 리소스 관리', description: '공유 링크와 권한, 활동 이력 관리', category: '관리' },
|
||||
{ value: 'plan-board', label: '자동화 현황', description: '작업 현황과 요청 보드 접근', category: '관리' },
|
||||
@@ -219,7 +222,9 @@ export function TokenSettingManagementPage({
|
||||
const [isSaving, setIsSaving] = useState(false);
|
||||
const [saveErrorMessage, setSaveErrorMessage] = useState('');
|
||||
const [saveSuccessMessage, setSaveSuccessMessage] = useState('');
|
||||
const [activeDetailTab, setActiveDetailTab] = useState<'basic' | 'quota' | 'apps'>('basic');
|
||||
const [activeDetailTab, setActiveDetailTab] = useState<'basic' | 'quota' | 'apps' | 'history'>('basic');
|
||||
const [activities, setActivities] = useState<TokenSettingActivityRecord[]>([]);
|
||||
const [isActivityLoading, setIsActivityLoading] = useState(false);
|
||||
const [form] = Form.useForm<TokenSettingFormValue>();
|
||||
const [modalApi, modalContextHolder] = Modal.useModal();
|
||||
const lastHydratedFormKeyRef = useRef('');
|
||||
@@ -264,6 +269,83 @@ export function TokenSettingManagementPage({
|
||||
form.setFieldsValue(toFormValue(isCreating ? null : effectiveSelectedTokenSetting));
|
||||
}, [detailMode, effectiveSelectedTokenSetting?.id, form, isCreating]);
|
||||
|
||||
useEffect(() => {
|
||||
if (detailMode !== 'detail' || isCreating || !effectiveSelectedTokenSetting?.id || isSharedPreviewMode) {
|
||||
setActivities([]);
|
||||
setIsActivityLoading(false);
|
||||
return;
|
||||
}
|
||||
|
||||
let cancelled = false;
|
||||
setIsActivityLoading(true);
|
||||
|
||||
void fetchTokenSettingActivities(
|
||||
effectiveSelectedTokenSetting.id,
|
||||
isSharedManageMode ? { shareToken: sharedAccess?.shareToken } : undefined,
|
||||
)
|
||||
.then((nextItems) => {
|
||||
if (!cancelled) {
|
||||
setActivities(nextItems);
|
||||
}
|
||||
})
|
||||
.catch(() => {
|
||||
if (!cancelled) {
|
||||
setActivities([]);
|
||||
}
|
||||
})
|
||||
.finally(() => {
|
||||
if (!cancelled) {
|
||||
setIsActivityLoading(false);
|
||||
}
|
||||
});
|
||||
|
||||
return () => {
|
||||
cancelled = true;
|
||||
};
|
||||
}, [detailMode, effectiveSelectedTokenSetting?.id, isCreating, isSharedManageMode, isSharedPreviewMode, sharedAccess?.shareToken]);
|
||||
|
||||
const activityColumns = useMemo(
|
||||
() => [
|
||||
{
|
||||
title: '시각',
|
||||
dataIndex: 'createdAt',
|
||||
key: 'createdAt',
|
||||
render: (value: string) => new Date(value).toLocaleString('ko-KR'),
|
||||
},
|
||||
{
|
||||
title: '유형',
|
||||
dataIndex: 'activityType',
|
||||
key: 'activityType',
|
||||
render: (value: TokenSettingActivityRecord['activityType']) => <Tag>{value}</Tag>,
|
||||
},
|
||||
{
|
||||
title: '내용',
|
||||
dataIndex: 'summary',
|
||||
key: 'summary',
|
||||
},
|
||||
{
|
||||
title: '변경 상세',
|
||||
dataIndex: 'detail',
|
||||
key: 'detail',
|
||||
render: (value: string | null) => value || '-',
|
||||
},
|
||||
{
|
||||
title: 'IP',
|
||||
key: 'ip',
|
||||
render: (_value: unknown, item: TokenSettingActivityRecord) => {
|
||||
const lines = [
|
||||
item.externalIp ? `외부 ${item.externalIp}` : null,
|
||||
item.clientIp ? `서버 ${item.clientIp}` : null,
|
||||
item.forwardedFor ? `XFF ${item.forwardedFor}` : null,
|
||||
item.clientId ? `client ${item.clientId}` : null,
|
||||
].filter(Boolean);
|
||||
return lines.length > 0 ? <Text style={{ whiteSpace: 'pre-line' }}>{lines.join('\n')}</Text> : '-';
|
||||
},
|
||||
},
|
||||
],
|
||||
[],
|
||||
);
|
||||
|
||||
const openCreateForm = () => {
|
||||
setIsCreating(true);
|
||||
setSelectedTokenSettingId(null);
|
||||
@@ -522,7 +604,7 @@ export function TokenSettingManagementPage({
|
||||
<div className="chat-type-management-page__editor-scroll">
|
||||
<Tabs
|
||||
activeKey={activeDetailTab}
|
||||
onChange={(key) => setActiveDetailTab(key as 'basic' | 'quota' | 'apps')}
|
||||
onChange={(key) => setActiveDetailTab(key as 'basic' | 'quota' | 'apps' | 'history')}
|
||||
className="token-setting-management-page__detail-tabs"
|
||||
items={[
|
||||
{
|
||||
@@ -680,6 +762,35 @@ export function TokenSettingManagementPage({
|
||||
</div>
|
||||
),
|
||||
},
|
||||
{
|
||||
key: 'history',
|
||||
label: '변경 이력',
|
||||
children: (
|
||||
<div className="token-setting-management-page__section-scroll">
|
||||
{isCreating ? (
|
||||
<Alert
|
||||
showIcon
|
||||
type="info"
|
||||
message="신규 등록 전에는 변경 이력이 없습니다."
|
||||
description="설정을 먼저 저장하면 이후 수정/삭제 이력과 IP 기록이 여기에 쌓입니다."
|
||||
/>
|
||||
) : isActivityLoading ? (
|
||||
<Paragraph>불러오는 중...</Paragraph>
|
||||
) : activities.length > 0 ? (
|
||||
<Table
|
||||
size="small"
|
||||
rowKey="id"
|
||||
columns={activityColumns}
|
||||
dataSource={activities}
|
||||
pagination={{ pageSize: 8, hideOnSinglePage: true }}
|
||||
scroll={{ x: 760 }}
|
||||
/>
|
||||
) : (
|
||||
<Empty description="기록된 변경 이력이 없습니다." />
|
||||
)}
|
||||
</div>
|
||||
),
|
||||
},
|
||||
]}
|
||||
/>
|
||||
</div>
|
||||
|
||||
Reference in New Issue
Block a user