This commit is contained in:
2025-08-31 01:06:02 -04:00
parent d8f1fb3753
commit 7a7ad1e44d
3 changed files with 320 additions and 278 deletions

View File

@ -1,18 +1,18 @@
import React, { useEffect, useState } from 'react';
import {
Table,
Badge,
Button,
Group,
Text,
ActionIcon,
Menu,
Paper,
Stack,
Title,
Alert,
Group,
ActionIcon,
Badge,
Card,
Text,
Loader,
Center,
Alert,
Tooltip,
Menu,
} from '@mantine/core';
import {
IconPlayerPlay,
@ -114,40 +114,50 @@ export const FunctionList: React.FC<FunctionListProps> = ({
}
};
if (loading) {
if (loading && functions.length === 0) {
return (
<Center py={60}>
<Loader size="lg" />
</Center>
);
}
<Stack gap="lg">
<Group justify="space-between">
<Title order={2}>Functions</Title>
<Group>
<Button
leftSection={<IconRefresh size={16} />}
onClick={loadFunctions}
loading={loading}
>
Refresh
</Button>
<Button
leftSection={<IconPlus size={16} />}
onClick={onCreateFunction}
>
Create Function
</Button>
</Group>
</Group>
if (error) {
return (
<Alert icon={<IconExclamationCircle size={16} />} title="Error" color="red" mb="md">
{error}
<Button variant="light" size="xs" mt="sm" onClick={loadFunctions}>
Retry
</Button>
</Alert>
<Stack align="center" justify="center" h={200}>
<Loader size="lg" />
<Text>Loading functions...</Text>
</Stack>
</Stack>
);
}
return (
<Paper shadow="xs" p="md">
<Group justify="space-between" mb="md">
<Stack gap="lg">
<Group justify="space-between">
<Title order={2}>Functions</Title>
<Group>
<Button
leftSection={<IconRefresh size={14} />}
variant="light"
leftSection={<IconRefresh size={16} />}
onClick={loadFunctions}
loading={loading}
>
Refresh
</Button>
<Button
leftSection={<IconPlus size={14} />}
leftSection={<IconPlus size={16} />}
onClick={onCreateFunction}
>
Create Function
@ -155,121 +165,129 @@ export const FunctionList: React.FC<FunctionListProps> = ({
</Group>
</Group>
{error && (
<Alert color="red" title="Error">
{error}
<Button variant="light" size="xs" mt="sm" onClick={loadFunctions}>
Retry
</Button>
</Alert>
)}
{functions.length === 0 ? (
<Center py={40}>
<div style={{ textAlign: 'center' }}>
<Card shadow="sm" radius="md" withBorder p="xl">
<Stack align="center" gap="md">
<IconCode size={48} color="gray" />
<Text size="lg" mt="md" c="dimmed">
No functions found
</Text>
<Text size="sm" c="dimmed" mb="md">
Create your first serverless function to get started
</Text>
<div style={{ textAlign: 'center' }}>
<Text fw={500} mb="xs">
No functions found
</Text>
<Text size="sm" c="dimmed">
Create your first serverless function to get started
</Text>
</div>
<Button
leftSection={<IconPlus size={14} />}
leftSection={<IconPlus size={16} />}
onClick={onCreateFunction}
>
Create Function
</Button>
</div>
</Center>
</Stack>
</Card>
) : (
<Table striped>
<Table.Thead>
<Table.Tr>
<Table.Th>Name</Table.Th>
<Table.Th>Runtime</Table.Th>
<Table.Th>Image</Table.Th>
<Table.Th>Memory</Table.Th>
<Table.Th>Timeout</Table.Th>
<Table.Th>Owner</Table.Th>
<Table.Th>Created</Table.Th>
<Table.Th>Actions</Table.Th>
</Table.Tr>
</Table.Thead>
<Table.Tbody>
{functions.map((func) => (
<Table.Tr key={func.id}>
<Table.Td>
<Text fw={500}>{func.name}</Text>
<Text size="xs" c="dimmed">{func.handler}</Text>
</Table.Td>
<Table.Td>
<Badge color={getRuntimeColor(func.runtime)} variant="light">
{func.runtime}
</Badge>
</Table.Td>
<Table.Td>
<Text size="sm" c="dimmed">
{func.image}
</Text>
</Table.Td>
<Table.Td>
<Text size="sm">{func.memory} MB</Text>
</Table.Td>
<Table.Td>
<Text size="sm">{func.timeout}</Text>
</Table.Td>
<Table.Td>
<Text size="sm">
{func.owner?.name || 'Unknown'}
{func.owner?.type && (
<Text size="xs" c="dimmed">({func.owner.type})</Text>
)}
</Text>
</Table.Td>
<Table.Td>
<Text size="sm">
{func.created_at ? new Date(func.created_at).toLocaleDateString() : 'N/A'}
</Text>
</Table.Td>
<Table.Td>
<Group gap="xs">
<Tooltip label="Execute Function">
<ActionIcon
variant="light"
color="green"
size="sm"
onClick={() => onExecuteFunction(func)}
>
<IconPlayerPlay size={16} />
</ActionIcon>
</Tooltip>
<Menu position="bottom-end">
<Menu.Target>
<ActionIcon variant="light" size="sm">
<IconDots size={16} />
</ActionIcon>
</Menu.Target>
<Menu.Dropdown>
<Menu.Item
leftSection={<IconSettings size={16} />}
onClick={() => onEditFunction(func)}
>
Edit
</Menu.Item>
<Menu.Item
leftSection={<IconRocket size={16} />}
onClick={() => handleDeploy(func)}
>
Deploy
</Menu.Item>
<Menu.Item
leftSection={<IconTrash size={16} />}
color="red"
onClick={() => handleDelete(func)}
>
Delete
</Menu.Item>
</Menu.Dropdown>
</Menu>
</Group>
</Table.Td>
<Card shadow="sm" radius="md" withBorder>
<Table>
<Table.Thead>
<Table.Tr>
<Table.Th>Name</Table.Th>
<Table.Th>Runtime</Table.Th>
<Table.Th>Image</Table.Th>
<Table.Th>Memory</Table.Th>
<Table.Th>Timeout</Table.Th>
<Table.Th>Owner</Table.Th>
<Table.Th>Created</Table.Th>
<Table.Th>Actions</Table.Th>
</Table.Tr>
))}
</Table.Tbody>
</Table>
</Table.Thead>
<Table.Tbody>
{functions.map((func) => (
<Table.Tr key={func.id}>
<Table.Td>
<Text fw={500}>{func.name}</Text>
<Text size="xs" c="dimmed">{func.description || ''}</Text>
</Table.Td>
<Table.Td>
<Badge color={getRuntimeColor(func.runtime)} variant="light">
{func.runtime}
</Badge>
</Table.Td>
<Table.Td>
<Text size="sm" c="dimmed">
{func.image || ''}
</Text>
</Table.Td>
<Table.Td>
<Text size="sm">{func.memoryLimit || 'N/A'} MB</Text>
</Table.Td>
<Table.Td>
<Text size="sm">{func.timeout || 'N/A'}</Text>
</Table.Td>
<Table.Td>
<Text size="sm">N/A</Text>
</Table.Td>
<Table.Td>
<Text size="sm">
{func.createdAt ? new Date(func.createdAt).toLocaleDateString() : 'N/A'}
</Text>
</Table.Td>
<Table.Td>
<Group gap="xs">
<Tooltip label="Execute Function">
<ActionIcon
variant="subtle"
color="green"
size="sm"
onClick={() => onExecuteFunction(func)}
>
<IconPlayerPlay size={16} />
</ActionIcon>
</Tooltip>
<Menu position="bottom-end">
<Menu.Target>
<ActionIcon variant="subtle" size="sm">
<IconDots size={16} />
</ActionIcon>
</Menu.Target>
<Menu.Dropdown>
<Menu.Item
leftSection={<IconSettings size={16} />}
onClick={() => onEditFunction(func)}
>
Edit
</Menu.Item>
<Menu.Item
leftSection={<IconRocket size={16} />}
onClick={() => handleDeploy(func)}
>
Deploy
</Menu.Item>
<Menu.Item
leftSection={<IconTrash size={16} />}
color="red"
onClick={() => handleDelete(func)}
>
Delete
</Menu.Item>
</Menu.Dropdown>
</Menu>
</Group>
</Table.Td>
</Table.Tr>
))}
</Table.Tbody>
</Table>
</Card>
)}
</Paper>
</Stack>
);
};