@backstage-community/plugin-announcements 0.7.2 → 0.8.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (33) hide show
  1. package/CHANGELOG.md +12 -0
  2. package/README.md +1 -1
  3. package/dist/components/Admin/AdminPortal/AdminPortal.esm.js +5 -2
  4. package/dist/components/Admin/AdminPortal/AdminPortal.esm.js.map +1 -1
  5. package/dist/components/Admin/AnnouncementsContent/AnnouncementsContent.esm.js +13 -4
  6. package/dist/components/Admin/AnnouncementsContent/AnnouncementsContent.esm.js.map +1 -1
  7. package/dist/components/Admin/TagsContent/TagsContent.esm.js +146 -0
  8. package/dist/components/Admin/TagsContent/TagsContent.esm.js.map +1 -0
  9. package/dist/components/AnnouncementForm/AnnouncementForm.esm.js +44 -13
  10. package/dist/components/AnnouncementForm/AnnouncementForm.esm.js.map +1 -1
  11. package/dist/components/AnnouncementForm/CategoryInput.esm.js.map +1 -1
  12. package/dist/components/AnnouncementForm/TagsInput.esm.js +98 -0
  13. package/dist/components/AnnouncementForm/TagsInput.esm.js.map +1 -0
  14. package/dist/components/AnnouncementsCard/AnnouncementsCard.esm.js +20 -2
  15. package/dist/components/AnnouncementsCard/AnnouncementsCard.esm.js.map +1 -1
  16. package/dist/components/AnnouncementsPage/AnnouncementsPage.esm.js +15 -3
  17. package/dist/components/AnnouncementsPage/AnnouncementsPage.esm.js.map +1 -1
  18. package/dist/components/AnnouncementsPage/ContextMenu.esm.js +6 -1
  19. package/dist/components/AnnouncementsPage/ContextMenu.esm.js.map +1 -1
  20. package/dist/components/NewAnnouncementBanner/NewAnnouncementBanner.esm.js +2 -1
  21. package/dist/components/NewAnnouncementBanner/NewAnnouncementBanner.esm.js.map +1 -1
  22. package/dist/components/Router.esm.js +1 -0
  23. package/dist/components/Router.esm.js.map +1 -1
  24. package/dist/components/TagsForm/TagsForm.esm.js +85 -0
  25. package/dist/components/TagsForm/TagsForm.esm.js.map +1 -0
  26. package/dist/components/TagsPage/DeleteTagDialog.esm.js +16 -0
  27. package/dist/components/TagsPage/DeleteTagDialog.esm.js.map +1 -0
  28. package/dist/components/TagsPage/useDeleteTagDialogState.esm.js +29 -0
  29. package/dist/components/TagsPage/useDeleteTagDialogState.esm.js.map +1 -0
  30. package/dist/index.d.ts +1 -0
  31. package/dist/routes.esm.js +6 -1
  32. package/dist/routes.esm.js.map +1 -1
  33. package/package.json +3 -3
package/CHANGELOG.md CHANGED
@@ -1,5 +1,17 @@
1
1
  # @backstage-community/plugin-announcements
2
2
 
3
+ ## 0.8.0
4
+
5
+ ### Minor Changes
6
+
7
+ - 8c803d8: Added support for tags in announcements
8
+
9
+ ### Patch Changes
10
+
11
+ - Updated dependencies [8c803d8]
12
+ - @backstage-community/plugin-announcements-react@0.7.0
13
+ - @backstage-community/plugin-announcements-common@0.5.1
14
+
3
15
  ## 0.7.2
4
16
 
5
17
  ### Patch Changes
package/README.md CHANGED
@@ -110,7 +110,7 @@ Example
110
110
 
111
111
  ### Overriding the AnnouncementsPage
112
112
 
113
- It is possible to specify the Announcements within a specific category rendered on the `AnnouncementsPage`. You can do this by passing a `category` prop to the `AnnouncementsPage` component. The `AnnouncementsPage` prop accepts an value such as:
113
+ It is possible to specify the Announcements within a specific category or tags rendered on the `AnnouncementsPage`. You can do this by passing a `category` or `tags` prop to the `AnnouncementsPage` component. The `AnnouncementsPage` prop accepts a value such as:
114
114
 
