@alepha/devtools 0.13.6 → 0.13.8
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/assets/devtools/asset.BZV40eAE.css +1 -0
- package/assets/devtools/asset.Bhpm0ujk.css +1 -0
- package/assets/devtools/chunk.17gtbQUO.js +1 -0
- package/assets/devtools/chunk.1mem8WHh.js +1 -0
- package/assets/devtools/chunk.B0aYes_4.js +1 -0
- package/assets/devtools/chunk.BULoWCgJ.js +7 -0
- package/assets/devtools/chunk.BYrPfJRg.js +1 -0
- package/assets/devtools/chunk.BjFrJKj1.js +1 -0
- package/assets/devtools/chunk.BlqFPyLh.js +1 -0
- package/assets/devtools/chunk.BmJ7-uBd.js +1 -0
- package/assets/devtools/chunk.BzE7YYkj.js +6 -0
- package/assets/devtools/chunk.C49FcqzR.js +1 -0
- package/assets/devtools/chunk.CBGZOsrp.js +9 -0
- package/assets/devtools/chunk.CHRbA_gU.js +1 -0
- package/assets/devtools/chunk.Ct58VlQl.js +1 -0
- package/assets/devtools/chunk.Cw2RCl4F.js +1 -0
- package/assets/devtools/chunk.CyQeq1kA.js +1 -0
- package/assets/devtools/chunk.CzbujtK7.js +1 -0
- package/assets/devtools/chunk.DA9XnVAa.js +1 -0
- package/assets/devtools/chunk.DEUHUxKv.js +1 -0
- package/assets/devtools/chunk.DGW-W4Kc.js +1 -0
- package/assets/devtools/chunk.DHWcJNNS.js +1 -0
- package/assets/devtools/chunk.DIfRZc20.js +1 -0
- package/assets/devtools/chunk.DJJIo7HU.js +1 -0
- package/assets/devtools/chunk.DJOi4_So.js +1 -0
- package/assets/devtools/chunk.DR0SHXXd.js +1 -0
- package/assets/devtools/chunk.DinJSUfH.js +1 -0
- package/assets/devtools/chunk.DooL4OcT.js +1 -0
- package/assets/devtools/chunk.Dry2LXOT.js +1 -0
- package/assets/devtools/chunk.IVvrfXp1.js +1 -0
- package/assets/devtools/chunk.OlMI8g2F.js +1 -0
- package/assets/devtools/chunk.Pj_uCbSv.js +1 -0
- package/assets/devtools/chunk.RTodzvo0.js +2 -0
- package/assets/devtools/chunk.YXYL4YAO.js +2 -0
- package/assets/devtools/chunk.fpKvkQeU.js +1 -0
- package/assets/devtools/chunk.qDx9cjbN.js +1 -0
- package/assets/devtools/chunk.rJToME5k.js +1 -0
- package/assets/devtools/chunk.rohGhT-A.js +1 -0
- package/assets/devtools/chunk.uyVen0u2.js +1 -0
- package/assets/devtools/entry.BY4L2Uc6.js +75 -0
- package/assets/devtools/index.html +11 -0
- package/dist/index.d.ts +249 -32
- package/dist/index.js +253 -22
- package/dist/index.js.map +1 -1
- package/package.json +18 -12
- package/src/{DevToolsProvider.ts → api/DevToolsProvider.ts} +29 -1
- package/src/{providers → api/providers}/DevToolsMetadataProvider.ts +210 -2
- package/src/api/schemas/DevAtomMetadata.ts +26 -0
- package/src/api/schemas/DevCommandMetadata.ts +9 -0
- package/src/api/schemas/DevEntityMetadata.ts +57 -0
- package/src/api/schemas/DevEnvMetadata.ts +22 -0
- package/src/{schemas → api/schemas}/DevMetadata.ts +10 -1
- package/src/api/schemas/DevRouteMetadata.ts +8 -0
- package/src/index.ts +23 -16
- package/src/ui/AppRouter.tsx +85 -2
- package/src/ui/components/DevAtomsViewer.tsx +636 -0
- package/src/ui/components/DevCacheInspector.tsx +423 -0
- package/src/ui/components/DevDashboard.tsx +188 -0
- package/src/ui/components/DevEnvExplorer.tsx +462 -0
- package/src/ui/components/DevLayout.tsx +65 -4
- package/src/ui/components/DevLogViewer.tsx +161 -163
- package/src/ui/components/DevQueueMonitor.tsx +51 -0
- package/src/ui/components/DevTopicsViewer.tsx +690 -0
- package/src/ui/components/actions/ActionGroup.tsx +37 -0
- package/src/ui/components/actions/ActionItem.tsx +138 -0
- package/src/ui/components/actions/DevActionsExplorer.tsx +132 -0
- package/src/ui/components/actions/MethodBadge.tsx +18 -0
- package/src/ui/components/actions/SchemaViewer.tsx +21 -0
- package/src/ui/components/actions/TryItPanel.tsx +140 -0
- package/src/ui/components/actions/constants.ts +7 -0
- package/src/ui/components/actions/helpers.ts +18 -0
- package/src/ui/components/actions/index.ts +8 -0
- package/src/ui/components/db/ColumnBadge.tsx +55 -0
- package/src/ui/components/db/DevDbStudio.tsx +485 -0
- package/src/ui/components/db/constants.ts +11 -0
- package/src/ui/components/db/index.ts +4 -0
- package/src/ui/components/db/types.ts +7 -0
- package/src/ui/components/graph/DevDependencyGraph.tsx +358 -0
- package/src/ui/components/graph/GraphControls.tsx +162 -0
- package/src/ui/components/graph/NodeDetails.tsx +181 -0
- package/src/ui/components/graph/ProviderNode.tsx +97 -0
- package/src/ui/components/graph/constants.ts +35 -0
- package/src/ui/components/graph/helpers.ts +443 -0
- package/src/ui/components/graph/index.ts +7 -0
- package/src/ui/components/graph/types.ts +28 -0
- package/src/ui/styles.css +0 -6
- package/src/ui/resources/wotfardregular/stylesheet.css +0 -12
- package/src/ui/resources/wotfardregular/wotfard-regular-webfont.eot +0 -0
- package/src/ui/resources/wotfardregular/wotfard-regular-webfont.ttf +0 -0
- package/src/ui/resources/wotfardregular/wotfard-regular-webfont.woff2 +0 -0
- /package/src/{entities → api/entities}/logs.ts +0 -0
- /package/src/{providers → api/providers}/DevToolsDatabaseProvider.ts +0 -0
- /package/src/{repositories → api/repositories}/LogRepository.ts +0 -0
- /package/src/{schemas → api/schemas}/DevActionMetadata.ts +0 -0
- /package/src/{schemas → api/schemas}/DevBucketMetadata.ts +0 -0
- /package/src/{schemas → api/schemas}/DevCacheMetadata.ts +0 -0
- /package/src/{schemas → api/schemas}/DevModuleMetadata.ts +0 -0
- /package/src/{schemas → api/schemas}/DevPageMetadata.ts +0 -0
- /package/src/{schemas → api/schemas}/DevProviderMetadata.ts +0 -0
- /package/src/{schemas → api/schemas}/DevQueueMetadata.ts +0 -0
- /package/src/{schemas → api/schemas}/DevRealmMetadata.ts +0 -0
- /package/src/{schemas → api/schemas}/DevSchedulerMetadata.ts +0 -0
- /package/src/{schemas → api/schemas}/DevTopicMetadata.ts +0 -0
|
@@ -0,0 +1,423 @@
|
|
|
1
|
+
import { useInject } from "@alepha/react";
|
|
2
|
+
import { ui } from "@alepha/ui";
|
|
3
|
+
import {
|
|
4
|
+
ActionIcon,
|
|
5
|
+
Badge,
|
|
6
|
+
Box,
|
|
7
|
+
Button,
|
|
8
|
+
Flex,
|
|
9
|
+
ScrollArea,
|
|
10
|
+
Stack,
|
|
11
|
+
Table,
|
|
12
|
+
Tabs,
|
|
13
|
+
Text,
|
|
14
|
+
TextInput,
|
|
15
|
+
Tooltip,
|
|
16
|
+
} from "@mantine/core";
|
|
17
|
+
import {
|
|
18
|
+
IconArchive,
|
|
19
|
+
IconClock,
|
|
20
|
+
IconKey,
|
|
21
|
+
IconSearch,
|
|
22
|
+
IconServer,
|
|
23
|
+
IconTrash,
|
|
24
|
+
} from "@tabler/icons-react";
|
|
25
|
+
import { HttpClient } from "alepha/server";
|
|
26
|
+
import { useEffect, useMemo, useState } from "react";
|
|
27
|
+
import type { DevCacheMetadata } from "../../api/schemas/DevCacheMetadata.ts";
|
|
28
|
+
import { devMetadataSchema } from "../../api/schemas/DevMetadata.ts";
|
|
29
|
+
|
|
30
|
+
const PROVIDER_COLORS: Record<string, string> = {
|
|
31
|
+
memory: "#69db7c",
|
|
32
|
+
redis: "#ff6b6b",
|
|
33
|
+
default: "#495057",
|
|
34
|
+
};
|
|
35
|
+
|
|
36
|
+
const getProviderColor = (provider: string): string => {
|
|
37
|
+
return PROVIDER_COLORS[provider] ?? PROVIDER_COLORS.default;
|
|
38
|
+
};
|
|
39
|
+
|
|
40
|
+
const formatTtl = (ttl: any): string => {
|
|
41
|
+
if (!ttl) return "No TTL";
|
|
42
|
+
if (typeof ttl === "number") {
|
|
43
|
+
if (ttl < 60) return `${ttl}s`;
|
|
44
|
+
if (ttl < 3600) return `${Math.floor(ttl / 60)}m`;
|
|
45
|
+
if (ttl < 86400) return `${Math.floor(ttl / 3600)}h`;
|
|
46
|
+
return `${Math.floor(ttl / 86400)}d`;
|
|
47
|
+
}
|
|
48
|
+
return String(ttl);
|
|
49
|
+
};
|
|
50
|
+
|
|
51
|
+
const CacheSidebar = ({
|
|
52
|
+
caches,
|
|
53
|
+
selectedCache,
|
|
54
|
+
onSelectCache,
|
|
55
|
+
search,
|
|
56
|
+
onSearchChange,
|
|
57
|
+
}: {
|
|
58
|
+
caches: DevCacheMetadata[];
|
|
59
|
+
selectedCache: DevCacheMetadata | null;
|
|
60
|
+
onSelectCache: (cache: DevCacheMetadata) => void;
|
|
61
|
+
search: string;
|
|
62
|
+
onSearchChange: (search: string) => void;
|
|
63
|
+
}) => {
|
|
64
|
+
// Group caches by provider
|
|
65
|
+
const grouped = useMemo(() => {
|
|
66
|
+
const groups: Record<string, DevCacheMetadata[]> = {};
|
|
67
|
+
for (const cache of caches) {
|
|
68
|
+
const provider = cache.provider || "default";
|
|
69
|
+
if (!groups[provider]) groups[provider] = [];
|
|
70
|
+
groups[provider].push(cache);
|
|
71
|
+
}
|
|
72
|
+
return groups;
|
|
73
|
+
}, [caches]);
|
|
74
|
+
|
|
75
|
+
const providers = Object.keys(grouped).sort();
|
|
76
|
+
|
|
77
|
+
return (
|
|
78
|
+
<Stack gap={0} h="100%">
|
|
79
|
+
<Box p="sm" style={{ borderBottom: `1px solid ${ui.colors.border}` }}>
|
|
80
|
+
<TextInput
|
|
81
|
+
placeholder="Search caches..."
|
|
82
|
+
leftSection={<IconSearch size={14} />}
|
|
83
|
+
size="xs"
|
|
84
|
+
value={search}
|
|
85
|
+
onChange={(e) => onSearchChange(e.target.value)}
|
|
86
|
+
/>
|
|
87
|
+
</Box>
|
|
88
|
+
<ScrollArea style={{ flex: 1 }}>
|
|
89
|
+
{providers.map((provider) => (
|
|
90
|
+
<Box key={provider}>
|
|
91
|
+
<Text
|
|
92
|
+
size="xs"
|
|
93
|
+
fw={600}
|
|
94
|
+
c="dimmed"
|
|
95
|
+
px="sm"
|
|
96
|
+
py="xs"
|
|
97
|
+
style={{
|
|
98
|
+
backgroundColor: ui.colors.background,
|
|
99
|
+
borderBottom: `1px solid ${ui.colors.border}`,
|
|
100
|
+
textTransform: "uppercase",
|
|
101
|
+
letterSpacing: "0.05em",
|
|
102
|
+
}}
|
|
103
|
+
>
|
|
104
|
+
{provider}
|
|
105
|
+
</Text>
|
|
106
|
+
{grouped[provider].map((cache) => {
|
|
107
|
+
const isSelected = selectedCache?.name === cache.name;
|
|
108
|
+
const providerColor = getProviderColor(cache.provider);
|
|
109
|
+
return (
|
|
110
|
+
<Flex
|
|
111
|
+
key={cache.name}
|
|
112
|
+
align="center"
|
|
113
|
+
gap="xs"
|
|
114
|
+
px="sm"
|
|
115
|
+
py={6}
|
|
116
|
+
onClick={() => onSelectCache(cache)}
|
|
117
|
+
style={{
|
|
118
|
+
cursor: "pointer",
|
|
119
|
+
backgroundColor: isSelected
|
|
120
|
+
? `${providerColor}15`
|
|
121
|
+
: undefined,
|
|
122
|
+
borderLeft: isSelected
|
|
123
|
+
? `2px solid ${providerColor}`
|
|
124
|
+
: "2px solid transparent",
|
|
125
|
+
borderBottom: `1px solid ${ui.colors.border}`,
|
|
126
|
+
opacity: cache.disabled ? 0.5 : 1,
|
|
127
|
+
}}
|
|
128
|
+
>
|
|
129
|
+
<IconArchive size={14} opacity={0.5} />
|
|
130
|
+
<Text size="sm" style={{ flex: 1 }} truncate>
|
|
131
|
+
{cache.name}
|
|
132
|
+
</Text>
|
|
133
|
+
{cache.disabled && (
|
|
134
|
+
<Badge size="xs" variant="light" color="gray">
|
|
135
|
+
off
|
|
136
|
+
</Badge>
|
|
137
|
+
)}
|
|
138
|
+
</Flex>
|
|
139
|
+
);
|
|
140
|
+
})}
|
|
141
|
+
</Box>
|
|
142
|
+
))}
|
|
143
|
+
</ScrollArea>
|
|
144
|
+
</Stack>
|
|
145
|
+
);
|
|
146
|
+
};
|
|
147
|
+
|
|
148
|
+
const ConfigTab = ({ cache }: { cache: DevCacheMetadata }) => {
|
|
149
|
+
return (
|
|
150
|
+
<ScrollArea h="100%" p="md">
|
|
151
|
+
<Stack gap="lg">
|
|
152
|
+
<Box>
|
|
153
|
+
<Text size="sm" fw={600} mb="xs">
|
|
154
|
+
Configuration
|
|
155
|
+
</Text>
|
|
156
|
+
<Table striped highlightOnHover withTableBorder>
|
|
157
|
+
<Table.Tbody>
|
|
158
|
+
<Table.Tr>
|
|
159
|
+
<Table.Td w={150}>
|
|
160
|
+
<Text size="sm" c="dimmed">
|
|
161
|
+
Name
|
|
162
|
+
</Text>
|
|
163
|
+
</Table.Td>
|
|
164
|
+
<Table.Td>
|
|
165
|
+
<Text size="sm" ff="monospace">
|
|
166
|
+
{cache.name}
|
|
167
|
+
</Text>
|
|
168
|
+
</Table.Td>
|
|
169
|
+
</Table.Tr>
|
|
170
|
+
<Table.Tr>
|
|
171
|
+
<Table.Td>
|
|
172
|
+
<Text size="sm" c="dimmed">
|
|
173
|
+
Provider
|
|
174
|
+
</Text>
|
|
175
|
+
</Table.Td>
|
|
176
|
+
<Table.Td>
|
|
177
|
+
<Badge
|
|
178
|
+
size="xs"
|
|
179
|
+
variant="light"
|
|
180
|
+
color={cache.provider === "redis" ? "red" : "green"}
|
|
181
|
+
>
|
|
182
|
+
{cache.provider}
|
|
183
|
+
</Badge>
|
|
184
|
+
</Table.Td>
|
|
185
|
+
</Table.Tr>
|
|
186
|
+
<Table.Tr>
|
|
187
|
+
<Table.Td>
|
|
188
|
+
<Text size="sm" c="dimmed">
|
|
189
|
+
TTL
|
|
190
|
+
</Text>
|
|
191
|
+
</Table.Td>
|
|
192
|
+
<Table.Td>
|
|
193
|
+
<Flex align="center" gap="xs">
|
|
194
|
+
<IconClock size={14} opacity={0.5} />
|
|
195
|
+
<Text size="sm" ff="monospace">
|
|
196
|
+
{formatTtl(cache.ttl)}
|
|
197
|
+
</Text>
|
|
198
|
+
</Flex>
|
|
199
|
+
</Table.Td>
|
|
200
|
+
</Table.Tr>
|
|
201
|
+
<Table.Tr>
|
|
202
|
+
<Table.Td>
|
|
203
|
+
<Text size="sm" c="dimmed">
|
|
204
|
+
Status
|
|
205
|
+
</Text>
|
|
206
|
+
</Table.Td>
|
|
207
|
+
<Table.Td>
|
|
208
|
+
<Badge
|
|
209
|
+
size="xs"
|
|
210
|
+
variant="light"
|
|
211
|
+
color={cache.disabled ? "gray" : "green"}
|
|
212
|
+
>
|
|
213
|
+
{cache.disabled ? "Disabled" : "Enabled"}
|
|
214
|
+
</Badge>
|
|
215
|
+
</Table.Td>
|
|
216
|
+
</Table.Tr>
|
|
217
|
+
</Table.Tbody>
|
|
218
|
+
</Table>
|
|
219
|
+
</Box>
|
|
220
|
+
|
|
221
|
+
<Box>
|
|
222
|
+
<Text size="sm" fw={600} mb="xs">
|
|
223
|
+
Actions
|
|
224
|
+
</Text>
|
|
225
|
+
<Flex gap="sm">
|
|
226
|
+
<Tooltip label="Clear all cached entries (coming soon)">
|
|
227
|
+
<Button
|
|
228
|
+
size="xs"
|
|
229
|
+
variant="light"
|
|
230
|
+
color="red"
|
|
231
|
+
leftSection={<IconTrash size={14} />}
|
|
232
|
+
disabled
|
|
233
|
+
>
|
|
234
|
+
Clear All
|
|
235
|
+
</Button>
|
|
236
|
+
</Tooltip>
|
|
237
|
+
</Flex>
|
|
238
|
+
</Box>
|
|
239
|
+
</Stack>
|
|
240
|
+
</ScrollArea>
|
|
241
|
+
);
|
|
242
|
+
};
|
|
243
|
+
|
|
244
|
+
const KeysTab = ({ cache }: { cache: DevCacheMetadata }) => {
|
|
245
|
+
const [pattern, setPattern] = useState("");
|
|
246
|
+
|
|
247
|
+
return (
|
|
248
|
+
<Flex direction="column" h="100%">
|
|
249
|
+
<Box p="sm" style={{ borderBottom: `1px solid ${ui.colors.border}` }}>
|
|
250
|
+
<Flex gap="sm" align="center">
|
|
251
|
+
<TextInput
|
|
252
|
+
placeholder="Filter by pattern..."
|
|
253
|
+
leftSection={<IconSearch size={14} />}
|
|
254
|
+
size="xs"
|
|
255
|
+
value={pattern}
|
|
256
|
+
onChange={(e) => setPattern(e.target.value)}
|
|
257
|
+
style={{ flex: 1 }}
|
|
258
|
+
/>
|
|
259
|
+
<Tooltip label="Invalidate matching keys (coming soon)">
|
|
260
|
+
<ActionIcon size="sm" variant="light" color="orange" disabled>
|
|
261
|
+
<IconTrash size={14} />
|
|
262
|
+
</ActionIcon>
|
|
263
|
+
</Tooltip>
|
|
264
|
+
</Flex>
|
|
265
|
+
</Box>
|
|
266
|
+
<Flex align="center" justify="center" style={{ flex: 1 }} c="dimmed">
|
|
267
|
+
<Stack align="center" gap="xs">
|
|
268
|
+
<IconKey size={48} opacity={0.3} />
|
|
269
|
+
<Text size="sm">Key browser coming soon</Text>
|
|
270
|
+
<Text size="xs" c="dimmed">
|
|
271
|
+
Browse and manage cached keys for {cache.name}
|
|
272
|
+
</Text>
|
|
273
|
+
</Stack>
|
|
274
|
+
</Flex>
|
|
275
|
+
</Flex>
|
|
276
|
+
);
|
|
277
|
+
};
|
|
278
|
+
|
|
279
|
+
const CachePanel = ({ cache }: { cache: DevCacheMetadata }) => {
|
|
280
|
+
const providerColor = getProviderColor(cache.provider);
|
|
281
|
+
|
|
282
|
+
return (
|
|
283
|
+
<Flex direction="column" h="100%">
|
|
284
|
+
{/* Header */}
|
|
285
|
+
<Box
|
|
286
|
+
px="md"
|
|
287
|
+
py="sm"
|
|
288
|
+
style={{
|
|
289
|
+
borderBottom: `1px solid ${ui.colors.border}`,
|
|
290
|
+
backgroundColor: `${providerColor}08`,
|
|
291
|
+
}}
|
|
292
|
+
>
|
|
293
|
+
<Flex align="center" gap="sm">
|
|
294
|
+
<IconArchive size={18} opacity={0.7} />
|
|
295
|
+
<Text size="md" fw={600}>
|
|
296
|
+
{cache.name}
|
|
297
|
+
</Text>
|
|
298
|
+
<Badge
|
|
299
|
+
size="xs"
|
|
300
|
+
variant="light"
|
|
301
|
+
color={cache.provider === "redis" ? "red" : "green"}
|
|
302
|
+
>
|
|
303
|
+
{cache.provider}
|
|
304
|
+
</Badge>
|
|
305
|
+
{cache.disabled && (
|
|
306
|
+
<Badge size="xs" variant="light" color="gray">
|
|
307
|
+
disabled
|
|
308
|
+
</Badge>
|
|
309
|
+
)}
|
|
310
|
+
</Flex>
|
|
311
|
+
</Box>
|
|
312
|
+
|
|
313
|
+
{/* Tabs */}
|
|
314
|
+
<Tabs
|
|
315
|
+
defaultValue="config"
|
|
316
|
+
style={{ flex: 1, display: "flex", flexDirection: "column" }}
|
|
317
|
+
>
|
|
318
|
+
<Tabs.List px="md">
|
|
319
|
+
<Tabs.Tab value="config">Config</Tabs.Tab>
|
|
320
|
+
<Tabs.Tab value="keys">Keys</Tabs.Tab>
|
|
321
|
+
</Tabs.List>
|
|
322
|
+
|
|
323
|
+
<Tabs.Panel value="config" style={{ flex: 1, overflow: "hidden" }}>
|
|
324
|
+
<ConfigTab cache={cache} />
|
|
325
|
+
</Tabs.Panel>
|
|
326
|
+
|
|
327
|
+
<Tabs.Panel value="keys" style={{ flex: 1, overflow: "hidden" }}>
|
|
328
|
+
<KeysTab cache={cache} />
|
|
329
|
+
</Tabs.Panel>
|
|
330
|
+
</Tabs>
|
|
331
|
+
</Flex>
|
|
332
|
+
);
|
|
333
|
+
};
|
|
334
|
+
|
|
335
|
+
const EmptyState = () => (
|
|
336
|
+
<Flex align="center" justify="center" h="100%" c="dimmed">
|
|
337
|
+
<Stack align="center" gap="xs">
|
|
338
|
+
<IconArchive size={48} opacity={0.3} />
|
|
339
|
+
<Text size="sm">Select a cache to view its configuration</Text>
|
|
340
|
+
</Stack>
|
|
341
|
+
</Flex>
|
|
342
|
+
);
|
|
343
|
+
|
|
344
|
+
const NoCachesState = () => (
|
|
345
|
+
<Flex align="center" justify="center" h="100%" c="dimmed">
|
|
346
|
+
<Stack align="center" gap="xs">
|
|
347
|
+
<IconServer size={48} opacity={0.3} />
|
|
348
|
+
<Text>No caches found</Text>
|
|
349
|
+
<Text size="sm" c="dimmed">
|
|
350
|
+
Add caches using $cache primitive to see them here
|
|
351
|
+
</Text>
|
|
352
|
+
</Stack>
|
|
353
|
+
</Flex>
|
|
354
|
+
);
|
|
355
|
+
|
|
356
|
+
export const DevCacheInspector = () => {
|
|
357
|
+
const http = useInject(HttpClient);
|
|
358
|
+
const [caches, setCaches] = useState<DevCacheMetadata[]>([]);
|
|
359
|
+
const [loading, setLoading] = useState(true);
|
|
360
|
+
const [selectedCache, setSelectedCache] = useState<DevCacheMetadata | null>(
|
|
361
|
+
null,
|
|
362
|
+
);
|
|
363
|
+
const [search, setSearch] = useState("");
|
|
364
|
+
|
|
365
|
+
// Fetch caches
|
|
366
|
+
useEffect(() => {
|
|
367
|
+
http
|
|
368
|
+
.fetch("/devtools/api/metadata", {
|
|
369
|
+
schema: { response: devMetadataSchema },
|
|
370
|
+
})
|
|
371
|
+
.then((res) => {
|
|
372
|
+
setCaches(res.data.caches);
|
|
373
|
+
setLoading(false);
|
|
374
|
+
});
|
|
375
|
+
}, []);
|
|
376
|
+
|
|
377
|
+
// Filter caches by search
|
|
378
|
+
const filteredCaches = useMemo(() => {
|
|
379
|
+
if (!search) return caches;
|
|
380
|
+
const searchLower = search.toLowerCase();
|
|
381
|
+
return caches.filter((c) => c.name.toLowerCase().includes(searchLower));
|
|
382
|
+
}, [caches, search]);
|
|
383
|
+
|
|
384
|
+
if (loading) {
|
|
385
|
+
return (
|
|
386
|
+
<Flex align="center" justify="center" h="100%">
|
|
387
|
+
<Text c="dimmed">Loading...</Text>
|
|
388
|
+
</Flex>
|
|
389
|
+
);
|
|
390
|
+
}
|
|
391
|
+
|
|
392
|
+
if (caches.length === 0) {
|
|
393
|
+
return <NoCachesState />;
|
|
394
|
+
}
|
|
395
|
+
|
|
396
|
+
return (
|
|
397
|
+
<Flex h="100%" style={{ overflow: "hidden" }}>
|
|
398
|
+
{/* Sidebar */}
|
|
399
|
+
<Box
|
|
400
|
+
w={240}
|
|
401
|
+
style={{
|
|
402
|
+
borderRight: `1px solid ${ui.colors.border}`,
|
|
403
|
+
backgroundColor: ui.colors.surface,
|
|
404
|
+
}}
|
|
405
|
+
>
|
|
406
|
+
<CacheSidebar
|
|
407
|
+
caches={filteredCaches}
|
|
408
|
+
selectedCache={selectedCache}
|
|
409
|
+
onSelectCache={setSelectedCache}
|
|
410
|
+
search={search}
|
|
411
|
+
onSearchChange={setSearch}
|
|
412
|
+
/>
|
|
413
|
+
</Box>
|
|
414
|
+
|
|
415
|
+
{/* Main content */}
|
|
416
|
+
<Box style={{ flex: 1, overflow: "hidden" }}>
|
|
417
|
+
{selectedCache ? <CachePanel cache={selectedCache} /> : <EmptyState />}
|
|
418
|
+
</Box>
|
|
419
|
+
</Flex>
|
|
420
|
+
);
|
|
421
|
+
};
|
|
422
|
+
|
|
423
|
+
export default DevCacheInspector;
|
|
@@ -0,0 +1,188 @@
|
|
|
1
|
+
import { useInject } from "@alepha/react";
|
|
2
|
+
import { ui } from "@alepha/ui";
|
|
3
|
+
import { Box, Flex, SimpleGrid, Text } from "@mantine/core";
|
|
4
|
+
import {
|
|
5
|
+
IconApi,
|
|
6
|
+
IconArchive,
|
|
7
|
+
IconAtom,
|
|
8
|
+
IconBucket,
|
|
9
|
+
IconCalendarEvent,
|
|
10
|
+
IconDatabase,
|
|
11
|
+
IconLock,
|
|
12
|
+
IconMessageCircle,
|
|
13
|
+
IconPackages,
|
|
14
|
+
IconServer,
|
|
15
|
+
IconStack2,
|
|
16
|
+
IconTerminal,
|
|
17
|
+
IconTools,
|
|
18
|
+
IconVariable,
|
|
19
|
+
} from "@tabler/icons-react";
|
|
20
|
+
import { HttpClient } from "alepha/server";
|
|
21
|
+
import type { ReactNode } from "react";
|
|
22
|
+
import { useEffect, useState } from "react";
|
|
23
|
+
import {
|
|
24
|
+
type DevMetadata,
|
|
25
|
+
devMetadataSchema,
|
|
26
|
+
} from "../../api/schemas/DevMetadata.ts";
|
|
27
|
+
|
|
28
|
+
interface StatCardProps {
|
|
29
|
+
label: string;
|
|
30
|
+
count: number;
|
|
31
|
+
icon: ReactNode;
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
const StatCard = ({ label, count, icon }: StatCardProps) => (
|
|
35
|
+
<Flex
|
|
36
|
+
direction="column"
|
|
37
|
+
align="center"
|
|
38
|
+
justify="center"
|
|
39
|
+
p="xl"
|
|
40
|
+
style={{
|
|
41
|
+
borderRadius: 12,
|
|
42
|
+
border: `1px solid ${ui.colors.border}`,
|
|
43
|
+
cursor: "pointer",
|
|
44
|
+
transition: "all 0.2s ease",
|
|
45
|
+
}}
|
|
46
|
+
bg={ui.colors.surface}
|
|
47
|
+
className="devtools-card"
|
|
48
|
+
>
|
|
49
|
+
<Box opacity={0.5} mb="md">
|
|
50
|
+
{icon}
|
|
51
|
+
</Box>
|
|
52
|
+
<Text size="sm" c="dimmed">
|
|
53
|
+
<Text span fw={600} size="lg" c="var(--mantine-color-text)">
|
|
54
|
+
{count}
|
|
55
|
+
</Text>{" "}
|
|
56
|
+
{label}
|
|
57
|
+
</Text>
|
|
58
|
+
</Flex>
|
|
59
|
+
);
|
|
60
|
+
|
|
61
|
+
export const DevDashboard = () => {
|
|
62
|
+
const http = useInject(HttpClient);
|
|
63
|
+
const [metadata, setMetadata] = useState<DevMetadata | null>(null);
|
|
64
|
+
|
|
65
|
+
useEffect(() => {
|
|
66
|
+
http
|
|
67
|
+
.fetch("/devtools/api/metadata", {
|
|
68
|
+
schema: { response: devMetadataSchema },
|
|
69
|
+
})
|
|
70
|
+
.then((res) => setMetadata(res.data));
|
|
71
|
+
}, []);
|
|
72
|
+
|
|
73
|
+
if (!metadata) {
|
|
74
|
+
return (
|
|
75
|
+
<Flex align="center" justify="center" h="100%">
|
|
76
|
+
<Text c="dimmed">Loading...</Text>
|
|
77
|
+
</Flex>
|
|
78
|
+
);
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
const stats = [
|
|
82
|
+
{
|
|
83
|
+
label: "actions",
|
|
84
|
+
count: metadata.actions.length,
|
|
85
|
+
icon: <IconApi size={48} stroke={1.2} />,
|
|
86
|
+
},
|
|
87
|
+
{
|
|
88
|
+
label: "entities",
|
|
89
|
+
count: metadata.entities.length,
|
|
90
|
+
icon: <IconDatabase size={48} stroke={1.2} />,
|
|
91
|
+
},
|
|
92
|
+
{
|
|
93
|
+
label: "queues",
|
|
94
|
+
count: metadata.queues.length,
|
|
95
|
+
icon: <IconStack2 size={48} stroke={1.2} />,
|
|
96
|
+
},
|
|
97
|
+
{
|
|
98
|
+
label: "schedulers",
|
|
99
|
+
count: metadata.schedulers.length,
|
|
100
|
+
icon: <IconCalendarEvent size={48} stroke={1.2} />,
|
|
101
|
+
},
|
|
102
|
+
{
|
|
103
|
+
label: "topics",
|
|
104
|
+
count: metadata.topics.length,
|
|
105
|
+
icon: <IconMessageCircle size={48} stroke={1.2} />,
|
|
106
|
+
},
|
|
107
|
+
{
|
|
108
|
+
label: "buckets",
|
|
109
|
+
count: metadata.buckets.length,
|
|
110
|
+
icon: <IconBucket size={48} stroke={1.2} />,
|
|
111
|
+
},
|
|
112
|
+
{
|
|
113
|
+
label: "realms",
|
|
114
|
+
count: metadata.realms.length,
|
|
115
|
+
icon: <IconLock size={48} stroke={1.2} />,
|
|
116
|
+
},
|
|
117
|
+
{
|
|
118
|
+
label: "caches",
|
|
119
|
+
count: metadata.caches.length,
|
|
120
|
+
icon: <IconArchive size={48} stroke={1.2} />,
|
|
121
|
+
},
|
|
122
|
+
{
|
|
123
|
+
label: "commands",
|
|
124
|
+
count: metadata.commands.length,
|
|
125
|
+
icon: <IconTerminal size={48} stroke={1.2} />,
|
|
126
|
+
},
|
|
127
|
+
{
|
|
128
|
+
label: "env schemas",
|
|
129
|
+
count: metadata.envs.length,
|
|
130
|
+
icon: <IconVariable size={48} stroke={1.2} />,
|
|
131
|
+
},
|
|
132
|
+
{
|
|
133
|
+
label: "atoms",
|
|
134
|
+
count: metadata.atoms.length,
|
|
135
|
+
icon: <IconAtom size={48} stroke={1.2} />,
|
|
136
|
+
},
|
|
137
|
+
{
|
|
138
|
+
label: "modules",
|
|
139
|
+
count: metadata.modules.length,
|
|
140
|
+
icon: <IconPackages size={48} stroke={1.2} />,
|
|
141
|
+
},
|
|
142
|
+
{
|
|
143
|
+
label: "services",
|
|
144
|
+
count: metadata.providers.length,
|
|
145
|
+
icon: <IconServer size={48} stroke={1.2} />,
|
|
146
|
+
},
|
|
147
|
+
].filter((stat) => stat.count > 0);
|
|
148
|
+
|
|
149
|
+
return (
|
|
150
|
+
<Flex
|
|
151
|
+
direction="column"
|
|
152
|
+
align="center"
|
|
153
|
+
justify="center"
|
|
154
|
+
flex={1}
|
|
155
|
+
w="100%"
|
|
156
|
+
gap="xl"
|
|
157
|
+
>
|
|
158
|
+
<style>
|
|
159
|
+
{`
|
|
160
|
+
.devtools-card:hover {
|
|
161
|
+
border-color: var(--mantine-color-dimmed);
|
|
162
|
+
transform: translateY(-2px);
|
|
163
|
+
}
|
|
164
|
+
`}
|
|
165
|
+
</style>
|
|
166
|
+
|
|
167
|
+
<Flex direction="column" align="center" gap={4} mb="md">
|
|
168
|
+
<Flex align="center" gap="sm">
|
|
169
|
+
<IconTools size={40} stroke={1.5} opacity={0.8} />
|
|
170
|
+
<Text size="2rem" fw={300} style={{ letterSpacing: "-0.02em" }}>
|
|
171
|
+
Alepha DevTools
|
|
172
|
+
</Text>
|
|
173
|
+
</Flex>
|
|
174
|
+
<Text size="xs" c="dimmed">
|
|
175
|
+
Explore your Alepha application metadata
|
|
176
|
+
</Text>
|
|
177
|
+
</Flex>
|
|
178
|
+
|
|
179
|
+
<SimpleGrid cols={{ base: 2, sm: 3, md: 4 }} spacing="md">
|
|
180
|
+
{stats.map((stat) => (
|
|
181
|
+
<StatCard key={stat.label} {...stat} />
|
|
182
|
+
))}
|
|
183
|
+
</SimpleGrid>
|
|
184
|
+
</Flex>
|
|
185
|
+
);
|
|
186
|
+
};
|
|
187
|
+
|
|
188
|
+
export default DevDashboard;
|