@alepha/devtools 0.11.5 → 0.11.7
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/LICENSE +1 -1
- package/dist/index.d.ts +166 -284
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +227 -166
- package/dist/index.js.map +1 -1
- package/package.json +22 -20
- package/src/DevToolsProvider.ts +134 -0
- package/src/entities/logs.ts +21 -0
- package/src/index.ts +16 -6
- package/src/providers/DevToolsDatabaseProvider.ts +11 -0
- package/src/{DevCollectorProvider.ts → providers/DevToolsMetadataProvider.ts} +40 -101
- package/src/repositories/LogRepository.ts +9 -0
- package/src/ui/AppRouter.tsx +3 -36
- package/src/ui/components/DevLayout.tsx +80 -0
- package/src/ui/components/DevLogViewer.tsx +254 -0
- package/src/ui/styles.css +1 -1
- package/src/ui/DevLogs.tsx +0 -143
- /package/src/ui/{wotfardregular → resources/wotfardregular}/stylesheet.css +0 -0
- /package/src/ui/{wotfardregular → resources/wotfardregular}/wotfard-regular-webfont.eot +0 -0
- /package/src/ui/{wotfardregular → resources/wotfardregular}/wotfard-regular-webfont.ttf +0 -0
- /package/src/ui/{wotfardregular → resources/wotfardregular}/wotfard-regular-webfont.woff2 +0 -0
|
@@ -0,0 +1,134 @@
|
|
|
1
|
+
import { join } from "node:path";
|
|
2
|
+
import { fileURLToPath } from "node:url";
|
|
3
|
+
import { $batch } from "@alepha/batch";
|
|
4
|
+
import { $hook, $inject, Alepha, pageQuerySchema, t } from "@alepha/core";
|
|
5
|
+
import {
|
|
6
|
+
$logger,
|
|
7
|
+
JsonFormatterProvider,
|
|
8
|
+
type LogEntry,
|
|
9
|
+
logEntrySchema,
|
|
10
|
+
} from "@alepha/logger";
|
|
11
|
+
import { parseQueryString } from "@alepha/postgres";
|
|
12
|
+
import { $route, ServerProvider } from "@alepha/server";
|
|
13
|
+
import { $serve } from "@alepha/server-static";
|
|
14
|
+
import { type DevLogEntry, logs } from "./entities/logs.ts";
|
|
15
|
+
import { DevToolsMetadataProvider } from "./providers/DevToolsMetadataProvider.ts";
|
|
16
|
+
import { LogRepository } from "./repositories/LogRepository.ts";
|
|
17
|
+
import { devMetadataSchema } from "./schemas/DevMetadata.ts";
|
|
18
|
+
|
|
19
|
+
export class DevToolsProvider {
|
|
20
|
+
protected readonly log = $logger();
|
|
21
|
+
protected readonly alepha = $inject(Alepha);
|
|
22
|
+
protected readonly serverProvider = $inject(ServerProvider);
|
|
23
|
+
protected readonly jsonFormatter = $inject(JsonFormatterProvider);
|
|
24
|
+
protected readonly logs = $inject(LogRepository);
|
|
25
|
+
protected readonly devCollectorProvider = $inject(DevToolsMetadataProvider);
|
|
26
|
+
|
|
27
|
+
protected readonly onStart = $hook({
|
|
28
|
+
on: "start",
|
|
29
|
+
handler: () => {
|
|
30
|
+
this.log.info(
|
|
31
|
+
`Devtools available at ${this.serverProvider.hostname}/devtools/`,
|
|
32
|
+
);
|
|
33
|
+
},
|
|
34
|
+
});
|
|
35
|
+
|
|
36
|
+
protected batchLogs = $batch({
|
|
37
|
+
maxSize: 50,
|
|
38
|
+
maxDuration: [10, "seconds"],
|
|
39
|
+
schema: logs.insertSchema,
|
|
40
|
+
handler: async (entries) => {
|
|
41
|
+
await this.logs.createMany(entries);
|
|
42
|
+
},
|
|
43
|
+
});
|
|
44
|
+
|
|
45
|
+
protected readonly onLog = $hook({
|
|
46
|
+
on: "log",
|
|
47
|
+
handler: async (ev: { message?: string; entry: LogEntry }) => {
|
|
48
|
+
// !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
|
|
49
|
+
// CAUTION: It's very easy to create an infinite loop here.
|
|
50
|
+
// !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
|
|
51
|
+
|
|
52
|
+
try {
|
|
53
|
+
if (!this.alepha.isStarted()) {
|
|
54
|
+
return;
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
if (ev.entry.module === "alepha.devtools") {
|
|
58
|
+
// skip devtools logs to avoid infinite loop
|
|
59
|
+
return;
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
if (ev.entry.level === "TRACE" && ev.entry.module === "alepha.batch") {
|
|
63
|
+
// skip batch trace logs to avoid infinite loop
|
|
64
|
+
return;
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
if (this.alepha.isProduction() && ev.entry.level === "TRACE") {
|
|
68
|
+
// skip trace logs in production
|
|
69
|
+
return;
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
const entry = {
|
|
73
|
+
...ev.entry,
|
|
74
|
+
data:
|
|
75
|
+
ev.entry.data instanceof Error
|
|
76
|
+
? this.jsonFormatter.formatJsonError(ev.entry.data)
|
|
77
|
+
: typeof ev.entry.data === "object" &&
|
|
78
|
+
!Array.isArray(ev.entry.data)
|
|
79
|
+
? ev.entry.data
|
|
80
|
+
: { data: ev.entry.data },
|
|
81
|
+
};
|
|
82
|
+
|
|
83
|
+
await this.batchLogs.push(entry as DevLogEntry);
|
|
84
|
+
} catch (error) {
|
|
85
|
+
// DO TO NOT WITH THE LOGGER HERE TO AVOID INFINITE LOOP
|
|
86
|
+
console.error(error, ev);
|
|
87
|
+
}
|
|
88
|
+
},
|
|
89
|
+
});
|
|
90
|
+
|
|
91
|
+
protected readonly uiRoute = $serve({
|
|
92
|
+
path: "/devtools",
|
|
93
|
+
root: join(fileURLToPath(import.meta.url), "../../assets/devtools"),
|
|
94
|
+
historyApiFallback: true,
|
|
95
|
+
});
|
|
96
|
+
|
|
97
|
+
protected readonly metadataRoute = $route({
|
|
98
|
+
method: "GET",
|
|
99
|
+
path: "/devtools/api/metadata",
|
|
100
|
+
silent: true,
|
|
101
|
+
schema: {
|
|
102
|
+
response: devMetadataSchema,
|
|
103
|
+
},
|
|
104
|
+
handler: () => {
|
|
105
|
+
return this.devCollectorProvider.getMetadata();
|
|
106
|
+
},
|
|
107
|
+
});
|
|
108
|
+
|
|
109
|
+
protected readonly logsRoute = $route({
|
|
110
|
+
method: "GET",
|
|
111
|
+
path: "/devtools/api/logs",
|
|
112
|
+
silent: true,
|
|
113
|
+
schema: {
|
|
114
|
+
query: t.extend(pageQuerySchema, {
|
|
115
|
+
search: t.optional(t.string()),
|
|
116
|
+
}),
|
|
117
|
+
response: t.page(logEntrySchema),
|
|
118
|
+
},
|
|
119
|
+
handler: ({ query }) => {
|
|
120
|
+
query.sort ??= "-timestamp";
|
|
121
|
+
return this.logs.paginate(
|
|
122
|
+
query,
|
|
123
|
+
query.search
|
|
124
|
+
? {
|
|
125
|
+
where: parseQueryString(query.search),
|
|
126
|
+
}
|
|
127
|
+
: {},
|
|
128
|
+
{
|
|
129
|
+
count: true,
|
|
130
|
+
},
|
|
131
|
+
);
|
|
132
|
+
},
|
|
133
|
+
});
|
|
134
|
+
}
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
import { type Static, t } from "@alepha/core";
|
|
2
|
+
import { $entity, pg } from "@alepha/postgres";
|
|
3
|
+
|
|
4
|
+
export const logs = $entity({
|
|
5
|
+
name: "logs",
|
|
6
|
+
schema: t.object({
|
|
7
|
+
id: pg.primaryKey(),
|
|
8
|
+
level: t.enum(["TRACE", "DEBUG", "INFO", "WARN", "ERROR"]),
|
|
9
|
+
message: t.text({
|
|
10
|
+
size: "rich",
|
|
11
|
+
}),
|
|
12
|
+
service: t.text(),
|
|
13
|
+
module: t.text(),
|
|
14
|
+
context: t.optional(t.text()),
|
|
15
|
+
app: t.optional(t.text()),
|
|
16
|
+
data: t.optional(t.json()),
|
|
17
|
+
timestamp: t.datetime(),
|
|
18
|
+
}),
|
|
19
|
+
});
|
|
20
|
+
|
|
21
|
+
export type DevLogEntry = Static<typeof logs.schema>;
|
package/src/index.ts
CHANGED
|
@@ -1,9 +1,12 @@
|
|
|
1
1
|
import { $module } from "@alepha/core";
|
|
2
|
-
import {
|
|
2
|
+
import { DevToolsProvider } from "./DevToolsProvider.ts";
|
|
3
|
+
import { DevToolsDatabaseProvider } from "./providers/DevToolsDatabaseProvider.ts";
|
|
4
|
+
import { DevToolsMetadataProvider } from "./providers/DevToolsMetadataProvider.ts";
|
|
5
|
+
import { LogRepository } from "./repositories/LogRepository.ts";
|
|
3
6
|
|
|
4
7
|
// ---------------------------------------------------------------------------------------------------------------------
|
|
5
8
|
|
|
6
|
-
export * from "./
|
|
9
|
+
export * from "./providers/DevToolsMetadataProvider.ts";
|
|
7
10
|
export * from "./schemas/DevActionMetadata.ts";
|
|
8
11
|
export * from "./schemas/DevBucketMetadata.ts";
|
|
9
12
|
export * from "./schemas/DevCacheMetadata.ts";
|
|
@@ -24,15 +27,22 @@ export * from "./schemas/DevTopicMetadata.ts";
|
|
|
24
27
|
* This module provides comprehensive data collection capabilities for tracking application behavior,
|
|
25
28
|
* performance metrics, and debugging information in real-time.
|
|
26
29
|
*
|
|
27
|
-
* @see {@link
|
|
30
|
+
* @see {@link DevToolsMetadataProvider}
|
|
28
31
|
* @module alepha.devtools
|
|
29
32
|
*/
|
|
30
33
|
export const AlephaDevtools = $module({
|
|
31
34
|
name: "alepha.devtools",
|
|
32
35
|
descriptors: [],
|
|
33
|
-
services: [
|
|
36
|
+
services: [
|
|
37
|
+
DevToolsMetadataProvider,
|
|
38
|
+
DevToolsProvider,
|
|
39
|
+
DevToolsDatabaseProvider,
|
|
40
|
+
LogRepository,
|
|
41
|
+
],
|
|
34
42
|
register: (alepha) => {
|
|
35
|
-
alepha.with(
|
|
36
|
-
alepha.
|
|
43
|
+
alepha.with(DevToolsProvider);
|
|
44
|
+
alepha.with(DevToolsDatabaseProvider);
|
|
45
|
+
alepha.with(DevToolsMetadataProvider);
|
|
46
|
+
alepha.state.push("alepha.build.assets", "@alepha/devtools");
|
|
37
47
|
},
|
|
38
48
|
});
|
|
@@ -1,90 +1,27 @@
|
|
|
1
|
-
import { join } from "node:path";
|
|
2
|
-
import { fileURLToPath } from "node:url";
|
|
3
1
|
import { $bucket } from "@alepha/bucket";
|
|
4
2
|
import { $cache } from "@alepha/cache";
|
|
5
|
-
import { $
|
|
6
|
-
import { $logger
|
|
3
|
+
import { $inject, Alepha } from "@alepha/core";
|
|
4
|
+
import { $logger } from "@alepha/logger";
|
|
7
5
|
import { $queue } from "@alepha/queue";
|
|
8
|
-
import { $page } from "@alepha/react";
|
|
9
6
|
import { $scheduler } from "@alepha/scheduler";
|
|
10
7
|
import { $realm } from "@alepha/security";
|
|
11
|
-
import { $action
|
|
12
|
-
import { $serve } from "@alepha/server-static";
|
|
8
|
+
import { $action } from "@alepha/server";
|
|
13
9
|
import { $topic } from "@alepha/topic";
|
|
14
|
-
import type { DevActionMetadata } from "
|
|
15
|
-
import type { DevBucketMetadata } from "
|
|
16
|
-
import type { DevCacheMetadata } from "
|
|
17
|
-
import {
|
|
18
|
-
import type { DevModuleMetadata } from "
|
|
19
|
-
import type { DevPageMetadata } from "
|
|
20
|
-
import type { DevProviderMetadata } from "
|
|
21
|
-
import type { DevQueueMetadata } from "
|
|
22
|
-
import type { DevRealmMetadata } from "
|
|
23
|
-
import type { DevSchedulerMetadata } from "
|
|
24
|
-
import type { DevTopicMetadata } from "
|
|
25
|
-
|
|
26
|
-
export class
|
|
10
|
+
import type { DevActionMetadata } from "../schemas/DevActionMetadata.ts";
|
|
11
|
+
import type { DevBucketMetadata } from "../schemas/DevBucketMetadata.ts";
|
|
12
|
+
import type { DevCacheMetadata } from "../schemas/DevCacheMetadata.ts";
|
|
13
|
+
import type { DevMetadata } from "../schemas/DevMetadata.ts";
|
|
14
|
+
import type { DevModuleMetadata } from "../schemas/DevModuleMetadata.ts";
|
|
15
|
+
import type { DevPageMetadata } from "../schemas/DevPageMetadata.ts";
|
|
16
|
+
import type { DevProviderMetadata } from "../schemas/DevProviderMetadata.ts";
|
|
17
|
+
import type { DevQueueMetadata } from "../schemas/DevQueueMetadata.ts";
|
|
18
|
+
import type { DevRealmMetadata } from "../schemas/DevRealmMetadata.ts";
|
|
19
|
+
import type { DevSchedulerMetadata } from "../schemas/DevSchedulerMetadata.ts";
|
|
20
|
+
import type { DevTopicMetadata } from "../schemas/DevTopicMetadata.ts";
|
|
21
|
+
|
|
22
|
+
export class DevToolsMetadataProvider {
|
|
27
23
|
protected readonly alepha = $inject(Alepha);
|
|
28
|
-
protected readonly serverProvider = $inject(ServerProvider);
|
|
29
24
|
protected readonly log = $logger();
|
|
30
|
-
protected readonly logs: LogEntry[] = [];
|
|
31
|
-
protected readonly maxLogs = 10000;
|
|
32
|
-
|
|
33
|
-
protected readonly onStart = $hook({
|
|
34
|
-
on: "start",
|
|
35
|
-
handler: () => {
|
|
36
|
-
this.log.info(
|
|
37
|
-
`Devtools available at ${this.serverProvider.hostname}/devtools/`,
|
|
38
|
-
);
|
|
39
|
-
},
|
|
40
|
-
});
|
|
41
|
-
|
|
42
|
-
protected readonly onLog = $hook({
|
|
43
|
-
on: "log",
|
|
44
|
-
handler: (ev: { message?: string; entry: LogEntry }) => {
|
|
45
|
-
this.logs.unshift(ev.entry);
|
|
46
|
-
|
|
47
|
-
// keep only the last 10000 logs
|
|
48
|
-
if (this.logs.length > this.maxLogs) {
|
|
49
|
-
this.logs.pop();
|
|
50
|
-
}
|
|
51
|
-
},
|
|
52
|
-
});
|
|
53
|
-
|
|
54
|
-
protected readonly uiRoute = $serve({
|
|
55
|
-
path: "/devtools",
|
|
56
|
-
root: join(fileURLToPath(import.meta.url), "../../assets/devtools"),
|
|
57
|
-
});
|
|
58
|
-
|
|
59
|
-
protected readonly metadataRoute = $route({
|
|
60
|
-
method: "GET",
|
|
61
|
-
path: "/devtools/api/metadata",
|
|
62
|
-
silent: true,
|
|
63
|
-
schema: {
|
|
64
|
-
response: devMetadataSchema,
|
|
65
|
-
},
|
|
66
|
-
handler: () => {
|
|
67
|
-
return this.getMetadata();
|
|
68
|
-
},
|
|
69
|
-
});
|
|
70
|
-
|
|
71
|
-
protected readonly logsRoute = $route({
|
|
72
|
-
method: "GET",
|
|
73
|
-
path: "/devtools/api/logs",
|
|
74
|
-
silent: true,
|
|
75
|
-
schema: {
|
|
76
|
-
response: t.array(logEntrySchema),
|
|
77
|
-
},
|
|
78
|
-
handler: () => {
|
|
79
|
-
return this.getLogs();
|
|
80
|
-
},
|
|
81
|
-
});
|
|
82
|
-
|
|
83
|
-
public getLogs(): LogEntry[] {
|
|
84
|
-
return this.logs;
|
|
85
|
-
}
|
|
86
|
-
|
|
87
|
-
// -------------------------------------------------------------------------------------------------------------------
|
|
88
25
|
|
|
89
26
|
public getActions(): DevActionMetadata[] {
|
|
90
27
|
const actionDescriptors = this.alepha.descriptors($action);
|
|
@@ -191,28 +128,30 @@ export class DevCollectorProvider {
|
|
|
191
128
|
}
|
|
192
129
|
|
|
193
130
|
public getPages(): DevPageMetadata[] {
|
|
194
|
-
const pageDescriptors = this.alepha.descriptors($page);
|
|
195
|
-
|
|
196
|
-
return pageDescriptors.map((page) => ({
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
}));
|
|
131
|
+
// const pageDescriptors = this.alepha.descriptors($page);
|
|
132
|
+
//
|
|
133
|
+
// return pageDescriptors.map((page) => ({
|
|
134
|
+
// name: page.name,
|
|
135
|
+
// description: page.options.description,
|
|
136
|
+
// path: page.options.path,
|
|
137
|
+
// params: page.options.schema?.params,
|
|
138
|
+
// query: page.options.schema?.query,
|
|
139
|
+
// hasComponent: !!page.options.component,
|
|
140
|
+
// hasLazy: !!page.options.lazy,
|
|
141
|
+
// hasResolve: !!page.options.resolve,
|
|
142
|
+
// hasChildren: !!page.options.children,
|
|
143
|
+
// hasParent: !!page.options.parent,
|
|
144
|
+
// hasErrorHandler: !!page.options.errorHandler,
|
|
145
|
+
// static:
|
|
146
|
+
// typeof page.options.static === "boolean"
|
|
147
|
+
// ? page.options.static
|
|
148
|
+
// : !!page.options.static,
|
|
149
|
+
// cache: page.options.cache,
|
|
150
|
+
// client: page.options.client,
|
|
151
|
+
// animation: page.options.animation,
|
|
152
|
+
// }));
|
|
153
|
+
|
|
154
|
+
return [];
|
|
216
155
|
}
|
|
217
156
|
|
|
218
157
|
public getProviders(): DevProviderMetadata[] {
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
import { Repository } from "@alepha/postgres";
|
|
2
|
+
import { logs } from "../entities/logs.ts";
|
|
3
|
+
import { DevToolsDatabaseProvider } from "../providers/DevToolsDatabaseProvider.ts";
|
|
4
|
+
|
|
5
|
+
export class LogRepository extends Repository<typeof logs.schema> {
|
|
6
|
+
constructor() {
|
|
7
|
+
super(logs, DevToolsDatabaseProvider);
|
|
8
|
+
}
|
|
9
|
+
}
|
package/src/ui/AppRouter.tsx
CHANGED
|
@@ -1,44 +1,11 @@
|
|
|
1
1
|
import { $page } from "@alepha/react";
|
|
2
|
-
import {
|
|
2
|
+
import { RootRouter } from "@alepha/ui";
|
|
3
3
|
import { IconDashboard, IconLogs } from "@tabler/icons-react";
|
|
4
|
-
import DevLogs from "./DevLogs.tsx";
|
|
5
4
|
|
|
6
5
|
export class AppRouter extends RootRouter {
|
|
7
6
|
layout = $page({
|
|
8
7
|
parent: this.root,
|
|
9
|
-
|
|
10
|
-
<AdminShell
|
|
11
|
-
appShellProps={{
|
|
12
|
-
bg: ui.colors.surface,
|
|
13
|
-
}}
|
|
14
|
-
appShellNavbarProps={{
|
|
15
|
-
bg: ui.colors.transparent,
|
|
16
|
-
}}
|
|
17
|
-
appShellHeaderProps={{
|
|
18
|
-
bg: ui.colors.transparent,
|
|
19
|
-
style: {
|
|
20
|
-
backdropFilter: "blur(10px)",
|
|
21
|
-
},
|
|
22
|
-
}}
|
|
23
|
-
sidebarProps={{
|
|
24
|
-
collapsed: true,
|
|
25
|
-
gap: "xs",
|
|
26
|
-
}}
|
|
27
|
-
appBarProps={{
|
|
28
|
-
items: [
|
|
29
|
-
{ position: "left", type: "burger" },
|
|
30
|
-
{
|
|
31
|
-
position: "center",
|
|
32
|
-
element: (
|
|
33
|
-
<Text fw="bold" size="lg">
|
|
34
|
-
Alepha DevTools
|
|
35
|
-
</Text>
|
|
36
|
-
),
|
|
37
|
-
},
|
|
38
|
-
],
|
|
39
|
-
}}
|
|
40
|
-
/>
|
|
41
|
-
),
|
|
8
|
+
lazy: () => import("./components/DevLayout.tsx"),
|
|
42
9
|
});
|
|
43
10
|
|
|
44
11
|
dashboard = $page({
|
|
@@ -56,6 +23,6 @@ export class AppRouter extends RootRouter {
|
|
|
56
23
|
icon: <IconLogs />,
|
|
57
24
|
static: true,
|
|
58
25
|
parent: this.layout,
|
|
59
|
-
|
|
26
|
+
lazy: () => import("./components/DevLogViewer.tsx"),
|
|
60
27
|
});
|
|
61
28
|
}
|
|
@@ -0,0 +1,80 @@
|
|
|
1
|
+
import { NestedView } from "@alepha/react";
|
|
2
|
+
import {
|
|
3
|
+
ActionButton,
|
|
4
|
+
AdminShell,
|
|
5
|
+
DarkModeButton,
|
|
6
|
+
Flex,
|
|
7
|
+
OmnibarButton,
|
|
8
|
+
ui,
|
|
9
|
+
} from "@alepha/ui";
|
|
10
|
+
import { IconDashboard, IconLogs, IconTools } from "@tabler/icons-react";
|
|
11
|
+
|
|
12
|
+
export const DevLayout = () => {
|
|
13
|
+
return (
|
|
14
|
+
<AdminShell
|
|
15
|
+
appShellProps={{
|
|
16
|
+
withBorder: false,
|
|
17
|
+
bg: ui.colors.surface,
|
|
18
|
+
}}
|
|
19
|
+
appShellNavbarProps={{
|
|
20
|
+
bg: ui.colors.transparent,
|
|
21
|
+
}}
|
|
22
|
+
appShellHeaderProps={{
|
|
23
|
+
bg: ui.colors.transparent,
|
|
24
|
+
style: {
|
|
25
|
+
backdropFilter: "blur(10px)",
|
|
26
|
+
},
|
|
27
|
+
}}
|
|
28
|
+
sidebarProps={{
|
|
29
|
+
gap: "xs",
|
|
30
|
+
collapsed: true,
|
|
31
|
+
menu: [
|
|
32
|
+
{
|
|
33
|
+
label: "Dashboard",
|
|
34
|
+
icon: <IconDashboard />,
|
|
35
|
+
href: "/",
|
|
36
|
+
},
|
|
37
|
+
{
|
|
38
|
+
label: "Logs",
|
|
39
|
+
icon: <IconLogs />,
|
|
40
|
+
href: "/logs",
|
|
41
|
+
},
|
|
42
|
+
],
|
|
43
|
+
}}
|
|
44
|
+
appBarProps={{
|
|
45
|
+
items: [
|
|
46
|
+
{ position: "left", type: "burger" },
|
|
47
|
+
{
|
|
48
|
+
position: "left",
|
|
49
|
+
element: (
|
|
50
|
+
<ActionButton icon={<IconTools />} href={"/"} active={false}>
|
|
51
|
+
Devtools
|
|
52
|
+
</ActionButton>
|
|
53
|
+
),
|
|
54
|
+
},
|
|
55
|
+
{
|
|
56
|
+
position: "center",
|
|
57
|
+
element: <OmnibarButton />,
|
|
58
|
+
},
|
|
59
|
+
{
|
|
60
|
+
position: "right",
|
|
61
|
+
element: <DarkModeButton />,
|
|
62
|
+
},
|
|
63
|
+
],
|
|
64
|
+
}}
|
|
65
|
+
>
|
|
66
|
+
<Flex
|
|
67
|
+
bd={`1px solid ${ui.colors.border}`}
|
|
68
|
+
bg={ui.colors.elevated}
|
|
69
|
+
bdrs={"lg"}
|
|
70
|
+
p={"xl"}
|
|
71
|
+
ml={-8}
|
|
72
|
+
mt={-8}
|
|
73
|
+
>
|
|
74
|
+
<NestedView />
|
|
75
|
+
</Flex>
|
|
76
|
+
</AdminShell>
|
|
77
|
+
);
|
|
78
|
+
};
|
|
79
|
+
|
|
80
|
+
export default DevLayout;
|