@backstage-community/plugin-announcements 2.0.0 → 2.1.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 (26) hide show
  1. package/CHANGELOG.md +31 -0
  2. package/dist/Router.esm.js +3 -0
  3. package/dist/Router.esm.js.map +1 -1
  4. package/dist/alpha/components/admin/announcements/AnnouncementForm/AnnouncementForm.esm.js +194 -126
  5. package/dist/alpha/components/admin/announcements/AnnouncementForm/AnnouncementForm.esm.js.map +1 -1
  6. package/dist/alpha/components/admin/announcements/AnnouncementForm/OnBehalfTeamDropdown.esm.js +35 -44
  7. package/dist/alpha/components/admin/announcements/AnnouncementForm/OnBehalfTeamDropdown.esm.js.map +1 -1
  8. package/dist/alpha/components/admin/announcements/AnnouncementsContent.esm.js +10 -43
  9. package/dist/alpha/components/admin/announcements/AnnouncementsContent.esm.js.map +1 -1
  10. package/dist/alpha/components/admin/announcements/AnnouncementsTable.esm.js +15 -1
  11. package/dist/alpha/components/admin/announcements/AnnouncementsTable.esm.js.map +1 -1
  12. package/dist/alpha/components/admin/categories/CategoriesContent.esm.js +2 -2
  13. package/dist/alpha/components/admin/categories/CategoriesContent.esm.js.map +1 -1
  14. package/dist/alpha/components/admin/categories/{CreateCatagoryDialog.esm.js → CreateCategoryDialog.esm.js} +3 -3
  15. package/dist/alpha/components/admin/categories/{CreateCatagoryDialog.esm.js.map → CreateCategoryDialog.esm.js.map} +1 -1
  16. package/dist/alpha/components/announcements/AnnouncementsFilterBar.esm.js +6 -2
  17. package/dist/alpha/components/announcements/AnnouncementsFilterBar.esm.js.map +1 -1
  18. package/dist/alpha/components/shared/CategorySelectInput/CategorySelectInput.esm.js +4 -3
  19. package/dist/alpha/components/shared/CategorySelectInput/CategorySelectInput.esm.js.map +1 -1
  20. package/dist/alpha/components/shared/TagsSelectInput/TagsSelectInput.esm.js +4 -3
  21. package/dist/alpha/components/shared/TagsSelectInput/TagsSelectInput.esm.js.map +1 -1
  22. package/package.json +12 -12
  23. package/dist/alpha/components/admin/announcements/AnnouncementForm/CategoryInput.esm.js +0 -82
  24. package/dist/alpha/components/admin/announcements/AnnouncementForm/CategoryInput.esm.js.map +0 -1
  25. package/dist/alpha/components/admin/announcements/AnnouncementForm/TagsInput.esm.js +0 -100
  26. package/dist/alpha/components/admin/announcements/AnnouncementForm/TagsInput.esm.js.map +0 -1
package/CHANGELOG.md CHANGED
@@ -1,5 +1,36 @@
1
1
  # @backstage-community/plugin-announcements
2
2
 
3
+ ## 2.1.0
4
+
5
+ ### Minor Changes
6
+
7
+ - 99b4b52: Backstage version bump to v1.47.2
8
+
9
+ ### Patch Changes
10
+
11
+ - 1a71b9a: The On Behalf Of form component is now rewritten with `@backstage/ui`. The announcements table will now display the on behalf of group alongside publisher if present.
12
+ - 3e53912: The tags select component in the announcements form has been swapped with our new `@backstage/ui` select implementation
13
+ - 44a0337: The category selection in the announcements form has been refactored to use the new `CategorySelectInput` and `CreateCategoryDialog` components written with `@backstage/ui`. Category creation is now handled through the same mechanism as the categories table.
14
+ - 64a82b0: Updates all non-date related text inputs to use the `TextField` from `@backstage/ui`.
15
+ - 6f6d4c8: Switch components in the announcements form have been migrated to use `@backstage/ui`. The "Send notifications" label now includes support for translations.
16
+ - 41f738e: Updates the announcements form to use `@backstage/ui` for the following components:
17
+
18
+ - Box
19
+ - Button
20
+ - InfoCard -> Card, CardBody, CardHeader
21
+ - Grid
22
+ - Typography -> Text
23
+ - Paper -> _removed_
24
+ - Divider -> _removed_
25
+
26
+ These are minor changes that do not affect the functionality of the announcements form.
27
+
28
+ - Updated dependencies [44a0337]
29
+ - Updated dependencies [6f6d4c8]
30
+ - Updated dependencies [99b4b52]
31
+ - @backstage-community/plugin-announcements-react@0.20.0
32
+ - @backstage-community/plugin-announcements-common@0.17.0
33
+
3
34
  ## 2.0.0
4
35
 
5
36
  ### Major Changes
@@ -5,6 +5,9 @@ import { announcementCreatePermission } from '@backstage-community/plugin-announ
5
5
  import { AnnouncementsAdminPage } from './alpha/components/admin/AnnouncementsAdminPage.esm.js';
6
6
  import { AnnouncementsContent } from './alpha/components/admin/announcements/AnnouncementsContent.esm.js';
7
7
  import { CategoriesContent } from './alpha/components/admin/categories/CategoriesContent.esm.js';
8
+ import '@backstage-community/plugin-announcements-react';
9
+ import 'react';
10
+ import '@backstage/ui';
8
11
  import { TagsContent } from './alpha/components/admin/tags/TagsContent.esm.js';
9
12
  import { AnnouncementsPage } from './alpha/components/announcements/AnnouncementsPage.esm.js';
10
13
  import { ViewAnnouncementPage } from './alpha/components/announcements/ViewAnnouncementPage.esm.js';
