@alepha/devtools 0.11.6 → 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 -322
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +207 -176
- package/dist/index.js.map +1 -1
- package/package.json +21 -21
- package/src/DevToolsProvider.ts +134 -0
- package/src/entities/logs.ts +21 -0
- package/src/index.ts +15 -5
- package/src/providers/DevToolsDatabaseProvider.ts +11 -0
- package/src/{DevCollectorProvider.ts → providers/DevToolsMetadataProvider.ts} +16 -146
- package/src/repositories/LogRepository.ts +9 -0
- package/src/ui/AppRouter.tsx +3 -66
- 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 -145
- package/src/ui/main.server.ts +0 -10
- /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(
|
|
43
|
+
alepha.with(DevToolsProvider);
|
|
44
|
+
alepha.with(DevToolsDatabaseProvider);
|
|
45
|
+
alepha.with(DevToolsMetadataProvider);
|
|
36
46
|
alepha.state.push("alepha.build.assets", "@alepha/devtools");
|
|
37
47
|
},
|
|
38
48
|
});
|
|
@@ -1,158 +1,28 @@
|
|
|
1
|
-
import { join } from "node:path";
|
|
2
|
-
import { fileURLToPath } from "node:url";
|
|
3
|
-
import { $batch } from "@alepha/batch";
|
|
4
1
|
import { $bucket } from "@alepha/bucket";
|
|
5
2
|
import { $cache } from "@alepha/cache";
|
|
6
|
-
import { $
|
|
7
|
-
import { $logger
|
|
8
|
-
import {
|
|
9
|
-
$entity,
|
|
10
|
-
NodeSqliteProvider,
|
|
11
|
-
parseQueryString,
|
|
12
|
-
pg,
|
|
13
|
-
} from "@alepha/postgres";
|
|
14
|
-
import { Repository } from "@alepha/postgres/src/services/Repository.ts";
|
|
3
|
+
import { $inject, Alepha } from "@alepha/core";
|
|
4
|
+
import { $logger } from "@alepha/logger";
|
|
15
5
|
import { $queue } from "@alepha/queue";
|
|
16
6
|
import { $scheduler } from "@alepha/scheduler";
|
|
17
7
|
import { $realm } from "@alepha/security";
|
|
18
|
-
import { $action
|
|
19
|
-
import { $serve } from "@alepha/server-static";
|
|
8
|
+
import { $action } from "@alepha/server";
|
|
20
9
|
import { $topic } from "@alepha/topic";
|
|
21
|
-
import type { DevActionMetadata } from "
|
|
22
|
-
import type { DevBucketMetadata } from "
|
|
23
|
-
import type { DevCacheMetadata } from "
|
|
24
|
-
import {
|
|
25
|
-
import type { DevModuleMetadata } from "
|
|
26
|
-
import type { DevPageMetadata } from "
|
|
27
|
-
import type { DevProviderMetadata } from "
|
|
28
|
-
import type { DevQueueMetadata } from "
|
|
29
|
-
import type { DevRealmMetadata } from "
|
|
30
|
-
import type { DevSchedulerMetadata } from "
|
|
31
|
-
import type { DevTopicMetadata } from "
|
|
32
|
-
|
|
33
|
-
class
|
|
34
|
-
get name() {
|
|
35
|
-
return "devtools";
|
|
36
|
-
}
|
|
37
|
-
options = {
|
|
38
|
-
path: ":memory:",
|
|
39
|
-
};
|
|
40
|
-
}
|
|
41
|
-
|
|
42
|
-
const logs = $entity({
|
|
43
|
-
name: "logs",
|
|
44
|
-
schema: t.object({
|
|
45
|
-
id: pg.primaryKey(),
|
|
46
|
-
level: t.enum(["SILENT", "TRACE", "DEBUG", "INFO", "WARN", "ERROR"]),
|
|
47
|
-
message: t.text({
|
|
48
|
-
size: "rich",
|
|
49
|
-
}),
|
|
50
|
-
service: t.text(),
|
|
51
|
-
module: t.text(),
|
|
52
|
-
context: t.optional(t.text()),
|
|
53
|
-
app: t.optional(t.text()),
|
|
54
|
-
data: t.optional(t.json()),
|
|
55
|
-
timestamp: t.datetime(),
|
|
56
|
-
}),
|
|
57
|
-
});
|
|
58
|
-
|
|
59
|
-
class LogRepository extends Repository<typeof logs.schema> {
|
|
60
|
-
constructor() {
|
|
61
|
-
super(logs, DevToolsDatabaseProvider);
|
|
62
|
-
}
|
|
63
|
-
}
|
|
64
|
-
|
|
65
|
-
export class DevCollectorProvider {
|
|
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 {
|
|
66
23
|
protected readonly alepha = $inject(Alepha);
|
|
67
|
-
protected readonly serverProvider = $inject(ServerProvider);
|
|
68
|
-
protected readonly sqliteProvider = $inject(DevToolsDatabaseProvider);
|
|
69
24
|
protected readonly log = $logger();
|
|
70
25
|
|
|
71
|
-
logs = $inject(LogRepository);
|
|
72
|
-
|
|
73
|
-
protected readonly onStart = $hook({
|
|
74
|
-
on: "start",
|
|
75
|
-
handler: () => {
|
|
76
|
-
this.log.info(
|
|
77
|
-
`Devtools available at ${this.serverProvider.hostname}/devtools/`,
|
|
78
|
-
);
|
|
79
|
-
},
|
|
80
|
-
});
|
|
81
|
-
|
|
82
|
-
protected batchLogs = $batch({
|
|
83
|
-
maxSize: 50,
|
|
84
|
-
maxDuration: [10, "seconds"],
|
|
85
|
-
schema: logEntrySchema,
|
|
86
|
-
handler: async (entries: LogEntry[]) => {
|
|
87
|
-
await this.logs.createMany(entries);
|
|
88
|
-
},
|
|
89
|
-
});
|
|
90
|
-
|
|
91
|
-
protected readonly onLog = $hook({
|
|
92
|
-
on: "log",
|
|
93
|
-
handler: async (ev: { message?: string; entry: LogEntry }) => {
|
|
94
|
-
if (!this.alepha.isReady()) {
|
|
95
|
-
return;
|
|
96
|
-
}
|
|
97
|
-
|
|
98
|
-
if (ev.entry.level === "TRACE" && ev.entry.module === "alepha.batch") {
|
|
99
|
-
// skip batch trace logs to avoid infinite loop
|
|
100
|
-
return;
|
|
101
|
-
}
|
|
102
|
-
|
|
103
|
-
await this.batchLogs.push(ev.entry);
|
|
104
|
-
},
|
|
105
|
-
});
|
|
106
|
-
|
|
107
|
-
protected readonly uiRoute = $serve({
|
|
108
|
-
path: "/devtools",
|
|
109
|
-
root: join(fileURLToPath(import.meta.url), "../../assets/devtools"),
|
|
110
|
-
historyApiFallback: true,
|
|
111
|
-
});
|
|
112
|
-
|
|
113
|
-
protected readonly metadataRoute = $route({
|
|
114
|
-
method: "GET",
|
|
115
|
-
path: "/devtools/api/metadata",
|
|
116
|
-
silent: true,
|
|
117
|
-
schema: {
|
|
118
|
-
response: devMetadataSchema,
|
|
119
|
-
},
|
|
120
|
-
handler: () => {
|
|
121
|
-
return this.getMetadata();
|
|
122
|
-
},
|
|
123
|
-
});
|
|
124
|
-
|
|
125
|
-
protected readonly logsRoute = $route({
|
|
126
|
-
method: "GET",
|
|
127
|
-
path: "/devtools/api/logs",
|
|
128
|
-
silent: true,
|
|
129
|
-
schema: {
|
|
130
|
-
query: t.interface([pageQuerySchema], {
|
|
131
|
-
search: t.optional(t.string()),
|
|
132
|
-
}),
|
|
133
|
-
response: t.page(logEntrySchema),
|
|
134
|
-
},
|
|
135
|
-
handler: ({ query }) => {
|
|
136
|
-
query.sort ??= "-timestamp";
|
|
137
|
-
if (query.search) {
|
|
138
|
-
console.log(parseQueryString(query.search));
|
|
139
|
-
}
|
|
140
|
-
return this.logs.paginate(
|
|
141
|
-
query,
|
|
142
|
-
query.search
|
|
143
|
-
? {
|
|
144
|
-
where: parseQueryString(query.search),
|
|
145
|
-
}
|
|
146
|
-
: {},
|
|
147
|
-
{
|
|
148
|
-
count: true,
|
|
149
|
-
},
|
|
150
|
-
);
|
|
151
|
-
},
|
|
152
|
-
});
|
|
153
|
-
|
|
154
|
-
// -------------------------------------------------------------------------------------------------------------------
|
|
155
|
-
|
|
156
26
|
public getActions(): DevActionMetadata[] {
|
|
157
27
|
const actionDescriptors = this.alepha.descriptors($action);
|
|
158
28
|
|
|
@@ -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,74 +1,11 @@
|
|
|
1
1
|
import { $page } from "@alepha/react";
|
|
2
|
-
import {
|
|
3
|
-
AdminShell,
|
|
4
|
-
DarkModeButton,
|
|
5
|
-
OmnibarButton,
|
|
6
|
-
RootRouter,
|
|
7
|
-
Text,
|
|
8
|
-
ui,
|
|
9
|
-
} from "@alepha/ui";
|
|
10
|
-
import ToggleSidebarButton from "@alepha/ui/src/components/buttons/ToggleSidebarButton.tsx";
|
|
2
|
+
import { RootRouter } from "@alepha/ui";
|
|
11
3
|
import { IconDashboard, IconLogs } from "@tabler/icons-react";
|
|
12
|
-
import DevLogs from "./DevLogs.tsx";
|
|
13
4
|
|
|
14
5
|
export class AppRouter extends RootRouter {
|
|
15
6
|
layout = $page({
|
|
16
7
|
parent: this.root,
|
|
17
|
-
|
|
18
|
-
<AdminShell
|
|
19
|
-
appShellProps={{
|
|
20
|
-
bg: ui.colors.surface,
|
|
21
|
-
}}
|
|
22
|
-
appShellNavbarProps={{
|
|
23
|
-
bg: ui.colors.transparent,
|
|
24
|
-
}}
|
|
25
|
-
appShellHeaderProps={{
|
|
26
|
-
bg: ui.colors.transparent,
|
|
27
|
-
style: {
|
|
28
|
-
backdropFilter: "blur(10px)",
|
|
29
|
-
},
|
|
30
|
-
}}
|
|
31
|
-
sidebarProps={{
|
|
32
|
-
gap: "xs",
|
|
33
|
-
menu: [
|
|
34
|
-
{
|
|
35
|
-
element: <ToggleSidebarButton />,
|
|
36
|
-
},
|
|
37
|
-
{
|
|
38
|
-
label: "Dashboard",
|
|
39
|
-
icon: <IconDashboard />,
|
|
40
|
-
href: "/",
|
|
41
|
-
},
|
|
42
|
-
{
|
|
43
|
-
label: "Logs",
|
|
44
|
-
icon: <IconLogs />,
|
|
45
|
-
href: "/logs",
|
|
46
|
-
},
|
|
47
|
-
],
|
|
48
|
-
}}
|
|
49
|
-
appBarProps={{
|
|
50
|
-
items: [
|
|
51
|
-
{ position: "left", type: "burger" },
|
|
52
|
-
{
|
|
53
|
-
position: "left",
|
|
54
|
-
element: (
|
|
55
|
-
<Text fw="bold" size="lg">
|
|
56
|
-
Alepha DevTools
|
|
57
|
-
</Text>
|
|
58
|
-
),
|
|
59
|
-
},
|
|
60
|
-
{
|
|
61
|
-
position: "center",
|
|
62
|
-
element: <OmnibarButton />,
|
|
63
|
-
},
|
|
64
|
-
{
|
|
65
|
-
position: "right",
|
|
66
|
-
element: <DarkModeButton />,
|
|
67
|
-
},
|
|
68
|
-
],
|
|
69
|
-
}}
|
|
70
|
-
/>
|
|
71
|
-
),
|
|
8
|
+
lazy: () => import("./components/DevLayout.tsx"),
|
|
72
9
|
});
|
|
73
10
|
|
|
74
11
|
dashboard = $page({
|
|
@@ -86,6 +23,6 @@ export class AppRouter extends RootRouter {
|
|
|
86
23
|
icon: <IconLogs />,
|
|
87
24
|
static: true,
|
|
88
25
|
parent: this.layout,
|
|
89
|
-
|
|
26
|
+
lazy: () => import("./components/DevLogViewer.tsx"),
|
|
90
27
|
});
|
|
91
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;
|