-
This commit is contained in:
2
kms/web/dist/665.js
vendored
2
kms/web/dist/665.js
vendored
File diff suppressed because one or more lines are too long
2
kms/web/dist/main.js
vendored
2
kms/web/dist/main.js
vendored
File diff suppressed because one or more lines are too long
@ -1,7 +1,15 @@
|
||||
import React from 'react';
|
||||
import {
|
||||
FormSidebar,
|
||||
FormField
|
||||
Sidebar,
|
||||
FormField,
|
||||
Stack,
|
||||
Group,
|
||||
Button,
|
||||
TextInput,
|
||||
Select,
|
||||
MultiSelect,
|
||||
useForm,
|
||||
notifications
|
||||
} from '@skybridge/web-components';
|
||||
import { apiService, Application, CreateApplicationRequest } from '../services/apiService';
|
||||
|
||||
@ -18,113 +26,149 @@ const ApplicationSidebar: React.FC<ApplicationSidebarProps> = ({
|
||||
onSuccess,
|
||||
editingApp,
|
||||
}) => {
|
||||
const form = useForm({
|
||||
initialValues: {
|
||||
app_id: editingApp?.app_id || '',
|
||||
app_link: editingApp?.app_link || '',
|
||||
type: editingApp?.type || [],
|
||||
callback_url: editingApp?.callback_url || '',
|
||||
token_prefix: editingApp?.token_prefix || '',
|
||||
token_renewal_duration: '24h',
|
||||
max_token_duration: '168h',
|
||||
},
|
||||
validate: {
|
||||
app_id: (value) => value.length < 1 ? 'Application ID is required' : null,
|
||||
app_link: (value) => value.length < 1 ? 'Application Link is required' : null,
|
||||
type: (value) => value.length < 1 ? 'Application Type is required' : null,
|
||||
callback_url: (value) => value.length < 1 ? 'Callback URL is required' : null,
|
||||
},
|
||||
});
|
||||
|
||||
const parseDuration = (duration: string): number => {
|
||||
// Convert duration string like "24h" to seconds
|
||||
const match = duration.match(/^(\d+)([hmd]?)$/);
|
||||
if (!match) return 86400; // Default to 24h in seconds
|
||||
if (!match) return 86400;
|
||||
|
||||
const value = parseInt(match[1]);
|
||||
const unit = match[2] || 'h';
|
||||
|
||||
switch (unit) {
|
||||
case 'm': return value * 60; // minutes to seconds
|
||||
case 'h': return value * 3600; // hours to seconds
|
||||
case 'd': return value * 86400; // days to seconds
|
||||
default: return value * 3600; // default to hours
|
||||
case 'm': return value * 60;
|
||||
case 'h': return value * 3600;
|
||||
case 'd': return value * 86400;
|
||||
default: return value * 3600;
|
||||
}
|
||||
};
|
||||
|
||||
const fields: FormField[] = [
|
||||
{
|
||||
name: 'app_id',
|
||||
label: 'Application ID',
|
||||
type: 'text',
|
||||
required: true,
|
||||
placeholder: 'my-app-id',
|
||||
disabled: !!editingApp, // Disable editing for existing apps
|
||||
},
|
||||
{
|
||||
name: 'app_link',
|
||||
label: 'Application Link',
|
||||
type: 'text',
|
||||
required: true,
|
||||
placeholder: 'https://myapp.example.com',
|
||||
validation: { url: true },
|
||||
},
|
||||
{
|
||||
name: 'type',
|
||||
label: 'Application Type',
|
||||
type: 'multiselect',
|
||||
required: true,
|
||||
options: [
|
||||
{ value: 'static', label: 'Static Token App' },
|
||||
{ value: 'user', label: 'User Token App' },
|
||||
],
|
||||
},
|
||||
{
|
||||
name: 'callback_url',
|
||||
label: 'Callback URL',
|
||||
type: 'text',
|
||||
required: true,
|
||||
placeholder: 'https://myapp.example.com/callback',
|
||||
validation: { url: true },
|
||||
},
|
||||
{
|
||||
name: 'token_prefix',
|
||||
label: 'Token Prefix (Optional)',
|
||||
type: 'text',
|
||||
required: false,
|
||||
placeholder: 'myapp_',
|
||||
},
|
||||
{
|
||||
name: 'token_renewal_duration',
|
||||
label: 'Token Renewal Duration',
|
||||
type: 'text',
|
||||
required: false,
|
||||
placeholder: '24h',
|
||||
defaultValue: '24h',
|
||||
},
|
||||
{
|
||||
name: 'max_token_duration',
|
||||
label: 'Max Token Duration',
|
||||
type: 'text',
|
||||
required: false,
|
||||
placeholder: '168h',
|
||||
defaultValue: '168h',
|
||||
},
|
||||
];
|
||||
|
||||
const handleSubmit = async (values: any) => {
|
||||
const submitData = {
|
||||
...values,
|
||||
token_renewal_duration_seconds: parseDuration(values.token_renewal_duration || '24h'),
|
||||
max_token_duration_seconds: parseDuration(values.max_token_duration || '168h'),
|
||||
owner: {
|
||||
type: 'individual',
|
||||
name: 'Admin User',
|
||||
owner: 'admin@example.com',
|
||||
},
|
||||
};
|
||||
try {
|
||||
const submitData = {
|
||||
...values,
|
||||
token_renewal_duration_seconds: parseDuration(values.token_renewal_duration || '24h'),
|
||||
max_token_duration_seconds: parseDuration(values.max_token_duration || '168h'),
|
||||
owner: {
|
||||
type: 'individual',
|
||||
name: 'Admin User',
|
||||
owner: 'admin@example.com',
|
||||
},
|
||||
};
|
||||
|
||||
if (editingApp) {
|
||||
await apiService.updateApplication(editingApp.app_id, submitData);
|
||||
} else {
|
||||
await apiService.createApplication(submitData);
|
||||
if (editingApp) {
|
||||
await apiService.updateApplication(editingApp.app_id, submitData);
|
||||
} else {
|
||||
await apiService.createApplication(submitData);
|
||||
}
|
||||
|
||||
notifications.show({
|
||||
title: 'Success',
|
||||
message: `Application ${editingApp ? 'updated' : 'created'} successfully`,
|
||||
color: 'green',
|
||||
});
|
||||
|
||||
onSuccess();
|
||||
onClose();
|
||||
} catch (error) {
|
||||
notifications.show({
|
||||
title: 'Error',
|
||||
message: `Failed to ${editingApp ? 'update' : 'create'} application`,
|
||||
color: 'red',
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
const footer = (
|
||||
<Group justify="flex-end" gap="sm">
|
||||
<Button variant="light" onClick={onClose}>
|
||||
Cancel
|
||||
</Button>
|
||||
<Button onClick={form.onSubmit(handleSubmit)}>
|
||||
{editingApp ? 'Update' : 'Create'} Application
|
||||
</Button>
|
||||
</Group>
|
||||
);
|
||||
|
||||
return (
|
||||
<FormSidebar
|
||||
<Sidebar
|
||||
opened={opened}
|
||||
onClose={onClose}
|
||||
onSuccess={onSuccess}
|
||||
title="Application"
|
||||
editMode={!!editingApp}
|
||||
editItem={editingApp}
|
||||
fields={fields}
|
||||
onSubmit={handleSubmit}
|
||||
width={450}
|
||||
/>
|
||||
title={editingApp ? 'Edit Application' : 'Create Application'}
|
||||
layoutMode={true}
|
||||
footer={footer}
|
||||
>
|
||||
<form onSubmit={form.onSubmit(handleSubmit)}>
|
||||
<Stack gap="md">
|
||||
<TextInput
|
||||
label="Application ID"
|
||||
placeholder="my-app-id"
|
||||
required
|
||||
disabled={!!editingApp}
|
||||
{...form.getInputProps('app_id')}
|
||||
/>
|
||||
|
||||
<TextInput
|
||||
label="Application Link"
|
||||
placeholder="https://myapp.example.com"
|
||||
required
|
||||
{...form.getInputProps('app_link')}
|
||||
/>
|
||||
|
||||
<MultiSelect
|
||||
label="Application Type"
|
||||
placeholder="Select application types"
|
||||
required
|
||||
data={[
|
||||
{ value: 'static', label: 'Static Token App' },
|
||||
{ value: 'user', label: 'User Token App' },
|
||||
]}
|
||||
{...form.getInputProps('type')}
|
||||
/>
|
||||
|
||||
<TextInput
|
||||
label="Callback URL"
|
||||
placeholder="https://myapp.example.com/callback"
|
||||
required
|
||||
{...form.getInputProps('callback_url')}
|
||||
/>
|
||||
|
||||
<TextInput
|
||||
label="Token Prefix (Optional)"
|
||||
placeholder="myapp_"
|
||||
{...form.getInputProps('token_prefix')}
|
||||
/>
|
||||
|
||||
<TextInput
|
||||
label="Token Renewal Duration"
|
||||
placeholder="24h"
|
||||
{...form.getInputProps('token_renewal_duration')}
|
||||
/>
|
||||
|
||||
<TextInput
|
||||
label="Max Token Duration"
|
||||
placeholder="168h"
|
||||
{...form.getInputProps('max_token_duration')}
|
||||
/>
|
||||
</Stack>
|
||||
</form>
|
||||
</Sidebar>
|
||||
);
|
||||
};
|
||||
|
||||
|
||||
@ -5,7 +5,8 @@ import {
|
||||
Badge,
|
||||
Group,
|
||||
Text,
|
||||
Stack
|
||||
SidebarLayout,
|
||||
Sidebar
|
||||
} from '@skybridge/web-components';
|
||||
import { IconEye, IconCopy } from '@tabler/icons-react';
|
||||
import { notifications } from '@mantine/notifications';
|
||||
@ -123,7 +124,18 @@ const Applications: React.FC = () => {
|
||||
];
|
||||
|
||||
return (
|
||||
<Stack gap="md">
|
||||
<SidebarLayout
|
||||
sidebarOpened={sidebarOpen}
|
||||
sidebarWidth={450}
|
||||
sidebar={
|
||||
<ApplicationSidebar
|
||||
opened={sidebarOpen}
|
||||
onClose={() => setSidebarOpen(false)}
|
||||
onSuccess={handleSuccess}
|
||||
editingApp={editingApp}
|
||||
/>
|
||||
}
|
||||
>
|
||||
<DataTable
|
||||
data={applications}
|
||||
columns={columns}
|
||||
@ -137,14 +149,7 @@ const Applications: React.FC = () => {
|
||||
customActions={customActions}
|
||||
emptyMessage="No applications found"
|
||||
/>
|
||||
|
||||
<ApplicationSidebar
|
||||
opened={sidebarOpen}
|
||||
onClose={() => setSidebarOpen(false)}
|
||||
onSuccess={handleSuccess}
|
||||
editingApp={editingApp}
|
||||
/>
|
||||
</Stack>
|
||||
</SidebarLayout>
|
||||
);
|
||||
};
|
||||
|
||||
|
||||
@ -28,7 +28,7 @@ import {
|
||||
IconRefresh,
|
||||
} from '@tabler/icons-react';
|
||||
import { DatePickerInput } from '@mantine/dates';
|
||||
import { notifications } from '@mantine/notifications';
|
||||
import { notifications, StatusBadge, LoadingState, EmptyState } from '@skybridge/web-components';
|
||||
import {
|
||||
apiService,
|
||||
AuditEvent,
|
||||
@ -124,19 +124,6 @@ const Audit: React.FC = () => {
|
||||
});
|
||||
};
|
||||
|
||||
const getStatusColor = (status: string) => {
|
||||
switch (status.toLowerCase()) {
|
||||
case 'success':
|
||||
return 'green';
|
||||
case 'failure':
|
||||
case 'error':
|
||||
return 'red';
|
||||
case 'warning':
|
||||
return 'yellow';
|
||||
default:
|
||||
return 'gray';
|
||||
}
|
||||
};
|
||||
|
||||
const getEventTypeColor = (type: string) => {
|
||||
if (type.startsWith('auth.')) return 'blue';
|
||||
@ -166,9 +153,7 @@ const Audit: React.FC = () => {
|
||||
</Badge>
|
||||
</Table.Td>
|
||||
<Table.Td>
|
||||
<Badge color={getStatusColor(event.status)} variant="light" size="sm">
|
||||
{event.status}
|
||||
</Badge>
|
||||
<StatusBadge value={event.status} size="sm" />
|
||||
</Table.Td>
|
||||
<Table.Td>
|
||||
<Text size="sm" c="dimmed">
|
||||
@ -360,9 +345,7 @@ const Audit: React.FC = () => {
|
||||
|
||||
<Group justify="space-between">
|
||||
<Text fw={500}>Status:</Text>
|
||||
<Badge color={getStatusColor(selectedEvent.status)} variant="light">
|
||||
{selectedEvent.status}
|
||||
</Badge>
|
||||
<StatusBadge value={selectedEvent.status} />
|
||||
</Group>
|
||||
|
||||
<Group justify="space-between">
|
||||
|
||||
Reference in New Issue
Block a user