@@ -1 +1 @@
1
- {"version":3,"file":"Router.esm.js","sources":["../src/Router.tsx"],"sourcesContent":["/*\n * Copyright 2026 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 { Routes, Route } from 'react-router-dom';\nimport { RequirePermission } from '@backstage/plugin-permission-react';\nimport { announcementCreatePermission } from '@backstage-community/plugin-announcements-common';\nimport {\n AnnouncementsAdminPage,\n AnnouncementsContent,\n CategoriesContent,\n TagsContent,\n AnnouncementsPage,\n AnnouncementsPageProps,\n ViewAnnouncementPage,\n} from './alpha/components';\nimport { MarkdownRendererTypeProps } from './components';\n\nimport { compatWrapper } from '@backstage/core-compat-api';\n\ntype RouterProps = {\n title?: string;\n category?: string;\n hideStartAt?: boolean;\n markdownRenderer?: MarkdownRendererTypeProps;\n defaultInactive?: boolean;\n};\n\nexport const Router = (props: RouterProps) => {\n const propsWithDefaults: AnnouncementsPageProps = {\n title: 'Announcements',\n hideStartAt: false,\n markdownRenderer: 'backstage',\n ...props,\n };\n\n return (\n <Routes>\n <Route path=\"/\" element={<AnnouncementsPage {...propsWithDefaults} />} />\n <Route\n path=\"/view/:id\"\n element={\n <ViewAnnouncementPage\n markdownRenderer={propsWithDefaults.markdownRenderer}\n title={propsWithDefaults.title}\n />\n }\n />\n <Route\n path=\"/admin\"\n element={\n <RequirePermission permission={announcementCreatePermission}>\n <AnnouncementsAdminPage\n title={props.title}\n defaultInactive={props.defaultInactive}\n />\n </RequirePermission>\n }\n >\n <Route\n path=\"\"\n element={\n <AnnouncementsContent\n formDefaults={{ defaultInactive: props.defaultInactive }}\n />\n }\n />\n <Route path=\"categories\" element={<CategoriesContent />} />\n <Route path=\"tags\" element={<TagsContent />} />\n </Route>\n </Routes>\n );\n};\n\nexport const OldFrontendSystemCompatibleRouter = (props: RouterProps) => {\n return compatWrapper(<Router {...props} />);\n};\n"],"names":[],"mappings":";;;;;;;;;;;;AAuCa,MAAA,MAAA,GAAS,CAAC,KAAuB,KAAA;AAC5C,EAAA,MAAM,iBAA4C,GAAA;AAAA,IAChD,KAAO,EAAA,eAAA;AAAA,IACP,WAAa,EAAA,KAAA;AAAA,IACb,gBAAkB,EAAA,WAAA;AAAA,IAClB,GAAG;AAAA,GACL;AAEA,EAAA,4BACG,MACC,EAAA,EAAA,QAAA,EAAA;AAAA,oBAAC,GAAA,CAAA,KAAA,EAAA,EAAM,MAAK,GAAI,EAAA,OAAA,sBAAU,iBAAmB,EAAA,EAAA,GAAG,mBAAmB,CAAI,EAAA,CAAA;AAAA,oBACvE,GAAA;AAAA,MAAC,KAAA;AAAA,MAAA;AAAA,QACC,IAAK,EAAA,WAAA;AAAA,QACL,OACE,kBAAA,GAAA;AAAA,UAAC,oBAAA;AAAA,UAAA;AAAA,YACC,kBAAkB,iBAAkB,CAAA,gBAAA;AAAA,YACpC,OAAO,iBAAkB,CAAA;AAAA;AAAA;AAC3B;AAAA,KAEJ;AAAA,oBACA,IAAA;AAAA,MAAC,KAAA;AAAA,MAAA;AAAA,QACC,IAAK,EAAA,QAAA;AAAA,QACL,OACE,kBAAA,GAAA,CAAC,iBAAkB,EAAA,EAAA,UAAA,EAAY,4BAC7B,EAAA,QAAA,kBAAA,GAAA;AAAA,UAAC,sBAAA;AAAA,UAAA;AAAA,YACC,OAAO,KAAM,CAAA,KAAA;AAAA,YACb,iBAAiB,KAAM,CAAA;AAAA;AAAA,SAE3B,EAAA,CAAA;AAAA,QAGF,QAAA,EAAA;AAAA,0BAAA,GAAA;AAAA,YAAC,KAAA;AAAA,YAAA;AAAA,cACC,IAAK,EAAA,EAAA;AAAA,cACL,OACE,kBAAA,GAAA;AAAA,gBAAC,oBAAA;AAAA,gBAAA;AAAA,kBACC,YAAc,EAAA,EAAE,eAAiB,EAAA,KAAA,CAAM,eAAgB;AAAA;AAAA;AACzD;AAAA,WAEJ;AAAA,8BACC,KAAM,EAAA,EAAA,IAAA,EAAK,cAAa,OAAS,kBAAA,GAAA,CAAC,qBAAkB,CAAI,EAAA,CAAA;AAAA,8BACxD,KAAM,EAAA,EAAA,IAAA,EAAK,QAAO,OAAS,kBAAA,GAAA,CAAC,eAAY,CAAI,EAAA;AAAA;AAAA;AAAA;AAC/C,GACF,EAAA,CAAA;AAEJ;AAEa,MAAA,iCAAA,GAAoC,CAAC,KAAuB,KAAA;AACvE,EAAA,OAAO,aAAc,iBAAA,GAAA,CAAC,MAAQ,EAAA,EAAA,GAAG,OAAO,CAAE,CAAA;AAC5C;;;;"}
1
+ {"version":3,"file":"Router.esm.js","sources":["../src/Router.tsx"],"sourcesContent":["/*\n * Copyright 2026 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 { Routes, Route } from 'react-router-dom';\nimport { RequirePermission } from '@backstage/plugin-permission-react';\nimport { announcementCreatePermission } from '@backstage-community/plugin-announcements-common';\nimport {\n AnnouncementsAdminPage,\n AnnouncementsContent,\n CategoriesContent,\n TagsContent,\n AnnouncementsPage,\n AnnouncementsPageProps,\n ViewAnnouncementPage,\n} from './alpha/components';\nimport { MarkdownRendererTypeProps } from './components';\n\nimport { compatWrapper } from '@backstage/core-compat-api';\n\ntype RouterProps = {\n title?: string;\n category?: string;\n hideStartAt?: boolean;\n markdownRenderer?: MarkdownRendererTypeProps;\n defaultInactive?: boolean;\n};\n\nexport const Router = (props: RouterProps) => {\n const propsWithDefaults: AnnouncementsPageProps = {\n title: 'Announcements',\n hideStartAt: false,\n markdownRenderer: 'backstage',\n ...props,\n };\n\n return (\n <Routes>\n <Route path=\"/\" element={<AnnouncementsPage {...propsWithDefaults} />} />\n <Route\n path=\"/view/:id\"\n element={\n <ViewAnnouncementPage\n markdownRenderer={propsWithDefaults.markdownRenderer}\n title={propsWithDefaults.title}\n />\n }\n />\n <Route\n path=\"/admin\"\n element={\n <RequirePermission permission={announcementCreatePermission}>\n <AnnouncementsAdminPage\n title={props.title}\n defaultInactive={props.defaultInactive}\n />\n </RequirePermission>\n }\n >\n <Route\n path=\"\"\n element={\n <AnnouncementsContent\n formDefaults={{ defaultInactive: props.defaultInactive }}\n />\n }\n />\n <Route path=\"categories\" element={<CategoriesContent />} />\n <Route path=\"tags\" element={<TagsContent />} />\n </Route>\n </Routes>\n );\n};\n\nexport const OldFrontendSystemCompatibleRouter = (props: RouterProps) => {\n return compatWrapper(<Router {...props} />);\n};\n"],"names":[],"mappings":";;;;;;;;;;;;;;;AAuCa,MAAA,MAAA,GAAS,CAAC,KAAuB,KAAA;AAC5C,EAAA,MAAM,iBAA4C,GAAA;AAAA,IAChD,KAAO,EAAA,eAAA;AAAA,IACP,WAAa,EAAA,KAAA;AAAA,IACb,gBAAkB,EAAA,WAAA;AAAA,IAClB,GAAG;AAAA,GACL;AAEA,EAAA,4BACG,MACC,EAAA,EAAA,QAAA,EAAA;AAAA,oBAAC,GAAA,CAAA,KAAA,EAAA,EAAM,MAAK,GAAI,EAAA,OAAA,sBAAU,iBAAmB,EAAA,EAAA,GAAG,mBAAmB,CAAI,EAAA,CAAA;AAAA,oBACvE,GAAA;AAAA,MAAC,KAAA;AAAA,MAAA;AAAA,QACC,IAAK,EAAA,WAAA;AAAA,QACL,OACE,kBAAA,GAAA;AAAA,UAAC,oBAAA;AAAA,UAAA;AAAA,YACC,kBAAkB,iBAAkB,CAAA,gBAAA;AAAA,YACpC,OAAO,iBAAkB,CAAA;AAAA;AAAA;AAC3B;AAAA,KAEJ;AAAA,oBACA,IAAA;AAAA,MAAC,KAAA;AAAA,MAAA;AAAA,QACC,IAAK,EAAA,QAAA;AAAA,QACL,OACE,kBAAA,GAAA,CAAC,iBAAkB,EAAA,EAAA,UAAA,EAAY,4BAC7B,EAAA,QAAA,kBAAA,GAAA;AAAA,UAAC,sBAAA;AAAA,UAAA;AAAA,YACC,OAAO,KAAM,CAAA,KAAA;AAAA,YACb,iBAAiB,KAAM,CAAA;AAAA;AAAA,SAE3B,EAAA,CAAA;AAAA,QAGF,QAAA,EAAA;AAAA,0BAAA,GAAA;AAAA,YAAC,KAAA;AAAA,YAAA;AAAA,cACC,IAAK,EAAA,EAAA;AAAA,cACL,OACE,kBAAA,GAAA;AAAA,gBAAC,oBAAA;AAAA,gBAAA;AAAA,kBACC,YAAc,EAAA,EAAE,eAAiB,EAAA,KAAA,CAAM,eAAgB;AAAA;AAAA;AACzD;AAAA,WAEJ;AAAA,8BACC,KAAM,EAAA,EAAA,IAAA,EAAK,cAAa,OAAS,kBAAA,GAAA,CAAC,qBAAkB,CAAI,EAAA,CAAA;AAAA,8BACxD,KAAM,EAAA,EAAA,IAAA,EAAK,QAAO,OAAS,kBAAA,GAAA,CAAC,eAAY,CAAI,EAAA;AAAA;AAAA;AAAA;AAC/C,GACF,EAAA,CAAA;AAEJ;AAEa,MAAA,iCAAA,GAAoC,CAAC,KAAuB,KAAA;AACvE,EAAA,OAAO,aAAc,iBAAA,GAAA,CAAC,MAAQ,EAAA,EAAA,GAAG,OAAO,CAAE,CAAA;AAC5C;;;;"}
@@ -1,25 +1,22 @@
1
- import { jsx, jsxs } from 'react/jsx-runtime';
1
+ import { jsxs, jsx } from 'react/jsx-runtime';
2
2
  import { useState } from 'react';
