@checkstack/queue-frontend 0.0.2

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 ADDED
@@ -0,0 +1,60 @@
1
+ # @checkstack/queue-frontend
2
+
3
+ ## 0.0.2
4
+
5
+ ### Patch Changes
6
+
7
+ - d20d274: Initial release of all @checkstack packages. Rebranded from Checkmate to Checkstack with new npm organization @checkstack and domain checkstack.dev.
8
+ - Updated dependencies [d20d274]
9
+ - @checkstack/common@0.0.2
10
+ - @checkstack/frontend-api@0.0.2
11
+ - @checkstack/queue-common@0.0.2
12
+ - @checkstack/ui@0.0.2
13
+
14
+ ## 0.0.4
15
+
16
+ ### Patch Changes
17
+
18
+ - 32ea706: ### User Menu Loading State Fix
19
+
20
+ Fixed user menu items "popping in" one after another due to independent async permission checks.
21
+
22
+ **Changes:**
23
+
24
+ - Added `UserMenuItemsContext` interface with `permissions` and `hasCredentialAccount` to `@checkstack/frontend-api`
25
+ - `LoginNavbarAction` now pre-fetches all permissions and credential account info before rendering the menu
26
+ - All user menu item components now use the passed context for synchronous permission checks instead of async hooks
27
+ - Uses `qualifyPermissionId` helper for fully-qualified permission IDs
28
+
29
+ **Result:** All menu items appear simultaneously when the user menu opens.
30
+
31
+ - Updated dependencies [52231ef]
32
+ - Updated dependencies [b0124ef]
33
+ - Updated dependencies [54cc787]
34
+ - Updated dependencies [a65e002]
35
+ - Updated dependencies [ae33df2]
36
+ - Updated dependencies [32ea706]
37
+ - @checkstack/ui@0.1.2
38
+ - @checkstack/common@0.2.0
39
+ - @checkstack/frontend-api@0.1.0
40
+ - @checkstack/queue-common@0.0.3
41
+
42
+ ## 0.0.3
43
+
44
+ ### Patch Changes
45
+
46
+ - Updated dependencies [0f8cc7d]
47
+ - @checkstack/frontend-api@0.0.3
48
+ - @checkstack/ui@0.1.1
49
+
50
+ ## 0.0.2
51
+
52
+ ### Patch Changes
53
+
54
+ - Updated dependencies [eff5b4e]
55
+ - Updated dependencies [ffc28f6]
56
+ - Updated dependencies [b354ab3]
57
+ - @checkstack/ui@0.1.0
58
+ - @checkstack/common@0.1.0
59
+ - @checkstack/frontend-api@0.0.2
60
+ - @checkstack/queue-common@0.0.2
package/package.json ADDED
@@ -0,0 +1,34 @@
1
+ {
2
+ "name": "@checkstack/queue-frontend",
3
+ "version": "0.0.2",
4
+ "type": "module",
5
+ "main": "./dist/index.js",
6
+ "types": "./dist/index.d.ts",
7
+ "scripts": {
8
+ "build": "tsc",
9
+ "dev": "tsc --watch",
10
+ "typecheck": "tsc --noEmit",
11
+ "lint": "bun run lint:code",
12
+ "lint:code": "eslint . --max-warnings 0"
13
+ },
14
+ "dependencies": {
15
+ "@checkstack/common": "workspace:*",
16
+ "@checkstack/frontend-api": "workspace:*",
17
+ "@checkstack/queue-common": "workspace:*",
18
+ "@checkstack/ui": "workspace:*",
19
+ "ajv": "^8.17.1",
20
+ "ajv-formats": "^3.0.1",
21
+ "lucide-react": "^0.468.0",
22
+ "prismjs": "^1.29.0",
23
+ "react": "^18.3.1",
24
+ "react-router-dom": "^7.1.1",
25
+ "react-simple-code-editor": "^0.14.1",
26
+ "zod": "^4.0.0"
27
+ },
28
+ "devDependencies": {
29
+ "@types/react": "^18.3.18",
30
+ "typescript": "^5.7.2",
31
+ "@checkstack/tsconfig": "workspace:*",
32
+ "@checkstack/scripts": "workspace:*"
33
+ }
34
+ }
package/src/api.ts ADDED
@@ -0,0 +1,15 @@
1
+ import { createApiRef } from "@checkstack/frontend-api";
2
+ import { QueueApi } from "@checkstack/queue-common";
3
+ import type { InferClient } from "@checkstack/common";
4
+
5
+ // Re-export types for convenience
6
+ export type {
7
+ QueuePluginDto,
8
+ QueueConfigurationDto,
9
+ UpdateQueueConfiguration,
10
+ } from "@checkstack/queue-common";
11
+
12
+ // QueueApiClient type inferred from the client definition
13
+ export type QueueApiClient = InferClient<typeof QueueApi>;
14
+
15
+ export const queueApiRef = createApiRef<QueueApiClient>("queue-api");
@@ -0,0 +1,33 @@
1
+ import React from "react";
2
+ import { Link } from "react-router-dom";
3
+ import { ListOrdered } from "lucide-react";
4
+ import type { UserMenuItemsContext } from "@checkstack/frontend-api";
5
+ import { DropdownMenuItem } from "@checkstack/ui";
6
+ import { qualifyPermissionId, resolveRoute } from "@checkstack/common";
7
+ import {
8
+ queueRoutes,
9
+ permissions,
10
+ pluginMetadata,
11
+ } from "@checkstack/queue-common";
12
+
13
+ export const QueueUserMenuItems = ({
14
+ permissions: userPerms,
15
+ }: UserMenuItemsContext) => {
16
+ const qualifiedId = qualifyPermissionId(
17
+ pluginMetadata,
18
+ permissions.queueRead
19
+ );
20
+ const canRead = userPerms.includes("*") || userPerms.includes(qualifiedId);
21
+
22
+ if (!canRead) {
23
+ return <React.Fragment />;
24
+ }
25
+
26
+ return (
27
+ <Link to={resolveRoute(queueRoutes.routes.config)}>
28
+ <DropdownMenuItem icon={<ListOrdered className="h-4 w-4" />}>
29
+ Queue Settings
30
+ </DropdownMenuItem>
31
+ </Link>
32
+ );
33
+ };
package/src/index.tsx ADDED
@@ -0,0 +1,44 @@
1
+ import {
2
+ rpcApiRef,
3
+ ApiRef,
4
+ UserMenuItemsSlot,
5
+ createSlotExtension,
6
+ createFrontendPlugin,
7
+ } from "@checkstack/frontend-api";
8
+ import { queueApiRef, type QueueApiClient } from "./api";
9
+ import { QueueConfigPage } from "./pages/QueueConfigPage";
10
+ import { QueueUserMenuItems } from "./components/UserMenuItems";
11
+ import {
12
+ queueRoutes,
13
+ QueueApi,
14
+ pluginMetadata,
15
+ permissions,
16
+ } from "@checkstack/queue-common";
17
+
18
+ export const queuePlugin = createFrontendPlugin({
19
+ metadata: pluginMetadata,
20
+ apis: [
21
+ {
22
+ ref: queueApiRef,
23
+ factory: (deps: { get: <T>(ref: ApiRef<T>) => T }): QueueApiClient => {
24
+ const rpcApi = deps.get(rpcApiRef);
25
+ return rpcApi.forPlugin(QueueApi);
26
+ },
27
+ },
28
+ ],
29
+ routes: [
30
+ {
31
+ route: queueRoutes.routes.config,
32
+ element: <QueueConfigPage />,
33
+ permission: permissions.queueRead,
34
+ },
35
+ ],
36
+ extensions: [
37
+ createSlotExtension(UserMenuItemsSlot, {
38
+ id: "queue.user-menu.items",
39
+ component: QueueUserMenuItems,
40
+ }),
41
+ ],
42
+ });
43
+
44
+ export * from "./api";
@@ -0,0 +1,126 @@
1
+ import { useEffect, useState } from "react";
2
+ import {
3
+ useApi,
4
+ wrapInSuspense,
5
+ permissionApiRef,
6
+ } from "@checkstack/frontend-api";
7
+ import { queueApiRef } from "../api";
8
+ import { QueuePluginDto } from "@checkstack/queue-common";
9
+ import {
10
+ Button,
11
+ Alert,
12
+ AlertTitle,
13
+ AlertDescription,
14
+ PageLayout,
15
+ PluginConfigForm,
16
+ Card,
17
+ CardContent,
18
+ CardFooter,
19
+ CardHeader,
20
+ CardTitle,
21
+ useToast,
22
+ } from "@checkstack/ui";
23
+ import { AlertTriangle, Save } from "lucide-react";
24
+
25
+ const QueueConfigPageContent = () => {
26
+ const api = useApi(queueApiRef);
27
+ const permissionApi = useApi(permissionApiRef);
28
+ const toast = useToast();
29
+ const { allowed: canRead, loading: permissionLoading } =
30
+ permissionApi.useResourcePermission("queue", "read");
31
+ const { allowed: canUpdate } = permissionApi.useManagePermission("queue");
32
+
33
+ const [plugins, setPlugins] = useState<QueuePluginDto[]>([]);
34
+ const [selectedPluginId, setSelectedPluginId] = useState<string>("");
35
+ const [config, setConfig] = useState<Record<string, unknown>>({});
36
+ const [isSaving, setIsSaving] = useState(false);
37
+
38
+ useEffect(() => {
39
+ const fetchData = async () => {
40
+ const [pluginsList, configuration] = await Promise.all([
41
+ api.getPlugins(),
42
+ api.getConfiguration(),
43
+ ]);
44
+ setPlugins(pluginsList);
45
+ setSelectedPluginId(configuration.pluginId);
46
+ setConfig(configuration.config);
47
+ };
48
+ fetchData();
49
+ }, [api]);
50
+
51
+ const handleSave = async () => {
52
+ if (!selectedPluginId) return;
53
+ setIsSaving(true);
54
+ try {
55
+ await api.updateConfiguration({
56
+ pluginId: selectedPluginId,
57
+ config,
58
+ });
59
+ toast.success("Configuration saved successfully!");
60
+ } catch (error) {
61
+ const message = error instanceof Error ? error.message : String(error);
62
+ toast.error(`Failed to save configuration: ${message}`);
63
+ } finally {
64
+ setIsSaving(false);
65
+ }
66
+ };
67
+
68
+ const isMemoryQueue = selectedPluginId === "memory";
69
+
70
+ return (
71
+ <PageLayout
72
+ title="Queue Settings"
73
+ subtitle="Configure the queue system for background jobs"
74
+ loading={permissionLoading}
75
+ allowed={canRead}
76
+ maxWidth="3xl"
77
+ >
78
+ <Card>
79
+ <CardHeader>
80
+ <CardTitle>Queue Configuration</CardTitle>
81
+ <p className="text-sm text-muted-foreground">
82
+ Select and configure the queue plugin
83
+ </p>
84
+ </CardHeader>
85
+ <CardContent className="space-y-6">
86
+ {isMemoryQueue && (
87
+ <Alert variant="warning">
88
+ <AlertTriangle className="h-5 w-5" />
89
+ <div>
90
+ <AlertTitle>In-Memory Queue Warning</AlertTitle>
91
+ <AlertDescription>
92
+ The in-memory queue is suitable for development and
93
+ single-instance deployments only. It will not scale across
94
+ multiple instances and jobs will be lost on restart. For
95
+ production environments with multiple instances, consider
96
+ using a persistent queue implementation.
97
+ </AlertDescription>
98
+ </div>
99
+ </Alert>
100
+ )}
101
+
102
+ <PluginConfigForm
103
+ label="Queue Plugin"
104
+ plugins={plugins}
105
+ selectedPluginId={selectedPluginId}
106
+ onPluginChange={(value) => {
107
+ setSelectedPluginId(value);
108
+ setConfig({});
109
+ }}
110
+ config={config}
111
+ onConfigChange={setConfig}
112
+ disabled={!canUpdate}
113
+ />
114
+ </CardContent>
115
+ <CardFooter className="flex justify-end gap-2">
116
+ <Button onClick={handleSave} disabled={!canUpdate || isSaving}>
117
+ <Save className="mr-2 h-4 w-4" />
118
+ {isSaving ? "Saving..." : "Save Configuration"}
119
+ </Button>
120
+ </CardFooter>
121
+ </Card>
122
+ </PageLayout>
123
+ );
124
+ };
125
+
126
+ export const QueueConfigPage = wrapInSuspense(QueueConfigPageContent);
package/tsconfig.json ADDED
@@ -0,0 +1,6 @@
1
+ {
2
+ "extends": "@checkstack/tsconfig/frontend.json",
3
+ "include": [
4
+ "src"
5
+ ]
6
+ }