Skip to content

Commit f31b0ca

Browse files
committed
update
1 parent 5e61671 commit f31b0ca

File tree

14 files changed

+1224
-68
lines changed

14 files changed

+1224
-68
lines changed

.claude/settings.local.json

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
{
2+
"permissions": {
3+
"allow": [
4+
"Bash(find:*)",
5+
"Bash(npx shadcn@latest add:*)",
6+
"Bash(npm run build:*)",
7+
"Bash(npm install:*)"
8+
],
9+
"deny": [],
10+
"ask": []
11+
}
12+
}

.mcp.json

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
{
2+
"mcpServers": {
3+
"shadcn": {
4+
"command": "npx",
5+
"args": [
6+
"shadcn@latest",
7+
"mcp"
8+
]
9+
}
10+
}
11+
}

CLAUDE.md

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
# Project Instructions
2+
3+
## Use Context7 and Shadcn registry by Default
4+
Always use context7, Shadcn registry when I need code generation, setup or configuration steps, or library/API documentation. This means you should automatically use the Context7 MCP tools and Shadcn registry to resolve library id and get library docs without me having to explicitly ask.

ui/src/App.jsx

Lines changed: 18 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,12 @@
11
import React from 'react';
2+
import { ThemeProvider } from './components/theme-provider';
23
import { AppProvider, useApp } from './contexts/AppContext';
34
import { ResourceProvider } from './contexts/ResourceContext';
45
import { MiddlewareProvider } from './contexts/MiddlewareContext';
56
import { DataSourceProvider } from './contexts/DataSourceContext';
67
import { ServiceProvider } from './contexts/ServiceContext';
78
import { PluginProvider } from './contexts/PluginContext';
8-
import { Header } from './components/common';
9+
import Header from './components/common/Header';
910

1011
import Dashboard from './components/dashboard/Dashboard';
1112
import ResourcesList from './components/resources/ResourcesList';
@@ -63,7 +64,7 @@ const MainContent = () => {
6364
};
6465