3
3
  import MDEditor from '@uiw/react-md-editor';
4
4
  import { DateTime } from 'luxon';
5
5
  import slugify from 'slugify';
6
- import { InfoCard } from '@backstage/core-components';
7
- import { useApi, identityApiRef } from '@backstage/core-plugin-api';
8
- import { announcementsApiRef, useAnnouncementsTranslation } from '@backstage-community/plugin-announcements-react';
9
- import CategoryInput from './CategoryInput.esm.js';
6
+ import { useApi, identityApiRef, alertApiRef } from '@backstage/core-plugin-api';
7
+ import { Card, CardHeader, Text, CardBody, Box, Grid, TextField, Flex, Button, Switch } from '@backstage/ui';
8
+ import { RiAddLine, RiSave2Line } from '@remixicon/react';
9
+ import { announcementsApiRef, useAnnouncementsTranslation, useCategories, useTags } from '@backstage-community/plugin-announcements-react';
10
+ import '@backstage/frontend-plugin-api';
11
+ import '../../../../../routes.esm.js';
12
+ import '@backstage/plugin-catalog-react';
13
+ import 'react-router-dom';
14
+ import { CategorySelectInput } from '../../../shared/CategorySelectInput/CategorySelectInput.esm.js';
15
+ import { TagsSelectInput } from '../../../shared/TagsSelectInput/TagsSelectInput.esm.js';
16
+ import { CreateCategoryDialog } from '../../categories/CreateCategoryDialog.esm.js';
17
+ import { CreateTagDialog } from '../../tags/CreateTagDialog.esm.js';
10
18
  import OnBehalfTeamDropdown from './OnBehalfTeamDropdown.esm.js';
