@kuadrant/kuadrant-backstage-plugin-frontend 0.0.1-test.1-d62c1cdb → 0.0.1-test.1-57ace816
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/dist/components/ApiKeyManagementTab/ApiKeyManagementTab.esm.js +52 -23
- package/dist/components/ApiKeyManagementTab/ApiKeyManagementTab.esm.js.map +1 -1
- package/dist/components/ApprovalQueueCard/ApprovalQueueCard.esm.js +38 -28
- package/dist/components/ApprovalQueueCard/ApprovalQueueCard.esm.js.map +1 -1
- package/dist/components/CreateAPIProductDialog/CreateAPIProductDialog.esm.js +43 -28
- package/dist/components/CreateAPIProductDialog/CreateAPIProductDialog.esm.js.map +1 -1
- package/dist/components/EditAPIProductDialog/EditAPIProductDialog.esm.js +258 -0
- package/dist/components/EditAPIProductDialog/EditAPIProductDialog.esm.js.map +1 -0
- package/dist/components/KuadrantPage/KuadrantPage.esm.js +85 -34
- package/dist/components/KuadrantPage/KuadrantPage.esm.js.map +1 -1
- package/dist/components/PermissionGate/PermissionGate.esm.js +7 -16
- package/dist/components/PermissionGate/PermissionGate.esm.js.map +1 -1
- package/dist/index.d.ts +81 -1
- package/dist/index.esm.js +1 -0
- package/dist/index.esm.js.map +1 -1
- package/dist/permissions.esm.js +103 -0
- package/dist/permissions.esm.js.map +1 -0
- package/dist/utils/permissions.esm.js +19 -0
- package/dist/utils/permissions.esm.js.map +1 -0
- package/package.json +1 -1
- package/dist/hooks/useUserRole.esm.js +0 -49
- package/dist/hooks/useUserRole.esm.js.map +0 -1
|
@@ -0,0 +1,258 @@
|
|
|
1
|
+
import React, { useState, useEffect } from 'react';
|
|
2
|
+
import { Dialog, DialogTitle, DialogContent, Grid, TextField, MenuItem, Typography, Box, Chip, Button, DialogActions } from '@material-ui/core';
|
|
3
|
+
import { useApi, configApiRef, fetchApiRef } from '@backstage/core-plugin-api';
|
|
4
|
+
import { Alert } from '@material-ui/lab';
|
|
5
|
+
import { Progress } from '@backstage/core-components';
|
|
6
|
+
|
|
7
|
+
const EditAPIProductDialog = ({ open, onClose, onSuccess, namespace, name }) => {
|
|
8
|
+
const config = useApi(configApiRef);
|
|
9
|
+
const fetchApi = useApi(fetchApiRef);
|
|
10
|
+
const backendUrl = config.getString("backend.baseUrl");
|
|
11
|
+
const [loading, setLoading] = useState(false);
|
|
12
|
+
const [displayName, setDisplayName] = useState("");
|
|
13
|
+
const [description, setDescription] = useState("");
|
|
14
|
+
const [version, setVersion] = useState("v1");
|
|
15
|
+
const [publishStatus, setPublishStatus] = useState("Draft");
|
|
16
|
+
const [approvalMode, setApprovalMode] = useState("manual");
|
|
17
|
+
const [tags, setTags] = useState([]);
|
|
18
|
+
const [targetRef, setTargetRef] = useState(null);
|
|
19
|
+
const [tagInput, setTagInput] = useState("");
|
|
20
|
+
const [contactEmail, setContactEmail] = useState("");
|
|
21
|
+
const [contactTeam, setContactTeam] = useState("");
|
|
22
|
+
const [docsURL, setDocsURL] = useState("");
|
|
23
|
+
const [openAPISpec, setOpenAPISpec] = useState("");
|
|
24
|
+
const [error, setError] = useState("");
|
|
25
|
+
const [saving, setSaving] = useState(false);
|
|
26
|
+
useEffect(() => {
|
|
27
|
+
if (open && namespace && name) {
|
|
28
|
+
setLoading(true);
|
|
29
|
+
setError("");
|
|
30
|
+
fetchApi.fetch(`${backendUrl}/api/kuadrant/apiproducts/${namespace}/${name}`).then((res) => res.json()).then((data) => {
|
|
31
|
+
setDisplayName(data.spec.displayName || "");
|
|
32
|
+
setDescription(data.spec.description || "");
|
|
33
|
+
setVersion(data.spec.version || "v1");
|
|
34
|
+
setPublishStatus(data.spec.publishStatus || "Draft");
|
|
35
|
+
setApprovalMode(data.spec.approvalMode || "manual");
|
|
36
|
+
setTags(data.spec.tags || []);
|
|
37
|
+
setTargetRef(data.spec.targetRef || null);
|
|
38
|
+
setContactEmail(data.spec.contact?.email || "");
|
|
39
|
+
setContactTeam(data.spec.contact?.team || "");
|
|
40
|
+
setDocsURL(data.spec.documentation?.docsURL || "");
|
|
41
|
+
setOpenAPISpec(data.spec.documentation?.openAPISpec || "");
|
|
42
|
+
setLoading(false);
|
|
43
|
+
}).catch((err) => {
|
|
44
|
+
setError(err.message || "Failed to load API Product");
|
|
45
|
+
setLoading(false);
|
|
46
|
+
});
|
|
47
|
+
}
|
|
48
|
+
}, [open, namespace, name, backendUrl, fetchApi]);
|
|
49
|
+
const handleAddTag = () => {
|
|
50
|
+
if (tagInput.trim() && !tags.includes(tagInput.trim())) {
|
|
51
|
+
setTags([...tags, tagInput.trim()]);
|
|
52
|
+
setTagInput("");
|
|
53
|
+
}
|
|
54
|
+
};
|
|
55
|
+
const handleDeleteTag = (tagToDelete) => {
|
|
56
|
+
setTags(tags.filter((tag) => tag !== tagToDelete));
|
|
57
|
+
};
|
|
58
|
+
const handleSave = async () => {
|
|
59
|
+
setError("");
|
|
60
|
+
setSaving(true);
|
|
61
|
+
try {
|
|
62
|
+
const patch = {
|
|
63
|
+
spec: {
|
|
64
|
+
displayName,
|
|
65
|
+
description,
|
|
66
|
+
version,
|
|
67
|
+
publishStatus,
|
|
68
|
+
approvalMode,
|
|
69
|
+
tags,
|
|
70
|
+
targetRef,
|
|
71
|
+
...contactEmail || contactTeam ? {
|
|
72
|
+
contact: {
|
|
73
|
+
...contactEmail && { email: contactEmail },
|
|
74
|
+
...contactTeam && { team: contactTeam }
|
|
75
|
+
}
|
|
76
|
+
} : {},
|
|
77
|
+
...docsURL || openAPISpec ? {
|
|
78
|
+
documentation: {
|
|
79
|
+
...docsURL && { docsURL },
|
|
80
|
+
...openAPISpec && { openAPISpec }
|
|
81
|
+
}
|
|
82
|
+
} : {}
|
|
83
|
+
}
|
|
84
|
+
};
|
|
85
|
+
const response = await fetchApi.fetch(
|
|
86
|
+
`${backendUrl}/api/kuadrant/apiproducts/${namespace}/${name}`,
|
|
87
|
+
{
|
|
88
|
+
method: "PATCH",
|
|
89
|
+
headers: {
|
|
90
|
+
"Content-Type": "application/json"
|
|
91
|
+
},
|
|
92
|
+
body: JSON.stringify(patch)
|
|
93
|
+
}
|
|
94
|
+
);
|
|
95
|
+
if (!response.ok) {
|
|
96
|
+
const errorData = await response.json();
|
|
97
|
+
throw new Error(errorData.error || "failed to update apiproduct");
|
|
98
|
+
}
|
|
99
|
+
onSuccess();
|
|
100
|
+
onClose();
|
|
101
|
+
} catch (err) {
|
|
102
|
+
setError(err instanceof Error ? err.message : String(err));
|
|
103
|
+
} finally {
|
|
104
|
+
setSaving(false);
|
|
105
|
+
}
|
|
106
|
+
};
|
|
107
|
+
return /* @__PURE__ */ React.createElement(Dialog, { open, onClose, maxWidth: "md", fullWidth: true }, /* @__PURE__ */ React.createElement(DialogTitle, null, "Edit API Product"), /* @__PURE__ */ React.createElement(DialogContent, null, error && /* @__PURE__ */ React.createElement(Alert, { severity: "error", style: { marginBottom: 16 } }, error), loading ? /* @__PURE__ */ React.createElement(Progress, null) : /* @__PURE__ */ React.createElement(Grid, { container: true, spacing: 2 }, /* @__PURE__ */ React.createElement(Grid, { item: true, xs: 6 }, /* @__PURE__ */ React.createElement(
|
|
108
|
+
TextField,
|
|
109
|
+
{
|
|
110
|
+
fullWidth: true,
|
|
111
|
+
label: "Name",
|
|
112
|
+
value: name,
|
|
113
|
+
disabled: true,
|
|
114
|
+
helperText: "Kubernetes resource name (immutable)",
|
|
115
|
+
margin: "normal"
|
|
116
|
+
}
|
|
117
|
+
)), /* @__PURE__ */ React.createElement(Grid, { item: true, xs: 6 }, /* @__PURE__ */ React.createElement(
|
|
118
|
+
TextField,
|
|
119
|
+
{
|
|
120
|
+
fullWidth: true,
|
|
121
|
+
label: "Namespace",
|
|
122
|
+
value: namespace,
|
|
123
|
+
disabled: true,
|
|
124
|
+
helperText: "Kubernetes namespace (immutable)",
|
|
125
|
+
margin: "normal"
|
|
126
|
+
}
|
|
127
|
+
)), /* @__PURE__ */ React.createElement(Grid, { item: true, xs: 6 }, /* @__PURE__ */ React.createElement(
|
|
128
|
+
TextField,
|
|
129
|
+
{
|
|
130
|
+
fullWidth: true,
|
|
131
|
+
label: "Display Name",
|
|
132
|
+
value: displayName,
|
|
133
|
+
onChange: (e) => setDisplayName(e.target.value),
|
|
134
|
+
placeholder: "My API",
|
|
135
|
+
margin: "normal",
|
|
136
|
+
required: true
|
|
137
|
+
}
|
|
138
|
+
)), /* @__PURE__ */ React.createElement(Grid, { item: true, xs: 6 }, /* @__PURE__ */ React.createElement(
|
|
139
|
+
TextField,
|
|
140
|
+
{
|
|
141
|
+
fullWidth: true,
|
|
142
|
+
label: "Version",
|
|
143
|
+
value: version,
|
|
144
|
+
onChange: (e) => setVersion(e.target.value),
|
|
145
|
+
placeholder: "v1",
|
|
146
|
+
margin: "normal"
|
|
147
|
+
}
|
|
148
|
+
)), /* @__PURE__ */ React.createElement(Grid, { item: true, xs: 6 }, /* @__PURE__ */ React.createElement(
|
|
149
|
+
TextField,
|
|
150
|
+
{
|
|
151
|
+
fullWidth: true,
|
|
152
|
+
select: true,
|
|
153
|
+
label: "Publish Status",
|
|
154
|
+
value: publishStatus,
|
|
155
|
+
onChange: (e) => setPublishStatus(e.target.value),
|
|
156
|
+
margin: "normal",
|
|
157
|
+
helperText: "Draft: Hidden from catalog. Published: Visible in catalog."
|
|
158
|
+
},
|
|
159
|
+
/* @__PURE__ */ React.createElement(MenuItem, { value: "Draft" }, "Draft (Hidden)"),
|
|
160
|
+
/* @__PURE__ */ React.createElement(MenuItem, { value: "Published" }, "Published (Visible)")
|
|
161
|
+
)), /* @__PURE__ */ React.createElement(Grid, { item: true, xs: 6 }, /* @__PURE__ */ React.createElement(
|
|
162
|
+
TextField,
|
|
163
|
+
{
|
|
164
|
+
fullWidth: true,
|
|
165
|
+
select: true,
|
|
166
|
+
label: "Approval Mode",
|
|
167
|
+
value: approvalMode,
|
|
168
|
+
onChange: (e) => setApprovalMode(e.target.value),
|
|
169
|
+
margin: "normal",
|
|
170
|
+
helperText: "Automatic: keys created immediately. Manual: requires approval."
|
|
171
|
+
},
|
|
172
|
+
/* @__PURE__ */ React.createElement(MenuItem, { value: "manual" }, "Manual"),
|
|
173
|
+
/* @__PURE__ */ React.createElement(MenuItem, { value: "automatic" }, "Automatic")
|
|
174
|
+
)), /* @__PURE__ */ React.createElement(Grid, { item: true, xs: 12 }, /* @__PURE__ */ React.createElement(
|
|
175
|
+
TextField,
|
|
176
|
+
{
|
|
177
|
+
fullWidth: true,
|
|
178
|
+
label: "Description",
|
|
179
|
+
value: description,
|
|
180
|
+
onChange: (e) => setDescription(e.target.value),
|
|
181
|
+
placeholder: "API description",
|
|
182
|
+
margin: "normal",
|
|
183
|
+
multiline: true,
|
|
184
|
+
rows: 2,
|
|
185
|
+
required: true
|
|
186
|
+
}
|
|
187
|
+
)), /* @__PURE__ */ React.createElement(Grid, { item: true, xs: 12 }, /* @__PURE__ */ React.createElement(Typography, { variant: "subtitle2", gutterBottom: true, style: { marginTop: 16 } }, "Tags"), /* @__PURE__ */ React.createElement(Box, { display: "flex", flexWrap: "wrap", marginBottom: 1, style: { gap: 8 } }, tags.map((tag) => /* @__PURE__ */ React.createElement(
|
|
188
|
+
Chip,
|
|
189
|
+
{
|
|
190
|
+
key: tag,
|
|
191
|
+
label: tag,
|
|
192
|
+
onDelete: () => handleDeleteTag(tag),
|
|
193
|
+
size: "small"
|
|
194
|
+
}
|
|
195
|
+
))), /* @__PURE__ */ React.createElement(Box, { display: "flex", style: { gap: 8 } }, /* @__PURE__ */ React.createElement(
|
|
196
|
+
TextField,
|
|
197
|
+
{
|
|
198
|
+
fullWidth: true,
|
|
199
|
+
size: "small",
|
|
200
|
+
value: tagInput,
|
|
201
|
+
onChange: (e) => setTagInput(e.target.value),
|
|
202
|
+
onKeyPress: (e) => e.key === "Enter" && handleAddTag(),
|
|
203
|
+
placeholder: "Add tag"
|
|
204
|
+
}
|
|
205
|
+
), /* @__PURE__ */ React.createElement(Button, { onClick: handleAddTag, variant: "outlined", size: "small" }, "Add"))), /* @__PURE__ */ React.createElement(Grid, { item: true, xs: 6 }, /* @__PURE__ */ React.createElement(
|
|
206
|
+
TextField,
|
|
207
|
+
{
|
|
208
|
+
fullWidth: true,
|
|
209
|
+
label: "Contact Email",
|
|
210
|
+
value: contactEmail,
|
|
211
|
+
onChange: (e) => setContactEmail(e.target.value),
|
|
212
|
+
placeholder: "api-team@example.com",
|
|
213
|
+
margin: "normal"
|
|
214
|
+
}
|
|
215
|
+
)), /* @__PURE__ */ React.createElement(Grid, { item: true, xs: 6 }, /* @__PURE__ */ React.createElement(
|
|
216
|
+
TextField,
|
|
217
|
+
{
|
|
218
|
+
fullWidth: true,
|
|
219
|
+
label: "Contact Team",
|
|
220
|
+
value: contactTeam,
|
|
221
|
+
onChange: (e) => setContactTeam(e.target.value),
|
|
222
|
+
placeholder: "platform-team",
|
|
223
|
+
margin: "normal"
|
|
224
|
+
}
|
|
225
|
+
)), /* @__PURE__ */ React.createElement(Grid, { item: true, xs: 6 }, /* @__PURE__ */ React.createElement(
|
|
226
|
+
TextField,
|
|
227
|
+
{
|
|
228
|
+
fullWidth: true,
|
|
229
|
+
label: "Docs URL",
|
|
230
|
+
value: docsURL,
|
|
231
|
+
onChange: (e) => setDocsURL(e.target.value),
|
|
232
|
+
placeholder: "https://api.example.com/docs",
|
|
233
|
+
margin: "normal"
|
|
234
|
+
}
|
|
235
|
+
)), /* @__PURE__ */ React.createElement(Grid, { item: true, xs: 6 }, /* @__PURE__ */ React.createElement(
|
|
236
|
+
TextField,
|
|
237
|
+
{
|
|
238
|
+
fullWidth: true,
|
|
239
|
+
label: "OpenAPI Spec URL",
|
|
240
|
+
value: openAPISpec,
|
|
241
|
+
onChange: (e) => setOpenAPISpec(e.target.value),
|
|
242
|
+
placeholder: "https://api.example.com/openapi.json",
|
|
243
|
+
margin: "normal"
|
|
244
|
+
}
|
|
245
|
+
)))), /* @__PURE__ */ React.createElement(DialogActions, null, /* @__PURE__ */ React.createElement(Button, { onClick: onClose }, "Cancel"), /* @__PURE__ */ React.createElement(
|
|
246
|
+
Button,
|
|
247
|
+
{
|
|
248
|
+
onClick: handleSave,
|
|
249
|
+
color: "primary",
|
|
250
|
+
variant: "contained",
|
|
251
|
+
disabled: saving || loading || !displayName || !description
|
|
252
|
+
},
|
|
253
|
+
saving ? "Saving..." : "Save"
|
|
254
|
+
)));
|
|
255
|
+
};
|
|
256
|
+
|
|
257
|
+
export { EditAPIProductDialog };
|
|
258
|
+
//# sourceMappingURL=EditAPIProductDialog.esm.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"EditAPIProductDialog.esm.js","sources":["../../../src/components/EditAPIProductDialog/EditAPIProductDialog.tsx"],"sourcesContent":["import React, { useState, useEffect } from 'react';\nimport {\n Dialog,\n DialogTitle,\n DialogContent,\n DialogActions,\n Button,\n TextField,\n Box,\n Typography,\n Chip,\n Grid,\n MenuItem,\n} from '@material-ui/core';\nimport { useApi, configApiRef, fetchApiRef } from '@backstage/core-plugin-api';\nimport { Alert } from '@material-ui/lab';\nimport { Progress } from '@backstage/core-components';\n\ninterface EditAPIProductDialogProps {\n open: boolean;\n onClose: () => void;\n onSuccess: () => void;\n namespace: string;\n name: string;\n}\n\nexport const EditAPIProductDialog = ({open, onClose, onSuccess, namespace, name}: EditAPIProductDialogProps) => {\n const config = useApi(configApiRef);\n const fetchApi = useApi(fetchApiRef);\n const backendUrl = config.getString('backend.baseUrl');\n const [loading, setLoading] = useState(false);\n const [displayName, setDisplayName] = useState('');\n const [description, setDescription] = useState('');\n const [version, setVersion] = useState('v1');\n const [publishStatus, setPublishStatus] = useState<'Draft' | 'Published'>('Draft');\n const [approvalMode, setApprovalMode] = useState<'automatic' | 'manual'>('manual');\n const [tags, setTags] = useState<string[]>([]);\n const [targetRef, setTargetRef] = useState<any>(null);\n const [tagInput, setTagInput] = useState('');\n const [contactEmail, setContactEmail] = useState('');\n const [contactTeam, setContactTeam] = useState('');\n const [docsURL, setDocsURL] = useState('');\n const [openAPISpec, setOpenAPISpec] = useState('');\n const [error, setError] = useState('');\n const [saving, setSaving] = useState(false);\n\n // Load APIProduct data when dialog opens\n useEffect(() => {\n if (open && namespace && name) {\n setLoading(true);\n setError('');\n\n fetchApi.fetch(`${backendUrl}/api/kuadrant/apiproducts/${namespace}/${name}`)\n .then(res => res.json())\n .then(data => {\n setDisplayName(data.spec.displayName || '');\n setDescription(data.spec.description || '');\n setVersion(data.spec.version || 'v1');\n setPublishStatus(data.spec.publishStatus || 'Draft');\n setApprovalMode(data.spec.approvalMode || 'manual');\n setTags(data.spec.tags || []);\n setTargetRef(data.spec.targetRef || null);\n setContactEmail(data.spec.contact?.email || '');\n setContactTeam(data.spec.contact?.team || '');\n setDocsURL(data.spec.documentation?.docsURL || '');\n setOpenAPISpec(data.spec.documentation?.openAPISpec || '');\n setLoading(false);\n })\n .catch(err => {\n setError(err.message || 'Failed to load API Product');\n setLoading(false);\n });\n }\n }, [open, namespace, name, backendUrl, fetchApi]);\n\n const handleAddTag = () => {\n if (tagInput.trim() && !tags.includes(tagInput.trim())) {\n setTags([...tags, tagInput.trim()]);\n setTagInput('');\n }\n };\n\n const handleDeleteTag = (tagToDelete: string) => {\n setTags(tags.filter(tag => tag !== tagToDelete));\n };\n\n const handleSave = async () => {\n setError('');\n setSaving(true);\n\n try {\n const patch = {\n spec: {\n displayName,\n description,\n version,\n publishStatus,\n approvalMode,\n tags,\n targetRef,\n ...(contactEmail || contactTeam ? {\n contact: {\n ...(contactEmail && { email: contactEmail }),\n ...(contactTeam && { team: contactTeam }),\n },\n } : {}),\n ...(docsURL || openAPISpec ? {\n documentation: {\n ...(docsURL && { docsURL }),\n ...(openAPISpec && { openAPISpec }),\n },\n } : {}),\n },\n };\n\n const response = await fetchApi.fetch(\n `${backendUrl}/api/kuadrant/apiproducts/${namespace}/${name}`,\n {\n method: 'PATCH',\n headers: {\n 'Content-Type': 'application/json',\n },\n body: JSON.stringify(patch),\n }\n );\n\n if (!response.ok) {\n const errorData = await response.json();\n throw new Error(errorData.error || 'failed to update apiproduct');\n }\n\n onSuccess();\n onClose();\n } catch (err) {\n setError(err instanceof Error ? err.message : String(err));\n } finally {\n setSaving(false);\n }\n };\n\n return (\n <Dialog open={open} onClose={onClose} maxWidth=\"md\" fullWidth>\n <DialogTitle>Edit API Product</DialogTitle>\n <DialogContent>\n {error && (\n <Alert severity=\"error\" style={{ marginBottom: 16 }}>\n {error}\n </Alert>\n )}\n\n {loading ? (\n <Progress />\n ) : (\n <Grid container spacing={2}>\n <Grid item xs={6}>\n <TextField\n fullWidth\n label=\"Name\"\n value={name}\n disabled\n helperText=\"Kubernetes resource name (immutable)\"\n margin=\"normal\"\n />\n </Grid>\n <Grid item xs={6}>\n <TextField\n fullWidth\n label=\"Namespace\"\n value={namespace}\n disabled\n helperText=\"Kubernetes namespace (immutable)\"\n margin=\"normal\"\n />\n </Grid>\n <Grid item xs={6}>\n <TextField\n fullWidth\n label=\"Display Name\"\n value={displayName}\n onChange={e => setDisplayName(e.target.value)}\n placeholder=\"My API\"\n margin=\"normal\"\n required\n />\n </Grid>\n <Grid item xs={6}>\n <TextField\n fullWidth\n label=\"Version\"\n value={version}\n onChange={e => setVersion(e.target.value)}\n placeholder=\"v1\"\n margin=\"normal\"\n />\n </Grid>\n <Grid item xs={6}>\n <TextField\n fullWidth\n select\n label=\"Publish Status\"\n value={publishStatus}\n onChange={e => setPublishStatus(e.target.value as 'Draft' | 'Published')}\n margin=\"normal\"\n helperText=\"Draft: Hidden from catalog. Published: Visible in catalog.\"\n >\n <MenuItem value=\"Draft\">Draft (Hidden)</MenuItem>\n <MenuItem value=\"Published\">Published (Visible)</MenuItem>\n </TextField>\n </Grid>\n <Grid item xs={6}>\n <TextField\n fullWidth\n select\n label=\"Approval Mode\"\n value={approvalMode}\n onChange={e => setApprovalMode(e.target.value as 'automatic' | 'manual')}\n margin=\"normal\"\n helperText=\"Automatic: keys created immediately. Manual: requires approval.\"\n >\n <MenuItem value=\"manual\">Manual</MenuItem>\n <MenuItem value=\"automatic\">Automatic</MenuItem>\n </TextField>\n </Grid>\n <Grid item xs={12}>\n <TextField\n fullWidth\n label=\"Description\"\n value={description}\n onChange={e => setDescription(e.target.value)}\n placeholder=\"API description\"\n margin=\"normal\"\n multiline\n rows={2}\n required\n />\n </Grid>\n\n <Grid item xs={12}>\n <Typography variant=\"subtitle2\" gutterBottom style={{ marginTop: 16 }}>\n Tags\n </Typography>\n <Box display=\"flex\" flexWrap=\"wrap\" marginBottom={1} style={{ gap: 8 }}>\n {tags.map(tag => (\n <Chip\n key={tag}\n label={tag}\n onDelete={() => handleDeleteTag(tag)}\n size=\"small\"\n />\n ))}\n </Box>\n <Box display=\"flex\" style={{ gap: 8 }}>\n <TextField\n fullWidth\n size=\"small\"\n value={tagInput}\n onChange={e => setTagInput(e.target.value)}\n onKeyPress={e => e.key === 'Enter' && handleAddTag()}\n placeholder=\"Add tag\"\n />\n <Button onClick={handleAddTag} variant=\"outlined\" size=\"small\">\n Add\n </Button>\n </Box>\n </Grid>\n\n <Grid item xs={6}>\n <TextField\n fullWidth\n label=\"Contact Email\"\n value={contactEmail}\n onChange={e => setContactEmail(e.target.value)}\n placeholder=\"api-team@example.com\"\n margin=\"normal\"\n />\n </Grid>\n <Grid item xs={6}>\n <TextField\n fullWidth\n label=\"Contact Team\"\n value={contactTeam}\n onChange={e => setContactTeam(e.target.value)}\n placeholder=\"platform-team\"\n margin=\"normal\"\n />\n </Grid>\n <Grid item xs={6}>\n <TextField\n fullWidth\n label=\"Docs URL\"\n value={docsURL}\n onChange={e => setDocsURL(e.target.value)}\n placeholder=\"https://api.example.com/docs\"\n margin=\"normal\"\n />\n </Grid>\n <Grid item xs={6}>\n <TextField\n fullWidth\n label=\"OpenAPI Spec URL\"\n value={openAPISpec}\n onChange={e => setOpenAPISpec(e.target.value)}\n placeholder=\"https://api.example.com/openapi.json\"\n margin=\"normal\"\n />\n </Grid>\n </Grid>\n )}\n </DialogContent>\n <DialogActions>\n <Button onClick={onClose}>Cancel</Button>\n <Button\n onClick={handleSave}\n color=\"primary\"\n variant=\"contained\"\n disabled={saving || loading || !displayName || !description}\n >\n {saving ? 'Saving...' : 'Save'}\n </Button>\n </DialogActions>\n </Dialog>\n );\n};\n"],"names":[],"mappings":"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"}
|
|
@@ -1,25 +1,52 @@
|
|
|
1
1
|
import React, { useState } from 'react';
|
|
2
|
-
import { Box,
|
|
2
|
+
import { Box, Button, Typography, Grid, Dialog, DialogTitle, DialogContent, DialogContentText, DialogActions, Chip, IconButton } from '@material-ui/core';
|
|
3
3
|
import AddIcon from '@material-ui/icons/Add';
|
|
4
4
|
import DeleteIcon from '@material-ui/icons/Delete';
|
|
5
|
+
import EditIcon from '@material-ui/icons/Edit';
|
|
5
6
|
import { Page, Header, SupportButton, Content, ContentHeader, Progress, ResponseErrorPanel, InfoCard, Table, Link } from '@backstage/core-components';
|
|
6
7
|
import useAsync from 'react-use/lib/useAsync';
|
|
7
8
|
import { useApi, configApiRef, fetchApiRef } from '@backstage/core-plugin-api';
|
|
8
9
|
import { ApprovalQueueCard } from '../ApprovalQueueCard/ApprovalQueueCard.esm.js';
|
|
9
10
|
import { PermissionGate } from '../PermissionGate/PermissionGate.esm.js';
|
|
10
|
-
import { useUserRole } from '../../hooks/useUserRole.esm.js';
|
|
11
11
|
import { CreateAPIProductDialog } from '../CreateAPIProductDialog/CreateAPIProductDialog.esm.js';
|
|
12
|
+
import { kuadrantApiProductListPermission, kuadrantApiProductCreatePermission, kuadrantApiKeyRequestReadAllPermission, kuadrantApiProductDeletePermission, kuadrantApiProductUpdatePermission, kuadrantPlanPolicyListPermission } from '../../permissions.esm.js';
|
|
13
|
+
import { useKuadrantPermission } from '../../utils/permissions.esm.js';
|
|
14
|
+
import { EditAPIProductDialog } from '../EditAPIProductDialog/EditAPIProductDialog.esm.js';
|
|
12
15
|
|
|
13
16
|
const ResourceList = () => {
|
|
14
17
|
const config = useApi(configApiRef);
|
|
15
18
|
const fetchApi = useApi(fetchApiRef);
|
|
16
19
|
const backendUrl = config.getString("backend.baseUrl");
|
|
17
|
-
const { userInfo, loading: userLoading } = useUserRole();
|
|
18
20
|
const [createDialogOpen, setCreateDialogOpen] = useState(false);
|
|
21
|
+
const [editDialogOpen, setEditDialogOpen] = useState(false);
|
|
19
22
|
const [refreshTrigger, setRefreshTrigger] = useState(0);
|
|
20
23
|
const [deleteDialogOpen, setDeleteDialogOpen] = useState(false);
|
|
21
24
|
const [apiProductToDelete, setApiProductToDelete] = useState(null);
|
|
25
|
+
const [apiProductToEdit, setApiProductToEdit] = useState(null);
|
|
22
26
|
const [deleting, setDeleting] = useState(false);
|
|
27
|
+
const {
|
|
28
|
+
allowed: canCreateApiProduct,
|
|
29
|
+
loading: createPermissionLoading,
|
|
30
|
+
error: createPermissionError
|
|
31
|
+
} = useKuadrantPermission(kuadrantApiProductCreatePermission);
|
|
32
|
+
const {
|
|
33
|
+
allowed: canViewApprovalQueue,
|
|
34
|
+
loading: approvalQueuePermissionLoading,
|
|
35
|
+
error: approvalQueuePermissionError
|
|
36
|
+
} = useKuadrantPermission(kuadrantApiKeyRequestReadAllPermission);
|
|
37
|
+
const {
|
|
38
|
+
allowed: canDeleteApiProduct,
|
|
39
|
+
loading: deletePermissionLoading,
|
|
40
|
+
error: deletePermissionError
|
|
41
|
+
} = useKuadrantPermission(kuadrantApiProductDeletePermission);
|
|
42
|
+
const {
|
|
43
|
+
allowed: canUpdateApiProduct
|
|
44
|
+
} = useKuadrantPermission(kuadrantApiProductUpdatePermission);
|
|
45
|
+
const {
|
|
46
|
+
allowed: canListPlanPolicies,
|
|
47
|
+
loading: planPolicyPermissionLoading,
|
|
48
|
+
error: planPolicyPermissionError
|
|
49
|
+
} = useKuadrantPermission(kuadrantPlanPolicyListPermission);
|
|
23
50
|
const { value: apiProducts, loading: apiProductsLoading, error: apiProductsError } = useAsync(async () => {
|
|
24
51
|
const response = await fetchApi.fetch(`${backendUrl}/api/kuadrant/apiproducts`);
|
|
25
52
|
return await response.json();
|
|
@@ -28,11 +55,19 @@ const ResourceList = () => {
|
|
|
28
55
|
const response = await fetchApi.fetch(`${backendUrl}/api/kuadrant/planpolicies`);
|
|
29
56
|
return await response.json();
|
|
30
57
|
}, [backendUrl, fetchApi, refreshTrigger]);
|
|
31
|
-
const loading =
|
|
58
|
+
const loading = apiProductsLoading || planPoliciesLoading || createPermissionLoading || approvalQueuePermissionLoading || deletePermissionLoading || planPolicyPermissionLoading;
|
|
32
59
|
const error = apiProductsError || planPoliciesError;
|
|
60
|
+
const permissionError = createPermissionError || approvalQueuePermissionError || deletePermissionError || planPolicyPermissionError;
|
|
33
61
|
const handleCreateSuccess = () => {
|
|
34
62
|
setRefreshTrigger((prev) => prev + 1);
|
|
35
63
|
};
|
|
64
|
+
const handleEditClick = (namespace, name) => {
|
|
65
|
+
setApiProductToEdit({ namespace, name });
|
|
66
|
+
setEditDialogOpen(true);
|
|
67
|
+
};
|
|
68
|
+
const handleEditSuccess = () => {
|
|
69
|
+
setRefreshTrigger((prev) => prev + 1);
|
|
70
|
+
};
|
|
36
71
|
const handleDeleteClick = (namespace, name) => {
|
|
37
72
|
setApiProductToDelete({ namespace, name });
|
|
38
73
|
setDeleteDialogOpen(true);
|
|
@@ -85,12 +120,23 @@ const ResourceList = () => {
|
|
|
85
120
|
render: (row) => row.spec?.version || "-"
|
|
86
121
|
},
|
|
87
122
|
{
|
|
88
|
-
title: "
|
|
89
|
-
field: "
|
|
123
|
+
title: "HTTPRoute",
|
|
124
|
+
field: "spec.targetRef.name",
|
|
125
|
+
render: (row) => row.spec?.targetRef?.name || "-"
|
|
126
|
+
},
|
|
127
|
+
{
|
|
128
|
+
title: "Publish Status",
|
|
129
|
+
field: "spec.publishStatus",
|
|
90
130
|
render: (row) => {
|
|
91
|
-
const
|
|
92
|
-
|
|
93
|
-
|
|
131
|
+
const status = row.spec?.publishStatus || "Draft";
|
|
132
|
+
return /* @__PURE__ */ React.createElement(
|
|
133
|
+
Chip,
|
|
134
|
+
{
|
|
135
|
+
label: status,
|
|
136
|
+
size: "small",
|
|
137
|
+
color: status === "Published" ? "primary" : "default"
|
|
138
|
+
}
|
|
139
|
+
);
|
|
94
140
|
}
|
|
95
141
|
},
|
|
96
142
|
{
|
|
@@ -105,15 +151,23 @@ const ResourceList = () => {
|
|
|
105
151
|
{
|
|
106
152
|
title: "Actions",
|
|
107
153
|
field: "actions",
|
|
108
|
-
render: (row) => /* @__PURE__ */ React.createElement(
|
|
154
|
+
render: (row) => /* @__PURE__ */ React.createElement(Box, { display: "flex", style: { gap: 4 } }, canUpdateApiProduct && /* @__PURE__ */ React.createElement(
|
|
155
|
+
IconButton,
|
|
156
|
+
{
|
|
157
|
+
size: "small",
|
|
158
|
+
onClick: () => handleEditClick(row.metadata.namespace, row.metadata.name),
|
|
159
|
+
title: "Edit API Product"
|
|
160
|
+
},
|
|
161
|
+
/* @__PURE__ */ React.createElement(EditIcon, { fontSize: "small" })
|
|
162
|
+
), canDeleteApiProduct && /* @__PURE__ */ React.createElement(
|
|
109
163
|
IconButton,
|
|
110
164
|
{
|
|
111
165
|
size: "small",
|
|
112
166
|
onClick: () => handleDeleteClick(row.metadata.namespace, row.metadata.name),
|
|
113
|
-
title: "
|
|
167
|
+
title: "Delete API Product"
|
|
114
168
|
},
|
|
115
169
|
/* @__PURE__ */ React.createElement(DeleteIcon, { fontSize: "small" })
|
|
116
|
-
)
|
|
170
|
+
))
|
|
117
171
|
}
|
|
118
172
|
];
|
|
119
173
|
const planPolicyColumns = [
|
|
@@ -153,26 +207,7 @@ const ResourceList = () => {
|
|
|
153
207
|
}
|
|
154
208
|
);
|
|
155
209
|
};
|
|
156
|
-
|
|
157
|
-
switch (role) {
|
|
158
|
-
case "platform-engineer":
|
|
159
|
-
return { label: "Platform Engineer", color: "secondary" };
|
|
160
|
-
case "api-owner":
|
|
161
|
-
return { label: "API Owner", color: "primary" };
|
|
162
|
-
case "api-consumer":
|
|
163
|
-
return { label: "API Consumer", color: "default" };
|
|
164
|
-
default:
|
|
165
|
-
return { label: role, color: "default" };
|
|
166
|
-
}
|
|
167
|
-
};
|
|
168
|
-
return /* @__PURE__ */ React.createElement(Page, { themeId: "tool" }, /* @__PURE__ */ React.createElement(Header, { title: "Kuadrant", subtitle: "API Management for Kubernetes" }, /* @__PURE__ */ React.createElement(SupportButton, null, "Manage API products and access requests")), /* @__PURE__ */ React.createElement(Content, null, /* @__PURE__ */ React.createElement(ContentHeader, { title: "API Products" }, /* @__PURE__ */ React.createElement(Box, { display: "flex", alignItems: "center", style: { gap: 16 } }, userInfo && /* @__PURE__ */ React.createElement(Box, { display: "flex", alignItems: "center", style: { gap: 8 } }, /* @__PURE__ */ React.createElement(Typography, { variant: "body2" }, "Viewing as:"), /* @__PURE__ */ React.createElement(Chip, { label: userInfo.userId, color: "primary", size: "small" }), /* @__PURE__ */ React.createElement(
|
|
169
|
-
Chip,
|
|
170
|
-
{
|
|
171
|
-
label: getRoleLabel(userInfo.role).label,
|
|
172
|
-
color: getRoleLabel(userInfo.role).color,
|
|
173
|
-
size: "small"
|
|
174
|
-
}
|
|
175
|
-
)), /* @__PURE__ */ React.createElement(
|
|
210
|
+
return /* @__PURE__ */ React.createElement(Page, { themeId: "tool" }, /* @__PURE__ */ React.createElement(Header, { title: "Kuadrant", subtitle: "API Management for Kubernetes" }, /* @__PURE__ */ React.createElement(SupportButton, null, "Manage API products and access requests")), /* @__PURE__ */ React.createElement(Content, null, /* @__PURE__ */ React.createElement(ContentHeader, { title: "API Products" }, /* @__PURE__ */ React.createElement(Box, { display: "flex", alignItems: "center", style: { gap: 16 } }, canCreateApiProduct && /* @__PURE__ */ React.createElement(
|
|
176
211
|
Button,
|
|
177
212
|
{
|
|
178
213
|
variant: "contained",
|
|
@@ -181,17 +216,33 @@ const ResourceList = () => {
|
|
|
181
216
|
onClick: () => setCreateDialogOpen(true)
|
|
182
217
|
},
|
|
183
218
|
"Create API Product"
|
|
184
|
-
))), loading && /* @__PURE__ */ React.createElement(Progress, null), error && /* @__PURE__ */ React.createElement(ResponseErrorPanel, { error }), !loading && !error && /* @__PURE__ */ React.createElement(Grid, { container: true, spacing: 3, direction: "column" }, /* @__PURE__ */ React.createElement(Grid, { item: true }, /* @__PURE__ */ React.createElement(InfoCard, { title: "API Products" }, renderResources(apiProducts?.items))), /* @__PURE__ */ React.createElement(Grid, { item: true }, /* @__PURE__ */ React.createElement(InfoCard, { title: "Plan Policies" }, renderPlanPolicies(planPolicies?.items))),
|
|
219
|
+
))), loading && /* @__PURE__ */ React.createElement(Progress, null), error && /* @__PURE__ */ React.createElement(ResponseErrorPanel, { error }), permissionError && /* @__PURE__ */ React.createElement(Box, { p: 2 }, /* @__PURE__ */ React.createElement(Typography, { color: "error" }, "Unable to check permissions: ", permissionError.message), /* @__PURE__ */ React.createElement(Typography, { variant: "body2", color: "textSecondary" }, "Permission: ", createPermissionError ? "kuadrant.apiproduct.create" : deletePermissionError ? "kuadrant.apiproduct.delete" : approvalQueuePermissionError ? "kuadrant.apikeyrequest.read.all" : planPolicyPermissionError ? "kuadrant.planpolicy.list" : "unknown"), /* @__PURE__ */ React.createElement(Typography, { variant: "body2", color: "textSecondary" }, "Please try again or contact your administrator")), !loading && !error && !permissionError && /* @__PURE__ */ React.createElement(Grid, { container: true, spacing: 3, direction: "column" }, /* @__PURE__ */ React.createElement(Grid, { item: true }, /* @__PURE__ */ React.createElement(InfoCard, { title: "API Products" }, renderResources(apiProducts?.items))), canListPlanPolicies && /* @__PURE__ */ React.createElement(Grid, { item: true }, /* @__PURE__ */ React.createElement(InfoCard, { title: "Plan Policies" }, renderPlanPolicies(planPolicies?.items))), canViewApprovalQueue && /* @__PURE__ */ React.createElement(Grid, { item: true }, /* @__PURE__ */ React.createElement(ApprovalQueueCard, null))), /* @__PURE__ */ React.createElement(
|
|
185
220
|
CreateAPIProductDialog,
|
|
186
221
|
{
|
|
187
222
|
open: createDialogOpen,
|
|
188
223
|
onClose: () => setCreateDialogOpen(false),
|
|
189
224
|
onSuccess: handleCreateSuccess
|
|
190
225
|
}
|
|
226
|
+
), /* @__PURE__ */ React.createElement(
|
|
227
|
+
EditAPIProductDialog,
|
|
228
|
+
{
|
|
229
|
+
open: editDialogOpen,
|
|
230
|
+
onClose: () => setEditDialogOpen(false),
|
|
231
|
+
onSuccess: handleEditSuccess,
|
|
232
|
+
namespace: apiProductToEdit?.namespace || "",
|
|
233
|
+
name: apiProductToEdit?.name || ""
|
|
234
|
+
}
|
|
191
235
|
), /* @__PURE__ */ React.createElement(Dialog, { open: deleteDialogOpen, onClose: handleDeleteCancel }, /* @__PURE__ */ React.createElement(DialogTitle, null, "Delete API Product"), /* @__PURE__ */ React.createElement(DialogContent, null, /* @__PURE__ */ React.createElement(DialogContentText, null, "Are you sure you want to delete ", apiProductToDelete?.name, " from namespace ", apiProductToDelete?.namespace, "? This will permanently remove the API Product from Kubernetes.")), /* @__PURE__ */ React.createElement(DialogActions, null, /* @__PURE__ */ React.createElement(Button, { onClick: handleDeleteCancel, color: "primary" }, "Cancel"), /* @__PURE__ */ React.createElement(Button, { onClick: handleDeleteConfirm, color: "secondary", disabled: deleting }, deleting ? "Deleting..." : "Delete")))));
|
|
192
236
|
};
|
|
193
237
|
const KuadrantPage = () => {
|
|
194
|
-
return /* @__PURE__ */ React.createElement(
|
|
238
|
+
return /* @__PURE__ */ React.createElement(
|
|
239
|
+
PermissionGate,
|
|
240
|
+
{
|
|
241
|
+
permission: kuadrantApiProductListPermission,
|
|
242
|
+
errorMessage: "you don't have permission to view the Kuadrant page"
|
|
243
|
+
},
|
|
244
|
+
/* @__PURE__ */ React.createElement(ResourceList, null)
|
|
245
|
+
);
|
|
195
246
|
};
|
|
196
247
|
|
|
197
248
|
export { KuadrantPage, ResourceList };
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"KuadrantPage.esm.js","sources":["../../../src/components/KuadrantPage/KuadrantPage.tsx"],"sourcesContent":["import React, { useState } from 'react';\nimport { Typography, Grid, Box, Chip, Button, IconButton, Dialog, DialogTitle, DialogContent, DialogContentText, DialogActions } from '@material-ui/core';\nimport AddIcon from '@material-ui/icons/Add';\nimport DeleteIcon from '@material-ui/icons/Delete';\nimport {\n InfoCard,\n Header,\n Page,\n Content,\n ContentHeader,\n SupportButton,\n Progress,\n ResponseErrorPanel,\n Link,\n Table,\n TableColumn,\n} from '@backstage/core-components';\nimport useAsync from 'react-use/lib/useAsync';\nimport { useApi, configApiRef, fetchApiRef } from '@backstage/core-plugin-api';\nimport { ApprovalQueueCard } from '../ApprovalQueueCard';\nimport { PermissionGate } from '../PermissionGate';\nimport { useUserRole } from '../../hooks/useUserRole';\nimport { CreateAPIProductDialog } from '../CreateAPIProductDialog';\n\ntype KuadrantResource = {\n metadata: {\n name: string;\n namespace: string;\n creationTimestamp: string;\n };\n spec?: any;\n};\n\ntype KuadrantList = {\n items: KuadrantResource[];\n};\n\nexport const ResourceList = () => {\n const config = useApi(configApiRef);\n const fetchApi = useApi(fetchApiRef);\n const backendUrl = config.getString('backend.baseUrl');\n const { userInfo, loading: userLoading } = useUserRole();\n const [createDialogOpen, setCreateDialogOpen] = useState(false);\n const [refreshTrigger, setRefreshTrigger] = useState(0);\n const [deleteDialogOpen, setDeleteDialogOpen] = useState(false);\n const [apiProductToDelete, setApiProductToDelete] = useState<{ namespace: string; name: string } | null>(null);\n const [deleting, setDeleting] = useState(false);\n\n const { value: apiProducts, loading: apiProductsLoading, error: apiProductsError } = useAsync(async (): Promise<KuadrantList> => {\n const response = await fetchApi.fetch(`${backendUrl}/api/kuadrant/apiproducts`);\n return await response.json();\n }, [backendUrl, fetchApi, refreshTrigger]);\n\n const { value: planPolicies, loading: planPoliciesLoading, error: planPoliciesError } = useAsync(async (): Promise<KuadrantList> => {\n const response = await fetchApi.fetch(`${backendUrl}/api/kuadrant/planpolicies`);\n return await response.json();\n }, [backendUrl, fetchApi, refreshTrigger]);\n\n const loading = userLoading || apiProductsLoading || planPoliciesLoading;\n const error = apiProductsError || planPoliciesError;\n\n const handleCreateSuccess = () => {\n setRefreshTrigger(prev => prev + 1);\n };\n\n const handleDeleteClick = (namespace: string, name: string) => {\n setApiProductToDelete({ namespace, name });\n setDeleteDialogOpen(true);\n };\n\n const handleDeleteConfirm = async () => {\n if (!apiProductToDelete) return;\n\n setDeleting(true);\n try {\n const response = await fetchApi.fetch(\n `${backendUrl}/api/kuadrant/apiproducts/${apiProductToDelete.namespace}/${apiProductToDelete.name}`,\n { method: 'DELETE' }\n );\n\n if (!response.ok) {\n throw new Error('failed to delete apiproduct');\n }\n\n setRefreshTrigger(prev => prev + 1);\n } catch (err) {\n console.error('error deleting apiproduct:', err);\n } finally {\n setDeleting(false);\n setDeleteDialogOpen(false);\n setApiProductToDelete(null);\n }\n };\n\n const handleDeleteCancel = () => {\n setDeleteDialogOpen(false);\n setApiProductToDelete(null);\n };\n\n const formatDate = (dateString: string) => {\n const date = new Date(dateString);\n return date.toLocaleDateString('en-GB', {\n year: 'numeric',\n month: 'short',\n day: 'numeric',\n });\n };\n\n const columns: TableColumn[] = [\n {\n title: 'Name',\n field: 'name',\n render: (row: any) => (\n <Link to={`/catalog/default/api/${row.metadata.name}/api-product`}>\n <strong>{row.spec?.displayName || row.metadata.name}</strong>\n </Link>\n ),\n },\n {\n title: 'Resource Name',\n field: 'metadata.name',\n },\n {\n title: 'Version',\n field: 'spec.version',\n render: (row: any) => row.spec?.version || '-',\n },\n {\n title: 'Plans',\n field: 'plans',\n render: (row: any) => {\n const plans = row.spec?.plans || [];\n if (plans.length === 0) return '-';\n return (\n <Box display=\"flex\" style={{ gap: 4 }}>\n {plans.map((plan: any, idx: number) => (\n <Chip key={idx} label={plan.tier} size=\"small\" />\n ))}\n </Box>\n );\n },\n },\n {\n title: 'Namespace',\n field: 'metadata.namespace',\n },\n {\n title: 'Created',\n field: 'metadata.creationTimestamp',\n render: (row: any) => formatDate(row.metadata.creationTimestamp),\n },\n {\n title: 'Actions',\n field: 'actions',\n render: (row: any) => (\n <IconButton\n size=\"small\"\n onClick={() => handleDeleteClick(row.metadata.namespace, row.metadata.name)}\n title=\"delete apiproduct\"\n >\n <DeleteIcon fontSize=\"small\" />\n </IconButton>\n ),\n },\n ];\n\n const planPolicyColumns: TableColumn[] = [\n {\n title: 'Name',\n field: 'metadata.name',\n render: (row: any) => (\n <Link to={`/kuadrant/planpolicy/${row.metadata.namespace}/${row.metadata.name}`}>\n <strong>{row.metadata.name}</strong>\n </Link>\n ),\n },\n {\n title: 'Namespace',\n field: 'metadata.namespace',\n },\n ];\n\n const renderResources = (resources: KuadrantResource[] | undefined) => {\n if (!resources || resources.length === 0) {\n return <Typography variant=\"body2\" color=\"textSecondary\">No API products found</Typography>;\n }\n return (\n <Table\n options={{ paging: false, search: false, toolbar: false }}\n columns={columns}\n data={resources}\n />\n );\n };\n\n const renderPlanPolicies = (resources: KuadrantResource[] | undefined) => {\n if (!resources || resources.length === 0) {\n return <Typography variant=\"body2\" color=\"textSecondary\">No plan policies found</Typography>;\n }\n return (\n <Table\n options={{ paging: false, search: false, toolbar: false }}\n columns={planPolicyColumns}\n data={resources}\n />\n );\n };\n\n const getRoleLabel = (role: string) => {\n switch (role) {\n case 'platform-engineer':\n return { label: 'Platform Engineer', color: 'secondary' as const };\n case 'api-owner':\n return { label: 'API Owner', color: 'primary' as const };\n case 'api-consumer':\n return { label: 'API Consumer', color: 'default' as const };\n default:\n return { label: role, color: 'default' as const };\n }\n };\n\n return (\n <Page themeId=\"tool\">\n <Header title=\"Kuadrant\" subtitle=\"API Management for Kubernetes\">\n <SupportButton>Manage API products and access requests</SupportButton>\n </Header>\n <Content>\n <ContentHeader title=\"API Products\">\n <Box display=\"flex\" alignItems=\"center\" style={{ gap: 16 }}>\n {userInfo && (\n <Box display=\"flex\" alignItems=\"center\" style={{ gap: 8 }}>\n <Typography variant=\"body2\">Viewing as:</Typography>\n <Chip label={userInfo.userId} color=\"primary\" size=\"small\" />\n <Chip\n label={getRoleLabel(userInfo.role).label}\n color={getRoleLabel(userInfo.role).color}\n size=\"small\"\n />\n </Box>\n )}\n <Button\n variant=\"contained\"\n color=\"primary\"\n startIcon={<AddIcon />}\n onClick={() => setCreateDialogOpen(true)}\n >\n Create API Product\n </Button>\n </Box>\n </ContentHeader>\n {loading && <Progress />}\n {error && <ResponseErrorPanel error={error} />}\n {!loading && !error && (\n <Grid container spacing={3} direction=\"column\">\n <Grid item>\n <InfoCard title=\"API Products\">\n {renderResources(apiProducts?.items)}\n </InfoCard>\n </Grid>\n\n <Grid item>\n <InfoCard title=\"Plan Policies\">\n {renderPlanPolicies(planPolicies?.items)}\n </InfoCard>\n </Grid>\n\n {userInfo?.isApiOwner && (\n <Grid item>\n <ApprovalQueueCard />\n </Grid>\n )}\n </Grid>\n )}\n <CreateAPIProductDialog\n open={createDialogOpen}\n onClose={() => setCreateDialogOpen(false)}\n onSuccess={handleCreateSuccess}\n />\n <Dialog open={deleteDialogOpen} onClose={handleDeleteCancel}>\n <DialogTitle>Delete API Product</DialogTitle>\n <DialogContent>\n <DialogContentText>\n Are you sure you want to delete {apiProductToDelete?.name} from namespace {apiProductToDelete?.namespace}?\n This will permanently remove the API Product from Kubernetes.\n </DialogContentText>\n </DialogContent>\n <DialogActions>\n <Button onClick={handleDeleteCancel} color=\"primary\">\n Cancel\n </Button>\n <Button onClick={handleDeleteConfirm} color=\"secondary\" disabled={deleting}>\n {deleting ? 'Deleting...' : 'Delete'}\n </Button>\n </DialogActions>\n </Dialog>\n </Content>\n </Page>\n );\n};\n\nexport const KuadrantPage = () => {\n return (\n <PermissionGate requireAnyRole={['platform-engineer', 'api-owner']}>\n <ResourceList />\n </PermissionGate>\n );\n};\n"],"names":[],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"KuadrantPage.esm.js","sources":["../../../src/components/KuadrantPage/KuadrantPage.tsx"],"sourcesContent":["import React, { useState } from 'react';\nimport { Typography, Grid, Box, Chip, Button, IconButton, Dialog, DialogTitle, DialogContent, DialogContentText, DialogActions } from '@material-ui/core';\nimport AddIcon from '@material-ui/icons/Add';\nimport DeleteIcon from '@material-ui/icons/Delete';\nimport EditIcon from '@material-ui/icons/Edit';\nimport {\n InfoCard,\n Header,\n Page,\n Content,\n ContentHeader,\n SupportButton,\n Progress,\n ResponseErrorPanel,\n Link,\n Table,\n TableColumn,\n} from '@backstage/core-components';\nimport useAsync from 'react-use/lib/useAsync';\nimport { useApi, configApiRef, fetchApiRef } from '@backstage/core-plugin-api';\nimport { ApprovalQueueCard } from '../ApprovalQueueCard';\nimport { PermissionGate } from '../PermissionGate';\nimport { CreateAPIProductDialog } from '../CreateAPIProductDialog';\nimport {\n kuadrantApiProductCreatePermission,\n kuadrantApiProductDeletePermission,\n kuadrantApiProductUpdatePermission,\n kuadrantApiProductListPermission,\n kuadrantApiKeyRequestReadAllPermission,\n kuadrantPlanPolicyListPermission,\n} from '../../permissions';\nimport { useKuadrantPermission } from '../../utils/permissions';\nimport { EditAPIProductDialog } from '../EditAPIProductDialog';\n\ntype KuadrantResource = {\n metadata: {\n name: string;\n namespace: string;\n creationTimestamp: string;\n };\n spec?: any;\n};\n\ntype KuadrantList = {\n items: KuadrantResource[];\n};\n\nexport const ResourceList = () => {\n const config = useApi(configApiRef);\n const fetchApi = useApi(fetchApiRef);\n const backendUrl = config.getString('backend.baseUrl');\n const [createDialogOpen, setCreateDialogOpen] = useState(false);\n const [editDialogOpen, setEditDialogOpen] = useState(false);\n const [refreshTrigger, setRefreshTrigger] = useState(0);\n const [deleteDialogOpen, setDeleteDialogOpen] = useState(false);\n const [apiProductToDelete, setApiProductToDelete] = useState<{ namespace: string; name: string } | null>(null);\n const [apiProductToEdit, setApiProductToEdit] = useState<{ namespace: string; name: string } | null>(null);\n const [deleting, setDeleting] = useState(false);\n\n const {\n allowed: canCreateApiProduct,\n loading: createPermissionLoading,\n error: createPermissionError,\n } = useKuadrantPermission(kuadrantApiProductCreatePermission);\n\n const {\n allowed: canViewApprovalQueue,\n loading: approvalQueuePermissionLoading,\n error: approvalQueuePermissionError,\n } = useKuadrantPermission(kuadrantApiKeyRequestReadAllPermission);\n\n const {\n allowed: canDeleteApiProduct,\n loading: deletePermissionLoading,\n error: deletePermissionError,\n } = useKuadrantPermission(kuadrantApiProductDeletePermission);\n\n const {\n allowed: canUpdateApiProduct,\n } = useKuadrantPermission(kuadrantApiProductUpdatePermission);\n\n const {\n allowed: canListPlanPolicies,\n loading: planPolicyPermissionLoading,\n error: planPolicyPermissionError,\n } = useKuadrantPermission(kuadrantPlanPolicyListPermission);\n\n const { value: apiProducts, loading: apiProductsLoading, error: apiProductsError } = useAsync(async (): Promise<KuadrantList> => {\n const response = await fetchApi.fetch(`${backendUrl}/api/kuadrant/apiproducts`);\n return await response.json();\n }, [backendUrl, fetchApi, refreshTrigger]);\n\n const { value: planPolicies, loading: planPoliciesLoading, error: planPoliciesError } = useAsync(async (): Promise<KuadrantList> => {\n const response = await fetchApi.fetch(`${backendUrl}/api/kuadrant/planpolicies`);\n return await response.json();\n }, [backendUrl, fetchApi, refreshTrigger]);\n\n const loading = apiProductsLoading || planPoliciesLoading || createPermissionLoading || approvalQueuePermissionLoading || deletePermissionLoading || planPolicyPermissionLoading;\n const error = apiProductsError || planPoliciesError;\n const permissionError = createPermissionError || approvalQueuePermissionError || deletePermissionError || planPolicyPermissionError;\n\n const handleCreateSuccess = () => {\n setRefreshTrigger(prev => prev + 1);\n };\n\n const handleEditClick = (namespace: string, name: string) => {\n setApiProductToEdit({ namespace, name });\n setEditDialogOpen(true);\n };\n\n const handleEditSuccess = () => {\n setRefreshTrigger(prev => prev + 1);\n };\n\n const handleDeleteClick = (namespace: string, name: string) => {\n setApiProductToDelete({ namespace, name });\n setDeleteDialogOpen(true);\n };\n\n const handleDeleteConfirm = async () => {\n if (!apiProductToDelete) return;\n\n setDeleting(true);\n try {\n const response = await fetchApi.fetch(\n `${backendUrl}/api/kuadrant/apiproducts/${apiProductToDelete.namespace}/${apiProductToDelete.name}`,\n { method: 'DELETE' }\n );\n\n if (!response.ok) {\n throw new Error('failed to delete apiproduct');\n }\n\n setRefreshTrigger(prev => prev + 1);\n } catch (err) {\n console.error('error deleting apiproduct:', err);\n } finally {\n setDeleting(false);\n setDeleteDialogOpen(false);\n setApiProductToDelete(null);\n }\n };\n\n const handleDeleteCancel = () => {\n setDeleteDialogOpen(false);\n setApiProductToDelete(null);\n };\n\n const formatDate = (dateString: string) => {\n const date = new Date(dateString);\n return date.toLocaleDateString('en-GB', {\n year: 'numeric',\n month: 'short',\n day: 'numeric',\n });\n };\n\n const columns: TableColumn[] = [\n {\n title: 'Name',\n field: 'name',\n render: (row: any) => (\n <Link to={`/catalog/default/api/${row.metadata.name}/api-product`}>\n <strong>{row.spec?.displayName || row.metadata.name}</strong>\n </Link>\n ),\n },\n {\n title: 'Resource Name',\n field: 'metadata.name',\n },\n {\n title: 'Version',\n field: 'spec.version',\n render: (row: any) => row.spec?.version || '-',\n },\n {\n title: 'HTTPRoute',\n field: 'spec.targetRef.name',\n render: (row: any) => row.spec?.targetRef?.name || '-',\n },\n {\n title: 'Publish Status',\n field: 'spec.publishStatus',\n render: (row: any) => {\n const status = row.spec?.publishStatus || 'Draft';\n return (\n <Chip \n label={status} \n size=\"small\" \n color={status === 'Published' ? 'primary' : 'default'}\n />\n );\n },\n },\n {\n title: 'Namespace',\n field: 'metadata.namespace',\n },\n {\n title: 'Created',\n field: 'metadata.creationTimestamp',\n render: (row: any) => formatDate(row.metadata.creationTimestamp),\n },\n {\n title: 'Actions',\n field: 'actions',\n render: (row: any) => (\n <Box display=\"flex\" style={{ gap: 4 }}>\n {canUpdateApiProduct && (\n <IconButton\n size=\"small\"\n onClick={() => handleEditClick(row.metadata.namespace, row.metadata.name)}\n title=\"Edit API Product\"\n >\n <EditIcon fontSize=\"small\" />\n </IconButton>\n )}\n \n {canDeleteApiProduct && (\n <IconButton\n size=\"small\"\n onClick={() => handleDeleteClick(row.metadata.namespace, row.metadata.name)}\n title=\"Delete API Product\"\n >\n <DeleteIcon fontSize=\"small\" />\n </IconButton>\n )}\n </Box>\n ),\n },\n ];\n\n const planPolicyColumns: TableColumn[] = [\n {\n title: 'Name',\n field: 'metadata.name',\n render: (row: any) => (\n <Link to={`/kuadrant/planpolicy/${row.metadata.namespace}/${row.metadata.name}`}>\n <strong>{row.metadata.name}</strong>\n </Link>\n ),\n },\n {\n title: 'Namespace',\n field: 'metadata.namespace',\n },\n ];\n\n const renderResources = (resources: KuadrantResource[] | undefined) => {\n if (!resources || resources.length === 0) {\n return <Typography variant=\"body2\" color=\"textSecondary\">No API products found</Typography>;\n }\n return (\n <Table\n options={{ paging: false, search: false, toolbar: false }}\n columns={columns}\n data={resources}\n />\n );\n };\n\n const renderPlanPolicies = (resources: KuadrantResource[] | undefined) => {\n if (!resources || resources.length === 0) {\n return <Typography variant=\"body2\" color=\"textSecondary\">No plan policies found</Typography>;\n }\n return (\n <Table\n options={{ paging: false, search: false, toolbar: false }}\n columns={planPolicyColumns}\n data={resources}\n />\n );\n };\n\n return (\n <Page themeId=\"tool\">\n <Header title=\"Kuadrant\" subtitle=\"API Management for Kubernetes\">\n <SupportButton>Manage API products and access requests</SupportButton>\n </Header>\n <Content>\n <ContentHeader title=\"API Products\">\n <Box display=\"flex\" alignItems=\"center\" style={{ gap: 16 }}>\n {canCreateApiProduct && (\n <Button\n variant=\"contained\"\n color=\"primary\"\n startIcon={<AddIcon />}\n onClick={() => setCreateDialogOpen(true)}\n >\n Create API Product\n </Button>\n )}\n </Box>\n </ContentHeader>\n {loading && <Progress />}\n {error && <ResponseErrorPanel error={error} />}\n {permissionError && (\n <Box p={2}>\n <Typography color=\"error\">\n Unable to check permissions: {permissionError.message}\n </Typography>\n <Typography variant=\"body2\" color=\"textSecondary\">\n Permission: {createPermissionError ? 'kuadrant.apiproduct.create' :\n deletePermissionError ? 'kuadrant.apiproduct.delete' :\n approvalQueuePermissionError ? 'kuadrant.apikeyrequest.read.all' :\n planPolicyPermissionError ? 'kuadrant.planpolicy.list' : 'unknown'}\n </Typography>\n <Typography variant=\"body2\" color=\"textSecondary\">\n Please try again or contact your administrator\n </Typography>\n </Box>\n )}\n {!loading && !error && !permissionError && (\n <Grid container spacing={3} direction=\"column\">\n <Grid item>\n <InfoCard title=\"API Products\">\n {renderResources(apiProducts?.items)}\n </InfoCard>\n </Grid>\n\n {canListPlanPolicies && (\n <Grid item>\n <InfoCard title=\"Plan Policies\">\n {renderPlanPolicies(planPolicies?.items)}\n </InfoCard>\n </Grid>\n )}\n\n {canViewApprovalQueue && (\n <Grid item>\n <ApprovalQueueCard />\n </Grid>\n )}\n </Grid>\n )}\n <CreateAPIProductDialog\n open={createDialogOpen}\n onClose={() => setCreateDialogOpen(false)}\n onSuccess={handleCreateSuccess}\n />\n <EditAPIProductDialog\n open={editDialogOpen}\n onClose={() => setEditDialogOpen(false)}\n onSuccess={handleEditSuccess}\n namespace={apiProductToEdit?.namespace || ''}\n name={apiProductToEdit?.name || ''}\n />\n <Dialog open={deleteDialogOpen} onClose={handleDeleteCancel}>\n <DialogTitle>Delete API Product</DialogTitle>\n <DialogContent>\n <DialogContentText>\n Are you sure you want to delete {apiProductToDelete?.name} from namespace {apiProductToDelete?.namespace}?\n This will permanently remove the API Product from Kubernetes.\n </DialogContentText>\n </DialogContent>\n <DialogActions>\n <Button onClick={handleDeleteCancel} color=\"primary\">\n Cancel\n </Button>\n <Button onClick={handleDeleteConfirm} color=\"secondary\" disabled={deleting}>\n {deleting ? 'Deleting...' : 'Delete'}\n </Button>\n </DialogActions>\n </Dialog>\n </Content>\n </Page>\n );\n};\n\nexport const KuadrantPage = () => {\n return (\n <PermissionGate\n permission={kuadrantApiProductListPermission}\n errorMessage=\"you don't have permission to view the Kuadrant page\"\n >\n <ResourceList />\n </PermissionGate>\n );\n};\n"],"names":[],"mappings":";;;;;;;;;;;;;;;AA+CO,MAAM,eAAe,MAAM;AAChC,EAAM,MAAA,MAAA,GAAS,OAAO,YAAY,CAAA;AAClC,EAAM,MAAA,QAAA,GAAW,OAAO,WAAW,CAAA;AACnC,EAAM,MAAA,UAAA,GAAa,MAAO,CAAA,SAAA,CAAU,iBAAiB,CAAA;AACrD,EAAA,MAAM,CAAC,gBAAA,EAAkB,mBAAmB,CAAA,GAAI,SAAS,KAAK,CAAA;AAC9D,EAAA,MAAM,CAAC,cAAA,EAAgB,iBAAiB,CAAA,GAAI,SAAS,KAAK,CAAA;AAC1D,EAAA,MAAM,CAAC,cAAA,EAAgB,iBAAiB,CAAA,GAAI,SAAS,CAAC,CAAA;AACtD,EAAA,MAAM,CAAC,gBAAA,EAAkB,mBAAmB,CAAA,GAAI,SAAS,KAAK,CAAA;AAC9D,EAAA,MAAM,CAAC,kBAAA,EAAoB,qBAAqB,CAAA,GAAI,SAAqD,IAAI,CAAA;AAC7G,EAAA,MAAM,CAAC,gBAAA,EAAkB,mBAAmB,CAAA,GAAI,SAAqD,IAAI,CAAA;AACzG,EAAA,MAAM,CAAC,QAAA,EAAU,WAAW,CAAA,GAAI,SAAS,KAAK,CAAA;AAE9C,EAAM,MAAA;AAAA,IACJ,OAAS,EAAA,mBAAA;AAAA,IACT,OAAS,EAAA,uBAAA;AAAA,IACT,KAAO,EAAA;AAAA,GACT,GAAI,sBAAsB,kCAAkC,CAAA;AAE5D,EAAM,MAAA;AAAA,IACJ,OAAS,EAAA,oBAAA;AAAA,IACT,OAAS,EAAA,8BAAA;AAAA,IACT,KAAO,EAAA;AAAA,GACT,GAAI,sBAAsB,sCAAsC,CAAA;AAEhE,EAAM,MAAA;AAAA,IACJ,OAAS,EAAA,mBAAA;AAAA,IACT,OAAS,EAAA,uBAAA;AAAA,IACT,KAAO,EAAA;AAAA,GACT,GAAI,sBAAsB,kCAAkC,CAAA;AAE5D,EAAM,MAAA;AAAA,IACJ,OAAS,EAAA;AAAA,GACX,GAAI,sBAAsB,kCAAkC,CAAA;AAE5D,EAAM,MAAA;AAAA,IACJ,OAAS,EAAA,mBAAA;AAAA,IACT,OAAS,EAAA,2BAAA;AAAA,IACT,KAAO,EAAA;AAAA,GACT,GAAI,sBAAsB,gCAAgC,CAAA;AAE1D,EAAM,MAAA,EAAE,OAAO,WAAa,EAAA,OAAA,EAAS,oBAAoB,KAAO,EAAA,gBAAA,EAAqB,GAAA,QAAA,CAAS,YAAmC;AAC/H,IAAA,MAAM,WAAW,MAAM,QAAA,CAAS,KAAM,CAAA,CAAA,EAAG,UAAU,CAA2B,yBAAA,CAAA,CAAA;AAC9E,IAAO,OAAA,MAAM,SAAS,IAAK,EAAA;AAAA,GAC1B,EAAA,CAAC,UAAY,EAAA,QAAA,EAAU,cAAc,CAAC,CAAA;AAEzC,EAAM,MAAA,EAAE,OAAO,YAAc,EAAA,OAAA,EAAS,qBAAqB,KAAO,EAAA,iBAAA,EAAsB,GAAA,QAAA,CAAS,YAAmC;AAClI,IAAA,MAAM,WAAW,MAAM,QAAA,CAAS,KAAM,CAAA,CAAA,EAAG,UAAU,CAA4B,0BAAA,CAAA,CAAA;AAC/E,IAAO,OAAA,MAAM,SAAS,IAAK,EAAA;AAAA,GAC1B,EAAA,CAAC,UAAY,EAAA,QAAA,EAAU,cAAc,CAAC,CAAA;AAEzC,EAAA,MAAM,OAAU,GAAA,kBAAA,IAAsB,mBAAuB,IAAA,uBAAA,IAA2B,kCAAkC,uBAA2B,IAAA,2BAAA;AACrJ,EAAA,MAAM,QAAQ,gBAAoB,IAAA,iBAAA;AAClC,EAAM,MAAA,eAAA,GAAkB,qBAAyB,IAAA,4BAAA,IAAgC,qBAAyB,IAAA,yBAAA;AAE1G,EAAA,MAAM,sBAAsB,MAAM;AAChC,IAAkB,iBAAA,CAAA,CAAA,IAAA,KAAQ,OAAO,CAAC,CAAA;AAAA,GACpC;AAEA,EAAM,MAAA,eAAA,GAAkB,CAAC,SAAA,EAAmB,IAAiB,KAAA;AAC3D,IAAoB,mBAAA,CAAA,EAAE,SAAW,EAAA,IAAA,EAAM,CAAA;AACvC,IAAA,iBAAA,CAAkB,IAAI,CAAA;AAAA,GACxB;AAEA,EAAA,MAAM,oBAAoB,MAAM;AAC9B,IAAkB,iBAAA,CAAA,CAAA,IAAA,KAAQ,OAAO,CAAC,CAAA;AAAA,GACpC;AAEA,EAAM,MAAA,iBAAA,GAAoB,CAAC,SAAA,EAAmB,IAAiB,KAAA;AAC7D,IAAsB,qBAAA,CAAA,EAAE,SAAW,EAAA,IAAA,EAAM,CAAA;AACzC,IAAA,mBAAA,CAAoB,IAAI,CAAA;AAAA,GAC1B;AAEA,EAAA,MAAM,sBAAsB,YAAY;AACtC,IAAA,IAAI,CAAC,kBAAoB,EAAA;AAEzB,IAAA,WAAA,CAAY,IAAI,CAAA;AAChB,IAAI,IAAA;AACF,MAAM,MAAA,QAAA,GAAW,MAAM,QAAS,CAAA,KAAA;AAAA,QAC9B,GAAG,UAAU,CAAA,0BAAA,EAA6B,mBAAmB,SAAS,CAAA,CAAA,EAAI,mBAAmB,IAAI,CAAA,CAAA;AAAA,QACjG,EAAE,QAAQ,QAAS;AAAA,OACrB;AAEA,MAAI,IAAA,CAAC,SAAS,EAAI,EAAA;AAChB,QAAM,MAAA,IAAI,MAAM,6BAA6B,CAAA;AAAA;AAG/C,MAAkB,iBAAA,CAAA,CAAA,IAAA,KAAQ,OAAO,CAAC,CAAA;AAAA,aAC3B,GAAK,EAAA;AACZ,MAAQ,OAAA,CAAA,KAAA,CAAM,8BAA8B,GAAG,CAAA;AAAA,KAC/C,SAAA;AACA,MAAA,WAAA,CAAY,KAAK,CAAA;AACjB,MAAA,mBAAA,CAAoB,KAAK,CAAA;AACzB,MAAA,qBAAA,CAAsB,IAAI,CAAA;AAAA;AAC5B,GACF;AAEA,EAAA,MAAM,qBAAqB,MAAM;AAC/B,IAAA,mBAAA,CAAoB,KAAK,CAAA;AACzB,IAAA,qBAAA,CAAsB,IAAI,CAAA;AAAA,GAC5B;AAEA,EAAM,MAAA,UAAA,GAAa,CAAC,UAAuB,KAAA;AACzC,IAAM,MAAA,IAAA,GAAO,IAAI,IAAA,CAAK,UAAU,CAAA;AAChC,IAAO,OAAA,IAAA,CAAK,mBAAmB,OAAS,EAAA;AAAA,MACtC,IAAM,EAAA,SAAA;AAAA,MACN,KAAO,EAAA,OAAA;AAAA,MACP,GAAK,EAAA;AAAA,KACN,CAAA;AAAA,GACH;AAEA,EAAA,MAAM,OAAyB,GAAA;AAAA,IAC7B;AAAA,MACE,KAAO,EAAA,MAAA;AAAA,MACP,KAAO,EAAA,MAAA;AAAA,MACP,QAAQ,CAAC,GAAA,yCACN,IAAK,EAAA,EAAA,EAAA,EAAI,wBAAwB,GAAI,CAAA,QAAA,CAAS,IAAI,CACjD,YAAA,CAAA,EAAA,kBAAA,KAAA,CAAA,aAAA,CAAC,gBAAQ,GAAI,CAAA,IAAA,EAAM,eAAe,GAAI,CAAA,QAAA,CAAS,IAAK,CACtD;AAAA,KAEJ;AAAA,IACA;AAAA,MACE,KAAO,EAAA,eAAA;AAAA,MACP,KAAO,EAAA;AAAA,KACT;AAAA,IACA;AAAA,MACE,KAAO,EAAA,SAAA;AAAA,MACP,KAAO,EAAA,cAAA;AAAA,MACP,MAAQ,EAAA,CAAC,GAAa,KAAA,GAAA,CAAI,MAAM,OAAW,IAAA;AAAA,KAC7C;AAAA,IACA;AAAA,MACE,KAAO,EAAA,WAAA;AAAA,MACP,KAAO,EAAA,qBAAA;AAAA,MACP,QAAQ,CAAC,GAAA,KAAa,GAAI,CAAA,IAAA,EAAM,WAAW,IAAQ,IAAA;AAAA,KACrD;AAAA,IACA;AAAA,MACE,KAAO,EAAA,gBAAA;AAAA,MACP,KAAO,EAAA,oBAAA;AAAA,MACP,MAAA,EAAQ,CAAC,GAAa,KAAA;AACpB,QAAM,MAAA,MAAA,GAAS,GAAI,CAAA,IAAA,EAAM,aAAiB,IAAA,OAAA;AAC1C,QACE,uBAAA,KAAA,CAAA,aAAA;AAAA,UAAC,IAAA;AAAA,UAAA;AAAA,YACC,KAAO,EAAA,MAAA;AAAA,YACP,IAAK,EAAA,OAAA;AAAA,YACL,KAAA,EAAO,MAAW,KAAA,WAAA,GAAc,SAAY,GAAA;AAAA;AAAA,SAC9C;AAAA;AAEJ,KACF;AAAA,IACA;AAAA,MACE,KAAO,EAAA,WAAA;AAAA,MACP,KAAO,EAAA;AAAA,KACT;AAAA,IACA;AAAA,MACE,KAAO,EAAA,SAAA;AAAA,MACP,KAAO,EAAA,4BAAA;AAAA,MACP,QAAQ,CAAC,GAAA,KAAa,UAAW,CAAA,GAAA,CAAI,SAAS,iBAAiB;AAAA,KACjE;AAAA,IACA;AAAA,MACE,KAAO,EAAA,SAAA;AAAA,MACP,KAAO,EAAA,SAAA;AAAA,MACP,MAAQ,EAAA,CAAC,GACP,qBAAA,KAAA,CAAA,aAAA,CAAC,GAAI,EAAA,EAAA,OAAA,EAAQ,MAAO,EAAA,KAAA,EAAO,EAAE,GAAA,EAAK,CAAE,EAAA,EAAA,EACjC,mBACC,oBAAA,KAAA,CAAA,aAAA;AAAA,QAAC,UAAA;AAAA,QAAA;AAAA,UACC,IAAK,EAAA,OAAA;AAAA,UACL,OAAA,EAAS,MAAM,eAAgB,CAAA,GAAA,CAAI,SAAS,SAAW,EAAA,GAAA,CAAI,SAAS,IAAI,CAAA;AAAA,UACxE,KAAM,EAAA;AAAA,SAAA;AAAA,wBAEN,KAAA,CAAA,aAAA,CAAC,QAAS,EAAA,EAAA,QAAA,EAAS,OAAQ,EAAA;AAAA,SAI9B,mBACC,oBAAA,KAAA,CAAA,aAAA;AAAA,QAAC,UAAA;AAAA,QAAA;AAAA,UACC,IAAK,EAAA,OAAA;AAAA,UACL,OAAA,EAAS,MAAM,iBAAkB,CAAA,GAAA,CAAI,SAAS,SAAW,EAAA,GAAA,CAAI,SAAS,IAAI,CAAA;AAAA,UAC1E,KAAM,EAAA;AAAA,SAAA;AAAA,wBAEN,KAAA,CAAA,aAAA,CAAC,UAAW,EAAA,EAAA,QAAA,EAAS,OAAQ,EAAA;AAAA,OAGnC;AAAA;AAEJ,GACF;AAEA,EAAA,MAAM,iBAAmC,GAAA;AAAA,IACvC;AAAA,MACE,KAAO,EAAA,MAAA;AAAA,MACP,KAAO,EAAA,eAAA;AAAA,MACP,MAAA,EAAQ,CAAC,GACP,qBAAA,KAAA,CAAA,aAAA,CAAC,QAAK,EAAI,EAAA,CAAA,qBAAA,EAAwB,IAAI,QAAS,CAAA,SAAS,IAAI,GAAI,CAAA,QAAA,CAAS,IAAI,CAC3E,CAAA,EAAA,kBAAA,KAAA,CAAA,aAAA,CAAC,gBAAQ,GAAI,CAAA,QAAA,CAAS,IAAK,CAC7B;AAAA,KAEJ;AAAA,IACA;AAAA,MACE,KAAO,EAAA,WAAA;AAAA,MACP,KAAO,EAAA;AAAA;AACT,GACF;AAEA,EAAM,MAAA,eAAA,GAAkB,CAAC,SAA8C,KAAA;AACrE,IAAA,IAAI,CAAC,SAAA,IAAa,SAAU,CAAA,MAAA,KAAW,CAAG,EAAA;AACxC,MAAA,2CAAQ,UAAW,EAAA,EAAA,OAAA,EAAQ,OAAQ,EAAA,KAAA,EAAM,mBAAgB,uBAAqB,CAAA;AAAA;AAEhF,IACE,uBAAA,KAAA,CAAA,aAAA;AAAA,MAAC,KAAA;AAAA,MAAA;AAAA,QACC,SAAS,EAAE,MAAA,EAAQ,OAAO,MAAQ,EAAA,KAAA,EAAO,SAAS,KAAM,EAAA;AAAA,QACxD,OAAA;AAAA,QACA,IAAM,EAAA;AAAA;AAAA,KACR;AAAA,GAEJ;AAEA,EAAM,MAAA,kBAAA,GAAqB,CAAC,SAA8C,KAAA;AACxE,IAAA,IAAI,CAAC,SAAA,IAAa,SAAU,CAAA,MAAA,KAAW,CAAG,EAAA;AACxC,MAAA,2CAAQ,UAAW,EAAA,EAAA,OAAA,EAAQ,OAAQ,EAAA,KAAA,EAAM,mBAAgB,wBAAsB,CAAA;AAAA;AAEjF,IACE,uBAAA,KAAA,CAAA,aAAA;AAAA,MAAC,KAAA;AAAA,MAAA;AAAA,QACC,SAAS,EAAE,MAAA,EAAQ,OAAO,MAAQ,EAAA,KAAA,EAAO,SAAS,KAAM,EAAA;AAAA,QACxD,OAAS,EAAA,iBAAA;AAAA,QACT,IAAM,EAAA;AAAA;AAAA,KACR;AAAA,GAEJ;AAEA,EAAA,uBACG,KAAA,CAAA,aAAA,CAAA,IAAA,EAAA,EAAK,OAAQ,EAAA,MAAA,EAAA,sCACX,MAAO,EAAA,EAAA,KAAA,EAAM,UAAW,EAAA,QAAA,EAAS,+BAChC,EAAA,kBAAA,KAAA,CAAA,aAAA,CAAC,aAAc,EAAA,IAAA,EAAA,yCAAuC,CACxD,CACA,kBAAA,KAAA,CAAA,aAAA,CAAC,OACC,EAAA,IAAA,kBAAA,KAAA,CAAA,aAAA,CAAC,aAAc,EAAA,EAAA,KAAA,EAAM,cACnB,EAAA,kBAAA,KAAA,CAAA,aAAA,CAAC,OAAI,OAAQ,EAAA,MAAA,EAAO,UAAW,EAAA,QAAA,EAAS,KAAO,EAAA,EAAE,GAAK,EAAA,EAAA,MACnD,mBACC,oBAAA,KAAA,CAAA,aAAA;AAAA,IAAC,MAAA;AAAA,IAAA;AAAA,MACC,OAAQ,EAAA,WAAA;AAAA,MACR,KAAM,EAAA,SAAA;AAAA,MACN,SAAA,sCAAY,OAAQ,EAAA,IAAA,CAAA;AAAA,MACpB,OAAA,EAAS,MAAM,mBAAA,CAAoB,IAAI;AAAA,KAAA;AAAA,IACxC;AAAA,GAIL,CACF,CACC,EAAA,OAAA,wCAAY,QAAS,EAAA,IAAA,CAAA,EACrB,KAAS,oBAAA,KAAA,CAAA,aAAA,CAAC,kBAAmB,EAAA,EAAA,KAAA,EAAc,GAC3C,eACC,oBAAA,KAAA,CAAA,aAAA,CAAC,OAAI,CAAG,EAAA,CAAA,EAAA,sCACL,UAAW,EAAA,EAAA,KAAA,EAAM,OAAQ,EAAA,EAAA,+BAAA,EACM,eAAgB,CAAA,OAChD,mBACC,KAAA,CAAA,aAAA,CAAA,UAAA,EAAA,EAAW,SAAQ,OAAQ,EAAA,KAAA,EAAM,mBAAgB,cACnC,EAAA,qBAAA,GAAwB,4BAC1B,GAAA,qBAAA,GAAwB,4BACxB,GAAA,4BAAA,GAA+B,oCAC/B,yBAA4B,GAAA,0BAAA,GAA6B,SACtE,CAAA,kBACC,KAAA,CAAA,aAAA,CAAA,UAAA,EAAA,EAAW,SAAQ,OAAQ,EAAA,KAAA,EAAM,eAAgB,EAAA,EAAA,gDAElD,CACF,CAAA,EAED,CAAC,OAAW,IAAA,CAAC,SAAS,CAAC,eAAA,wCACrB,IAAK,EAAA,EAAA,SAAA,EAAS,IAAC,EAAA,OAAA,EAAS,CAAG,EAAA,SAAA,EAAU,4BACnC,KAAA,CAAA,aAAA,CAAA,IAAA,EAAA,EAAK,IAAI,EAAA,IAAA,EAAA,kBACP,KAAA,CAAA,aAAA,CAAA,QAAA,EAAA,EAAS,OAAM,cACb,EAAA,EAAA,eAAA,CAAgB,WAAa,EAAA,KAAK,CACrC,CACF,GAEC,mBACC,oBAAA,KAAA,CAAA,aAAA,CAAC,QAAK,IAAI,EAAA,IAAA,EAAA,sCACP,QAAS,EAAA,EAAA,KAAA,EAAM,eACb,EAAA,EAAA,kBAAA,CAAmB,YAAc,EAAA,KAAK,CACzC,CACF,CAAA,EAGD,oBACC,oBAAA,KAAA,CAAA,aAAA,CAAC,IAAK,EAAA,EAAA,IAAA,EAAI,wBACP,KAAA,CAAA,aAAA,CAAA,iBAAA,EAAA,IAAkB,CACrB,CAEJ,CAEF,kBAAA,KAAA,CAAA,aAAA;AAAA,IAAC,sBAAA;AAAA,IAAA;AAAA,MACC,IAAM,EAAA,gBAAA;AAAA,MACN,OAAA,EAAS,MAAM,mBAAA,CAAoB,KAAK,CAAA;AAAA,MACxC,SAAW,EAAA;AAAA;AAAA,GAEb,kBAAA,KAAA,CAAA,aAAA;AAAA,IAAC,oBAAA;AAAA,IAAA;AAAA,MACC,IAAM,EAAA,cAAA;AAAA,MACN,OAAA,EAAS,MAAM,iBAAA,CAAkB,KAAK,CAAA;AAAA,MACtC,SAAW,EAAA,iBAAA;AAAA,MACX,SAAA,EAAW,kBAAkB,SAAa,IAAA,EAAA;AAAA,MAC1C,IAAA,EAAM,kBAAkB,IAAQ,IAAA;AAAA;AAAA,GAClC,sCACC,MAAO,EAAA,EAAA,IAAA,EAAM,kBAAkB,OAAS,EAAA,kBAAA,EAAA,kBACtC,KAAA,CAAA,aAAA,CAAA,WAAA,EAAA,IAAA,EAAY,oBAAkB,CAAA,sCAC9B,aACC,EAAA,IAAA,kBAAA,KAAA,CAAA,aAAA,CAAC,yBAAkB,kCACgB,EAAA,kBAAA,EAAoB,MAAK,kBAAiB,EAAA,kBAAA,EAAoB,SAAU,EAAA,iEAE3G,CACF,CAAA,sCACC,aACC,EAAA,IAAA,kBAAA,KAAA,CAAA,aAAA,CAAC,UAAO,OAAS,EAAA,kBAAA,EAAoB,OAAM,SAAU,EAAA,EAAA,QAErD,CACA,kBAAA,KAAA,CAAA,aAAA,CAAC,MAAO,EAAA,EAAA,OAAA,EAAS,qBAAqB,KAAM,EAAA,WAAA,EAAY,UAAU,QAC/D,EAAA,EAAA,QAAA,GAAW,gBAAgB,QAC9B,CACF,CACF,CACF,CACF,CAAA;AAEJ;AAEO,MAAM,eAAe,MAAM;AAChC,EACE,uBAAA,KAAA,CAAA,aAAA;AAAA,IAAC,cAAA;AAAA,IAAA;AAAA,MACC,UAAY,EAAA,gCAAA;AAAA,MACZ,YAAa,EAAA;AAAA,KAAA;AAAA,wCAEZ,YAAa,EAAA,IAAA;AAAA,GAChB;AAEJ;;;;"}
|