Files
skybridge/faas/web/src/App.tsx
2025-08-31 00:56:09 -04:00

206 lines
6.0 KiB
TypeScript

import React, { useState } from 'react';
import { Box, Title, Tabs, Stack, ActionIcon, Group, Select } from '@mantine/core';
import {
IconFunction,
IconPlayerPlay,
IconStar,
IconStarFilled
} from '@tabler/icons-react';
import { FunctionList } from './components/FunctionList';
import { FunctionForm } from './components/FunctionForm';
import { ExecutionModal } from './components/ExecutionModal';
import ExecutionList from './components/ExecutionList';
import { FunctionDefinition } from './types';
const App: React.FC = () => {
// Determine current route based on pathname
const getCurrentRoute = () => {
const path = window.location.pathname;
if (path.includes('/functions')) return 'functions';
if (path.includes('/executions')) return 'executions';
return 'functions';
};
const [currentRoute, setCurrentRoute] = useState(getCurrentRoute());
const [isFavorited, setIsFavorited] = useState(false);
const [selectedColor, setSelectedColor] = useState('');
const [functionFormOpened, setFunctionFormOpened] = useState(false);
const [executionModalOpened, setExecutionModalOpened] = useState(false);
const [editingFunction, setEditingFunction] = useState<FunctionDefinition | null>(null);
const [executingFunction, setExecutingFunction] = useState<FunctionDefinition | null>(null);
const [refreshKey, setRefreshKey] = useState(0);
// Listen for URL changes (for when the shell navigates)
React.useEffect(() => {
const handlePopState = () => {
setCurrentRoute(getCurrentRoute());
};
window.addEventListener('popstate', handlePopState);
return () => window.removeEventListener('popstate', handlePopState);
}, []);
const handleTabChange = (value: string | null) => {
if (value) {
// Use history.pushState to update URL and notify shell router
const basePath = '/app/faas';
const newPath = `${basePath}/${value}`;
// Update the URL and internal state
window.history.pushState(null, '', newPath);
setCurrentRoute(value);
// Dispatch a custom event so shell can respond if needed
window.dispatchEvent(new PopStateEvent('popstate', { state: null }));
}
};
const handleCreateFunction = () => {
setEditingFunction(null);
setFunctionFormOpened(true);
};
const handleEditFunction = (func: FunctionDefinition) => {
setEditingFunction(func);
setFunctionFormOpened(true);
};
const handleExecuteFunction = (func: FunctionDefinition) => {
setExecutingFunction(func);
setExecutionModalOpened(true);
};
const handleFormSuccess = () => {
setRefreshKey(prev => prev + 1);
};
const handleFormClose = () => {
setFunctionFormOpened(false);
setEditingFunction(null);
};
const handleExecutionClose = () => {
setExecutionModalOpened(false);
setExecutingFunction(null);
};
const toggleFavorite = () => {
setIsFavorited(prev => !prev);
};
const colorOptions = [
{ value: 'red', label: 'Red' },
{ value: 'blue', label: 'Blue' },
{ value: 'green', label: 'Green' },
{ value: 'purple', label: 'Purple' },
{ value: 'orange', label: 'Orange' },
{ value: 'pink', label: 'Pink' },
{ value: 'teal', label: 'Teal' },
];
const renderContent = () => {
switch (currentRoute) {
case 'functions':
return (
<FunctionList
key={refreshKey}
onCreateFunction={handleCreateFunction}
onEditFunction={handleEditFunction}
onExecuteFunction={handleExecuteFunction}
/>
);
case 'executions':
return <ExecutionList />;
default:
return (
<FunctionList
key={refreshKey}
onCreateFunction={handleCreateFunction}
onEditFunction={handleEditFunction}
onExecuteFunction={handleExecuteFunction}
/>
);
}
};
return (
<Box w="100%" pos="relative">
<Stack gap="lg">
<div>
<Group justify="space-between" align="flex-start">
<div>
<Group align="center" gap="sm" mb="xs">
<Title order={1} size="h2">
Function as a Service
</Title>
<ActionIcon
variant="subtle"
size="lg"
onClick={toggleFavorite}
aria-label={isFavorited ? "Remove from favorites" : "Add to favorites"}
>
{isFavorited ? (
<IconStarFilled size={20} color="gold" />
) : (
<IconStar size={20} />
)}
</ActionIcon>
</Group>
</div>
{/* Right-side controls */}
<Group align="flex-start" gap="lg">
<div>
<Select
placeholder="Choose a color"
data={colorOptions}
value={selectedColor}
onChange={(value) => setSelectedColor(value || '')}
size="sm"
w={150}
/>
</div>
</Group>
</Group>
</div>
<Tabs value={currentRoute} onChange={handleTabChange}>
<Tabs.List>
<Tabs.Tab
value="functions"
leftSection={<IconFunction size={16} />}
>
Functions
</Tabs.Tab>
<Tabs.Tab
value="executions"
leftSection={<IconPlayerPlay size={16} />}
>
Executions
</Tabs.Tab>
</Tabs.List>
<Box pt="md">
{renderContent()}
</Box>
</Tabs>
</Stack>
<FunctionForm
opened={functionFormOpened}
onClose={handleFormClose}
onSuccess={handleFormSuccess}
editFunction={editingFunction}
/>
<ExecutionModal
opened={executionModalOpened}
onClose={handleExecutionClose}
function={executingFunction}
/>
</Box>
);
};
export default App;