11
- import TagsInput from './TagsInput.esm.js';
12
- import Box from '@mui/material/Box';
13
- import Button from '@mui/material/Button';
14
- import Divider from '@mui/material/Divider';
15
- import FormControlLabel from '@mui/material/FormControlLabel';
16
- import FormGroup from '@mui/material/FormGroup';
17
- import Grid from '@mui/material/Grid';
18
- import Paper from '@mui/material/Paper';
19
- import SaveAltIcon from '@mui/icons-material/SaveAlt';
20
- import Switch from '@mui/material/Switch';
21
- import TextField from '@mui/material/TextField';
22
- import Typography from '@mui/material/Typography';
19
+ import MuiTextField from '@mui/material/TextField';
23
20
 
24
21
  const AnnouncementForm = ({
25
22
  initialData,
@@ -27,63 +24,114 @@ const AnnouncementForm = ({
27
24
  }) => {
28
25
  const identityApi = useApi(identityApiRef);
29
26
  const announcementsApi = useApi(announcementsApiRef);
27
+ const alertApi = useApi(alertApiRef);
30
28
  const { t } = useAnnouncementsTranslation();
29
+ const {
30
+ categories,
31
+ loading: categoriesLoading,
32
+ retry: refreshCategories
33
+ } = useCategories();
34
+ const { tags, loading: tagsLoading, retry: refreshTags } = useTags();
31
35
  const formattedStartAt = initialData.start_at ? DateTime.fromISO(initialData.start_at).toISODate() : DateTime.now().toISODate();
32
36
  const formattedUntilDate = initialData.until_date ? DateTime.fromISO(initialData.until_date).toISODate() : DateTime.now().endOf("day").plus({ days: 7 }).toISODate();
33
37
  const [form, setForm] = useState({
34
38
  ...initialData,
35
39
  active: initialData.active ?? true,
36
- category: initialData.category?.slug,
40
+ category: initialData.category ?? null,
37
41
  start_at: formattedStartAt || "",
38
42
  until_date: formattedUntilDate || "",
39
- tags: initialData.tags?.map((tag) => tag.slug) || void 0,
43
+ tags: initialData.tags ?? null,
40
44
  sendNotification: initialData.sendNotification ?? false
41
45
  });
42
46
  const [loading, setLoading] = useState(false);
43
47
  const [onBehalfOfSelectedTeam, setOnBehalfOfSelectedTeam] = useState(
44
48
  initialData.on_behalf_of || ""
45
49
  );
46
- const handleChange = (event) => {
47
- setForm({
48
- ...form,
49
- [event.target.id]: event.target.value
50
- });
50
+ const [showCreateCategoryDialog, setShowCreateCategoryDialog] = useState(false);
51
+ const [showCreateTagDialog, setShowCreateTagDialog] = useState(false);
52
+ const handleCreateCategory = async (request) => {
53
+ const slugifiedTitle = slugify(request.title.trim(), { lower: true });
54
+ const existingCategory = categories.find(
55
+ (cat) => cat.slug === slugifiedTitle
56
+ );
57
+ if (existingCategory) {
58
+ alertApi.post({
59
+ message: t("categoriesForm.errors.alreadyExists"),
60
+ severity: "warning"
61
+ });
62
+ setForm((prevForm) => ({
63
+ ...prevForm,
64
+ category: existingCategory
65
+ }));
66
+ setShowCreateCategoryDialog(false);
67
+ } else {
68
+ try {
69
+ await announcementsApi.createCategory(request);
70
+ alertApi.post({
71
+ message: t("newCategoryDialog.createdMessage"),
72
+ severity: "success"
73
+ });
74
+ setShowCreateCategoryDialog(false);
75
+ refreshCategories();
76
+ setForm((prevForm) => ({
77
+ ...prevForm,
78
+ category: {
79
+ title: request.title,
80
+ slug: slugifiedTitle
81
+ }
82
+ }));
83
+ } catch (err) {
84
+ alertApi.post({ message: err.message, severity: "error" });
85
+ }
86
+ }
51
87
  };
52
- const handleChangeActive = (event) => {
53
- setForm({
54
- ...form,
55
- [event.target.name]: event.target.checked
56
- });
88
+ const handleCreateTag = async (request) => {
89
+ const slugifiedTitle = slugify(request.title.trim(), { lower: true });
90
+ const existingTag = tags?.find((tag) => tag.slug === slugifiedTitle);
91
+ if (existingTag) {
92
+ alertApi.post({
93
+ message: t("tagsForm.errors.alreadyExists"),
94
+ severity: "warning"
95
+ });
96
+ setForm((prevForm) => ({
97
+ ...prevForm,
98
+ tags: [...prevForm.tags ?? [], existingTag]
99
+ }));
100
+ setShowCreateTagDialog(false);
101
+ } else {
102
+ try {
103
+ await announcementsApi.createTag(request);
104
+ alertApi.post({
105
+ message: t("newTagDialog.createdMessage"),
106
+ severity: "success"
107
+ });
108
+ setShowCreateTagDialog(false);
109
+ refreshTags();
110
+ const newTag = { title: request.title, slug: slugifiedTitle };
111
+ setForm((prevForm) => ({
112
+ ...prevForm,
113
+ tags: [...prevForm.tags ?? [], newTag]
114
+ }));
115
+ } catch (err) {
116
+ alertApi.post({ message: err.message, severity: "error" });
117
+ }
118
+ }
57
119
  };
58
120
  const handleSubmit = async (event) => {
59
121
  setLoading(true);
60
122
  event.preventDefault();
61
123
  const userIdentity = await identityApi.getBackstageIdentity();
62
- if (form.tags && form.tags.length > 0) {
63
- const existingTags = await announcementsApi.tags();
64
- const processedTags = [];
65
- for (const tagValue of form.tags) {
66
- const slugifiedTag = slugify(tagValue.trim(), { lower: true });
67
- if (existingTags.some((tag) => tag.slug === slugifiedTag)) {
68
- processedTags.push(slugifiedTag);
69
- } else {
70
- try {
71
- await announcementsApi.createTag({ title: tagValue });
72
- processedTags.push(slugifiedTag);
73
- } catch (error) {
74
- if (error.status === 409) {
75
- processedTags.push(slugifiedTag);
76
- } else {
77
- throw error;
78
- }
79
- }
80
- }
81
- }
82
- form.tags = processedTags;
83
- }
84
- const { id, created_at, ...announcementData } = form;
124
+ const {
125
+ id,
126
+ created_at,
127
+ category,
128
+ tags: formTags,
129
+ ...announcementData
130
+ } = form;
85
131
  const createRequest = {
86
132
  ...announcementData,
133
+ category: category?.slug,
134
+ tags: formTags?.map((tag) => tag.slug),
87
135
  publisher: userIdentity.userEntityRef,
88
136
  on_behalf_of: onBehalfOfSelectedTeam
89
137
  };
@@ -95,68 +143,88 @@ const AnnouncementForm = ({
95
143
  setLoading(false);
96
144
  }
97
145
  };
98
- return /* @__PURE__ */ jsx(InfoCard, { children: /* @__PURE__ */ jsxs(Box, { p: 3, children: [
99
- /* @__PURE__ */ jsx(Typography, { variant: "h5", gutterBottom: true, children: initialData.title ? t("announcementForm.editAnnouncement") : t("announcementForm.newAnnouncement") }),
100
- /* @__PURE__ */ jsx(Divider, { sx: { mb: 3 } }),
101
- /* @__PURE__ */ jsx("form", { onSubmit: handleSubmit, children: /* @__PURE__ */ jsxs(Grid, { container: true, spacing: 3, children: [
102
- /* @__PURE__ */ jsx(Grid, { item: true, xs: 12, children: /* @__PURE__ */ jsx(
146
+ return /* @__PURE__ */ jsxs(Card, { children: [
147
+ /* @__PURE__ */ jsx(CardHeader, { children: /* @__PURE__ */ jsx(Text, { variant: "title-small", children: initialData.title ? t("announcementForm.editAnnouncement") : t("announcementForm.newAnnouncement") }) }),
148
+ /* @__PURE__ */ jsx(CardBody, { children: /* @__PURE__ */ jsx(Box, { p: "3", children: /* @__PURE__ */ jsx("form", { onSubmit: handleSubmit, children: /* @__PURE__ */ jsxs(Grid.Root, { columns: "12", children: [
149
+ /* @__PURE__ */ jsx(Grid.Item, { colSpan: "12", children: /* @__PURE__ */ jsx(
103
150
  TextField,
104
151
  {
105
- id: "title",
106
152
  label: t("announcementForm.title"),
107
153
  value: form.title,
108
- onChange: handleChange,
109
- variant: "outlined",
110
- fullWidth: true,
111
- required: true
154
+ onChange: (title) => setForm({ ...form, title }),
155
+ isRequired: true
112
156
  }
113
157
  ) }),
114
- /* @__PURE__ */ jsx(Grid, { item: true, xs: 12, children: /* @__PURE__ */ jsx(
158
+ /* @__PURE__ */ jsx(Grid.Item, { colSpan: "12", children: /* @__PURE__ */ jsx(
115
159
  TextField,
116
160
  {
117
- id: "excerpt",
118
161
  label: t("announcementForm.excerpt"),
119
162
  value: form.excerpt,
120
- onChange: handleChange,
121
- variant: "outlined",
122
- fullWidth: true,
123
- required: true,
124
- multiline: true
125
- }
126
- ) }),
127
- /* @__PURE__ */ jsx(Grid, { item: true, xs: 12, children: /* @__PURE__ */ jsx(
128
- Paper,
129
- {
130
- variant: "outlined",
131
- sx: { borderRadius: 2, borderColor: "divider", p: 2 },
132
- children: /* @__PURE__ */ jsx(
133
- MDEditor,
134
- {
135
- value: form.body,
136
- style: { minHeight: "30rem" },
137
- onChange: (value) => setForm({ ...form, ...{ body: value || "" } })
138
- }
139
- )
163
+ onChange: (excerpt) => setForm({ ...form, excerpt }),
164
+ isRequired: true
140
165
  }
141
166
  ) }),
142
- /* @__PURE__ */ jsx(Grid, { item: true, xs: 12, sm: 6, children: /* @__PURE__ */ jsx(
143
- CategoryInput,
167
+ /* @__PURE__ */ jsx(Grid.Item, { colSpan: "12", children: /* @__PURE__ */ jsx(
168
+ MDEditor,
144
169
  {
145
- setForm,
146
- form,
147
- initialValue: initialData.category?.title ?? ""
170
+ value: form.body,
171
+ style: { minHeight: "30rem" },
172
+ onChange: (value) => setForm({ ...form, ...{ body: value || "" } })
148
173
  }
149
174
  ) }),
150
- /* @__PURE__ */ jsx(Grid, { item: true, xs: 12, sm: 6, children: /* @__PURE__ */ jsx(
175
+ /* @__PURE__ */ jsx(Grid.Item, { colSpan: { xs: "12", md: "4" }, children: /* @__PURE__ */ jsxs(Flex, { gap: "2", align: "end", children: [
176
+ /* @__PURE__ */ jsx(Box, { style: { flex: 1 }, children: /* @__PURE__ */ jsx(
177
+ CategorySelectInput,
178
+ {
179
+ initialCategory: form.category ?? void 0,
180
+ categories,
181
+ isLoading: categoriesLoading,
182
+ setCategory: (category) => setForm((prev) => ({ ...prev, category }))
183
+ }
184
+ ) }),
185
+ /* @__PURE__ */ jsx(
186
+ Button,
187
+ {
188
+ "data-testid": "create-category-icon-button",
189
+ variant: "secondary",
190
+ size: "small",
191
+ iconStart: /* @__PURE__ */ jsx(RiAddLine, {}),
192
+ onClick: () => setShowCreateCategoryDialog(true),
193
+ "aria-label": t("admin.categoriesContent.createButton")
194
+ }
195
+ )
196
+ ] }) }),
197
+ /* @__PURE__ */ jsx(Grid.Item, { colSpan: { xs: "12", md: "4" }, children: /* @__PURE__ */ jsx(
151
198
  OnBehalfTeamDropdown,
152
199
  {
153
200
  selectedTeam: onBehalfOfSelectedTeam,
154
201
  onChange: setOnBehalfOfSelectedTeam
155
202
  }
156
203
  ) }),
157
- /* @__PURE__ */ jsx(Grid, { item: true, xs: 12, sm: 6, children: /* @__PURE__ */ jsx(TagsInput, { setForm, form }) }),
158
- /* @__PURE__ */ jsx(Grid, { item: true, xs: 12, sm: 3, children: /* @__PURE__ */ jsx(
159
- TextField,
204
+ /* @__PURE__ */ jsx(Grid.Item, { colSpan: { xs: "12", md: "4" }, children: /* @__PURE__ */ jsxs(Flex, { gap: "2", align: "end", children: [
205
+ /* @__PURE__ */ jsx(Box, { style: { flex: 1 }, children: /* @__PURE__ */ jsx(
206
+ TagsSelectInput,
207
+ {
208
+ initialTags: form.tags ?? void 0,
209
+ tags,
210
+ isLoading: tagsLoading,
211
+ setTags: (selectedTags) => setForm((prev) => ({ ...prev, tags: selectedTags }))
212
+ }
213
+ ) }),
214
+ /* @__PURE__ */ jsx(
215
+ Button,
216
+ {
217
+ "data-testid": "create-tag-icon-button",
218
+ variant: "secondary",
219
+ size: "small",
220
+ iconStart: /* @__PURE__ */ jsx(RiAddLine, {}),
221
+ onClick: () => setShowCreateTagDialog(true),
222
+ "aria-label": t("admin.tagsContent.createButton")
223
+ }
224
+ )
225
+ ] }) }),
226
+ /* @__PURE__ */ jsx(Grid.Item, { colSpan: { xs: "12", md: "4" }, children: /* @__PURE__ */ jsx(
227
+ MuiTextField,
160
228
  {
161
229
  variant: "outlined",
162
230
  label: t("announcementForm.startAt"),
@@ -172,8 +240,8 @@ const AnnouncementForm = ({
172
240
  })
173
241
  }
174
242
  ) }),
175
- /* @__PURE__ */ jsx(Grid, { item: true, xs: 12, sm: 3, children: /* @__PURE__ */ jsx(
176
- TextField,
243
+ /* @__PURE__ */ jsx(Grid.Item, { colSpan: { xs: "12", md: "4" }, children: /* @__PURE__ */ jsx(
244
+ MuiTextField,
177
245
  {
178
246
  variant: "outlined",
179
247
  label: t("announcementForm.untilDate"),
@@ -191,53 +259,53 @@ const AnnouncementForm = ({
191
259
  }
192
260
  }
193
261
  ) }),
194
- /* @__PURE__ */ jsx(Grid, { item: true, xs: 12, children: /* @__PURE__ */ jsx(Divider, {}) }),
195
- /* @__PURE__ */ jsx(Grid, { item: true, xs: 12, children: /* @__PURE__ */ jsxs(FormGroup, { row: true, style: { justifyContent: "flex-end" }, children: [
262
+ /* @__PURE__ */ jsx(Grid.Item, { colSpan: "12", children: /* @__PURE__ */ jsxs(Flex, { justify: "end", children: [
196
263
  /* @__PURE__ */ jsx(
197
- FormControlLabel,
264
+ Switch,
198
265
  {
199
- control: /* @__PURE__ */ jsx(
200
- Switch,
201
- {
202
- name: "active",
203
- checked: form.active,
204
- onChange: handleChangeActive,
205
- color: "primary"
206
- }
207
- ),
208
- label: t("announcementForm.active")
266
+ name: "active",
267
+ label: t("announcementForm.active"),
268
+ isSelected: form.active,
269
+ onChange: (isSelected) => setForm({ ...form, active: isSelected })
209
270
  }
210
271
  ),
211
272
  /* @__PURE__ */ jsx(
212
- FormControlLabel,
273
+ Switch,
213
274
  {
214
- control: /* @__PURE__ */ jsx(
215
- Switch,
216
- {
217
- name: "sendNotification",
218
- checked: form.sendNotification,
219
- onChange: handleChangeActive,
220
- color: "primary"
221
- }
222
- ),
223
- label: "Send Notification"
275
+ name: "sendNotification",
276
+ label: t("announcementForm.sendNotification"),
277
+ isSelected: form.sendNotification,
278
+ onChange: (isSelected) => setForm({ ...form, sendNotification: isSelected })
224
279
  }
225
280
  ),
226
281
  /* @__PURE__ */ jsx(
227
282
  Button,
228
283
  {
229
- variant: "contained",
230
- color: "primary",
231
284
  type: "submit",
232
- disabled: loading || !form.body,
233
- size: "large",
234
- startIcon: /* @__PURE__ */ jsx(SaveAltIcon, {}),
285
+ isDisabled: loading || !form.body,
286
+ iconStart: /* @__PURE__ */ jsx(RiSave2Line, {}),
235
287
  children: t("announcementForm.submit")
236
288
  }
237
289
  )
238
290
  ] }) })
239
- ] }) })
240
- ] }) });
291
+ ] }) }) }) }),
292
+ /* @__PURE__ */ jsx(
293
+ CreateCategoryDialog,
294
+ {
295
+ open: showCreateCategoryDialog,
296
+ onConfirm: handleCreateCategory,
297
+ onCancel: () => setShowCreateCategoryDialog(false)
298
+ }
299
+ ),
300
+ /* @__PURE__ */ jsx(
301
+ CreateTagDialog,
302
+ {
303
+ open: showCreateTagDialog,
304
+ onConfirm: handleCreateTag,
305
+ onCancel: () => setShowCreateTagDialog(false)
306
+ }
307
+ )
308
+ ] });
241
309
  };
242
310
 
243
311
  export { AnnouncementForm };
@@ -1 +1 @@
1
- {"version":3,"file":"AnnouncementForm.esm.js","sources":["../../../../../../src/alpha/components/admin/announcements/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 { DateTime } from 'luxon';\nimport slugify from 'slugify';\nimport { InfoCard } from '@backstage/core-components';\nimport { identityApiRef, useApi } from '@backstage/core-plugin-api';\n\nimport {\n CreateAnnouncementRequest,\n useAnnouncementsTranslation,\n announcementsApiRef,\n} from '@backstage-community/plugin-announcements-react';\nimport { Announcement } from '@backstage-community/plugin-announcements-common';\n\nimport CategoryInput from './CategoryInput';\nimport OnBehalfTeamDropdown from './OnBehalfTeamDropdown';\nimport TagsInput from './TagsInput';\n\nimport Box from '@mui/material/Box';\nimport Button from '@mui/material/Button';\nimport Divider from '@mui/material/Divider';\nimport FormControlLabel from '@mui/material/FormControlLabel';\nimport FormGroup from '@mui/material/FormGroup';\nimport Grid from '@mui/material/Grid';\nimport Paper from '@mui/material/Paper';\nimport SaveAltIcon from '@mui/icons-material/SaveAlt';\nimport Switch from '@mui/material/Switch';\nimport TextField from '@mui/material/TextField';\nimport Typography from '@mui/material/Typography';\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 formattedUntilDate = initialData.until_date\n ? DateTime.fromISO(initialData.until_date).toISODate()\n : DateTime.now().endOf('day').plus({ days: 7 }).toISODate();\n\n const [form, setForm] = useState({\n ...initialData,\n active: initialData.active ?? true,\n category: initialData.category?.slug,\n start_at: formattedStartAt || '',\n until_date: formattedUntilDate || '',\n tags: initialData.tags?.map(tag => tag.slug) || undefined,\n sendNotification: initialData.sendNotification ?? false,\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 <Divider sx={{ mb: 3 }} />\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}>\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 <Paper\n variant=\"outlined\"\n sx={{ borderRadius: 2, borderColor: 'divider', p: 2 }}\n >\n <MDEditor\n value={form.body}\n style={{ minHeight: '30rem' }}\n onChange={value =>\n setForm({ ...form, ...{ body: value || '' } })\n }\n />\n </Paper>\n </Grid>\n\n <Grid item xs={12} sm={6}>\n <CategoryInput\n setForm={setForm}\n form={form}\n initialValue={initialData.category?.title ?? ''}\n />\n </Grid>\n\n <Grid item xs={12} sm={6}>\n <OnBehalfTeamDropdown\n selectedTeam={onBehalfOfSelectedTeam}\n onChange={setOnBehalfOfSelectedTeam}\n />\n </Grid>\n\n <Grid item xs={12} sm={6}>\n <TagsInput setForm={setForm} form={form} />\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 fullWidth\n onChange={e =>\n setForm({\n ...form,\n start_at: e.target.value,\n })\n }\n />\n </Grid>\n\n <Grid item xs={12} sm={3}>\n <TextField\n variant=\"outlined\"\n label={t('announcementForm.untilDate')}\n id=\"until-date\"\n type=\"date\"\n value={form.until_date}\n InputLabelProps={{ shrink: true }}\n fullWidth\n onChange={e =>\n setForm({\n ...form,\n until_date: e.target.value,\n })\n }\n inputProps={{\n min: DateTime.fromISO(form.start_at)\n .endOf('day')\n .plus({ days: 1 })\n .toISODate(),\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 <FormControlLabel\n control={\n <Switch\n name=\"sendNotification\"\n checked={form.sendNotification}\n onChange={handleChangeActive}\n color=\"primary\"\n />\n }\n label=\"Send Notification\"\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/alpha/components/admin/announcements/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 FormEvent } from 'react';\nimport MDEditor from '@uiw/react-md-editor';\nimport { DateTime } from 'luxon';\nimport slugify from 'slugify';\nimport {\n identityApiRef,\n useApi,\n alertApiRef,\n} from '@backstage/core-plugin-api';\nimport {\n Box,\n Button,\n Card,\n CardBody,\n CardHeader,\n Flex,\n Grid,\n Text,\n TextField,\n Switch,\n} from '@backstage/ui';\nimport { RiSave2Line, RiAddLine } from '@remixicon/react';\nimport {\n CreateAnnouncementRequest,\n CreateCategoryRequest,\n CreateTagRequest,\n useAnnouncementsTranslation,\n announcementsApiRef,\n useCategories,\n useTags,\n} from '@backstage-community/plugin-announcements-react';\nimport {\n Announcement,\n Category,\n Tag,\n} from '@backstage-community/plugin-announcements-common';\n\nimport { CategorySelectInput, TagsSelectInput } from '../../../shared';\nimport { CreateCategoryDialog } from '../../categories';\nimport { CreateTagDialog } from '../../tags';\nimport OnBehalfTeamDropdown from './OnBehalfTeamDropdown';\n\nimport MuiTextField from '@mui/material/TextField';\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 alertApi = useApi(alertApiRef);\n const { t } = useAnnouncementsTranslation();\n const {\n categories,\n loading: categoriesLoading,\n retry: refreshCategories,\n } = useCategories();\n const { tags, loading: tagsLoading, retry: refreshTags } = useTags();\n\n const formattedStartAt = initialData.start_at\n ? DateTime.fromISO(initialData.start_at).toISODate()\n : DateTime.now().toISODate();\n\n const formattedUntilDate = initialData.until_date\n ? DateTime.fromISO(initialData.until_date).toISODate()\n : DateTime.now().endOf('day').plus({ days: 7 }).toISODate();\n\n const [form, setForm] = useState({\n ...initialData,\n active: initialData.active ?? true,\n category: initialData.category ?? null,\n start_at: formattedStartAt || '',\n until_date: formattedUntilDate || '',\n tags: initialData.tags ?? null,\n sendNotification: initialData.sendNotification ?? false,\n });\n const [loading, setLoading] = useState(false);\n const [onBehalfOfSelectedTeam, setOnBehalfOfSelectedTeam] = useState(\n initialData.on_behalf_of || '',\n );\n const [showCreateCategoryDialog, setShowCreateCategoryDialog] =\n useState(false);\n const [showCreateTagDialog, setShowCreateTagDialog] = useState(false);\n\n const handleCreateCategory = async (request: CreateCategoryRequest) => {\n const slugifiedTitle = slugify(request.title.trim(), { lower: true });\n const existingCategory = categories.find(\n cat => cat.slug === slugifiedTitle,\n );\n\n if (existingCategory) {\n alertApi.post({\n message: t('categoriesForm.errors.alreadyExists'),\n severity: 'warning',\n });\n\n // Select the existing category in the form\n setForm(prevForm => ({\n ...prevForm,\n category: existingCategory,\n }));\n\n setShowCreateCategoryDialog(false);\n } else {\n try {\n await announcementsApi.createCategory(request);\n\n alertApi.post({\n message: t('newCategoryDialog.createdMessage'),\n severity: 'success',\n });\n\n setShowCreateCategoryDialog(false);\n refreshCategories();\n\n // Select the new category in the form\n setForm(prevForm => ({\n ...prevForm,\n category: {\n title: request.title,\n slug: slugifiedTitle,\n },\n }));\n } catch (err) {\n alertApi.post({ message: (err as Error).message, severity: 'error' });\n }\n }\n };\n\n const handleCreateTag = async (request: CreateTagRequest) => {\n const slugifiedTitle = slugify(request.title.trim(), { lower: true });\n const existingTag = tags?.find(tag => tag.slug === slugifiedTitle);\n\n if (existingTag) {\n alertApi.post({\n message: t('tagsForm.errors.alreadyExists'),\n severity: 'warning',\n });\n\n setForm(prevForm => ({\n ...prevForm,\n tags: [...(prevForm.tags ?? []), existingTag],\n }));\n\n setShowCreateTagDialog(false);\n } else {\n try {\n await announcementsApi.createTag(request);\n\n alertApi.post({\n message: t('newTagDialog.createdMessage'),\n severity: 'success',\n });\n\n setShowCreateTagDialog(false);\n refreshTags();\n\n const newTag = { title: request.title, slug: slugifiedTitle };\n\n setForm(prevForm => ({\n ...prevForm,\n tags: [...(prevForm.tags ?? []), newTag],\n }));\n } catch (err) {\n alertApi.post({ message: (err as Error).message, severity: 'error' });\n }\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 const {\n id,\n created_at,\n category,\n tags: formTags,\n ...announcementData\n } = form;\n\n const createRequest: CreateAnnouncementRequest = {\n ...announcementData,\n category: category?.slug,\n tags: formTags?.map(tag => tag.slug),\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 <Card>\n <CardHeader>\n <Text variant=\"title-small\">\n {initialData.title\n ? t('announcementForm.editAnnouncement')\n : t('announcementForm.newAnnouncement')}\n </Text>\n </CardHeader>\n\n <CardBody>\n <Box p=\"3\">\n <form onSubmit={handleSubmit}>\n <Grid.Root columns=\"12\">\n <Grid.Item colSpan=\"12\">\n <TextField\n label={t('announcementForm.title')}\n value={form.title}\n onChange={title => setForm({ ...form, title })}\n isRequired\n />\n </Grid.Item>\n\n <Grid.Item colSpan=\"12\">\n <TextField\n label={t('announcementForm.excerpt')}\n value={form.excerpt}\n onChange={excerpt => setForm({ ...form, excerpt })}\n isRequired\n />\n </Grid.Item>\n\n <Grid.Item colSpan=\"12\">\n <MDEditor\n value={form.body}\n style={{ minHeight: '30rem' }}\n onChange={value =>\n setForm({ ...form, ...{ body: value || '' } })\n }\n />\n </Grid.Item>\n\n <Grid.Item colSpan={{ xs: '12', md: '4' }}>\n <Flex gap=\"2\" align=\"end\">\n <Box style={{ flex: 1 }}>\n <CategorySelectInput\n initialCategory={form.category ?? undefined}\n categories={categories}\n isLoading={categoriesLoading}\n setCategory={(category: Category | null) =>\n setForm(prev => ({ ...prev, category }))\n }\n />\n </Box>\n\n <Button\n data-testid=\"create-category-icon-button\"\n variant=\"secondary\"\n size=\"small\"\n iconStart={<RiAddLine />}\n onClick={() => setShowCreateCategoryDialog(true)}\n aria-label={t('admin.categoriesContent.createButton')}\n />\n </Flex>\n </Grid.Item>\n\n <Grid.Item colSpan={{ xs: '12', md: '4' }}>\n <OnBehalfTeamDropdown\n selectedTeam={onBehalfOfSelectedTeam}\n onChange={setOnBehalfOfSelectedTeam}\n />\n </Grid.Item>\n\n <Grid.Item colSpan={{ xs: '12', md: '4' }}>\n <Flex gap=\"2\" align=\"end\">\n <Box style={{ flex: 1 }}>\n <TagsSelectInput\n initialTags={form.tags ?? undefined}\n tags={tags}\n isLoading={tagsLoading}\n setTags={(selectedTags: Tag[] | null) =>\n setForm(prev => ({ ...prev, tags: selectedTags }))\n }\n />\n </Box>\n\n <Button\n data-testid=\"create-tag-icon-button\"\n variant=\"secondary\"\n size=\"small\"\n iconStart={<RiAddLine />}\n onClick={() => setShowCreateTagDialog(true)}\n aria-label={t('admin.tagsContent.createButton')}\n />\n </Flex>\n </Grid.Item>\n\n <Grid.Item colSpan={{ xs: '12', md: '4' }}>\n <MuiTextField\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 fullWidth\n onChange={e =>\n setForm({\n ...form,\n start_at: e.target.value,\n })\n }\n />\n </Grid.Item>\n\n <Grid.Item colSpan={{ xs: '12', md: '4' }}>\n <MuiTextField\n variant=\"outlined\"\n label={t('announcementForm.untilDate')}\n id=\"until-date\"\n type=\"date\"\n value={form.until_date}\n InputLabelProps={{ shrink: true }}\n fullWidth\n onChange={e =>\n setForm({\n ...form,\n until_date: e.target.value,\n })\n }\n inputProps={{\n min: DateTime.fromISO(form.start_at)\n .endOf('day')\n .plus({ days: 1 })\n .toISODate(),\n }}\n />\n </Grid.Item>\n\n <Grid.Item colSpan=\"12\">\n <Flex justify=\"end\">\n <Switch\n name=\"active\"\n label={t('announcementForm.active')}\n isSelected={form.active}\n onChange={isSelected =>\n setForm({ ...form, active: isSelected })\n }\n />\n <Switch\n name=\"sendNotification\"\n label={t('announcementForm.sendNotification')}\n isSelected={form.sendNotification}\n onChange={isSelected =>\n setForm({ ...form, sendNotification: isSelected })\n }\n />\n <Button\n type=\"submit\"\n isDisabled={loading || !form.body}\n iconStart={<RiSave2Line />}\n >\n {t('announcementForm.submit')}\n </Button>\n </Flex>\n </Grid.Item>\n </Grid.Root>\n </form>\n </Box>\n </CardBody>\n\n <CreateCategoryDialog\n open={showCreateCategoryDialog}\n onConfirm={handleCreateCategory}\n onCancel={() => setShowCreateCategoryDialog(false)}\n />\n\n <CreateTagDialog\n open={showCreateTagDialog}\n onConfirm={handleCreateTag}\n onCancel={() => setShowCreateTagDialog(false)}\n />\n </Card>\n );\n};\n"],"names":[],"mappings":"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"}