@zthun/romulator-web 1.18.4 → 1.19.0
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/CHANGELOG.md +22 -0
- package/dist/assets/index-BImCVu5p.js +891 -0
- package/dist/index.html +1 -1
- package/package.json +7 -7
- package/src/app/app.tsx +2 -0
- package/src/jobs/job-tile.tsx +54 -0
- package/src/jobs/jobs-page.cm.mts +31 -0
- package/src/jobs/jobs-page.test.tsx +81 -0
- package/src/jobs/jobs-page.tsx +62 -0
- package/src/jobs/jobs-service.ts +41 -0
- package/src/jobs/use-job-status-metadata.tsx +58 -0
- package/src/jobs/use-job-type-metadata.tsx +39 -0
- package/src/menu/menu.cm.mts +1 -0
- package/src/menu/menu.spec.tsx +7 -3
- package/src/menu/menu.tsx +13 -0
- package/dist/assets/index-CZMAQ_HC.js +0 -891
package/dist/index.html
CHANGED
|
@@ -4,7 +4,7 @@
|
|
|
4
4
|
<meta charset="UTF-8" />
|
|
5
5
|
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
|
6
6
|
<title>Romulator: Organize your Games</title>
|
|
7
|
-
<script type="module" crossorigin src="/assets/index-
|
|
7
|
+
<script type="module" crossorigin src="/assets/index-BImCVu5p.js"></script>
|
|
8
8
|
</head>
|
|
9
9
|
<body>
|
|
10
10
|
<div id="zthunworks-romulator"></div>
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@zthun/romulator-web",
|
|
3
|
-
"version": "1.
|
|
3
|
+
"version": "1.19.0",
|
|
4
4
|
"description": "Romulator frontend",
|
|
5
5
|
"author": "Anthony Bonta",
|
|
6
6
|
"license": "MIT",
|
|
@@ -18,18 +18,18 @@
|
|
|
18
18
|
"access": "public"
|
|
19
19
|
},
|
|
20
20
|
"devDependencies": {
|
|
21
|
-
"@types/node": "^25.
|
|
21
|
+
"@types/node": "^25.2.3",
|
|
22
22
|
"@zthun/cirque": "^7.2.5",
|
|
23
23
|
"@zthun/cirque-du-react": "^7.2.5",
|
|
24
24
|
"@zthun/fashion-boutique": "^13.0.4",
|
|
25
25
|
"@zthun/fashion-tailor": "^13.0.4",
|
|
26
26
|
"@zthun/fashion-theme": "^13.0.4",
|
|
27
|
-
"@zthun/helpful-fn": "^9.11.
|
|
28
|
-
"@zthun/helpful-query": "^9.11.
|
|
29
|
-
"@zthun/helpful-react": "^9.11.
|
|
27
|
+
"@zthun/helpful-fn": "^9.11.10",
|
|
28
|
+
"@zthun/helpful-query": "^9.11.10",
|
|
29
|
+
"@zthun/helpful-react": "^9.11.10",
|
|
30
30
|
"@zthun/janitor-build-config": "^19.5.6",
|
|
31
31
|
"@zthun/janitor-ts-config": "^19.5.3",
|
|
32
|
-
"@zthun/romulator-client": "^1.
|
|
32
|
+
"@zthun/romulator-client": "^1.19.0",
|
|
33
33
|
"@zthun/webigail-http": "^5.0.5",
|
|
34
34
|
"@zthun/webigail-rest": "^5.0.5",
|
|
35
35
|
"@zthun/webigail-url": "^5.0.5",
|
|
@@ -44,5 +44,5 @@
|
|
|
44
44
|
"vitest": "^4.0.18",
|
|
45
45
|
"vitest-mock-extended": "^3.1.0"
|
|
46
46
|
},
|
|
47
|
-
"gitHead": "
|
|
47
|
+
"gitHead": "8e97e1bc38a9b1f38dd055093cdb5521fd621d84"
|
|
48
48
|
}
|
package/src/app/app.tsx
CHANGED
|
@@ -9,6 +9,7 @@ import {
|
|
|
9
9
|
import { createDarkTheme } from "@zthun/fashion-theme";
|
|
10
10
|
import { ZRomulatorGamePage } from "../games/game-page.js";
|
|
11
11
|
import { ZRomulatorGamesPage } from "../games/games-page.js";
|
|
12
|
+
import { ZRomulatorJobsPage } from "../jobs/jobs-page.js";
|
|
12
13
|
import { ZRomulatorMenu } from "../menu/menu.js";
|
|
13
14
|
import { ZRomulatorSettingPage } from "../settings/setting-page.js";
|
|
14
15
|
import { ZRomulatorSettingsPage } from "../settings/settings-page.js";
|
|
@@ -36,6 +37,7 @@ export function ZRomulatorApp() {
|
|
|
36
37
|
<ZRoute path="/systems/:id" element={<ZRomulatorSystemPage />} />
|
|
37
38
|
<ZRoute path="/games" element={<ZRomulatorGamesPage />} />
|
|
38
39
|
<ZRoute path="/games/:id" element={<ZRomulatorGamePage />} />
|
|
40
|
+
<ZRoute path="/jobs" element={<ZRomulatorJobsPage />} />
|
|
39
41
|
<ZRoute path="" element={<ZNavigate to="/systems" />} />
|
|
40
42
|
<ZRoute path="*" element={<ZNotFound />} />
|
|
41
43
|
</ZRouteMap>
|
|
@@ -0,0 +1,54 @@
|
|
|
1
|
+
import type { IZComponentValueReadonly } from "@zthun/fashion-boutique";
|
|
2
|
+
import {
|
|
3
|
+
useFashionTheme,
|
|
4
|
+
ZContentTitle,
|
|
5
|
+
ZGrid,
|
|
6
|
+
ZLabel,
|
|
7
|
+
ZParagraph,
|
|
8
|
+
ZStack,
|
|
9
|
+
ZTile,
|
|
10
|
+
} from "@zthun/fashion-boutique";
|
|
11
|
+
import { ZSizeFixed, ZSizeVaried } from "@zthun/fashion-tailor";
|
|
12
|
+
import { formatDateTime } from "@zthun/helpful-fn";
|
|
13
|
+
import { type IZJob } from "@zthun/romulator-client";
|
|
14
|
+
import { useJobStatusMetadata } from "./use-job-status-metadata.js";
|
|
15
|
+
import { useJobTypeMetadata } from "./use-job-type-metadata.js";
|
|
16
|
+
|
|
17
|
+
export interface IZJobTile extends Required<IZComponentValueReadonly<IZJob>> {}
|
|
18
|
+
|
|
19
|
+
export function ZJobTile(props: IZJobTile) {
|
|
20
|
+
const { value } = props;
|
|
21
|
+
const { body } = useFashionTheme();
|
|
22
|
+
const { status, percent, type, createdAt } = value;
|
|
23
|
+
const created = formatDateTime(createdAt);
|
|
24
|
+
|
|
25
|
+
const _type = useJobTypeMetadata(type);
|
|
26
|
+
const { name, description, avatar } = _type;
|
|
27
|
+
|
|
28
|
+
const _status = useJobStatusMetadata(status);
|
|
29
|
+
|
|
30
|
+
return (
|
|
31
|
+
<ZTile className="ZRomulatorJobTile-root" fashion={body} name={value.id}>
|
|
32
|
+
<ZStack
|
|
33
|
+
height={ZSizeVaried.Full}
|
|
34
|
+
width={ZSizeVaried.Full}
|
|
35
|
+
gap={ZSizeFixed.Large}
|
|
36
|
+
>
|
|
37
|
+
<ZContentTitle
|
|
38
|
+
heading={name}
|
|
39
|
+
subHeading={description}
|
|
40
|
+
avatar={avatar}
|
|
41
|
+
suffix={_status.avatar}
|
|
42
|
+
/>
|
|
43
|
+
<ZGrid columns="auto 1fr" gap={ZSizeFixed.Small}>
|
|
44
|
+
<ZLabel>Created:</ZLabel>
|
|
45
|
+
<ZParagraph compact>{created}</ZParagraph>
|
|
46
|
+
<ZLabel>Status</ZLabel>
|
|
47
|
+
<ZParagraph compact>{_status.name}</ZParagraph>
|
|
48
|
+
<ZLabel>Progress</ZLabel>
|
|
49
|
+
<ZParagraph>{percent}%</ZParagraph>
|
|
50
|
+
</ZGrid>
|
|
51
|
+
</ZStack>
|
|
52
|
+
</ZTile>
|
|
53
|
+
);
|
|
54
|
+
}
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
import { ZCircusBy, ZCircusComponentModel } from "@zthun/cirque";
|
|
2
|
+
import {
|
|
3
|
+
ZGridViewComponentModel,
|
|
4
|
+
ZTileComponentModel,
|
|
5
|
+
} from "@zthun/fashion-boutique";
|
|
6
|
+
|
|
7
|
+
export class ZRomulatorJobsPageComponentModel extends ZCircusComponentModel {
|
|
8
|
+
public static readonly Selector = ".ZRomulatorJobsPage-root";
|
|
9
|
+
|
|
10
|
+
public grid(): Promise<ZGridViewComponentModel> {
|
|
11
|
+
return ZCircusBy.first(this.driver, ZGridViewComponentModel);
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
public async load() {
|
|
15
|
+
const grid = await this.grid();
|
|
16
|
+
const suspense = await grid.suspense();
|
|
17
|
+
await suspense.load();
|
|
18
|
+
|
|
19
|
+
return grid;
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
public async jobs(): Promise<ZTileComponentModel[]> {
|
|
23
|
+
const grid = await this.load();
|
|
24
|
+
|
|
25
|
+
return ZCircusBy.all(
|
|
26
|
+
grid.driver,
|
|
27
|
+
ZTileComponentModel,
|
|
28
|
+
".ZRomulatorJobTile-root",
|
|
29
|
+
);
|
|
30
|
+
}
|
|
31
|
+
}
|
|
@@ -0,0 +1,81 @@
|
|
|
1
|
+
import {
|
|
2
|
+
ZCircusBy,
|
|
3
|
+
type IZCircusDriver,
|
|
4
|
+
type IZCircusSetup,
|
|
5
|
+
} from "@zthun/cirque";
|
|
6
|
+
import { ZCircusSetupRenderer } from "@zthun/cirque-du-react";
|
|
7
|
+
import { ZTestRouter } from "@zthun/fashion-boutique";
|
|
8
|
+
import { ZDataSourceStatic } from "@zthun/helpful-query";
|
|
9
|
+
import { ZJobBuilder } from "@zthun/romulator-client";
|
|
10
|
+
import { createMemoryHistory } from "history";
|
|
11
|
+
import type { Mocked } from "vitest";
|
|
12
|
+
import { afterEach, beforeEach, describe, expect, it } from "vitest";
|
|
13
|
+
import { mock } from "vitest-mock-extended";
|
|
14
|
+
import { ZRomulatorJobsPageComponentModel } from "./jobs-page.cm.mjs";
|
|
15
|
+
import { ZRomulatorJobsPage } from "./jobs-page.js";
|
|
16
|
+
import {
|
|
17
|
+
ZRomulatorJobsServiceContext,
|
|
18
|
+
type IZRomulatorJobsService,
|
|
19
|
+
} from "./jobs-service.js";
|
|
20
|
+
|
|
21
|
+
describe("ZRomulatorJobsPage", () => {
|
|
22
|
+
let _renderer: IZCircusSetup | undefined;
|
|
23
|
+
let _driver: IZCircusDriver | undefined;
|
|
24
|
+
|
|
25
|
+
const now = new Date().toJSON();
|
|
26
|
+
|
|
27
|
+
const alpha = new ZJobBuilder().guid().idle().createdAt(now).build();
|
|
28
|
+
const bravo = new ZJobBuilder()
|
|
29
|
+
.guid()
|
|
30
|
+
.running()
|
|
31
|
+
.createdAt(now)
|
|
32
|
+
.percent(24)
|
|
33
|
+
.build();
|
|
34
|
+
const charlie = new ZJobBuilder().guid().canceled().build();
|
|
35
|
+
const delta = new ZJobBuilder().guid().failed().build();
|
|
36
|
+
const foxtrot = new ZJobBuilder().guid().success().build();
|
|
37
|
+
const jobs = [alpha, bravo, charlie, delta, foxtrot];
|
|
38
|
+
|
|
39
|
+
let service: Mocked<IZRomulatorJobsService>;
|
|
40
|
+
|
|
41
|
+
beforeEach(() => {
|
|
42
|
+
service = mock<IZRomulatorJobsService>();
|
|
43
|
+
const source = new ZDataSourceStatic(jobs);
|
|
44
|
+
|
|
45
|
+
service.retrieve.mockImplementation((r) => source.retrieve(r));
|
|
46
|
+
service.count.mockImplementation((r) => source.count(r));
|
|
47
|
+
});
|
|
48
|
+
|
|
49
|
+
afterEach(async () => {
|
|
50
|
+
await _driver?.destroy?.();
|
|
51
|
+
await _renderer?.destroy?.();
|
|
52
|
+
});
|
|
53
|
+
|
|
54
|
+
const createTestTarget = async () => {
|
|
55
|
+
const history = createMemoryHistory();
|
|
56
|
+
|
|
57
|
+
const element = (
|
|
58
|
+
<ZTestRouter location={history.location} navigator={history}>
|
|
59
|
+
<ZRomulatorJobsServiceContext.Provider value={service}>
|
|
60
|
+
<ZRomulatorJobsPage />;
|
|
61
|
+
</ZRomulatorJobsServiceContext.Provider>
|
|
62
|
+
</ZTestRouter>
|
|
63
|
+
);
|
|
64
|
+
|
|
65
|
+
_renderer = new ZCircusSetupRenderer(element);
|
|
66
|
+
_driver = await _renderer.setup();
|
|
67
|
+
|
|
68
|
+
return ZCircusBy.first(_driver, ZRomulatorJobsPageComponentModel);
|
|
69
|
+
};
|
|
70
|
+
|
|
71
|
+
it("should list all jobs", async () => {
|
|
72
|
+
// Arrange.
|
|
73
|
+
const target = await createTestTarget();
|
|
74
|
+
|
|
75
|
+
// Act.
|
|
76
|
+
const actual = await target.jobs();
|
|
77
|
+
|
|
78
|
+
// Assert.
|
|
79
|
+
expect(actual.length).toEqual(jobs.length);
|
|
80
|
+
});
|
|
81
|
+
});
|
|
@@ -0,0 +1,62 @@
|
|
|
1
|
+
import {
|
|
2
|
+
ZBreadcrumbsLocation,
|
|
3
|
+
ZCard,
|
|
4
|
+
ZGrid,
|
|
5
|
+
ZGridView,
|
|
6
|
+
ZIconFontAwesome,
|
|
7
|
+
ZSearch,
|
|
8
|
+
ZStack,
|
|
9
|
+
} from "@zthun/fashion-boutique";
|
|
10
|
+
import { ZSizeFixed, ZSizeVaried } from "@zthun/fashion-tailor";
|
|
11
|
+
import { ZDataRequestBuilder } from "@zthun/helpful-query";
|
|
12
|
+
import { useState } from "react";
|
|
13
|
+
import { ZJobTile } from "./job-tile.js";
|
|
14
|
+
import { useJobsService } from "./jobs-service.js";
|
|
15
|
+
|
|
16
|
+
export function ZRomulatorJobsPage() {
|
|
17
|
+
const jobs = useJobsService();
|
|
18
|
+
const [request, setRequest] = useState(new ZDataRequestBuilder().build());
|
|
19
|
+
|
|
20
|
+
return (
|
|
21
|
+
<ZStack
|
|
22
|
+
className="ZRomulatorJobsPage-root"
|
|
23
|
+
gap={ZSizeFixed.Medium}
|
|
24
|
+
width={ZSizeVaried.Full}
|
|
25
|
+
>
|
|
26
|
+
<ZBreadcrumbsLocation />
|
|
27
|
+
<ZCard
|
|
28
|
+
width={ZSizeVaried.Full}
|
|
29
|
+
TitleProps={{
|
|
30
|
+
avatar: (
|
|
31
|
+
<ZIconFontAwesome name="briefcase" width={ZSizeFixed.Medium} />
|
|
32
|
+
),
|
|
33
|
+
heading: "Jobs",
|
|
34
|
+
subHeading: "What's running in the background",
|
|
35
|
+
}}
|
|
36
|
+
>
|
|
37
|
+
<ZGridView
|
|
38
|
+
heading={
|
|
39
|
+
<ZGrid
|
|
40
|
+
align={{ items: "flex-end" }}
|
|
41
|
+
columns={{ xl: "1fr auto", sm: "1fr" }}
|
|
42
|
+
gap={ZSizeFixed.Medium}
|
|
43
|
+
>
|
|
44
|
+
<ZSearch value={request} onValueChange={setRequest} />
|
|
45
|
+
</ZGrid>
|
|
46
|
+
}
|
|
47
|
+
GridProps={{
|
|
48
|
+
columns: {
|
|
49
|
+
xl: "1fr 1fr 1fr",
|
|
50
|
+
lg: "1fr 1fr",
|
|
51
|
+
md: "1fr",
|
|
52
|
+
},
|
|
53
|
+
gap: ZSizeFixed.Medium,
|
|
54
|
+
}}
|
|
55
|
+
dataSource={jobs}
|
|
56
|
+
renderItem={(job) => <ZJobTile key={job.id} value={job} />}
|
|
57
|
+
value={request}
|
|
58
|
+
/>
|
|
59
|
+
</ZCard>
|
|
60
|
+
</ZStack>
|
|
61
|
+
);
|
|
62
|
+
}
|
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
import type { IZDataSource } from "@zthun/helpful-query";
|
|
2
|
+
import type { IZJob } from "@zthun/romulator-client";
|
|
3
|
+
import { ZHttpService } from "@zthun/webigail-http";
|
|
4
|
+
import { ZRestfulService, type IZRestfulGet } from "@zthun/webigail-rest";
|
|
5
|
+
import { ZUrlBuilder } from "@zthun/webigail-url";
|
|
6
|
+
import { createContext, useContext } from "react";
|
|
7
|
+
import { ZRomulatorEnvironmentBuilder } from "../environment/environment.mjs";
|
|
8
|
+
|
|
9
|
+
/**
|
|
10
|
+
* A service that retrieves jobs.
|
|
11
|
+
*/
|
|
12
|
+
export interface IZRomulatorJobsService
|
|
13
|
+
extends IZRestfulGet<IZJob>, IZDataSource<IZJob> {}
|
|
14
|
+
|
|
15
|
+
/**
|
|
16
|
+
* Creates the default implementation of the jobs service.
|
|
17
|
+
*
|
|
18
|
+
* @returns
|
|
19
|
+
* The default implementation of the jobs service
|
|
20
|
+
*/
|
|
21
|
+
export function createDefaultJobsService(): IZRomulatorJobsService {
|
|
22
|
+
const { api } = new ZRomulatorEnvironmentBuilder().build();
|
|
23
|
+
const endpoint = new ZUrlBuilder().parse(api).append("jobs").build();
|
|
24
|
+
const http = new ZHttpService();
|
|
25
|
+
return new ZRestfulService<IZJob>(http, endpoint);
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
/**
|
|
29
|
+
* The injection context for the jobs service.
|
|
30
|
+
*/
|
|
31
|
+
export const ZRomulatorJobsServiceContext = createContext(
|
|
32
|
+
createDefaultJobsService(),
|
|
33
|
+
);
|
|
34
|
+
|
|
35
|
+
/**
|
|
36
|
+
* Returns the current injectable {@link IZRomulatorJobsService} implementation.
|
|
37
|
+
*
|
|
38
|
+
* @returns
|
|
39
|
+
* The current service implementation to manage jobs
|
|
40
|
+
*/
|
|
41
|
+
export const useJobsService = () => useContext(ZRomulatorJobsServiceContext);
|
|
@@ -0,0 +1,58 @@
|
|
|
1
|
+
import { ZIconFontAwesome } from "@zthun/fashion-boutique";
|
|
2
|
+
import { ZSizeFixed } from "@zthun/fashion-tailor";
|
|
3
|
+
import type { IZEnumInfo } from "@zthun/helpful-fn";
|
|
4
|
+
import { firstDefined, ZEnumInfoBuilder } from "@zthun/helpful-fn";
|
|
5
|
+
import { ZJobStatus } from "@zthun/romulator-client";
|
|
6
|
+
import { useMemo } from "react";
|
|
7
|
+
|
|
8
|
+
export function useJobStatusMetadata(status?: ZJobStatus) {
|
|
9
|
+
const lookup = useMemo<Record<ZJobStatus, IZEnumInfo<ZJobStatus>>>(
|
|
10
|
+
() => ({
|
|
11
|
+
[ZJobStatus.Idle]: new ZEnumInfoBuilder(ZJobStatus.Idle)
|
|
12
|
+
.name("Idle")
|
|
13
|
+
.description("Waiting to start")
|
|
14
|
+
.avatar(
|
|
15
|
+
<ZIconFontAwesome name="hourglass-start" width={ZSizeFixed.Small} />,
|
|
16
|
+
)
|
|
17
|
+
.build(),
|
|
18
|
+
|
|
19
|
+
[ZJobStatus.Canceled]: new ZEnumInfoBuilder(ZJobStatus.Canceled)
|
|
20
|
+
.name("Canceled")
|
|
21
|
+
.description("The job was canceled by the user or never finished")
|
|
22
|
+
.avatar(<ZIconFontAwesome name="x-mark" width={ZSizeFixed.Small} />)
|
|
23
|
+
.build(),
|
|
24
|
+
|
|
25
|
+
[ZJobStatus.Failed]: new ZEnumInfoBuilder(ZJobStatus.Failed)
|
|
26
|
+
.name("Failed")
|
|
27
|
+
.description("Something went wrong with job")
|
|
28
|
+
.avatar(
|
|
29
|
+
<ZIconFontAwesome
|
|
30
|
+
name="circle-exclamation"
|
|
31
|
+
width={ZSizeFixed.Small}
|
|
32
|
+
/>,
|
|
33
|
+
)
|
|
34
|
+
.build(),
|
|
35
|
+
|
|
36
|
+
[ZJobStatus.Running]: new ZEnumInfoBuilder(ZJobStatus.Running)
|
|
37
|
+
.name("Running")
|
|
38
|
+
.description("Job is currently processing")
|
|
39
|
+
.avatar(
|
|
40
|
+
<ZIconFontAwesome
|
|
41
|
+
name="spinner"
|
|
42
|
+
animation="spin"
|
|
43
|
+
width={ZSizeFixed.Small}
|
|
44
|
+
/>,
|
|
45
|
+
)
|
|
46
|
+
.build(),
|
|
47
|
+
|
|
48
|
+
[ZJobStatus.Success]: new ZEnumInfoBuilder(ZJobStatus.Success)
|
|
49
|
+
.name("Success")
|
|
50
|
+
.description("Job completed successfully")
|
|
51
|
+
.avatar(<ZIconFontAwesome name="check" width={ZSizeFixed.Small} />)
|
|
52
|
+
.build(),
|
|
53
|
+
}),
|
|
54
|
+
[],
|
|
55
|
+
);
|
|
56
|
+
|
|
57
|
+
return lookup[firstDefined(ZJobStatus.Idle, status)];
|
|
58
|
+
}
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
import { ZIconFontAwesome } from "@zthun/fashion-boutique";
|
|
2
|
+
import { ZSizeFixed } from "@zthun/fashion-tailor";
|
|
3
|
+
import type { IZEnumInfo } from "@zthun/helpful-fn";
|
|
4
|
+
import { firstDefined, ZEnumInfoBuilder } from "@zthun/helpful-fn";
|
|
5
|
+
import { ZJobType } from "@zthun/romulator-client";
|
|
6
|
+
import { useMemo } from "react";
|
|
7
|
+
|
|
8
|
+
export function useJobTypeMetadata(type?: ZJobType) {
|
|
9
|
+
const _type = firstDefined(ZJobType.Unknown, type);
|
|
10
|
+
const lookup: Record<ZJobType, IZEnumInfo<ZJobType>> = useMemo(
|
|
11
|
+
() => ({
|
|
12
|
+
[ZJobType.Unknown]: new ZEnumInfoBuilder(ZJobType.Unknown)
|
|
13
|
+
.name("Unknown")
|
|
14
|
+
.description("Invalid job")
|
|
15
|
+
.avatar(<ZIconFontAwesome name="question" width={ZSizeFixed.Medium} />)
|
|
16
|
+
.build(),
|
|
17
|
+
|
|
18
|
+
[ZJobType.Ping]: new ZEnumInfoBuilder(ZJobType.Ping)
|
|
19
|
+
.name("Ping")
|
|
20
|
+
.description("Test the job framework")
|
|
21
|
+
.avatar(
|
|
22
|
+
<ZIconFontAwesome
|
|
23
|
+
name="table-tennis-paddle-ball"
|
|
24
|
+
width={ZSizeFixed.Medium}
|
|
25
|
+
/>,
|
|
26
|
+
)
|
|
27
|
+
.build(),
|
|
28
|
+
|
|
29
|
+
[ZJobType.Scrape]: new ZEnumInfoBuilder(ZJobType.Scrape)
|
|
30
|
+
.name("Scrape")
|
|
31
|
+
.description("Retrieves all media and metadata for your games")
|
|
32
|
+
.avatar(<ZIconFontAwesome name="image" width={ZSizeFixed.Medium} />)
|
|
33
|
+
.build(),
|
|
34
|
+
}),
|
|
35
|
+
[],
|
|
36
|
+
);
|
|
37
|
+
|
|
38
|
+
return lookup[_type];
|
|
39
|
+
}
|
package/src/menu/menu.cm.mts
CHANGED
|
@@ -23,6 +23,7 @@ export class ZRomulatorMenuComponentModel extends ZCircusComponentModel {
|
|
|
23
23
|
|
|
24
24
|
public systems = this.listItem.bind(this, "systems");
|
|
25
25
|
public games = this.listItem.bind(this, "games");
|
|
26
|
+
public jobs = this.listItem.bind(this, "jobs");
|
|
26
27
|
public settings = this.listItem.bind(this, "settings");
|
|
27
28
|
|
|
28
29
|
public drawer(): Promise<ZDialogComponentModel> {
|
package/src/menu/menu.spec.tsx
CHANGED
|
@@ -74,7 +74,7 @@ describe("ZRomulatorMenu", () => {
|
|
|
74
74
|
});
|
|
75
75
|
|
|
76
76
|
describe("Navigation", () => {
|
|
77
|
-
type NavigationName = "systems" | "settings" | "
|
|
77
|
+
type NavigationName = "systems" | "settings" | "jobs" | "games";
|
|
78
78
|
|
|
79
79
|
const shouldNavigateTo = async (expected: string, name: NavigationName) => {
|
|
80
80
|
// Arrange.
|
|
@@ -89,14 +89,18 @@ describe("ZRomulatorMenu", () => {
|
|
|
89
89
|
expect(_history.location.pathname).toEqual(expected);
|
|
90
90
|
};
|
|
91
91
|
|
|
92
|
-
it("should
|
|
92
|
+
it("should navigate to the systems page", async () => {
|
|
93
93
|
await shouldNavigateTo("/systems", "systems");
|
|
94
94
|
});
|
|
95
95
|
|
|
96
|
-
it("should
|
|
96
|
+
it("should navigate to the games page", async () => {
|
|
97
97
|
await shouldNavigateTo("/games", "games");
|
|
98
98
|
});
|
|
99
99
|
|
|
100
|
+
it("should navigate to the jobs page", async () => {
|
|
101
|
+
await shouldNavigateTo("/jobs", "jobs");
|
|
102
|
+
});
|
|
103
|
+
|
|
100
104
|
it("should navigate to the settings page", async () => {
|
|
101
105
|
await shouldNavigateTo("/settings", "settings");
|
|
102
106
|
});
|
package/src/menu/menu.tsx
CHANGED
|
@@ -85,6 +85,19 @@ export function ZRomulatorMenu() {
|
|
|
85
85
|
/>
|
|
86
86
|
</ZListItem>
|
|
87
87
|
|
|
88
|
+
<ZListItem
|
|
89
|
+
name="jobs"
|
|
90
|
+
interactive
|
|
91
|
+
cursor="pointer"
|
|
92
|
+
onClick={navigateAndClose.bind(null, "/jobs")}
|
|
93
|
+
>
|
|
94
|
+
<ZContentTitle
|
|
95
|
+
avatar={<ZIconFontAwesome name="briefcase" />}
|
|
96
|
+
heading={<ZH3 compact>Jobs</ZH3>}
|
|
97
|
+
subHeading={<ZCaption>What's running in the background</ZCaption>}
|
|
98
|
+
/>
|
|
99
|
+
</ZListItem>
|
|
100
|
+
|
|
88
101
|
<ZListItem
|
|
89
102
|
name="settings"
|
|
90
103
|
interactive
|