115
115
  ```ts
116
116
  category = 'conferences';
@@ -8,6 +8,7 @@ import { useAnnouncementsTranslation } from '@backstage-community/plugin-announc
8
8
  import { makeStyles, Tab } from '@material-ui/core';
9
9
  import { TabContext, TabList, TabPanel } from '@material-ui/lab';
10
10
  import { announcementCreatePermission } from '@backstage-community/plugin-announcements-common';
11
+ import { TagsContent } from '../TagsContent/TagsContent.esm.js';
11
12
 
12
13
  const useStyles = makeStyles(() => ({
13
14
  tabPanel: {
@@ -37,10 +38,12 @@ const AdminPortalContent = () => {
37
38
  label: t("admin.adminPortal.categoriesLabel"),
38
39
  value: "categories"
39
40
  }
40
- )
41
+ ),
42
+ /* @__PURE__ */ jsx(Tab, { label: t("admin.adminPortal.tagsLabel"), value: "tags" })
41
43
  ] }),
42
44
  /* @__PURE__ */ jsx(TabPanel, { value: "announcements", className: classes.tabPanel, children: /* @__PURE__ */ jsx(AnnouncementsContent, {}) }),
43
- /* @__PURE__ */ jsx(TabPanel, { value: "categories", className: classes.tabPanel, children: /* @__PURE__ */ jsx(CategoriesContent, {}) })
45
+ /* @__PURE__ */ jsx(TabPanel, { value: "categories", className: classes.tabPanel, children: /* @__PURE__ */ jsx(CategoriesContent, {}) }),
46
+ /* @__PURE__ */ jsx(TabPanel, { value: "tags", className: classes.tabPanel, children: /* @__PURE__ */ jsx(TagsContent, {}) })
44
47
  ] });
45
48
  };
46
49
  const AdminPortal = (props) => {
@@ -1 +1 @@
1
- {"version":3,"file":"AdminPortal.esm.js","sources":["../../../../src/components/Admin/AdminPortal/AdminPortal.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 { ChangeEvent, useState } from 'react';\nimport { Page, Header, Content } from '@backstage/core-components';\nimport { AnnouncementsContent } from '../AnnouncementsContent';\nimport { CategoriesContent } from '../CategoriesContent';\nimport { RequirePermission } from '@backstage/plugin-permission-react';\nimport { useAnnouncementsTranslation } from '@backstage-community/plugin-announcements-react';\nimport { makeStyles, Tab } from '@material-ui/core';\nimport { TabContext, TabList, TabPanel } from '@material-ui/lab';\nimport { announcementCreatePermission } from '@backstage-community/plugin-announcements-common';\n\nconst useStyles = makeStyles(() => ({\n tabPanel: {\n paddingLeft: '0px',\n paddingRight: '0px',\n },\n}));\n\ntype AdminPortalProps = {\n themeId?: string;\n title?: string;\n subtitle?: string;\n};\n\nconst AdminPortalContent = () => {\n const classes = useStyles();\n const [tab, setTab] = useState('announcements');\n const { t } = useAnnouncementsTranslation();\n const handleChange = (_event: ChangeEvent<{}>, tabValue: string) => {\n setTab(tabValue);\n };\n\n return (\n <TabContext value={tab}>\n <TabList onChange={handleChange}>\n <Tab\n label={t('admin.adminPortal.announcementsLabels')}\n value=\"announcements\"\n />\n <Tab\n label={t('admin.adminPortal.categoriesLabel')}\n value=\"categories\"\n />\n </TabList>\n <TabPanel value=\"announcements\" className={classes.tabPanel}>\n <AnnouncementsContent />\n </TabPanel>\n <TabPanel value=\"categories\" className={classes.tabPanel}>\n <CategoriesContent />\n </TabPanel>\n </TabContext>\n );\n};\n\n/** @public */\nexport const AdminPortal = (props?: AdminPortalProps) => {\n const { title, subtitle, themeId } = props ?? {};\n const { t } = useAnnouncementsTranslation();\n\n return (\n <Page themeId={themeId ?? 'tool'}>\n <Header\n title={title ?? t('admin.adminPortal.title')}\n subtitle={subtitle ?? t('admin.adminPortal.title')}\n />\n <RequirePermission permission={announcementCreatePermission}>\n <Content>\n <AdminPortalContent />\n </Content>\n </RequirePermission>\n </Page>\n );\n};\n"],"names":[],"mappings":";;;;;;;;;;;AAyBA,MAAM,SAAA,GAAY,WAAW,OAAO;AAAA,EAClC,QAAU,EAAA;AAAA,IACR,WAAa,EAAA,KAAA;AAAA,IACb,YAAc,EAAA;AAAA;AAElB,CAAE,CAAA,CAAA;AAQF,MAAM,qBAAqB,MAAM;AAC/B,EAAA,MAAM,UAAU,SAAU,EAAA;AAC1B,EAAA,MAAM,CAAC,GAAA,EAAK,MAAM,CAAA,GAAI,SAAS,eAAe,CAAA;AAC9C,EAAM,MAAA,EAAE,CAAE,EAAA,GAAI,2BAA4B,EAAA;AAC1C,EAAM,MAAA,YAAA,GAAe,CAAC,MAAA,EAAyB,QAAqB,KAAA;AAClE,IAAA,MAAA,CAAO,QAAQ,CAAA;AAAA,GACjB;AAEA,EACE,uBAAA,IAAA,CAAC,UAAW,EAAA,EAAA,KAAA,EAAO,GACjB,EAAA,QAAA,EAAA;AAAA,oBAAC,IAAA,CAAA,OAAA,EAAA,EAAQ,UAAU,YACjB,EAAA,QAAA,EAAA;AAAA,sBAAA,GAAA;AAAA,QAAC,GAAA;AAAA,QAAA;AAAA,UACC,KAAA,EAAO,EAAE,uCAAuC,CAAA;AAAA,UAChD,KAAM,EAAA;AAAA;AAAA,OACR;AAAA,sBACA,GAAA;AAAA,QAAC,GAAA;AAAA,QAAA;AAAA,UACC,KAAA,EAAO,EAAE,mCAAmC,CAAA;AAAA,UAC5C,KAAM,EAAA;AAAA;AAAA;AACR,KACF,EAAA,CAAA;AAAA,oBACA,GAAA,CAAC,YAAS,KAAM,EAAA,eAAA,EAAgB,WAAW,OAAQ,CAAA,QAAA,EACjD,QAAC,kBAAA,GAAA,CAAA,oBAAA,EAAA,EAAqB,CACxB,EAAA,CAAA;AAAA,oBACA,GAAA,CAAC,YAAS,KAAM,EAAA,YAAA,EAAa,WAAW,OAAQ,CAAA,QAAA,EAC9C,QAAC,kBAAA,GAAA,CAAA,iBAAA,EAAA,EAAkB,CACrB,EAAA;AAAA,GACF,EAAA,CAAA;AAEJ,CAAA;AAGa,MAAA,WAAA,GAAc,CAAC,KAA6B,KAAA;AACvD,EAAA,MAAM,EAAE,KAAO,EAAA,QAAA,EAAU,OAAQ,EAAA,GAAI,SAAS,EAAC;AAC/C,EAAM,MAAA,EAAE,CAAE,EAAA,GAAI,2BAA4B,EAAA;AAE1C,EAAA,uBACG,IAAA,CAAA,IAAA,EAAA,EAAK,OAAS,EAAA,OAAA,IAAW,MACxB,EAAA,QAAA,EAAA;AAAA,oBAAA,GAAA;AAAA,MAAC,MAAA;AAAA,MAAA;AAAA,QACC,KAAA,EAAO,KAAS,IAAA,CAAA,CAAE,yBAAyB,CAAA;AAAA,QAC3C,QAAA,EAAU,QAAY,IAAA,CAAA,CAAE,yBAAyB;AAAA;AAAA,KACnD;AAAA,oBACA,GAAA,CAAC,qBAAkB,UAAY,EAAA,4BAAA,EAC7B,8BAAC,OACC,EAAA,EAAA,QAAA,kBAAA,GAAA,CAAC,kBAAmB,EAAA,EAAA,CAAA,EACtB,CACF,EAAA;AAAA,GACF,EAAA,CAAA;AAEJ;;;;"}
1
+ {"version":3,"file":"AdminPortal.esm.js","sources":["../../../../src/components/Admin/AdminPortal/AdminPortal.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 { ChangeEvent, useState } from 'react';\nimport { Page, Header, Content } from '@backstage/core-components';\nimport { AnnouncementsContent } from '../AnnouncementsContent';\nimport { CategoriesContent } from '../CategoriesContent';\nimport { RequirePermission } from '@backstage/plugin-permission-react';\nimport { useAnnouncementsTranslation } from '@backstage-community/plugin-announcements-react';\nimport { makeStyles, Tab } from '@material-ui/core';\nimport { TabContext, TabList, TabPanel } from '@material-ui/lab';\nimport { announcementCreatePermission } from '@backstage-community/plugin-announcements-common';\nimport { TagsContent } from '../TagsContent';\n\nconst useStyles = makeStyles(() => ({\n tabPanel: {\n paddingLeft: '0px',\n paddingRight: '0px',\n },\n}));\n\ntype AdminPortalProps = {\n themeId?: string;\n title?: string;\n subtitle?: string;\n};\n\nconst AdminPortalContent = () => {\n const classes = useStyles();\n const [tab, setTab] = useState('announcements');\n const { t } = useAnnouncementsTranslation();\n const handleChange = (_event: ChangeEvent<{}>, tabValue: string) => {\n setTab(tabValue);\n };\n\n return (\n <TabContext value={tab}>\n <TabList onChange={handleChange}>\n <Tab\n label={t('admin.adminPortal.announcementsLabels')}\n value=\"announcements\"\n />\n <Tab\n label={t('admin.adminPortal.categoriesLabel')}\n value=\"categories\"\n />\n <Tab label={t('admin.adminPortal.tagsLabel')} value=\"tags\" />\n </TabList>\n <TabPanel value=\"announcements\" className={classes.tabPanel}>\n <AnnouncementsContent />\n </TabPanel>\n <TabPanel value=\"categories\" className={classes.tabPanel}>\n <CategoriesContent />\n </TabPanel>\n <TabPanel value=\"tags\" className={classes.tabPanel}>\n <TagsContent />\n </TabPanel>\n </TabContext>\n );\n};\n\n/** @public */\nexport const AdminPortal = (props?: AdminPortalProps) => {\n const { title, subtitle, themeId } = props ?? {};\n const { t } = useAnnouncementsTranslation();\n\n return (\n <Page themeId={themeId ?? 'tool'}>\n <Header\n title={title ?? t('admin.adminPortal.title')}\n subtitle={subtitle ?? t('admin.adminPortal.title')}\n />\n <RequirePermission permission={announcementCreatePermission}>\n <Content>\n <AdminPortalContent />\n </Content>\n </RequirePermission>\n </Page>\n );\n};\n"],"names":[],"mappings":";;;;;;;;;;;;AA0BA,MAAM,SAAA,GAAY,WAAW,OAAO;AAAA,EAClC,QAAU,EAAA;AAAA,IACR,WAAa,EAAA,KAAA;AAAA,IACb,YAAc,EAAA;AAAA;AAElB,CAAE,CAAA,CAAA;AAQF,MAAM,qBAAqB,MAAM;AAC/B,EAAA,MAAM,UAAU,SAAU,EAAA;AAC1B,EAAA,MAAM,CAAC,GAAA,EAAK,MAAM,CAAA,GAAI,SAAS,eAAe,CAAA;AAC9C,EAAM,MAAA,EAAE,CAAE,EAAA,GAAI,2BAA4B,EAAA;AAC1C,EAAM,MAAA,YAAA,GAAe,CAAC,MAAA,EAAyB,QAAqB,KAAA;AAClE,IAAA,MAAA,CAAO,QAAQ,CAAA;AAAA,GACjB;AAEA,EACE,uBAAA,IAAA,CAAC,UAAW,EAAA,EAAA,KAAA,EAAO,GACjB,EAAA,QAAA,EAAA;AAAA,oBAAC,IAAA,CAAA,OAAA,EAAA,EAAQ,UAAU,YACjB,EAAA,QAAA,EAAA;AAAA,sBAAA,GAAA;AAAA,QAAC,GAAA;AAAA,QAAA;AAAA,UACC,KAAA,EAAO,EAAE,uCAAuC,CAAA;AAAA,UAChD,KAAM,EAAA;AAAA;AAAA,OACR;AAAA,sBACA,GAAA;AAAA,QAAC,GAAA;AAAA,QAAA;AAAA,UACC,KAAA,EAAO,EAAE,mCAAmC,CAAA;AAAA,UAC5C,KAAM,EAAA;AAAA;AAAA,OACR;AAAA,0BACC,GAAI,EAAA,EAAA,KAAA,EAAO,EAAE,6BAA6B,CAAA,EAAG,OAAM,MAAO,EAAA;AAAA,KAC7D,EAAA,CAAA;AAAA,oBACA,GAAA,CAAC,YAAS,KAAM,EAAA,eAAA,EAAgB,WAAW,OAAQ,CAAA,QAAA,EACjD,QAAC,kBAAA,GAAA,CAAA,oBAAA,EAAA,EAAqB,CACxB,EAAA,CAAA;AAAA,oBACA,GAAA,CAAC,YAAS,KAAM,EAAA,YAAA,EAAa,WAAW,OAAQ,CAAA,QAAA,EAC9C,QAAC,kBAAA,GAAA,CAAA,iBAAA,EAAA,EAAkB,CACrB,EAAA,CAAA;AAAA,oBACA,GAAA,CAAC,YAAS,KAAM,EAAA,MAAA,EAAO,WAAW,OAAQ,CAAA,QAAA,EACxC,QAAC,kBAAA,GAAA,CAAA,WAAA,EAAA,EAAY,CACf,EAAA;AAAA,GACF,EAAA,CAAA;AAEJ,CAAA;AAGa,MAAA,WAAA,GAAc,CAAC,KAA6B,KAAA;AACvD,EAAA,MAAM,EAAE,KAAO,EAAA,QAAA,EAAU,OAAQ,EAAA,GAAI,SAAS,EAAC;AAC/C,EAAM,MAAA,EAAE,CAAE,EAAA,GAAI,2BAA4B,EAAA;AAE1C,EAAA,uBACG,IAAA,CAAA,IAAA,EAAA,EAAK,OAAS,EAAA,OAAA,IAAW,MACxB,EAAA,QAAA,EAAA;AAAA,oBAAA,GAAA;AAAA,MAAC,MAAA;AAAA,MAAA;AAAA,QACC,KAAA,EAAO,KAAS,IAAA,CAAA,CAAE,yBAAyB,CAAA;AAAA,QAC3C,QAAA,EAAU,QAAY,IAAA,CAAA,CAAE,yBAAyB;AAAA;AAAA,KACnD;AAAA,oBACA,GAAA,CAAC,qBAAkB,UAAY,EAAA,4BAAA,EAC7B,8BAAC,OACC,EAAA,EAAA,QAAA,kBAAA,GAAA,CAAC,kBAAmB,EAAA,EAAA,CAAA,EACtB,CACF,EAAA;AAAA,GACF,EAAA,CAAA;AAEJ;;;;"}
@@ -1,4 +1,4 @@
1
- import { jsx, jsxs, Fragment } from 'react/jsx-runtime';
1
+ import { jsx, jsxs } from 'react/jsx-runtime';
2
2
  import { useState } from 'react';
3
3
  import { Progress, ErrorPanel, StatusOK, StatusPending, Table } from '@backstage/core-components';
4
4
  import { useApi, alertApiRef } from '@backstage/core-plugin-api';
@@ -11,7 +11,7 @@ import { useNavigate } from 'react-router-dom';
11
11
  import { AnnouncementForm } from '../../AnnouncementForm/AnnouncementForm.esm.js';
12
12
  import slugify from 'slugify';
13
13
  import { usePermission, RequirePermission } from '@backstage/plugin-permission-react';
14
- import { Typography, IconButton, Grid, Button } from '@material-ui/core';
14
+ import { Typography, Box, IconButton, Grid, Button } from '@material-ui/core';
15
15
  import DeleteIcon from '@material-ui/icons/Delete';
16
16
  import EditIcon from '@material-ui/icons/Edit';
17
17
  import PreviewIcon from '@material-ui/icons/Visibility';
@@ -79,7 +79,7 @@ const AnnouncementsContent = () => {
79
79
  if (slugs.indexOf(categorySlug) === -1) {
80
80
  alertMsg = alertMsg.replace(".", "");
81
81
  alertMsg = `${alertMsg} ${t(
82
- "admin.announcementsContent.alertMessage"
82
+ "admin.announcementsContent.alertMessageWithNewCategory"
83
83
  )} ${category}.`;
84
84
  await announcementsApi.createCategory({
85
85
  title: category
@@ -134,6 +134,12 @@ const AnnouncementsContent = () => {
134
134
  field: "category",
135
135
  render: (rowData) => rowData.category?.title ?? ""
136
136
  },
137
+ {
138
+ title: /* @__PURE__ */ jsx(Typography, { children: t("admin.announcementsContent.table.tags") }),
139
+ sorting: true,
140
+ field: "tags",
141
+ render: (rowData) => rowData.tags?.map((tag) => tag.title).join(", ") || ""
142
+ },
137
143
  {
138
144
  title: /* @__PURE__ */ jsx(Typography, { children: t("admin.announcementsContent.table.status") }),
139
145
  sorting: true,
@@ -157,12 +163,13 @@ const AnnouncementsContent = () => {
157
163
  {
158
164
  title: /* @__PURE__ */ jsx(Typography, { children: t("admin.announcementsContent.table.actions") }),
159
165
  render: (rowData) => {
160
- return /* @__PURE__ */ jsxs(Fragment, { children: [
166
+ return /* @__PURE__ */ jsxs(Box, { display: "flex", flexDirection: "row", children: [
161
167
  /* @__PURE__ */ jsx(
162
168
  IconButton,
163
169
  {
164
170
  "aria-label": "preview",
165
171
  onClick: () => onTitleClick(rowData),
172
+ size: "small",
166
173
  children: /* @__PURE__ */ jsx(PreviewIcon, { fontSize: "small", "data-testid": "preview" })
167
174
  }
168
175
  ),
@@ -172,6 +179,7 @@ const AnnouncementsContent = () => {
172
179
  "aria-label": "edit",
173
180
  disabled: loadingUpdatePermission || !canUpdateAnnouncement,
174
181
  onClick: () => onEdit(rowData),
182
+ size: "small",
175
183
  children: /* @__PURE__ */ jsx(EditIcon, { fontSize: "small", "data-testid": "edit-icon" })
176
184
  }
177
185
  ),
@@ -181,6 +189,7 @@ const AnnouncementsContent = () => {
181
189
  "aria-label": "delete",
182
190
  disabled: loadingDeletePermission || !canDeleteAnnouncement,
183
191
  onClick: () => openDeleteDialog(rowData),
192
+ size: "small",
184
193
  children: /* @__PURE__ */ jsx(DeleteIcon, { fontSize: "small", "data-testid": "delete-icon" })
185
194
  }
186
195
  )
@@ -1 +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 { useState } from 'react';\nimport {\n ErrorPanel,\n Progress,\n Table,\n TableColumn,\n StatusOK,\n StatusPending,\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';\nimport { DateTime } from 'luxon';\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.announcementsContent.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.announcementsContent.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.announcementsContent.table.title')}</Typography>\n ),\n sorting: true,\n field: 'title',\n render: rowData => rowData.title,\n },\n {\n title: (\n <Typography>{t('admin.announcementsContent.table.body')}</Typography>\n ),\n sorting: true,\n field: 'body',\n render: rowData => rowData.body,\n },\n {\n title: (\n <Typography>\n {t('admin.announcementsContent.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.announcementsContent.table.onBehalfOf')}\n </Typography>\n ),\n sorting: true,\n field: 'on_behalf_of',\n render: rowData => rowData.on_behalf_of,\n },\n\n {\n title: (\n <Typography>\n {t('admin.announcementsContent.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.announcementsContent.table.status')}</Typography>\n ),\n sorting: true,\n field: 'active',\n render: rowData =>\n rowData.active ? (\n <StatusOK>{t('admin.announcementsContent.table.active')}</StatusOK>\n ) : (\n <StatusPending>\n {t('admin.announcementsContent.table.inactive')}\n </StatusPending>\n ),\n },\n {\n title: (\n <Typography>\n {t('admin.announcementsContent.table.created_at')}\n </Typography>\n ),\n sorting: true,\n field: 'created_at',\n type: 'date',\n render: rowData =>\n DateTime.fromISO(rowData.created_at).toFormat('M/d/yyyy'),\n },\n {\n title: (\n <Typography>\n {t('admin.announcementsContent.table.start_at')}\n </Typography>\n ),\n sorting: true,\n field: 'start_at',\n type: 'date',\n render: rowData =>\n DateTime.fromISO(rowData.start_at).toFormat('M/d/yyyy'),\n },\n {\n title: (\n <Typography>{t('admin.announcementsContent.table.actions')}</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.announcementsContent.cancelButton')\n : t('admin.announcementsContent.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.announcementsContent.announcements')}\n options={{ pageSize: 20, search: true }}\n columns={columns}\n data={announcements?.results ?? []}\n emptyContent={\n <Typography style={{ padding: 2 }}>\n {t('admin.announcementsContent.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":[],"mappings":"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"}
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 { useState } from 'react';\nimport {\n ErrorPanel,\n Progress,\n Table,\n TableColumn,\n StatusOK,\n StatusPending,\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 { Box, 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';\nimport { DateTime } from 'luxon';\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.announcementsContent.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.announcementsContent.alertMessageWithNewCategory',\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.announcementsContent.table.title')}</Typography>\n ),\n sorting: true,\n field: 'title',\n render: rowData => rowData.title,\n },\n {\n title: (\n <Typography>{t('admin.announcementsContent.table.body')}</Typography>\n ),\n sorting: true,\n field: 'body',\n render: rowData => rowData.body,\n },\n {\n title: (\n <Typography>\n {t('admin.announcementsContent.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.announcementsContent.table.onBehalfOf')}\n </Typography>\n ),\n sorting: true,\n field: 'on_behalf_of',\n render: rowData => rowData.on_behalf_of,\n },\n\n {\n title: (\n <Typography>\n {t('admin.announcementsContent.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.announcementsContent.table.tags')}</Typography>\n ),\n sorting: true,\n field: 'tags',\n render: rowData => rowData.tags?.map(tag => tag.title).join(', ') || '',\n },\n {\n title: (\n <Typography>{t('admin.announcementsContent.table.status')}</Typography>\n ),\n sorting: true,\n field: 'active',\n render: rowData =>\n rowData.active ? (\n <StatusOK>{t('admin.announcementsContent.table.active')}</StatusOK>\n ) : (\n <StatusPending>\n {t('admin.announcementsContent.table.inactive')}\n </StatusPending>\n ),\n },\n {\n title: (\n <Typography>\n {t('admin.announcementsContent.table.created_at')}\n </Typography>\n ),\n sorting: true,\n field: 'created_at',\n type: 'date',\n render: rowData =>\n DateTime.fromISO(rowData.created_at).toFormat('M/d/yyyy'),\n },\n {\n title: (\n <Typography>\n {t('admin.announcementsContent.table.start_at')}\n </Typography>\n ),\n sorting: true,\n field: 'start_at',\n type: 'date',\n render: rowData =>\n DateTime.fromISO(rowData.start_at).toFormat('M/d/yyyy'),\n },\n {\n title: (\n <Typography>{t('admin.announcementsContent.table.actions')}</Typography>\n ),\n render: rowData => {\n return (\n <Box display=\"flex\" flexDirection=\"row\">\n <IconButton\n aria-label=\"preview\"\n onClick={() => onTitleClick(rowData)}\n size=\"small\"\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 size=\"small\"\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 size=\"small\"\n >\n <DeleteIcon fontSize=\"small\" data-testid=\"delete-icon\" />\n </IconButton>\n </Box>\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.announcementsContent.cancelButton')\n : t('admin.announcementsContent.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.announcementsContent.announcements')}\n options={{ pageSize: 20, search: true }}\n columns={columns}\n data={announcements?.results ?? []}\n emptyContent={\n <Typography style={{ padding: 2 }}>\n {t('admin.announcementsContent.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":[],"mappings":"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"}
@@ -0,0 +1,146 @@
1
+ import { jsx, jsxs } from 'react/jsx-runtime';
2
+ import { useState } from 'react';
3
+ import { Progress, ErrorPanel, Table } from '@backstage/core-components';
4
+ import { useTags, announcementsApiRef, useAnnouncementsTranslation } from '@backstage-community/plugin-announcements-react';
5
+ import { announcementCreatePermission, announcementDeletePermission } from '@backstage-community/plugin-announcements-common';
6
+ import { TagsForm } from '../../TagsForm/TagsForm.esm.js';
7
+ import { useApi, alertApiRef } from '@backstage/core-plugin-api';
8
+ import { usePermission, RequirePermission } from '@backstage/plugin-permission-react';
9
+ import { useDeleteTagDialogState } from '../../TagsPage/useDeleteTagDialogState.esm.js';
10
+ import { DeleteTagDialog } from '../../TagsPage/DeleteTagDialog.esm.js';
11
+ import { Typography, IconButton, Grid, Button } from '@material-ui/core';
12
+ import DeleteIcon from '@material-ui/icons/Delete';
13
+
14
+ const TagsContent = () => {
15
+ const [showNewTagForm, setShowNewTagForm] = useState(false);
16
+ const { tags, loading, error, retry: refresh } = useTags();
17
+ const announcementsApi = useApi(announcementsApiRef);
18
+ const alertApi = useApi(alertApiRef);
19
+ const { t } = useAnnouncementsTranslation();
20
+ const {
21
+ isOpen: isDeleteDialogOpen,
22
+ open: openDeleteDialog,
23
+ close: closeDeleteDialog,
24
+ tag: tagToDelete
25
+ } = useDeleteTagDialogState();
26
+ const { loading: loadingCreatePermission, allowed: canCreateTag } = usePermission({
27
+ permission: announcementCreatePermission
28
+ });
29
+ const { loading: loadingDeletePermission, allowed: canDeleteAnnouncement } = usePermission({
30
+ permission: announcementDeletePermission
31
+ });
32
+ const onSubmit = async (request) => {
33
+ const { title } = request;
34
+ try {
35
+ await announcementsApi.createTag({
36
+ title
37
+ });
38
+ alertApi.post({
39
+ message: `${title} ${t("admin.tagsContent.createdMessage")}`,
40
+ severity: "success"
41
+ });
42
+ refresh();
43
+ } catch (err) {
44
+ if (err.response?.status === 409) {
45
+ alertApi.post({
46
+ message: t("admin.tagsContent.errors.alreadyExists"),
47
+ severity: "error"
48
+ });
49
+ } else {
50
+ alertApi.post({
51
+ message: err.message,
52
+ severity: "error"
53
+ });
54
+ }
55
+ }
56
+ };
57
+ const onCreateButtonClick = () => {
58
+ setShowNewTagForm(!showNewTagForm);
59
+ };
60
+ const onCancelDelete = () => {
61
+ closeDeleteDialog();
62
+ };
63
+ const onConfirmDelete = async () => {
64
+ closeDeleteDialog();
65
+ try {
66
+ await announcementsApi.deleteTag(tagToDelete.slug);
67
+ alertApi.post({
68
+ message: t("admin.tagsContent.table.tagDeleted"),
69
+ severity: "success"
70
+ });
71
+ } catch (err) {
72
+ alertApi.post({
73
+ message: err.body.error.message,
74
+ severity: "error"
75
+ });
76
+ }
77
+ refresh();
78
+ };
79
+ if (loading) {
80
+ return /* @__PURE__ */ jsx(Progress, {});
81
+ }
82
+ if (error) {
83
+ return /* @__PURE__ */ jsx(ErrorPanel, { error });
84
+ }
85
+ const columns = [
86
+ {
87
+ title: /* @__PURE__ */ jsx(Typography, { children: t("admin.tagsContent.table.title") }),
88
+ sorting: true,
89
+ field: "title",
90
+ render: (rowData) => rowData.title
91
+ },
92
+ {
93
+ title: /* @__PURE__ */ jsx(Typography, { children: t("admin.tagsContent.table.slug") }),
94
+ sorting: true,
95
+ field: "slug",
96
+ render: (rowData) => rowData.slug
97
+ },
98
+ {
99
+ title: /* @__PURE__ */ jsx(Typography, { children: t("admin.tagsContent.table.actions") }),
100
+ render: (rowData) => {
101
+ return /* @__PURE__ */ jsx(
102
+ IconButton,
103
+ {
104
+ "aria-label": "delete",
105
+ disabled: loadingDeletePermission || !canDeleteAnnouncement,
106
+ onClick: () => openDeleteDialog(rowData),
107
+ children: /* @__PURE__ */ jsx(DeleteIcon, { fontSize: "small", "data-testid": "delete-icon" })
108
+ }
109
+ );
110
+ }
111
+ }
112
+ ];
113
+ return /* @__PURE__ */ jsx(RequirePermission, { permission: announcementCreatePermission, children: /* @__PURE__ */ jsxs(Grid, { container: true, children: [
114
+ /* @__PURE__ */ jsx(Grid, { item: true, xs: 12, children: /* @__PURE__ */ jsx(
115
+ Button,
116
+ {
117
+ disabled: loadingCreatePermission || !canCreateTag,
118
+ variant: "contained",
119
+ onClick: () => onCreateButtonClick(),
120
+ children: showNewTagForm ? t("admin.tagsContent.cancelButton") : t("admin.tagsContent.createButton")
121
+ }
122
+ ) }),
123
+ showNewTagForm && /* @__PURE__ */ jsx(Grid, { item: true, xs: 12, children: /* @__PURE__ */ jsx(TagsForm, { initialData: {}, onSubmit }) }),
124
+ /* @__PURE__ */ jsx(Grid, { item: true, xs: 12, children: /* @__PURE__ */ jsx(
125
+ Table,
126
+ {
127
+ title: "Tags",
128
+ options: { pageSize: 20, search: true },
129
+ columns,
130
+ data: tags ?? [],
131
+ emptyContent: /* @__PURE__ */ jsx(Typography, { style: { padding: 2 }, children: t("admin.tagsContent.table.noTagsFound") })
132
+ }
133
+ ) }),
134
+ /* @__PURE__ */ jsx(
135
+ DeleteTagDialog,
136
+ {
137
+ open: isDeleteDialogOpen,
138
+ onCancel: onCancelDelete,
139
+ onConfirm: onConfirmDelete
140
+ }
141
+ )
142
+ ] }) });
143
+ };
144
+
145
+ export { TagsContent };
146
+ //# sourceMappingURL=TagsContent.esm.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"TagsContent.esm.js","sources":["../../../../src/components/Admin/TagsContent/TagsContent.tsx"],"sourcesContent":["/*\n * Copyright 2025 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 { useState } from 'react';\nimport {\n ErrorPanel,\n Progress,\n Table,\n TableColumn,\n} from '@backstage/core-components';\nimport {\n CreateTagRequest,\n announcementsApiRef,\n useAnnouncementsTranslation,\n useTags,\n} from '@backstage-community/plugin-announcements-react';\nimport {\n announcementCreatePermission,\n announcementDeletePermission,\n Tag,\n} from '@backstage-community/plugin-announcements-common';\nimport { TagsForm } from '../../TagsForm';\nimport { useApi, alertApiRef } from '@backstage/core-plugin-api';\nimport {\n RequirePermission,\n usePermission,\n} from '@backstage/plugin-permission-react';\nimport { useDeleteTagDialogState } from '../../TagsPage/useDeleteTagDialogState';\nimport { ResponseError } from '@backstage/errors';\nimport { DeleteTagDialog } from '../../TagsPage/DeleteTagDialog';\nimport { Button, Grid, IconButton, Typography } from '@material-ui/core';\nimport DeleteIcon from '@material-ui/icons/Delete';\n\nexport const TagsContent = () => {\n const [showNewTagForm, setShowNewTagForm] = useState(false);\n const { tags, loading, error, retry: refresh } = useTags();\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 tag: tagToDelete,\n } = useDeleteTagDialogState();\n\n const { loading: loadingCreatePermission, allowed: canCreateTag } =\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: CreateTagRequest) => {\n const { title } = request;\n\n try {\n await announcementsApi.createTag({\n title,\n });\n\n alertApi.post({\n message: `${title} ${t('admin.tagsContent.createdMessage')}`,\n severity: 'success',\n });\n\n refresh();\n } catch (err: any) {\n if (err.response?.status === 409) {\n alertApi.post({\n message: t('admin.tagsContent.errors.alreadyExists'),\n severity: 'error',\n });\n } else {\n alertApi.post({\n message: (err as Error).message,\n severity: 'error',\n });\n }\n }\n };\n\n const onCreateButtonClick = () => {\n setShowNewTagForm(!showNewTagForm);\n };\n\n const onCancelDelete = () => {\n closeDeleteDialog();\n };\n\n const onConfirmDelete = async () => {\n closeDeleteDialog();\n\n try {\n await announcementsApi.deleteTag(tagToDelete!.slug);\n\n alertApi.post({\n message: t('admin.tagsContent.table.tagDeleted'),\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<Tag>[] = [\n {\n title: <Typography>{t('admin.tagsContent.table.title')}</Typography>,\n sorting: true,\n field: 'title',\n render: rowData => rowData.title,\n },\n {\n title: <Typography>{t('admin.tagsContent.table.slug')}</Typography>,\n sorting: true,\n field: 'slug',\n render: rowData => rowData.slug,\n },\n {\n title: <Typography>{t('admin.tagsContent.table.actions')}</Typography>,\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 || !canCreateTag}\n variant=\"contained\"\n onClick={() => onCreateButtonClick()}\n >\n {showNewTagForm\n ? t('admin.tagsContent.cancelButton')\n : t('admin.tagsContent.createButton')}\n </Button>\n </Grid>\n\n {showNewTagForm && (\n <Grid item xs={12}>\n <TagsForm initialData={{} as Tag} onSubmit={onSubmit} />\n </Grid>\n )}\n\n <Grid item xs={12}>\n <Table\n title=\"Tags\"\n options={{ pageSize: 20, search: true }}\n columns={columns}\n data={tags ?? []}\n emptyContent={\n <Typography style={{ padding: 2 }}>\n {t('admin.tagsContent.table.noTagsFound')}\n </Typography>\n }\n />\n </Grid>\n\n <DeleteTagDialog\n open={isDeleteDialogOpen}\n onCancel={onCancelDelete}\n onConfirm={onConfirmDelete}\n />\n </Grid>\n </RequirePermission>\n );\n};\n"],"names":[],"mappings":"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"}
@@ -3,24 +3,28 @@ import { useState } from 'react';
3
3
  import MDEditor from '@uiw/react-md-editor';
4
4
  import { InfoCard } from '@backstage/core-components';
5
5
  import { useApi, identityApiRef } from '@backstage/core-plugin-api';
6
- import { useAnnouncementsTranslation } from '@backstage-community/plugin-announcements-react';
6
+ import { announcementsApiRef, useAnnouncementsTranslation } from '@backstage-community/plugin-announcements-react';
7
7
  import CategoryInput from './CategoryInput.esm.js';
8
8
  import OnBehalfTeamDropdown from './OnBehalfTeamDropdown.esm.js';
9
+ import TagsInput from './TagsInput.esm.js';
9
10
  import { Box, Typography, Grid, TextField, Divider, FormGroup, FormControlLabel, Switch, Button } from '@material-ui/core';
10
11
  import SaveAltIcon from '@material-ui/icons/SaveAlt';
11
12
  import { DateTime } from 'luxon';
13
+ import slugify from 'slugify';
12
14
 
13
15
  const AnnouncementForm = ({
14
16
  initialData,
15
17
  onSubmit
16
18
  }) => {
17
19
  const identityApi = useApi(identityApiRef);
20
+ const announcementsApi = useApi(announcementsApiRef);
18
21
  const { t } = useAnnouncementsTranslation();
19
22
  const formattedStartAt = initialData.start_at ? DateTime.fromISO(initialData.start_at).toISODate() : DateTime.now().toISODate();
20
23
  const [form, setForm] = useState({
21
24
  ...initialData,
22
25
  category: initialData.category?.slug,
23
- start_at: formattedStartAt || ""
26
+ start_at: formattedStartAt || "",
27
+ tags: initialData.tags?.map((tag) => tag.slug) || void 0
24
28
  });
25
29
  const [loading, setLoading] = useState(false);
26
30
  const [onBehalfOfSelectedTeam, setOnBehalfOfSelectedTeam] = useState(
@@ -42,15 +46,41 @@ const AnnouncementForm = ({
42
46
  setLoading(true);
43
47
  event.preventDefault();
44
48
  const userIdentity = await identityApi.getBackstageIdentity();
45
- const createRequest = {
46
- ...form,
47
- ...{
48
- publisher: userIdentity.userEntityRef,
49
- on_behalf_of: onBehalfOfSelectedTeam
49
+ if (form.tags && form.tags.length > 0) {
50
+ const existingTags = await announcementsApi.tags();
51
+ const processedTags = [];
52
+ for (const tagValue of form.tags) {
53
+ const slugifiedTag = slugify(tagValue.trim(), { lower: true });
54
+ if (existingTags.some((tag) => tag.slug === slugifiedTag)) {
55
+ processedTags.push(slugifiedTag);
56
+ } else {
57
+ try {
58
+ await announcementsApi.createTag({ title: tagValue });
59
+ processedTags.push(slugifiedTag);
60
+ } catch (error) {
61
+ if (error.status === 409) {
62
+ processedTags.push(slugifiedTag);
63
+ } else {
64
+ throw error;
65
+ }
66
+ }
67
+ }
50
68
  }
69
+ form.tags = processedTags;
70
+ }
71
+ const { id, created_at, ...announcementData } = form;
72
+ const createRequest = {
73
+ ...announcementData,
74
+ publisher: userIdentity.userEntityRef,
75
+ on_behalf_of: onBehalfOfSelectedTeam
51
76
  };
52
- await onSubmit(createRequest);
53
- setLoading(false);
77
+ try {
78
+ await onSubmit(createRequest);
79
+ } catch (error) {
80
+ throw error;
81
+ } finally {
82
+ setLoading(false);
83
+ }
54
84
  };
55
85
  return /* @__PURE__ */ jsx(InfoCard, { children: /* @__PURE__ */ jsxs(Box, { p: 3, children: [
56
86
  /* @__PURE__ */ jsx(Typography, { variant: "h5", gutterBottom: true, children: initialData.title ? t("announcementForm.editAnnouncement") : t("announcementForm.newAnnouncement") }),
@@ -67,7 +97,7 @@ const AnnouncementForm = ({
67
97
  required: true
68
98
  }
69
99
  ) }),
70
- /* @__PURE__ */ jsx(Grid, { item: true, xs: 12, sm: 4, children: /* @__PURE__ */ jsx(
100
+ /* @__PURE__ */ jsx(Grid, { item: true, xs: 12, sm: 3, children: /* @__PURE__ */ jsx(
71
101
  CategoryInput,
72
102
  {
73
103
  setForm,
@@ -75,14 +105,15 @@ const AnnouncementForm = ({
75
105
  initialValue: initialData.category?.title ?? ""
76
106
  }
77
107
  ) }),
78
- /* @__PURE__ */ jsx(Grid, { item: true, xs: 12, sm: 4, children: /* @__PURE__ */ jsx(
108
+ /* @__PURE__ */ jsx(Grid, { item: true, xs: 12, sm: 3, children: /* @__PURE__ */ jsx(TagsInput, { setForm, form }) }),
109
+ /* @__PURE__ */ jsx(Grid, { item: true, xs: 12, sm: 3, children: /* @__PURE__ */ jsx(
79
110
  OnBehalfTeamDropdown,
80
111
  {
81
112
  selectedTeam: onBehalfOfSelectedTeam,
82
113
  onChange: setOnBehalfOfSelectedTeam
83
114
  }
84
115
  ) }),
85
- /* @__PURE__ */ jsx(Grid, { item: true, xs: 12, sm: 4, children: /* @__PURE__ */ jsx(
116
+ /* @__PURE__ */ jsx(Grid, { item: true, xs: 12, sm: 3, children: /* @__PURE__ */ jsx(
86
117
  TextField,
87
118
  {
88
119
  variant: "outlined",
@@ -128,7 +159,7 @@ const AnnouncementForm = ({
128
159
  Switch,
129
160
  {
130
161
  name: "active",
131
- checked: form.active,
162
+ checked: !!form.active,
132
163
  onChange: handleChangeActive,
133
164
  color: "primary"
134
165
  }
@@ -1 +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 { useState, type ChangeEvent, type FormEvent } 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 OnBehalfTeamDropdown from './OnBehalfTeamDropdown';\nimport {\n TextField,\n FormGroup,\n FormControlLabel,\n Switch,\n Button,\n Box,\n Grid,\n Typography,\n Divider,\n} from '@material-ui/core';\nimport SaveAltIcon from '@material-ui/icons/SaveAlt';\nimport { DateTime } from 'luxon';\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 identityApi = useApi(identityApiRef);\n const { t } = useAnnouncementsTranslation();\n\n const formattedStartAt = initialData.start_at\n ? DateTime.fromISO(initialData.start_at).toISODate()\n : DateTime.now().toISODate();\n\n const [form, setForm] = useState({\n ...initialData,\n category: initialData.category?.slug,\n start_at: formattedStartAt || '',\n });\n const [loading, setLoading] = useState(false);\n const [onBehalfOfSelectedTeam, setOnBehalfOfSelectedTeam] = useState(\n initialData.on_behalf_of || '',\n );\n\n const handleChange = (event: ChangeEvent<HTMLInputElement>) => {\n setForm({\n ...form,\n [event.target.id]: event.target.value,\n });\n };\n\n const handleChangeActive = (event: ChangeEvent<HTMLInputElement>) => {\n setForm({\n ...form,\n [event.target.name]: event.target.checked,\n });\n };\n\n const handleSubmit = async (event: 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 on_behalf_of: onBehalfOfSelectedTeam,\n },\n };\n\n await onSubmit(createRequest);\n setLoading(false);\n };\n\n return (\n <InfoCard>\n <Box p={3}>\n <Typography variant=\"h5\" gutterBottom>\n {initialData.title\n ? t('announcementForm.editAnnouncement')\n : t('announcementForm.newAnnouncement')}\n </Typography>\n <form onSubmit={handleSubmit}>\n <Grid container spacing={3}>\n <Grid item xs={12}>\n <TextField\n id=\"title\"\n label={t('announcementForm.title')}\n value={form.title}\n onChange={handleChange}\n variant=\"outlined\"\n fullWidth\n required\n />\n </Grid>\n\n <Grid item xs={12} sm={4}>\n <CategoryInput\n setForm={setForm}\n form={form}\n initialValue={initialData.category?.title ?? ''}\n />\n </Grid>\n\n <Grid item xs={12} sm={4}>\n <OnBehalfTeamDropdown\n selectedTeam={onBehalfOfSelectedTeam}\n onChange={setOnBehalfOfSelectedTeam}\n />\n </Grid>\n\n <Grid item xs={12} sm={4}>\n <TextField\n variant=\"outlined\"\n label={t('announcementForm.startAt')}\n id=\"start-at-date\"\n type=\"date\"\n value={form.start_at}\n InputLabelProps={{ shrink: true }}\n required\n onChange={e =>\n setForm({\n ...form,\n start_at: e.target.value,\n })\n }\n />\n </Grid>\n\n <Grid item xs={12}>\n <TextField\n id=\"excerpt\"\n label={t('announcementForm.excerpt')}\n value={form.excerpt}\n onChange={handleChange}\n variant=\"outlined\"\n fullWidth\n required\n multiline\n />\n </Grid>\n\n <Grid item xs={12}>\n <MDEditor\n value={form.body}\n style={{ minHeight: '30rem' }}\n onChange={value =>\n setForm({ ...form, ...{ body: value || '' } })\n }\n />\n </Grid>\n\n <Grid item xs={12}>\n <Divider />\n </Grid>\n\n <Grid item xs={12}>\n <FormGroup row style={{ justifyContent: 'flex-end' }}>\n <FormControlLabel\n control={\n <Switch\n name=\"active\"\n checked={form.active}\n onChange={handleChangeActive}\n color=\"primary\"\n />\n }\n label={t('announcementForm.active')}\n />\n <Button\n variant=\"contained\"\n color=\"primary\"\n type=\"submit\"\n disabled={loading || !form.body}\n size=\"large\"\n startIcon={<SaveAltIcon />}\n >\n {t('announcementForm.submit')}\n </Button>\n </FormGroup>\n </Grid>\n </Grid>\n </form>\n </Box>\n </InfoCard>\n );\n};\n"],"names":[],"mappings":"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"}
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 { useState, type ChangeEvent, type FormEvent } 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 announcementsApiRef,\n} from '@backstage-community/plugin-announcements-react';\nimport { Announcement } from '@backstage-community/plugin-announcements-common';\nimport CategoryInput from './CategoryInput';\nimport OnBehalfTeamDropdown from './OnBehalfTeamDropdown';\nimport TagsInput from './TagsInput';\nimport {\n Box,\n Button,\n Divider,\n FormControlLabel,\n FormGroup,\n Grid,\n Switch,\n TextField,\n Typography,\n} from '@material-ui/core';\nimport SaveAltIcon from '@material-ui/icons/SaveAlt';\nimport { DateTime } from 'luxon';\nimport slugify from 'slugify';\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 identityApi = useApi(identityApiRef);\n const announcementsApi = useApi(announcementsApiRef);\n const { t } = useAnnouncementsTranslation();\n\n const formattedStartAt = initialData.start_at\n ? DateTime.fromISO(initialData.start_at).toISODate()\n : DateTime.now().toISODate();\n\n const [form, setForm] = useState({\n ...initialData,\n category: initialData.category?.slug,\n start_at: formattedStartAt || '',\n tags: initialData.tags?.map(tag => tag.slug) || undefined,\n });\n const [loading, setLoading] = useState(false);\n const [onBehalfOfSelectedTeam, setOnBehalfOfSelectedTeam] = useState(\n initialData.on_behalf_of || '',\n );\n\n const handleChange = (event: ChangeEvent<HTMLInputElement>) => {\n setForm({\n ...form,\n [event.target.id]: event.target.value,\n });\n };\n\n const handleChangeActive = (event: ChangeEvent<HTMLInputElement>) => {\n setForm({\n ...form,\n [event.target.name]: event.target.checked,\n });\n };\n\n const handleSubmit = async (event: FormEvent<HTMLFormElement>) => {\n setLoading(true);\n event.preventDefault();\n\n const userIdentity = await identityApi.getBackstageIdentity();\n\n if (form.tags && form.tags.length > 0) {\n const existingTags = await announcementsApi.tags();\n\n const processedTags = [];\n\n for (const tagValue of form.tags) {\n const slugifiedTag = slugify(tagValue.trim(), { lower: true });\n\n if (existingTags.some(tag => tag.slug === slugifiedTag)) {\n processedTags.push(slugifiedTag);\n } else {\n try {\n await announcementsApi.createTag({ title: tagValue });\n processedTags.push(slugifiedTag);\n } catch (error) {\n if (error.status === 409) {\n processedTags.push(slugifiedTag);\n } else {\n throw error;\n }\n }\n }\n }\n\n form.tags = processedTags;\n }\n\n const { id, created_at, ...announcementData } = form;\n\n const createRequest: CreateAnnouncementRequest = {\n ...announcementData,\n publisher: userIdentity.userEntityRef,\n on_behalf_of: onBehalfOfSelectedTeam,\n };\n\n try {\n await onSubmit(createRequest);\n } catch (error) {\n throw error;\n } finally {\n setLoading(false);\n }\n };\n\n return (\n <InfoCard>\n <Box p={3}>\n <Typography variant=\"h5\" gutterBottom>\n {initialData.title\n ? t('announcementForm.editAnnouncement')\n : t('announcementForm.newAnnouncement')}\n </Typography>\n <form onSubmit={handleSubmit}>\n <Grid container spacing={3}>\n <Grid item xs={12}>\n <TextField\n id=\"title\"\n label={t('announcementForm.title')}\n value={form.title}\n onChange={handleChange}\n variant=\"outlined\"\n fullWidth\n required\n />\n </Grid>\n\n <Grid item xs={12} sm={3}>\n <CategoryInput\n setForm={setForm}\n form={form}\n initialValue={initialData.category?.title ?? ''}\n />\n </Grid>\n\n <Grid item xs={12} sm={3}>\n <TagsInput setForm={setForm} form={form} />\n </Grid>\n\n <Grid item xs={12} sm={3}>\n <OnBehalfTeamDropdown\n selectedTeam={onBehalfOfSelectedTeam}\n onChange={setOnBehalfOfSelectedTeam}\n />\n </Grid>\n\n <Grid item xs={12} sm={3}>\n <TextField\n variant=\"outlined\"\n label={t('announcementForm.startAt')}\n id=\"start-at-date\"\n type=\"date\"\n value={form.start_at}\n InputLabelProps={{ shrink: true }}\n required\n onChange={e =>\n setForm({\n ...form,\n start_at: e.target.value,\n })\n }\n />\n </Grid>\n\n <Grid item xs={12}>\n <TextField\n id=\"excerpt\"\n label={t('announcementForm.excerpt')}\n value={form.excerpt}\n onChange={handleChange}\n variant=\"outlined\"\n fullWidth\n required\n multiline\n />\n </Grid>\n\n <Grid item xs={12}>\n <MDEditor\n value={form.body}\n style={{ minHeight: '30rem' }}\n onChange={value =>\n setForm({ ...form, ...{ body: value || '' } })\n }\n />\n </Grid>\n\n <Grid item xs={12}>\n <Divider />\n </Grid>\n\n <Grid item xs={12}>\n <FormGroup row style={{ justifyContent: 'flex-end' }}>\n <FormControlLabel\n control={\n <Switch\n name=\"active\"\n checked={!!form.active}\n onChange={handleChangeActive}\n color=\"primary\"\n />\n }\n label={t('announcementForm.active')}\n />\n <Button\n variant=\"contained\"\n color=\"primary\"\n type=\"submit\"\n disabled={loading || !form.body}\n size=\"large\"\n startIcon={<SaveAltIcon />}\n >\n {t('announcementForm.submit')}\n </Button>\n </FormGroup>\n </Grid>\n </Grid>\n </form>\n </Box>\n </InfoCard>\n );\n};\n"],"names":[],"mappings":"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"}
@@ -1 +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 { SetStateAction } 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: 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 start_at: string;\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 start_at: string;\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":"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"}
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 { SetStateAction } 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: SetStateAction<{\n category: string | undefined;\n tags: 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 start_at: string;\n }>,\n ) => void;\n form: {\n category: string | undefined;\n tags: 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 start_at: string;\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":"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"}