@backstage-community/plugin-announcements 0.1.1
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 +10 -0
- package/README.md +142 -0
- package/dist/alpha/apis.esm.js +26 -0
- package/dist/alpha/apis.esm.js.map +1 -0
- package/dist/alpha/entityCards.esm.js +16 -0
- package/dist/alpha/entityCards.esm.js.map +1 -0
- package/dist/alpha/navItems.esm.js +15 -0
- package/dist/alpha/navItems.esm.js.map +1 -0
- package/dist/alpha/pages.esm.js +18 -0
- package/dist/alpha/pages.esm.js.map +1 -0
- package/dist/alpha.d.ts +11 -0
- package/dist/alpha.esm.js +23 -0
- package/dist/alpha.esm.js.map +1 -0
- package/dist/api.esm.js +117 -0
- package/dist/api.esm.js.map +1 -0
- package/dist/components/Admin/AdminPortal/AdminPortal.esm.js +51 -0
- package/dist/components/Admin/AdminPortal/AdminPortal.esm.js.map +1 -0
- package/dist/components/Admin/AnnouncementsContent/AnnouncementsContent.esm.js +199 -0
- package/dist/components/Admin/AnnouncementsContent/AnnouncementsContent.esm.js.map +1 -0
- package/dist/components/Admin/CategoriesContent/CategoriesContent.esm.js +130 -0
- package/dist/components/Admin/CategoriesContent/CategoriesContent.esm.js.map +1 -0
- package/dist/components/Admin/index.esm.js +3 -0
- package/dist/components/Admin/index.esm.js.map +1 -0
- package/dist/components/AnnouncementForm/AnnouncementForm.esm.js +123 -0
- package/dist/components/AnnouncementForm/AnnouncementForm.esm.js.map +1 -0
- package/dist/components/AnnouncementForm/CategoryInput.esm.js +79 -0
- package/dist/components/AnnouncementForm/CategoryInput.esm.js.map +1 -0
- package/dist/components/AnnouncementPage/AnnouncementPage.esm.js +59 -0
- package/dist/components/AnnouncementPage/AnnouncementPage.esm.js.map +1 -0
- package/dist/components/AnnouncementSearchResultListItem/AnnouncementSearchResultListItem.esm.js +67 -0
- package/dist/components/AnnouncementSearchResultListItem/AnnouncementSearchResultListItem.esm.js.map +1 -0
- package/dist/components/AnnouncementSearchResultListItem/index.esm.js +2 -0
- package/dist/components/AnnouncementSearchResultListItem/index.esm.js.map +1 -0
- package/dist/components/AnnouncementsCard/AnnouncementsCard.esm.js +81 -0
- package/dist/components/AnnouncementsCard/AnnouncementsCard.esm.js.map +1 -0
- package/dist/components/AnnouncementsCard/index.esm.js +2 -0
- package/dist/components/AnnouncementsCard/index.esm.js.map +1 -0
- package/dist/components/AnnouncementsPage/AnnouncementsPage.esm.js +233 -0
- package/dist/components/AnnouncementsPage/AnnouncementsPage.esm.js.map +1 -0
- package/dist/components/AnnouncementsPage/ContextMenu.esm.js +59 -0
- package/dist/components/AnnouncementsPage/ContextMenu.esm.js.map +1 -0
- package/dist/components/AnnouncementsPage/DeleteAnnouncementDialog.esm.js +25 -0
- package/dist/components/AnnouncementsPage/DeleteAnnouncementDialog.esm.js.map +1 -0
- package/dist/components/AnnouncementsPage/useDeleteAnnouncementDialogState.esm.js +29 -0
- package/dist/components/AnnouncementsPage/useDeleteAnnouncementDialogState.esm.js.map +1 -0
- package/dist/components/AnnouncementsTimeline/AnnouncementsTimeline.esm.js +55 -0
- package/dist/components/AnnouncementsTimeline/AnnouncementsTimeline.esm.js.map +1 -0
- package/dist/components/CategoriesForm/CategoriesForm.esm.js +71 -0
- package/dist/components/CategoriesForm/CategoriesForm.esm.js.map +1 -0
- package/dist/components/CategoriesPage/CategoriesPage.esm.js +113 -0
- package/dist/components/CategoriesPage/CategoriesPage.esm.js.map +1 -0
- package/dist/components/CategoriesPage/DeleteCategoryDialog.esm.js +10 -0
- package/dist/components/CategoriesPage/DeleteCategoryDialog.esm.js.map +1 -0
- package/dist/components/CategoriesPage/useDeleteCategoryDialogState.esm.js +29 -0
- package/dist/components/CategoriesPage/useDeleteCategoryDialogState.esm.js.map +1 -0
- package/dist/components/CreateAnnouncementPage/CreateAnnouncementPage.esm.js +56 -0
- package/dist/components/CreateAnnouncementPage/CreateAnnouncementPage.esm.js.map +1 -0
- package/dist/components/EditAnnouncementPage/EditAnnouncementPage.esm.js +45 -0
- package/dist/components/EditAnnouncementPage/EditAnnouncementPage.esm.js.map +1 -0
- package/dist/components/NewAnnouncementBanner/NewAnnouncementBanner.esm.js +136 -0
- package/dist/components/NewAnnouncementBanner/NewAnnouncementBanner.esm.js.map +1 -0
- package/dist/components/NewAnnouncementBanner/index.esm.js +2 -0
- package/dist/components/NewAnnouncementBanner/index.esm.js.map +1 -0
- package/dist/components/NewCategoryDialog/NewCategoryDialog.esm.js +46 -0
- package/dist/components/NewCategoryDialog/NewCategoryDialog.esm.js.map +1 -0
- package/dist/components/Router.esm.js +64 -0
- package/dist/components/Router.esm.js.map +1 -0
- package/dist/components/index.esm.js +4 -0
- package/dist/components/index.esm.js.map +1 -0
- package/dist/index.d.ts +42 -0
- package/dist/index.esm.js +7 -0
- package/dist/index.esm.js.map +1 -0
- package/dist/plugin.esm.js +83 -0
- package/dist/plugin.esm.js.map +1 -0
- package/dist/routes.esm.js +33 -0
- package/dist/routes.esm.js.map +1 -0
- package/package.json +113 -0
|
@@ -0,0 +1,199 @@
|
|
|
1
|
+
import React__default, { useState } from 'react';
|
|
2
|
+
import { Progress, ErrorPanel, Table } from '@backstage/core-components';
|
|
3
|
+
import { useApi, alertApiRef } from '@backstage/core-plugin-api';
|
|
4
|
+
import { announcementsApiRef, useCategories, useAnnouncementsTranslation } from '@backstage-community/plugin-announcements-react';
|
|
5
|
+
import { announcementCreatePermission, announcementUpdatePermission, announcementDeletePermission } from '@backstage-community/plugin-announcements-common';
|
|
6
|
+
import useAsyncRetry from 'react-use/esm/useAsyncRetry';
|
|
7
|
+
import { useDeleteAnnouncementDialogState } from '../../AnnouncementsPage/useDeleteAnnouncementDialogState.esm.js';
|
|
8
|
+
import { DeleteAnnouncementDialog } from '../../AnnouncementsPage/DeleteAnnouncementDialog.esm.js';
|
|
9
|
+
import { useNavigate } from 'react-router-dom';
|
|
10
|
+
import { AnnouncementForm } from '../../AnnouncementForm/AnnouncementForm.esm.js';
|
|
11
|
+
import slugify from 'slugify';
|
|
12
|
+
import { usePermission, RequirePermission } from '@backstage/plugin-permission-react';
|
|
13
|
+
import { Typography, IconButton, Grid, Button } from '@material-ui/core';
|
|
14
|
+
import DeleteIcon from '@material-ui/icons/Delete';
|
|
15
|
+
import EditIcon from '@material-ui/icons/Edit';
|
|
16
|
+
import PreviewIcon from '@material-ui/icons/Visibility';
|
|
17
|
+
|
|
18
|
+
const AnnouncementsContent = () => {
|
|
19
|
+
const alertApi = useApi(alertApiRef);
|
|
20
|
+
const announcementsApi = useApi(announcementsApiRef);
|
|
21
|
+
const navigate = useNavigate();
|
|
22
|
+
const { categories } = useCategories();
|
|
23
|
+
const { t } = useAnnouncementsTranslation();
|
|
24
|
+
const { loading: loadingCreatePermission, allowed: canCreateAnnouncement } = usePermission({
|
|
25
|
+
permission: announcementCreatePermission
|
|
26
|
+
});
|
|
27
|
+
const { loading: loadingUpdatePermission, allowed: canUpdateAnnouncement } = usePermission({
|
|
28
|
+
permission: announcementUpdatePermission
|
|
29
|
+
});
|
|
30
|
+
const { loading: loadingDeletePermission, allowed: canDeleteAnnouncement } = usePermission({
|
|
31
|
+
permission: announcementDeletePermission
|
|
32
|
+
});
|
|
33
|
+
const [showCreateAnnouncementForm, setShowCreateAnnouncementForm] = useState(false);
|
|
34
|
+
const {
|
|
35
|
+
loading,
|
|
36
|
+
error,
|
|
37
|
+
value: announcements,
|
|
38
|
+
retry
|
|
39
|
+
} = useAsyncRetry(async () => await announcementsApi.announcements({}));
|
|
40
|
+
const {
|
|
41
|
+
isOpen: isDeleteDialogOpen,
|
|
42
|
+
open: openDeleteDialog,
|
|
43
|
+
close: closeDeleteDialog,
|
|
44
|
+
announcement: announcementToDelete
|
|
45
|
+
} = useDeleteAnnouncementDialogState();
|
|
46
|
+
const onCreateButtonClick = () => {
|
|
47
|
+
setShowCreateAnnouncementForm(!showCreateAnnouncementForm);
|
|
48
|
+
};
|
|
49
|
+
const onTitleClick = (announcement) => {
|
|
50
|
+
navigate(`/announcements/view/${announcement.id}`);
|
|
51
|
+
};
|
|
52
|
+
const onEdit = (announcement) => {
|
|
53
|
+
navigate(`/announcements/edit/${announcement.id}`);
|
|
54
|
+
};
|
|
55
|
+
const onCancelDelete = () => {
|
|
56
|
+
closeDeleteDialog();
|
|
57
|
+
};
|
|
58
|
+
const onConfirmDelete = async () => {
|
|
59
|
+
closeDeleteDialog();
|
|
60
|
+
try {
|
|
61
|
+
await announcementsApi.deleteAnnouncementByID(announcementToDelete.id);
|
|
62
|
+
alertApi.post({ message: "Announcement deleted.", severity: "success" });
|
|
63
|
+
} catch (err) {
|
|
64
|
+
alertApi.post({ message: err.message, severity: "error" });
|
|
65
|
+
}
|
|
66
|
+
retry();
|
|
67
|
+
};
|
|
68
|
+
const onSubmit = async (request) => {
|
|
69
|
+
const { category } = request;
|
|
70
|
+
const slugs = categories.map((c) => c.slug);
|
|
71
|
+
let alertMsg = t("admin.announecementsContent.alertMessage");
|
|
72
|
+
try {
|
|
73
|
+
if (category) {
|
|
74
|
+
const categorySlug = slugify(category, {
|
|
75
|
+
lower: true
|
|
76
|
+
});
|
|
77
|
+
if (slugs.indexOf(categorySlug) === -1) {
|
|
78
|
+
alertMsg = alertMsg.replace(".", "");
|
|
79
|
+
alertMsg = `${alertMsg} ${t(
|
|
80
|
+
"admin.announecementsContent.alertMessage"
|
|
81
|
+
)} ${category}.`;
|
|
82
|
+
await announcementsApi.createCategory({
|
|
83
|
+
title: category
|
|
84
|
+
});
|
|
85
|
+
}
|
|
86
|
+
}
|
|
87
|
+
await announcementsApi.createAnnouncement({
|
|
88
|
+
...request,
|
|
89
|
+
category: request.category?.toLocaleLowerCase("en-US")
|
|
90
|
+
});
|
|
91
|
+
alertApi.post({ message: alertMsg, severity: "success" });
|
|
92
|
+
setShowCreateAnnouncementForm(false);
|
|
93
|
+
retry();
|
|
94
|
+
} catch (err) {
|
|
95
|
+
alertApi.post({ message: err.message, severity: "error" });
|
|
96
|
+
}
|
|
97
|
+
};
|
|
98
|
+
if (loading) {
|
|
99
|
+
return /* @__PURE__ */ React__default.createElement(Progress, null);
|
|
100
|
+
}
|
|
101
|
+
if (error) {
|
|
102
|
+
return /* @__PURE__ */ React__default.createElement(ErrorPanel, { error });
|
|
103
|
+
}
|
|
104
|
+
const columns = [
|
|
105
|
+
{
|
|
106
|
+
title: /* @__PURE__ */ React__default.createElement(Typography, null, t("admin.announecementsContent.table.title")),
|
|
107
|
+
sorting: true,
|
|
108
|
+
field: "title",
|
|
109
|
+
render: (rowData) => rowData.title
|
|
110
|
+
},
|
|
111
|
+
{
|
|
112
|
+
title: /* @__PURE__ */ React__default.createElement(Typography, null, t("admin.announecementsContent.table.body")),
|
|
113
|
+
sorting: true,
|
|
114
|
+
field: "body",
|
|
115
|
+
render: (rowData) => rowData.body
|
|
116
|
+
},
|
|
117
|
+
{
|
|
118
|
+
title: /* @__PURE__ */ React__default.createElement(Typography, null, t("admin.announecementsContent.table.publisher")),
|
|
119
|
+
sorting: true,
|
|
120
|
+
field: "publisher",
|
|
121
|
+
render: (rowData) => rowData.publisher
|
|
122
|
+
},
|
|
123
|
+
{
|
|
124
|
+
title: /* @__PURE__ */ React__default.createElement(Typography, null, t("admin.announecementsContent.table.category")),
|
|
125
|
+
sorting: true,
|
|
126
|
+
field: "category",
|
|
127
|
+
render: (rowData) => rowData.category?.title ?? ""
|
|
128
|
+
},
|
|
129
|
+
{
|
|
130
|
+
title: /* @__PURE__ */ React__default.createElement(Typography, null, t("admin.announecementsContent.table.status")),
|
|
131
|
+
sorting: true,
|
|
132
|
+
field: "category",
|
|
133
|
+
render: (rowData) => rowData.active ? t("admin.announecementsContent.table.active") : t("admin.announecementsContent.table.inactive")
|
|
134
|
+
},
|
|
135
|
+
{
|
|
136
|
+
title: /* @__PURE__ */ React__default.createElement(Typography, null, t("admin.announecementsContent.table.actions")),
|
|
137
|
+
render: (rowData) => {
|
|
138
|
+
return /* @__PURE__ */ React__default.createElement(React__default.Fragment, null, /* @__PURE__ */ React__default.createElement(
|
|
139
|
+
IconButton,
|
|
140
|
+
{
|
|
141
|
+
"aria-label": "preview",
|
|
142
|
+
onClick: () => onTitleClick(rowData)
|
|
143
|
+
},
|
|
144
|
+
/* @__PURE__ */ React__default.createElement(PreviewIcon, { fontSize: "small", "data-testid": "preview" })
|
|
145
|
+
), /* @__PURE__ */ React__default.createElement(
|
|
146
|
+
IconButton,
|
|
147
|
+
{
|
|
148
|
+
"aria-label": "edit",
|
|
149
|
+
disabled: loadingUpdatePermission || !canUpdateAnnouncement,
|
|
150
|
+
onClick: () => onEdit(rowData)
|
|
151
|
+
},
|
|
152
|
+
/* @__PURE__ */ React__default.createElement(EditIcon, { fontSize: "small", "data-testid": "edit-icon" })
|
|
153
|
+
), /* @__PURE__ */ React__default.createElement(
|
|
154
|
+
IconButton,
|
|
155
|
+
{
|
|
156
|
+
"aria-label": "delete",
|
|
157
|
+
disabled: loadingDeletePermission || !canDeleteAnnouncement,
|
|
158
|
+
onClick: () => openDeleteDialog(rowData)
|
|
159
|
+
},
|
|
160
|
+
/* @__PURE__ */ React__default.createElement(DeleteIcon, { fontSize: "small", "data-testid": "delete-icon" })
|
|
161
|
+
));
|
|
162
|
+
}
|
|
163
|
+
}
|
|
164
|
+
];
|
|
165
|
+
return /* @__PURE__ */ React__default.createElement(RequirePermission, { permission: announcementCreatePermission }, /* @__PURE__ */ React__default.createElement(Grid, { container: true }, /* @__PURE__ */ React__default.createElement(Grid, { item: true, xs: 12 }, /* @__PURE__ */ React__default.createElement(
|
|
166
|
+
Button,
|
|
167
|
+
{
|
|
168
|
+
disabled: loadingCreatePermission || !canCreateAnnouncement,
|
|
169
|
+
variant: "contained",
|
|
170
|
+
onClick: () => onCreateButtonClick()
|
|
171
|
+
},
|
|
172
|
+
showCreateAnnouncementForm ? t("admin.announecementsContent.cancelButton") : t("admin.announecementsContent.createButton")
|
|
173
|
+
)), showCreateAnnouncementForm && /* @__PURE__ */ React__default.createElement(Grid, { item: true, xs: 12 }, /* @__PURE__ */ React__default.createElement(
|
|
174
|
+
AnnouncementForm,
|
|
175
|
+
{
|
|
176
|
+
initialData: {},
|
|
177
|
+
onSubmit
|
|
178
|
+
}
|
|
179
|
+
)), /* @__PURE__ */ React__default.createElement(Grid, { item: true, xs: 12 }, /* @__PURE__ */ React__default.createElement(
|
|
180
|
+
Table,
|
|
181
|
+
{
|
|
182
|
+
title: t("admin.announecementsContent.announcements"),
|
|
183
|
+
options: { pageSize: 20, search: true },
|
|
184
|
+
columns,
|
|
185
|
+
data: announcements?.results ?? [],
|
|
186
|
+
emptyContent: /* @__PURE__ */ React__default.createElement(Typography, { style: { padding: 2 } }, t("admin.announecementsContent.noAnnouncementsFound"))
|
|
187
|
+
}
|
|
188
|
+
), /* @__PURE__ */ React__default.createElement(
|
|
189
|
+
DeleteAnnouncementDialog,
|
|
190
|
+
{
|
|
191
|
+
open: isDeleteDialogOpen,
|
|
192
|
+
onCancel: onCancelDelete,
|
|
193
|
+
onConfirm: onConfirmDelete
|
|
194
|
+
}
|
|
195
|
+
))));
|
|
196
|
+
};
|
|
197
|
+
|
|
198
|
+
export { AnnouncementsContent };
|
|
199
|
+
//# sourceMappingURL=AnnouncementsContent.esm.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"AnnouncementsContent.esm.js","sources":["../../../../src/components/Admin/AnnouncementsContent/AnnouncementsContent.tsx"],"sourcesContent":["/*\n * Copyright 2024 The Backstage Authors\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\nimport React, { useState } from 'react';\nimport {\n ErrorPanel,\n Progress,\n Table,\n TableColumn,\n} from '@backstage/core-components';\nimport { alertApiRef, useApi } from '@backstage/core-plugin-api';\nimport {\n announcementsApiRef,\n CreateAnnouncementRequest,\n useAnnouncementsTranslation,\n useCategories,\n} from '@backstage-community/plugin-announcements-react';\nimport {\n Announcement,\n announcementCreatePermission,\n announcementDeletePermission,\n announcementUpdatePermission,\n Category,\n} from '@backstage-community/plugin-announcements-common';\nimport useAsyncRetry from 'react-use/esm/useAsyncRetry';\nimport { useDeleteAnnouncementDialogState } from '../../AnnouncementsPage/useDeleteAnnouncementDialogState';\nimport { DeleteAnnouncementDialog } from '../../AnnouncementsPage/DeleteAnnouncementDialog';\nimport { useNavigate } from 'react-router-dom';\nimport { AnnouncementForm } from '../../AnnouncementForm';\nimport slugify from 'slugify';\nimport {\n RequirePermission,\n usePermission,\n} from '@backstage/plugin-permission-react';\nimport { Button, Grid, IconButton, Typography } from '@material-ui/core';\nimport DeleteIcon from '@material-ui/icons/Delete';\nimport EditIcon from '@material-ui/icons/Edit';\nimport PreviewIcon from '@material-ui/icons/Visibility';\n\nexport const AnnouncementsContent = () => {\n const alertApi = useApi(alertApiRef);\n const announcementsApi = useApi(announcementsApiRef);\n const navigate = useNavigate();\n const { categories } = useCategories();\n const { t } = useAnnouncementsTranslation();\n\n const { loading: loadingCreatePermission, allowed: canCreateAnnouncement } =\n usePermission({\n permission: announcementCreatePermission,\n });\n\n const { loading: loadingUpdatePermission, allowed: canUpdateAnnouncement } =\n usePermission({\n permission: announcementUpdatePermission,\n });\n\n const { loading: loadingDeletePermission, allowed: canDeleteAnnouncement } =\n usePermission({\n permission: announcementDeletePermission,\n });\n\n const [showCreateAnnouncementForm, setShowCreateAnnouncementForm] =\n useState(false);\n\n const {\n loading,\n error,\n value: announcements,\n retry,\n } = useAsyncRetry(async () => await announcementsApi.announcements({}));\n\n const {\n isOpen: isDeleteDialogOpen,\n open: openDeleteDialog,\n close: closeDeleteDialog,\n announcement: announcementToDelete,\n } = useDeleteAnnouncementDialogState();\n\n const onCreateButtonClick = () => {\n setShowCreateAnnouncementForm(!showCreateAnnouncementForm);\n };\n\n const onTitleClick = (announcement: Announcement) => {\n navigate(`/announcements/view/${announcement.id}`);\n };\n\n const onEdit = (announcement: Announcement) => {\n navigate(`/announcements/edit/${announcement.id}`);\n };\n\n const onCancelDelete = () => {\n closeDeleteDialog();\n };\n const onConfirmDelete = async () => {\n closeDeleteDialog();\n\n try {\n await announcementsApi.deleteAnnouncementByID(announcementToDelete!.id);\n\n alertApi.post({ message: 'Announcement deleted.', severity: 'success' });\n } catch (err) {\n alertApi.post({ message: (err as Error).message, severity: 'error' });\n }\n\n retry();\n };\n\n const onSubmit = async (request: CreateAnnouncementRequest) => {\n const { category } = request;\n\n const slugs = categories.map((c: Category) => c.slug);\n let alertMsg = t('admin.announecementsContent.alertMessage') as string;\n\n try {\n if (category) {\n const categorySlug = slugify(category, {\n lower: true,\n });\n if (slugs.indexOf(categorySlug) === -1) {\n alertMsg = alertMsg.replace('.', '');\n alertMsg = `${alertMsg} ${t(\n 'admin.announecementsContent.alertMessage',\n )} ${category}.`;\n\n await announcementsApi.createCategory({\n title: category,\n });\n }\n }\n\n await announcementsApi.createAnnouncement({\n ...request,\n category: request.category?.toLocaleLowerCase('en-US'),\n });\n alertApi.post({ message: alertMsg, severity: 'success' });\n\n setShowCreateAnnouncementForm(false);\n retry();\n } catch (err) {\n alertApi.post({ message: (err as Error).message, severity: 'error' });\n }\n };\n\n if (loading) {\n return <Progress />;\n }\n if (error) {\n return <ErrorPanel error={error} />;\n }\n\n const columns: TableColumn<Announcement>[] = [\n {\n title: (\n <Typography>{t('admin.announecementsContent.table.title')}</Typography>\n ),\n sorting: true,\n field: 'title',\n render: rowData => rowData.title,\n },\n {\n title: (\n <Typography>{t('admin.announecementsContent.table.body')}</Typography>\n ),\n sorting: true,\n field: 'body',\n render: rowData => rowData.body,\n },\n {\n title: (\n <Typography>\n {t('admin.announecementsContent.table.publisher')}\n </Typography>\n ),\n sorting: true,\n field: 'publisher',\n render: rowData => rowData.publisher,\n },\n {\n title: (\n <Typography>\n {t('admin.announecementsContent.table.category')}\n </Typography>\n ),\n sorting: true,\n field: 'category',\n render: rowData => rowData.category?.title ?? '',\n },\n {\n title: (\n <Typography>{t('admin.announecementsContent.table.status')}</Typography>\n ),\n sorting: true,\n field: 'category',\n render: rowData =>\n rowData.active\n ? t('admin.announecementsContent.table.active')\n : t('admin.announecementsContent.table.inactive'),\n },\n {\n title: (\n <Typography>\n {t('admin.announecementsContent.table.actions')}\n </Typography>\n ),\n render: rowData => {\n return (\n <>\n <IconButton\n aria-label=\"preview\"\n onClick={() => onTitleClick(rowData)}\n >\n <PreviewIcon fontSize=\"small\" data-testid=\"preview\" />\n </IconButton>\n\n <IconButton\n aria-label=\"edit\"\n disabled={loadingUpdatePermission || !canUpdateAnnouncement}\n onClick={() => onEdit(rowData)}\n >\n <EditIcon fontSize=\"small\" data-testid=\"edit-icon\" />\n </IconButton>\n\n <IconButton\n aria-label=\"delete\"\n disabled={loadingDeletePermission || !canDeleteAnnouncement}\n onClick={() => openDeleteDialog(rowData)}\n >\n <DeleteIcon fontSize=\"small\" data-testid=\"delete-icon\" />\n </IconButton>\n </>\n );\n },\n },\n ];\n\n return (\n <RequirePermission permission={announcementCreatePermission}>\n <Grid container>\n <Grid item xs={12}>\n <Button\n disabled={loadingCreatePermission || !canCreateAnnouncement}\n variant=\"contained\"\n onClick={() => onCreateButtonClick()}\n >\n {showCreateAnnouncementForm\n ? t('admin.announecementsContent.cancelButton')\n : t('admin.announecementsContent.createButton')}\n </Button>\n </Grid>\n\n {showCreateAnnouncementForm && (\n <Grid item xs={12}>\n <AnnouncementForm\n initialData={{} as Announcement}\n onSubmit={onSubmit}\n />\n </Grid>\n )}\n\n <Grid item xs={12}>\n <Table\n title={t('admin.announecementsContent.announcements')}\n options={{ pageSize: 20, search: true }}\n columns={columns}\n data={announcements?.results ?? []}\n emptyContent={\n <Typography style={{ padding: 2 }}>\n {t('admin.announecementsContent.noAnnouncementsFound')}\n </Typography>\n }\n />\n\n <DeleteAnnouncementDialog\n open={isDeleteDialogOpen}\n onCancel={onCancelDelete}\n onConfirm={onConfirmDelete}\n />\n </Grid>\n </Grid>\n </RequirePermission>\n );\n};\n"],"names":["React"],"mappings":"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"}
|
|
@@ -0,0 +1,130 @@
|
|
|
1
|
+
import React__default, { useState } from 'react';
|
|
2
|
+
import { Progress, ErrorPanel, Table } from '@backstage/core-components';
|
|
3
|
+
import { useCategories, announcementsApiRef, useAnnouncementsTranslation } from '@backstage-community/plugin-announcements-react';
|
|
4
|
+
import { announcementCreatePermission, announcementDeletePermission } from '@backstage-community/plugin-announcements-common';
|
|
5
|
+
import { CategoriesForm } from '../../CategoriesForm/CategoriesForm.esm.js';
|
|
6
|
+
import { useApi, alertApiRef } from '@backstage/core-plugin-api';
|
|
7
|
+
import { usePermission, RequirePermission } from '@backstage/plugin-permission-react';
|
|
8
|
+
import { useDeleteCategoryDialogState } from '../../CategoriesPage/useDeleteCategoryDialogState.esm.js';
|
|
9
|
+
import { DeleteCategoryDialog } from '../../CategoriesPage/DeleteCategoryDialog.esm.js';
|
|
10
|
+
import { Typography, IconButton, Grid, Button } from '@material-ui/core';
|
|
11
|
+
import DeleteIcon from '@material-ui/icons/Delete';
|
|
12
|
+
|
|
13
|
+
const CategoriesContent = () => {
|
|
14
|
+
const [showNewCategoryForm, setShowNewCategoryForm] = useState(false);
|
|
15
|
+
const { categories, loading, error, retry: refresh } = useCategories();
|
|
16
|
+
const announcementsApi = useApi(announcementsApiRef);
|
|
17
|
+
const alertApi = useApi(alertApiRef);
|
|
18
|
+
const { t } = useAnnouncementsTranslation();
|
|
19
|
+
const {
|
|
20
|
+
isOpen: isDeleteDialogOpen,
|
|
21
|
+
open: openDeleteDialog,
|
|
22
|
+
close: closeDeleteDialog,
|
|
23
|
+
category: categoryToDelete
|
|
24
|
+
} = useDeleteCategoryDialogState();
|
|
25
|
+
const { loading: loadingCreatePermission, allowed: canCreateCategory } = usePermission({
|
|
26
|
+
permission: announcementCreatePermission
|
|
27
|
+
});
|
|
28
|
+
const { loading: loadingDeletePermission, allowed: canDeleteAnnouncement } = usePermission({
|
|
29
|
+
permission: announcementDeletePermission
|
|
30
|
+
});
|
|
31
|
+
const onSubmit = async (request) => {
|
|
32
|
+
const { title } = request;
|
|
33
|
+
try {
|
|
34
|
+
await announcementsApi.createCategory({
|
|
35
|
+
title
|
|
36
|
+
});
|
|
37
|
+
alertApi.post({
|
|
38
|
+
message: `${title} ${t("admin.categoriesContent.createdMessage")}`,
|
|
39
|
+
severity: "success"
|
|
40
|
+
});
|
|
41
|
+
refresh();
|
|
42
|
+
} catch (err) {
|
|
43
|
+
alertApi.post({ message: err.message, severity: "error" });
|
|
44
|
+
}
|
|
45
|
+
};
|
|
46
|
+
const onCreateButtonClick = () => {
|
|
47
|
+
setShowNewCategoryForm(!showNewCategoryForm);
|
|
48
|
+
};
|
|
49
|
+
const onCancelDelete = () => {
|
|
50
|
+
closeDeleteDialog();
|
|
51
|
+
};
|
|
52
|
+
const onConfirmDelete = async () => {
|
|
53
|
+
closeDeleteDialog();
|
|
54
|
+
try {
|
|
55
|
+
await announcementsApi.deleteCategory(categoryToDelete.slug);
|
|
56
|
+
alertApi.post({
|
|
57
|
+
message: t("admin.categoriesContent.table.categoryDeleted"),
|
|
58
|
+
severity: "success"
|
|
59
|
+
});
|
|
60
|
+
} catch (err) {
|
|
61
|
+
alertApi.post({
|
|
62
|
+
message: err.body.error.message,
|
|
63
|
+
severity: "error"
|
|
64
|
+
});
|
|
65
|
+
}
|
|
66
|
+
refresh();
|
|
67
|
+
};
|
|
68
|
+
if (loading) {
|
|
69
|
+
return /* @__PURE__ */ React__default.createElement(Progress, null);
|
|
70
|
+
}
|
|
71
|
+
if (error) {
|
|
72
|
+
return /* @__PURE__ */ React__default.createElement(ErrorPanel, { error });
|
|
73
|
+
}
|
|
74
|
+
const columns = [
|
|
75
|
+
{
|
|
76
|
+
title: /* @__PURE__ */ React__default.createElement(Typography, null, t("admin.categoriesContent.table.title")),
|
|
77
|
+
sorting: true,
|
|
78
|
+
field: "title",
|
|
79
|
+
render: (rowData) => rowData.title
|
|
80
|
+
},
|
|
81
|
+
{
|
|
82
|
+
title: /* @__PURE__ */ React__default.createElement(Typography, null, t("admin.categoriesContent.table.slug")),
|
|
83
|
+
sorting: true,
|
|
84
|
+
field: "slug",
|
|
85
|
+
render: (rowData) => rowData.slug
|
|
86
|
+
},
|
|
87
|
+
{
|
|
88
|
+
title: /* @__PURE__ */ React__default.createElement(Typography, null, t("admin.categoriesContent.table.actions")),
|
|
89
|
+
render: (rowData) => {
|
|
90
|
+
return /* @__PURE__ */ React__default.createElement(
|
|
91
|
+
IconButton,
|
|
92
|
+
{
|
|
93
|
+
"aria-label": "delete",
|
|
94
|
+
disabled: loadingDeletePermission || !canDeleteAnnouncement,
|
|
95
|
+
onClick: () => openDeleteDialog(rowData)
|
|
96
|
+
},
|
|
97
|
+
/* @__PURE__ */ React__default.createElement(DeleteIcon, { fontSize: "small", "data-testid": "delete-icon" })
|
|
98
|
+
);
|
|
99
|
+
}
|
|
100
|
+
}
|
|
101
|
+
];
|
|
102
|
+
return /* @__PURE__ */ React__default.createElement(RequirePermission, { permission: announcementCreatePermission }, /* @__PURE__ */ React__default.createElement(Grid, { container: true }, /* @__PURE__ */ React__default.createElement(Grid, { item: true, xs: 12 }, /* @__PURE__ */ React__default.createElement(
|
|
103
|
+
Button,
|
|
104
|
+
{
|
|
105
|
+
disabled: loadingCreatePermission || !canCreateCategory,
|
|
106
|
+
variant: "contained",
|
|
107
|
+
onClick: () => onCreateButtonClick()
|
|
108
|
+
},
|
|
109
|
+
showNewCategoryForm ? t("admin.categoriesContent.cancelButton") : t("admin.categoriesContent.createButton")
|
|
110
|
+
)), showNewCategoryForm && /* @__PURE__ */ React__default.createElement(Grid, { item: true, xs: 12 }, /* @__PURE__ */ React__default.createElement(CategoriesForm, { initialData: {}, onSubmit })), /* @__PURE__ */ React__default.createElement(Grid, { item: true, xs: 12 }, /* @__PURE__ */ React__default.createElement(
|
|
111
|
+
Table,
|
|
112
|
+
{
|
|
113
|
+
title: "Categories",
|
|
114
|
+
options: { pageSize: 20, search: true },
|
|
115
|
+
columns,
|
|
116
|
+
data: categories ?? [],
|
|
117
|
+
emptyContent: /* @__PURE__ */ React__default.createElement(Typography, { style: { padding: 2 } }, t("admin.categoriesContent.table.noCategoriesFound"))
|
|
118
|
+
}
|
|
119
|
+
)), /* @__PURE__ */ React__default.createElement(
|
|
120
|
+
DeleteCategoryDialog,
|
|
121
|
+
{
|
|
122
|
+
open: isDeleteDialogOpen,
|
|
123
|
+
onCancel: onCancelDelete,
|
|
124
|
+
onConfirm: onConfirmDelete
|
|
125
|
+
}
|
|
126
|
+
)));
|
|
127
|
+
};
|
|
128
|
+
|
|
129
|
+
export { CategoriesContent };
|
|
130
|
+
//# sourceMappingURL=CategoriesContent.esm.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"CategoriesContent.esm.js","sources":["../../../../src/components/Admin/CategoriesContent/CategoriesContent.tsx"],"sourcesContent":["/*\n * Copyright 2024 The Backstage Authors\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\nimport React, { useState } from 'react';\nimport {\n ErrorPanel,\n Progress,\n Table,\n TableColumn,\n} from '@backstage/core-components';\nimport {\n CreateCategoryRequest,\n announcementsApiRef,\n useAnnouncementsTranslation,\n useCategories,\n} from '@backstage-community/plugin-announcements-react';\nimport {\n announcementCreatePermission,\n announcementDeletePermission,\n Category,\n} from '@backstage-community/plugin-announcements-common';\nimport { CategoriesForm } from '../../CategoriesForm';\nimport { useApi, alertApiRef } from '@backstage/core-plugin-api';\nimport {\n RequirePermission,\n usePermission,\n} from '@backstage/plugin-permission-react';\nimport { useDeleteCategoryDialogState } from '../../CategoriesPage/useDeleteCategoryDialogState';\nimport { ResponseError } from '@backstage/errors';\nimport { DeleteCategoryDialog } from '../../CategoriesPage/DeleteCategoryDialog';\nimport { Button, Grid, IconButton, Typography } from '@material-ui/core';\nimport DeleteIcon from '@material-ui/icons/Delete';\n\nexport const CategoriesContent = () => {\n const [showNewCategoryForm, setShowNewCategoryForm] = useState(false);\n const { categories, loading, error, retry: refresh } = useCategories();\n const announcementsApi = useApi(announcementsApiRef);\n const alertApi = useApi(alertApiRef);\n const { t } = useAnnouncementsTranslation();\n\n const {\n isOpen: isDeleteDialogOpen,\n open: openDeleteDialog,\n close: closeDeleteDialog,\n category: categoryToDelete,\n } = useDeleteCategoryDialogState();\n\n const { loading: loadingCreatePermission, allowed: canCreateCategory } =\n usePermission({\n permission: announcementCreatePermission,\n });\n\n const { loading: loadingDeletePermission, allowed: canDeleteAnnouncement } =\n usePermission({\n permission: announcementDeletePermission,\n });\n\n const onSubmit = async (request: CreateCategoryRequest) => {\n const { title } = request;\n\n try {\n await announcementsApi.createCategory({\n title,\n });\n\n alertApi.post({\n message: `${title} ${t('admin.categoriesContent.createdMessage')}`,\n severity: 'success',\n });\n\n refresh();\n } catch (err) {\n alertApi.post({ message: (err as Error).message, severity: 'error' });\n }\n };\n\n const onCreateButtonClick = () => {\n setShowNewCategoryForm(!showNewCategoryForm);\n };\n\n const onCancelDelete = () => {\n closeDeleteDialog();\n };\n\n const onConfirmDelete = async () => {\n closeDeleteDialog();\n\n try {\n await announcementsApi.deleteCategory(categoryToDelete!.slug);\n\n alertApi.post({\n message: t('admin.categoriesContent.table.categoryDeleted'),\n severity: 'success',\n });\n } catch (err) {\n alertApi.post({\n message: (err as ResponseError).body.error.message,\n severity: 'error',\n });\n }\n\n refresh();\n };\n\n if (loading) {\n return <Progress />;\n }\n if (error) {\n return <ErrorPanel error={error} />;\n }\n\n const columns: TableColumn<Category>[] = [\n {\n title: (\n <Typography>{t('admin.categoriesContent.table.title')}</Typography>\n ),\n sorting: true,\n field: 'title',\n render: rowData => rowData.title,\n },\n {\n title: <Typography>{t('admin.categoriesContent.table.slug')}</Typography>,\n sorting: true,\n field: 'slug',\n render: rowData => rowData.slug,\n },\n {\n title: (\n <Typography>{t('admin.categoriesContent.table.actions')}</Typography>\n ),\n render: rowData => {\n return (\n <IconButton\n aria-label=\"delete\"\n disabled={loadingDeletePermission || !canDeleteAnnouncement}\n onClick={() => openDeleteDialog(rowData)}\n >\n <DeleteIcon fontSize=\"small\" data-testid=\"delete-icon\" />\n </IconButton>\n );\n },\n },\n ];\n\n return (\n <RequirePermission permission={announcementCreatePermission}>\n <Grid container>\n <Grid item xs={12}>\n <Button\n disabled={loadingCreatePermission || !canCreateCategory}\n variant=\"contained\"\n onClick={() => onCreateButtonClick()}\n >\n {showNewCategoryForm\n ? t('admin.categoriesContent.cancelButton')\n : t('admin.categoriesContent.createButton')}\n </Button>\n </Grid>\n\n {showNewCategoryForm && (\n <Grid item xs={12}>\n <CategoriesForm initialData={{} as Category} onSubmit={onSubmit} />\n </Grid>\n )}\n\n <Grid item xs={12}>\n <Table\n title=\"Categories\"\n options={{ pageSize: 20, search: true }}\n columns={columns}\n data={categories ?? []}\n emptyContent={\n <Typography style={{ padding: 2 }}>\n {t('admin.categoriesContent.table.noCategoriesFound')}\n </Typography>\n }\n />\n </Grid>\n\n <DeleteCategoryDialog\n open={isDeleteDialogOpen}\n onCancel={onCancelDelete}\n onConfirm={onConfirmDelete}\n />\n </Grid>\n </RequirePermission>\n );\n};\n"],"names":["React"],"mappings":"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"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.esm.js","sources":[],"sourcesContent":[],"names":[],"mappings":";"}
|
|
@@ -0,0 +1,123 @@
|
|
|
1
|
+
import React__default, { useState } from 'react';
|
|
2
|
+
import MDEditor from '@uiw/react-md-editor';
|
|
3
|
+
import { InfoCard } from '@backstage/core-components';
|
|
4
|
+
import { useApi, identityApiRef } from '@backstage/core-plugin-api';
|
|
5
|
+
import { useAnnouncementsTranslation } from '@backstage-community/plugin-announcements-react';
|
|
6
|
+
import CategoryInput from './CategoryInput.esm.js';
|
|
7
|
+
import { makeStyles, TextField, FormGroup, FormControlLabel, Switch, Button } from '@material-ui/core';
|
|
8
|
+
|
|
9
|
+
const useStyles = makeStyles((theme) => ({
|
|
10
|
+
formRoot: {
|
|
11
|
+
"& > *": {
|
|
12
|
+
margin: theme.spacing(1) ?? "8px"
|
|
13
|
+
}
|
|
14
|
+
}
|
|
15
|
+
}));
|
|
16
|
+
const AnnouncementForm = ({
|
|
17
|
+
initialData,
|
|
18
|
+
onSubmit
|
|
19
|
+
}) => {
|
|
20
|
+
const classes = useStyles();
|
|
21
|
+
const identityApi = useApi(identityApiRef);
|
|
22
|
+
const { t } = useAnnouncementsTranslation();
|
|
23
|
+
const [form, setForm] = React__default.useState({
|
|
24
|
+
...initialData,
|
|
25
|
+
category: initialData.category?.slug
|
|
26
|
+
});
|
|
27
|
+
const [loading, setLoading] = useState(false);
|
|
28
|
+
const handleChange = (event) => {
|
|
29
|
+
setForm({
|
|
30
|
+
...form,
|
|
31
|
+
[event.target.id]: event.target.value
|
|
32
|
+
});
|
|
33
|
+
};
|
|
34
|
+
const handleChangeActive = (event) => {
|
|
35
|
+
setForm({
|
|
36
|
+
...form,
|
|
37
|
+
[event.target.name]: event.target.checked
|
|
38
|
+
});
|
|
39
|
+
};
|
|
40
|
+
const handleSubmit = async (event) => {
|
|
41
|
+
setLoading(true);
|
|
42
|
+
event.preventDefault();
|
|
43
|
+
const userIdentity = await identityApi.getBackstageIdentity();
|
|
44
|
+
const createRequest = {
|
|
45
|
+
...form,
|
|
46
|
+
...{
|
|
47
|
+
publisher: userIdentity.userEntityRef
|
|
48
|
+
}
|
|
49
|
+
};
|
|
50
|
+
await onSubmit(createRequest);
|
|
51
|
+
setLoading(false);
|
|
52
|
+
};
|
|
53
|
+
return /* @__PURE__ */ React__default.createElement(
|
|
54
|
+
InfoCard,
|
|
55
|
+
{
|
|
56
|
+
title: initialData.title ? t("announcementForm.editAnnouncement") : t("announcementForm.newAnnouncement")
|
|
57
|
+
},
|
|
58
|
+
/* @__PURE__ */ React__default.createElement("form", { className: classes.formRoot, onSubmit: handleSubmit }, /* @__PURE__ */ React__default.createElement(
|
|
59
|
+
TextField,
|
|
60
|
+
{
|
|
61
|
+
id: "title",
|
|
62
|
+
type: "text",
|
|
63
|
+
label: t("announcementForm.title"),
|
|
64
|
+
value: form.title,
|
|
65
|
+
onChange: handleChange,
|
|
66
|
+
variant: "outlined",
|
|
67
|
+
fullWidth: true,
|
|
68
|
+
required: true
|
|
69
|
+
}
|
|
70
|
+
), /* @__PURE__ */ React__default.createElement(
|
|
71
|
+
CategoryInput,
|
|
72
|
+
{
|
|
73
|
+
setForm,
|
|
74
|
+
form,
|
|
75
|
+
initialValue: initialData.category?.title ?? ""
|
|
76
|
+
}
|
|
77
|
+
), /* @__PURE__ */ React__default.createElement(
|
|
78
|
+
TextField,
|
|
79
|
+
{
|
|
80
|
+
id: "excerpt",
|
|
81
|
+
type: "text",
|
|
82
|
+
label: t("announcementForm.excerpt"),
|
|
83
|
+
value: form.excerpt,
|
|
84
|
+
onChange: handleChange,
|
|
85
|
+
variant: "outlined",
|
|
86
|
+
fullWidth: true,
|
|
87
|
+
required: true
|
|
88
|
+
}
|
|
89
|
+
), /* @__PURE__ */ React__default.createElement(
|
|
90
|
+
MDEditor,
|
|
91
|
+
{
|
|
92
|
+
value: form.body,
|
|
93
|
+
style: { minHeight: "30rem" },
|
|
94
|
+
onChange: (value) => setForm({ ...form, ...{ body: value || "" } })
|
|
95
|
+
}
|
|
96
|
+
), /* @__PURE__ */ React__default.createElement(FormGroup, null, /* @__PURE__ */ React__default.createElement(
|
|
97
|
+
FormControlLabel,
|
|
98
|
+
{
|
|
99
|
+
control: /* @__PURE__ */ React__default.createElement(
|
|
100
|
+
Switch,
|
|
101
|
+
{
|
|
102
|
+
name: "active",
|
|
103
|
+
checked: form.active,
|
|
104
|
+
onChange: handleChangeActive
|
|
105
|
+
}
|
|
106
|
+
),
|
|
107
|
+
label: t("announcementForm.active")
|
|
108
|
+
}
|
|
109
|
+
)), /* @__PURE__ */ React__default.createElement(
|
|
110
|
+
Button,
|
|
111
|
+
{
|
|
112
|
+
variant: "contained",
|
|
113
|
+
color: "primary",
|
|
114
|
+
type: "submit",
|
|
115
|
+
disabled: loading || !form.body
|
|
116
|
+
},
|
|
117
|
+
t("announcementForm.submit")
|
|
118
|
+
))
|
|
119
|
+
);
|
|
120
|
+
};
|
|
121
|
+
|
|
122
|
+
export { AnnouncementForm };
|
|
123
|
+
//# sourceMappingURL=AnnouncementForm.esm.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"AnnouncementForm.esm.js","sources":["../../../src/components/AnnouncementForm/AnnouncementForm.tsx"],"sourcesContent":["/*\n * Copyright 2024 The Backstage Authors\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\nimport React, { useState } from 'react';\nimport MDEditor from '@uiw/react-md-editor';\nimport { InfoCard } from '@backstage/core-components';\nimport { identityApiRef, useApi } from '@backstage/core-plugin-api';\nimport {\n CreateAnnouncementRequest,\n useAnnouncementsTranslation,\n} from '@backstage-community/plugin-announcements-react';\nimport { Announcement } from '@backstage-community/plugin-announcements-common';\nimport CategoryInput from './CategoryInput';\nimport {\n makeStyles,\n TextField,\n FormGroup,\n FormControlLabel,\n Switch,\n Button,\n} from '@material-ui/core';\n\nconst useStyles = makeStyles(theme => ({\n formRoot: {\n '& > *': {\n margin: theme.spacing(1) ?? '8px',\n },\n },\n}));\n\ntype AnnouncementFormProps = {\n initialData: Announcement;\n onSubmit: (data: CreateAnnouncementRequest) => Promise<void>;\n};\n\nexport const AnnouncementForm = ({\n initialData,\n onSubmit,\n}: AnnouncementFormProps) => {\n const classes = useStyles();\n const identityApi = useApi(identityApiRef);\n const { t } = useAnnouncementsTranslation();\n\n const [form, setForm] = React.useState({\n ...initialData,\n category: initialData.category?.slug,\n });\n const [loading, setLoading] = useState(false);\n\n const handleChange = (event: React.ChangeEvent<HTMLInputElement>) => {\n setForm({\n ...form,\n [event.target.id]: event.target.value,\n });\n };\n\n const handleChangeActive = (event: React.ChangeEvent<HTMLInputElement>) => {\n setForm({\n ...form,\n [event.target.name]: event.target.checked,\n });\n };\n\n const handleSubmit = async (event: React.FormEvent<HTMLFormElement>) => {\n setLoading(true);\n event.preventDefault();\n\n const userIdentity = await identityApi.getBackstageIdentity();\n const createRequest = {\n ...form,\n ...{\n publisher: userIdentity.userEntityRef,\n },\n };\n\n await onSubmit(createRequest);\n setLoading(false);\n };\n\n return (\n <InfoCard\n title={\n initialData.title\n ? t('announcementForm.editAnnouncement')\n : t('announcementForm.newAnnouncement')\n }\n >\n <form className={classes.formRoot} onSubmit={handleSubmit}>\n <TextField\n id=\"title\"\n type=\"text\"\n label={t('announcementForm.title')}\n value={form.title}\n onChange={handleChange}\n variant=\"outlined\"\n fullWidth\n required\n />\n <CategoryInput\n setForm={setForm}\n form={form}\n initialValue={initialData.category?.title ?? ''}\n />\n <TextField\n id=\"excerpt\"\n type=\"text\"\n label={t('announcementForm.excerpt')}\n value={form.excerpt}\n onChange={handleChange}\n variant=\"outlined\"\n fullWidth\n required\n />\n <MDEditor\n value={form.body}\n style={{ minHeight: '30rem' }}\n onChange={value => setForm({ ...form, ...{ body: value || '' } })}\n />\n <FormGroup>\n <FormControlLabel\n control={\n <Switch\n name=\"active\"\n checked={form.active}\n onChange={handleChangeActive}\n />\n }\n label={t('announcementForm.active')}\n />\n </FormGroup>\n <Button\n variant=\"contained\"\n color=\"primary\"\n type=\"submit\"\n disabled={loading || !form.body}\n >\n {t('announcementForm.submit')}\n </Button>\n </form>\n </InfoCard>\n );\n};\n"],"names":["React"],"mappings":"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"}
|
|
@@ -0,0 +1,79 @@
|
|
|
1
|
+
import * as React from 'react';
|
|
2
|
+
import TextField from '@mui/material/TextField';
|
|
3
|
+
import Autocomplete, { createFilterOptions } from '@mui/material/Autocomplete';
|
|
4
|
+
import { useCategories, useAnnouncementsTranslation } from '@backstage-community/plugin-announcements-react';
|
|
5
|
+
import CircularProgress from '@mui/material/CircularProgress';
|
|
6
|
+
|
|
7
|
+
const filter = createFilterOptions();
|
|
8
|
+
function prepareCategoryFromInput(inputCategory, localizedCreate) {
|
|
9
|
+
return (typeof inputCategory === "string" ? inputCategory : inputCategory.title).replace(localizedCreate ? `${localizedCreate} ` : "Create ", "").replaceAll('"', "");
|
|
10
|
+
}
|
|
11
|
+
function CategoryInput({
|
|
12
|
+
setForm,
|
|
13
|
+
form,
|
|
14
|
+
initialValue
|
|
15
|
+
}) {
|
|
16
|
+
const { categories, loading: categoriesLoading } = useCategories();
|
|
17
|
+
const { t } = useAnnouncementsTranslation();
|
|
18
|
+
return /* @__PURE__ */ React.createElement(
|
|
19
|
+
Autocomplete,
|
|
20
|
+
{
|
|
21
|
+
fullWidth: true,
|
|
22
|
+
value: initialValue ?? "",
|
|
23
|
+
onChange: async (_, newValue) => {
|
|
24
|
+
if (!newValue) {
|
|
25
|
+
setForm({ ...form, category: void 0 });
|
|
26
|
+
return;
|
|
27
|
+
}
|
|
28
|
+
const newCategory = prepareCategoryFromInput(
|
|
29
|
+
newValue,
|
|
30
|
+
t("announcementForm.categoryInput.create")
|
|
31
|
+
);
|
|
32
|
+
setForm({ ...form, category: newCategory });
|
|
33
|
+
},
|
|
34
|
+
filterOptions: (options, params) => {
|
|
35
|
+
const filtered = filter(options, params);
|
|
36
|
+
const { inputValue } = params;
|
|
37
|
+
const isExisting = options.some(
|
|
38
|
+
(option) => inputValue.toLocaleLowerCase("en-US") === option.title.toLocaleLowerCase("en-US")
|
|
39
|
+
);
|
|
40
|
+
if (inputValue !== "" && !isExisting) {
|
|
41
|
+
filtered.push({
|
|
42
|
+
title: `${t(
|
|
43
|
+
"announcementForm.categoryInput.create"
|
|
44
|
+
)} "${inputValue}"`,
|
|
45
|
+
slug: inputValue.toLocaleLowerCase("en-US")
|
|
46
|
+
});
|
|
47
|
+
}
|
|
48
|
+
return filtered;
|
|
49
|
+
},
|
|
50
|
+
selectOnFocus: true,
|
|
51
|
+
handleHomeEndKeys: true,
|
|
52
|
+
loading: categoriesLoading,
|
|
53
|
+
id: "category-input-field",
|
|
54
|
+
options: categories || [],
|
|
55
|
+
getOptionLabel: (option) => {
|
|
56
|
+
return prepareCategoryFromInput(option);
|
|
57
|
+
},
|
|
58
|
+
renderOption: (props, option) => /* @__PURE__ */ React.createElement("li", { ...props }, option.title),
|
|
59
|
+
freeSolo: true,
|
|
60
|
+
renderInput: (params) => /* @__PURE__ */ React.createElement(
|
|
61
|
+
TextField,
|
|
62
|
+
{
|
|
63
|
+
...params,
|
|
64
|
+
id: "category",
|
|
65
|
+
label: t("announcementForm.categoryInput.label"),
|
|
66
|
+
variant: "outlined",
|
|
67
|
+
fullWidth: true,
|
|
68
|
+
InputProps: {
|
|
69
|
+
...params.InputProps,
|
|
70
|
+
endAdornment: /* @__PURE__ */ React.createElement(React.Fragment, null, categoriesLoading ? /* @__PURE__ */ React.createElement(CircularProgress, { color: "inherit", size: 20 }) : null, params.InputProps.endAdornment)
|
|
71
|
+
}
|
|
72
|
+
}
|
|
73
|
+
)
|
|
74
|
+
}
|
|
75
|
+
);
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
export { CategoryInput as default };
|
|
79
|
+
//# sourceMappingURL=CategoryInput.esm.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"CategoryInput.esm.js","sources":["../../../src/components/AnnouncementForm/CategoryInput.tsx"],"sourcesContent":["/*\n * Copyright 2024 The Backstage Authors\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\nimport * as React from 'react';\nimport TextField from '@mui/material/TextField';\nimport Autocomplete, { createFilterOptions } from '@mui/material/Autocomplete';\nimport { Category } from '@backstage-community/plugin-announcements-common';\nimport {\n useAnnouncementsTranslation,\n useCategories,\n} from '@backstage-community/plugin-announcements-react';\nimport CircularProgress from '@mui/material/CircularProgress';\n\ntype CategoryInputProps = {\n setForm: (\n value: React.SetStateAction<{\n category: string | undefined;\n id: string;\n publisher: string;\n title: string;\n excerpt: string;\n body: string;\n created_at: string;\n active: boolean;\n }>,\n ) => void;\n form: {\n category: string | undefined;\n id: string;\n publisher: string;\n title: string;\n excerpt: string;\n body: string;\n created_at: string;\n active: boolean;\n };\n initialValue: string;\n};\n\nconst filter = createFilterOptions<Category>();\n\nfunction prepareCategoryFromInput(\n inputCategory: Category | string,\n localizedCreate?: string,\n): string {\n return (\n typeof inputCategory === 'string' ? inputCategory : inputCategory.title\n )\n .replace(localizedCreate ? `${localizedCreate} ` : 'Create ', '')\n .replaceAll('\"', '');\n}\n\nexport default function CategoryInput({\n setForm,\n form,\n initialValue,\n}: CategoryInputProps) {\n const { categories, loading: categoriesLoading } = useCategories();\n const { t } = useAnnouncementsTranslation();\n\n return (\n <Autocomplete\n fullWidth\n value={initialValue ?? ''}\n onChange={async (_, newValue) => {\n if (!newValue) {\n setForm({ ...form, category: undefined });\n return;\n }\n\n const newCategory = prepareCategoryFromInput(\n newValue,\n t('announcementForm.categoryInput.create'),\n );\n setForm({ ...form, category: newCategory });\n }}\n filterOptions={(options, params) => {\n const filtered = filter(options, params);\n const { inputValue } = params;\n\n /*\n Suggest the creation of a new category. This adds the new value to the list of options\n and creates the new category when the form is submitted.\n */\n const isExisting = options.some(\n option =>\n inputValue.toLocaleLowerCase('en-US') ===\n option.title.toLocaleLowerCase('en-US'),\n );\n if (inputValue !== '' && !isExisting) {\n filtered.push({\n title: `${t(\n 'announcementForm.categoryInput.create',\n )} \"${inputValue}\"`,\n slug: inputValue.toLocaleLowerCase('en-US'),\n });\n }\n\n return filtered;\n }}\n selectOnFocus\n handleHomeEndKeys\n loading={categoriesLoading}\n id=\"category-input-field\"\n options={categories || []}\n getOptionLabel={option => {\n // Value selected with enter, right from the input\n return prepareCategoryFromInput(option);\n }}\n renderOption={(props, option) => <li {...props}>{option.title}</li>}\n freeSolo\n renderInput={params => (\n <TextField\n {...params}\n id=\"category\"\n label={t('announcementForm.categoryInput.label')}\n variant=\"outlined\"\n fullWidth\n InputProps={{\n ...params.InputProps,\n endAdornment: (\n <>\n {categoriesLoading ? (\n <CircularProgress color=\"inherit\" size={20} />\n ) : null}\n {params.InputProps.endAdornment}\n </>\n ),\n }}\n />\n )}\n />\n );\n}\n"],"names":[],"mappings":";;;;;;AAmDA,MAAM,SAAS,mBAA8B,EAAA;AAE7C,SAAS,wBAAA,CACP,eACA,eACQ,EAAA;AACR,EAAA,OAAA,CACE,OAAO,aAAkB,KAAA,QAAA,GAAW,aAAgB,GAAA,aAAA,CAAc,OAEjE,OAAQ,CAAA,eAAA,GAAkB,CAAG,EAAA,eAAe,MAAM,SAAW,EAAA,EAAE,CAC/D,CAAA,UAAA,CAAW,KAAK,EAAE,CAAA;AACvB;AAEA,SAAwB,aAAc,CAAA;AAAA,EACpC,OAAA;AAAA,EACA,IAAA;AAAA,EACA;AACF,CAAuB,EAAA;AACrB,EAAA,MAAM,EAAE,UAAA,EAAY,OAAS,EAAA,iBAAA,KAAsB,aAAc,EAAA;AACjE,EAAM,MAAA,EAAE,CAAE,EAAA,GAAI,2BAA4B,EAAA;AAE1C,EACE,uBAAA,KAAA,CAAA,aAAA;AAAA,IAAC,YAAA;AAAA,IAAA;AAAA,MACC,SAAS,EAAA,IAAA;AAAA,MACT,OAAO,YAAgB,IAAA,EAAA;AAAA,MACvB,QAAA,EAAU,OAAO,CAAA,EAAG,QAAa,KAAA;AAC/B,QAAA,IAAI,CAAC,QAAU,EAAA;AACb,UAAA,OAAA,CAAQ,EAAE,GAAG,IAAM,EAAA,QAAA,EAAU,QAAW,CAAA;AACxC,UAAA;AAAA;AAGF,QAAA,MAAM,WAAc,GAAA,wBAAA;AAAA,UAClB,QAAA;AAAA,UACA,EAAE,uCAAuC;AAAA,SAC3C;AACA,QAAA,OAAA,CAAQ,EAAE,GAAG,IAAM,EAAA,QAAA,EAAU,aAAa,CAAA;AAAA,OAC5C;AAAA,MACA,aAAA,EAAe,CAAC,OAAA,EAAS,MAAW,KAAA;AAClC,QAAM,MAAA,QAAA,GAAW,MAAO,CAAA,OAAA,EAAS,MAAM,CAAA;AACvC,QAAM,MAAA,EAAE,YAAe,GAAA,MAAA;AAMvB,QAAA,MAAM,aAAa,OAAQ,CAAA,IAAA;AAAA,UACzB,CAAA,MAAA,KACE,WAAW,iBAAkB,CAAA,OAAO,MACpC,MAAO,CAAA,KAAA,CAAM,kBAAkB,OAAO;AAAA,SAC1C;AACA,QAAI,IAAA,UAAA,KAAe,EAAM,IAAA,CAAC,UAAY,EAAA;AACpC,UAAA,QAAA,CAAS,IAAK,CAAA;AAAA,YACZ,OAAO,CAAG,EAAA,CAAA;AAAA,cACR;AAAA,aACD,KAAK,UAAU,CAAA,CAAA,CAAA;AAAA,YAChB,IAAA,EAAM,UAAW,CAAA,iBAAA,CAAkB,OAAO;AAAA,WAC3C,CAAA;AAAA;AAGH,QAAO,OAAA,QAAA;AAAA,OACT;AAAA,MACA,aAAa,EAAA,IAAA;AAAA,MACb,iBAAiB,EAAA,IAAA;AAAA,MACjB,OAAS,EAAA,iBAAA;AAAA,MACT,EAAG,EAAA,sBAAA;AAAA,MACH,OAAA,EAAS,cAAc,EAAC;AAAA,MACxB,gBAAgB,CAAU,MAAA,KAAA;AAExB,QAAA,OAAO,yBAAyB,MAAM,CAAA;AAAA,OACxC;AAAA,MACA,YAAA,EAAc,CAAC,KAAO,EAAA,MAAA,yCAAY,IAAI,EAAA,EAAA,GAAG,KAAQ,EAAA,EAAA,MAAA,CAAO,KAAM,CAAA;AAAA,MAC9D,QAAQ,EAAA,IAAA;AAAA,MACR,aAAa,CACX,MAAA,qBAAA,KAAA,CAAA,aAAA;AAAA,QAAC,SAAA;AAAA,QAAA;AAAA,UACE,GAAG,MAAA;AAAA,UACJ,EAAG,EAAA,UAAA;AAAA,UACH,KAAA,EAAO,EAAE,sCAAsC,CAAA;AAAA,UAC/C,OAAQ,EAAA,UAAA;AAAA,UACR,SAAS,EAAA,IAAA;AAAA,UACT,UAAY,EAAA;AAAA,YACV,GAAG,MAAO,CAAA,UAAA;AAAA,YACV,YACE,kBAAA,KAAA,CAAA,aAAA,CAAA,KAAA,CAAA,QAAA,EAAA,IAAA,EACG,iBACC,mBAAA,KAAA,CAAA,aAAA,CAAC,gBAAiB,EAAA,EAAA,KAAA,EAAM,SAAU,EAAA,IAAA,EAAM,EAAI,EAAA,CAAA,GAC1C,IACH,EAAA,MAAA,CAAO,WAAW,YACrB;AAAA;AAEJ;AAAA;AACF;AAAA,GAEJ;AAEJ;;;;"}
|