6566
return (
66-
<div className="min-h-screen flex flex-col">
67+
<div className="min-h-screen flex flex-col bg-background text-foreground">
6768
<Header
6869
currentPage={page}
6970
navigateTo={navigateTo}
@@ -78,19 +79,21 @@ const MainContent = () => {
7879

7980
const App = () => {
8081
return (
81-
<AppProvider>
82-
<DataSourceProvider>
83-
<ResourceProvider>
84-
<MiddlewareProvider>
85-
<ServiceProvider>
86-
<PluginProvider>
87-
<MainContent />
88-
</PluginProvider>
89-
</ServiceProvider>
90-
</MiddlewareProvider>
91-
</ResourceProvider>
92-
</DataSourceProvider>
93-
</AppProvider>
82+
<ThemeProvider defaultTheme="dark" storageKey="middleware-manager-theme">
83+
<AppProvider>
84+
<DataSourceProvider>
85+
<ResourceProvider>
86+
<MiddlewareProvider>
87+
<ServiceProvider>
88+
<PluginProvider>
89+
<MainContent />
90+
</PluginProvider>
91+
</ServiceProvider>
92+
</MiddlewareProvider>
93+
</ResourceProvider>
94+
</DataSourceProvider>
95+
</AppProvider>
96+
</ThemeProvider>
9497
);
9598
};
9699

ui/src/components/common/LoadingSpinner.jsx

Lines changed: 23 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -1,23 +1,34 @@
11
import React from 'react';
2+
import { Loader2 } from 'lucide-react';
23
import { cn } from '../../lib/utils';
34

5+
/**
6+
* Loading spinner component with optional message
7+
* Built from scratch using Shadcn UI patterns
8+
*
9+
* @param {Object} props
10+
* @param {string} props.message - Optional loading message
11+
* @param {string} props.size - Size of the spinner: "sm", "md", "lg"
12+
* @param {string} props.className - Additional CSS classes
13+
* @returns {JSX.Element}
14+
*/
415
const LoadingSpinner = ({ message = 'Loading...', size = 'md', className }) => {
5-
const spinnerSizes = {
6-
sm: 'w-6 h-6 border-2',
7-
md: 'w-12 h-12 border-3',
8-
lg: 'w-16 h-16 border-4',
16+
const sizeClasses = {
17+
sm: 'h-4 w-4',
18+
md: 'h-8 w-8',
19+
lg: 'h-12 w-12',
920
};
1021

11-
const spinnerSize = spinnerSizes[size] || spinnerSizes.md;
12-
1322
return (
14-
<div className={cn("flex flex-col items-center justify-center p-6", className)}>
15-
<div className={cn(
16-
spinnerSize,
17-
"border-primary border-t-transparent rounded-full animate-spin mb-4"
18-
)} />
23+
<div className={cn('flex flex-col items-center justify-center p-6', className)}>
24+
<Loader2
25+
className={cn(
26+
'animate-spin text-primary',
27+
sizeClasses[size] || sizeClasses.md
28+
)}
29+
/>
1930
{message && (
20-
<p className="text-muted-foreground text-sm">{message}</p>
31+
<p className="mt-4 text-sm text-muted-foreground">{message}</p>
2132
)}
2233
</div>
2334
);
Lines changed: 132 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,132 @@
1+
import React, { useState, useEffect } from 'react';
2+
import {
3+
Dialog,
4+
DialogContent,
5+
DialogDescription,
6+
DialogFooter,
7+
DialogHeader,
8+
DialogTitle,
9+
} from '../../ui/dialog';
10+
import { Button } from '../../ui/button';
11+
import { Input } from '../../ui/input';
12+
import { Label } from '../../ui/label';
13+
import { Alert, AlertDescription } from '../../ui/alert';
14+
import { AlertCircle, Info } from 'lucide-react';
15+
16+
/**
17+
* HTTP Configuration Modal for managing resource entrypoints
18+
* Refactored from scratch using Shadcn UI components
19+
*
20+
* @param {Object} props
21+
* @param {string} props.entrypoints - Current entrypoints string
22+
* @param {Function} props.setEntrypoints - Function to update entrypoints in parent state (optional)
23+
* @param {Function} props.onSave - Save handler function (receives { entrypoints: string })
24+
* @param {Function} props.onClose - Close modal handler
25+
* @param {boolean} props.isDisabled - Whether the resource is disabled
26+
*/
27+
const HTTPConfigModal = ({
28+
entrypoints: initialEntrypoints,
29+
setEntrypoints: setParentEntrypoints,
30+
onSave,
31+
onClose,
32+
isDisabled
33+
}) => {
34+
const [localEntrypoints, setLocalEntrypoints] = useState(initialEntrypoints || 'websecure');
35+
const [saving, setSaving] = useState(false);
36+
37+
useEffect(() => {
38+
setLocalEntrypoints(initialEntrypoints || 'websecure');
39+
}, [initialEntrypoints]);
40+
41+
const handleInputChange = (e) => {
42+
setLocalEntrypoints(e.target.value);
43+
if (setParentEntrypoints) {
44+
setParentEntrypoints(e.target.value);
45+
}
46+
};
47+
48+
const handleSubmit = async (e) => {
49+
e.preventDefault();
50+
if (isDisabled) return;
51+
52+
setSaving(true);
53+
try {
54+
await onSave({ entrypoints: localEntrypoints || 'websecure' });
55+
onClose();
56+
} catch (err) {
57+
console.error('HTTP config update error:', err);
58+
} finally {
59+
setSaving(false);
60+
}
61+
};
62+
63+
return (
64+
<Dialog open={true} onOpenChange={onClose}>
65+
<DialogContent className="sm:max-w-[500px]">
66+
<DialogHeader>
67+
<DialogTitle>HTTP Router Configuration</DialogTitle>
68+
<DialogDescription>
69+
Configure HTTP entrypoints for this resource.
70+
</DialogDescription>
71+
</DialogHeader>
72+
<form onSubmit={handleSubmit}>
73+
<div className="space-y-4 py-4">
74+
{isDisabled && (
75+
<Alert variant="destructive">
76+
<AlertCircle className="h-4 w-4" />
77+
<AlertDescription>
78+
Configuration cannot be changed while the resource is disabled.
79+
</AlertDescription>
80+
</Alert>
81+
)}
82+
83+
<div className="space-y-2">
84+
<Label htmlFor="http-entrypoints">
85+
HTTP Entry Points (comma-separated)
86+
</Label>
87+
<Input
88+
id="http-entrypoints"
89+
type="text"
90+
value={localEntrypoints}
91+
onChange={handleInputChange}
92+
placeholder="websecure,metrics"
93+
required
94+
disabled={saving || isDisabled}
95+
/>
96+
<div className="space-y-1">
97+
<p className="text-xs text-muted-foreground">
98+
Standard: websecure (HTTPS), web (HTTP). Default: websecure.
99+
</p>
100+
<Alert variant="info" className="py-2">
101+
<Info className="h-3 w-3" />
102+
<AlertDescription className="text-xs">
103+
<strong>Note:</strong> Must match entrypoints defined in Traefik static configuration.
104+
</AlertDescription>
105+
</Alert>
106+
</div>
107+
</div>
108+
</div>
109+
110+
<DialogFooter>
111+
<Button
112+
type="button"
113+
variant="outline"
114+
onClick={onClose}
115+
disabled={saving}
116+
>
117+
Cancel
118+
</Button>
119+
<Button
120+
type="submit"
121+
disabled={saving || isDisabled}
122+
>
123+
{saving ? 'Saving...' : 'Save Configuration'}
124+
</Button>
125+
</DialogFooter>
126+
</form>
127+
</DialogContent>
128+
</Dialog>
129+
);
130+
};
131+
132+
export default HTTPConfigModal;

0 commit comments

Comments
 (0)