@backstage-community/plugin-announcements 1.4.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 (70) hide show
  1. package/CHANGELOG.md +56 -0
  2. package/dist/{alpha/Router.esm.js → Router.esm.js} +14 -7
  3. package/dist/Router.esm.js.map +1 -0
  4. package/dist/alpha/components/admin/announcements/AnnouncementForm/AnnouncementForm.esm.js +312 -0
  5. package/dist/alpha/components/admin/announcements/AnnouncementForm/AnnouncementForm.esm.js.map +1 -0
  6. package/dist/alpha/components/admin/announcements/AnnouncementForm/OnBehalfTeamDropdown.esm.js +72 -0
  7. package/dist/alpha/components/admin/announcements/AnnouncementForm/OnBehalfTeamDropdown.esm.js.map +1 -0
  8. package/dist/alpha/components/admin/announcements/AnnouncementsContent.esm.js +11 -44
  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/announcements/ViewAnnouncementPage.esm.js +0 -30
  19. package/dist/alpha/components/announcements/ViewAnnouncementPage.esm.js.map +1 -1
  20. package/dist/alpha/components/shared/CategorySelectInput/CategorySelectInput.esm.js +4 -3
  21. package/dist/alpha/components/shared/CategorySelectInput/CategorySelectInput.esm.js.map +1 -1
  22. package/dist/alpha/components/shared/TagsSelectInput/TagsSelectInput.esm.js +4 -3
  23. package/dist/alpha/components/shared/TagsSelectInput/TagsSelectInput.esm.js.map +1 -1
  24. package/dist/alpha/pages.esm.js +1 -1
  25. package/dist/alpha/pages.esm.js.map +1 -1
  26. package/dist/alpha/search.esm.js +5 -3
  27. package/dist/alpha/search.esm.js.map +1 -1
  28. package/dist/alpha.d.ts +2 -2
  29. package/dist/index.d.ts +1 -80
  30. package/dist/index.esm.js +1 -1
  31. package/dist/plugin.esm.js +2 -18
  32. package/dist/plugin.esm.js.map +1 -1
  33. package/package.json +12 -12
  34. package/dist/alpha/Router.esm.js.map +0 -1
  35. package/dist/components/Admin/AdminPortal/AdminPortal.esm.js +0 -65
  36. package/dist/components/Admin/AdminPortal/AdminPortal.esm.js.map +0 -1
  37. package/dist/components/Admin/AnnouncementsContent/AnnouncementForm/AnnouncementForm.esm.js +0 -244
  38. package/dist/components/Admin/AnnouncementsContent/AnnouncementForm/AnnouncementForm.esm.js.map +0 -1
  39. package/dist/components/Admin/AnnouncementsContent/AnnouncementForm/CategoryInput.esm.js +0 -82
  40. package/dist/components/Admin/AnnouncementsContent/AnnouncementForm/CategoryInput.esm.js.map +0 -1
  41. package/dist/components/Admin/AnnouncementsContent/AnnouncementForm/OnBehalfTeamDropdown.esm.js +0 -81
  42. package/dist/components/Admin/AnnouncementsContent/AnnouncementForm/OnBehalfTeamDropdown.esm.js.map +0 -1
  43. package/dist/components/Admin/AnnouncementsContent/AnnouncementForm/TagsInput.esm.js +0 -100
  44. package/dist/components/Admin/AnnouncementsContent/AnnouncementForm/TagsInput.esm.js.map +0 -1
  45. package/dist/components/Admin/AnnouncementsContent/AnnouncementsContent.esm.js +0 -305
  46. package/dist/components/Admin/AnnouncementsContent/AnnouncementsContent.esm.js.map +0 -1
  47. package/dist/components/Admin/CategoriesContent/CategoriesContent.esm.js +0 -143
  48. package/dist/components/Admin/CategoriesContent/CategoriesContent.esm.js.map +0 -1
  49. package/dist/components/Admin/TagsContent/TagsContent.esm.js +0 -158
  50. package/dist/components/Admin/TagsContent/TagsContent.esm.js.map +0 -1
  51. package/dist/components/Admin/index.esm.js +0 -3
  52. package/dist/components/Admin/index.esm.js.map +0 -1
  53. package/dist/components/Admin/shared/DeleteDialog/DeleteDialog.esm.js +0 -30
  54. package/dist/components/Admin/shared/DeleteDialog/DeleteDialog.esm.js.map +0 -1
  55. package/dist/components/Admin/shared/DeleteDialog/useDeleteDialogState.esm.js +0 -29
  56. package/dist/components/Admin/shared/DeleteDialog/useDeleteDialogState.esm.js.map +0 -1
  57. package/dist/components/Admin/shared/TitleForm/TitleForm.esm.js +0 -71
  58. package/dist/components/Admin/shared/TitleForm/TitleForm.esm.js.map +0 -1
  59. package/dist/components/AnnouncementPage/AnnouncementPage.esm.js +0 -99
  60. package/dist/components/AnnouncementPage/AnnouncementPage.esm.js.map +0 -1
  61. package/dist/components/AnnouncementsPage/AnnouncementsPage.esm.js +0 -197
  62. package/dist/components/AnnouncementsPage/AnnouncementsPage.esm.js.map +0 -1
  63. package/dist/components/AnnouncementsPage/ContextMenu.esm.js +0 -60
  64. package/dist/components/AnnouncementsPage/ContextMenu.esm.js.map +0 -1
  65. package/dist/components/AnnouncementsTimeline/AnnouncementsTimeline.esm.js +0 -75
  66. package/dist/components/AnnouncementsTimeline/AnnouncementsTimeline.esm.js.map +0 -1
  67. package/dist/components/Router.esm.js +0 -75
  68. package/dist/components/Router.esm.js.map +0 -1
  69. package/dist/components/index.esm.js +0 -5
  70. package/dist/components/index.esm.js.map +0 -1
@@ -1,82 +0,0 @@
1
- import { jsx, jsxs, Fragment } from 'react/jsx-runtime';
2
- import TextField from '@mui/material/TextField';
3
- import Autocomplete, { createFilterOptions } from '@mui/material/Autocomplete';
4
- import { useCategories, useAnnouncementsTranslation } from '@backstage-community/plugin-announcements-react';
5
- import CircularProgress from '@mui/material/CircularProgress';
6
-
7
- const filter = createFilterOptions();
8
- function prepareCategoryFromInput(inputCategory, localizedCreate) {
9
- return (typeof inputCategory === "string" ? inputCategory : inputCategory.title).replace(localizedCreate ? `${localizedCreate} ` : "Create ", "").replaceAll('"', "");
10
- }
11
- function CategoryInput({
12
- setForm,
13
- form,
14
- initialValue
15
- }) {
16
- const { categories, loading: categoriesLoading } = useCategories();
17
- const { t } = useAnnouncementsTranslation();
18
- return /* @__PURE__ */ jsx(
19
- Autocomplete,
20
- {
21
- fullWidth: true,
22
- value: initialValue ?? "",
23
- onChange: async (_, newValue) => {
24
- if (!newValue) {
25
- setForm({ ...form, category: void 0 });
26
- return;
27
- }
28
- const newCategory = prepareCategoryFromInput(
29
- newValue,
30
- t("announcementForm.categoryInput.create")
31
- );
32
- setForm({ ...form, category: newCategory });
33
- },
34
- filterOptions: (options, params) => {
35
- const filtered = filter(options, params);
36
- const { inputValue } = params;
37
- const isExisting = options.some(
38
- (option) => inputValue.toLocaleLowerCase("en-US") === option.title.toLocaleLowerCase("en-US")
39
- );
40
- if (inputValue !== "" && !isExisting) {
41
- filtered.push({
42
- title: `${t(
43
- "announcementForm.categoryInput.create"
44
- )} "${inputValue}"`,
45
- slug: inputValue.toLocaleLowerCase("en-US")
46
- });
47
- }
48
- return filtered;
49
- },
50
- selectOnFocus: true,
51
- handleHomeEndKeys: true,
52
- loading: categoriesLoading,
53
- id: "category-input-field",
54
- options: categories || [],
55
- getOptionLabel: (option) => {
56
- return prepareCategoryFromInput(option);
57
- },
58
- renderOption: (props, option) => /* @__PURE__ */ jsx("li", { ...props, children: option.title }),
59
- freeSolo: true,
60
- renderInput: (params) => /* @__PURE__ */ jsx(
61
- TextField,
62
- {
63
- ...params,
64
- id: "category",
65
- label: t("announcementForm.categoryInput.label"),
66
- variant: "outlined",
67
- fullWidth: true,
68
- InputProps: {
69
- ...params.InputProps,
70
- endAdornment: /* @__PURE__ */ jsxs(Fragment, { children: [
71
- categoriesLoading ? /* @__PURE__ */ jsx(CircularProgress, { color: "inherit", size: 20 }) : null,
72
- params.InputProps.endAdornment
73
- ] })
74
- }
75
- }
76
- )
77
- }
78
- );
79
- }
80
-
81
- export { CategoryInput as default };
82
- //# sourceMappingURL=CategoryInput.esm.js.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"CategoryInput.esm.js","sources":["../../../../../src/components/Admin/AnnouncementsContent/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 until_date: string;\n sendNotification: boolean;\n updated_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 until_date: string;\n sendNotification: boolean;\n updated_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,81 +0,0 @@
1
- import { jsx, jsxs, Fragment } from 'react/jsx-runtime';
2
- import TextField from '@mui/material/TextField';
3
- import Autocomplete from '@mui/material/Autocomplete';
4
- import CircularProgress from '@mui/material/CircularProgress';
5
- import { useApi, identityApiRef } from '@backstage/core-plugin-api';
6
- import { useAnnouncementsTranslation, useCatalogEntities } from '@backstage-community/plugin-announcements-react';
7
- import useAsync from 'react-use/esm/useAsync';
8
- import { useMemo } from 'react';
9
- import { stringifyEntityRef } from '@backstage/catalog-model';
10
- import Box from '@mui/material/Box';
11
- import Typography from '@mui/material/Typography';
12
-
13
- function getTeamDisplayName(team) {
14
- if (team.kind && team.kind.toLowerCase() === "group") {
15
- return team.spec?.profile?.displayName ?? "";
16
- }
17
- return "";
18
- }
19
- function OnBehalfTeamDropdown({
20
- selectedTeam,
21
- onChange
22
- }) {
23
- const { t } = useAnnouncementsTranslation();
24
- const identityApi = useApi(identityApiRef);
25
- const { value: userOwns } = useAsync(async () => {
26
- const identity = await identityApi.getBackstageIdentity();
27
- return [identity.userEntityRef, ...identity.ownershipEntityRefs];
28
- }, [identityApi]);
29
- const { entities: teams, loading: teamsLoading } = useCatalogEntities(
30
- userOwns,
31
- // refs
32
- "",
33
- // searchTerm
34
- "Group"
35
- // kind
36
- );
37
- const teamOptions = useMemo(() => {
38
- return teams.map((team) => ({
39
- entityRef: stringifyEntityRef(team),
40
- displayName: getTeamDisplayName(team)
41
- }));
42
- }, [teams]);
43
- const selectedTeamOption = useMemo(() => {
44
- return teamOptions.find((team) => team.entityRef === selectedTeam) || null;
45
- }, [teamOptions, selectedTeam]);
46
- return /* @__PURE__ */ jsx(
47
- Autocomplete,
48
- {
49
- value: selectedTeamOption,
50
- onChange: (_, newValue) => {
51
- onChange(newValue?.entityRef || "");
52
- },
53
- options: teamOptions,
54
- getOptionLabel: (team) => team.entityRef,
55
- loading: teamsLoading,
56
- id: "team-dropdown-field",
57
- renderOption: (props, team) => /* @__PURE__ */ jsx(Box, { component: "li", ...props, children: /* @__PURE__ */ jsxs(Box, { sx: { display: "flex", flexDirection: "column" }, children: [
58
- /* @__PURE__ */ jsx(Typography, { variant: "body1", children: team.entityRef }),
59
- team.displayName && /* @__PURE__ */ jsx(Typography, { variant: "caption", color: "text.secondary", children: team.displayName })
60
- ] }) }),
61
- renderInput: (params) => /* @__PURE__ */ jsx(
62
- TextField,
63
- {
64
- ...params,
65
- id: "team",
66
- label: t("announcementForm.onBehalfOf"),
67
- InputProps: {
68
- ...params.InputProps,
69
- endAdornment: /* @__PURE__ */ jsxs(Fragment, { children: [
70
- teamsLoading ? /* @__PURE__ */ jsx(CircularProgress, { color: "inherit", size: 20 }) : null,
71
- params.InputProps.endAdornment
72
- ] })
73
- }
74
- }
75
- )
76
- }
77
- );
78
- }
79
-
80
- export { OnBehalfTeamDropdown as default };
81
- //# sourceMappingURL=OnBehalfTeamDropdown.esm.js.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"OnBehalfTeamDropdown.esm.js","sources":["../../../../../src/components/Admin/AnnouncementsContent/AnnouncementForm/OnBehalfTeamDropdown.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 TextField from '@mui/material/TextField';\nimport Autocomplete from '@mui/material/Autocomplete';\nimport CircularProgress from '@mui/material/CircularProgress';\nimport { identityApiRef, useApi } from '@backstage/core-plugin-api';\nimport {\n useAnnouncementsTranslation,\n useCatalogEntities,\n} from '@backstage-community/plugin-announcements-react';\nimport useAsync from 'react-use/esm/useAsync';\nimport { useMemo } from 'react';\nimport { stringifyEntityRef } from '@backstage/catalog-model';\nimport Box from '@mui/material/Box';\nimport Typography from '@mui/material/Typography';\n\ntype OnBehalfTeamDropdownProps = {\n selectedTeam: string;\n onChange: (team: string) => void;\n};\n\nfunction getTeamDisplayName(team: any): string {\n if (team.kind && team.kind.toLowerCase() === 'group') {\n return team.spec?.profile?.displayName ?? '';\n }\n return '';\n}\n\nexport default function OnBehalfTeamDropdown({\n selectedTeam,\n onChange,\n}: OnBehalfTeamDropdownProps) {\n const { t } = useAnnouncementsTranslation();\n const identityApi = useApi(identityApiRef);\n\n const { value: userOwns } = useAsync(async () => {\n const identity = await identityApi.getBackstageIdentity();\n return [identity.userEntityRef, ...identity.ownershipEntityRefs];\n }, [identityApi]);\n\n const { entities: teams, loading: teamsLoading } = useCatalogEntities(\n userOwns, // refs\n '', // searchTerm\n 'Group', // kind\n );\n\n const teamOptions = useMemo(() => {\n return teams.map(team => ({\n entityRef: stringifyEntityRef(team),\n displayName: getTeamDisplayName(team),\n }));\n }, [teams]);\n\n const selectedTeamOption = useMemo(() => {\n return teamOptions.find(team => team.entityRef === selectedTeam) || null;\n }, [teamOptions, selectedTeam]);\n\n return (\n <Autocomplete\n value={selectedTeamOption}\n onChange={(_, newValue) => {\n onChange(newValue?.entityRef || '');\n }}\n options={teamOptions}\n getOptionLabel={team => team.entityRef}\n loading={teamsLoading}\n id=\"team-dropdown-field\"\n renderOption={(props, team) => (\n <Box component=\"li\" {...props}>\n <Box sx={{ display: 'flex', flexDirection: 'column' }}>\n <Typography variant=\"body1\">{team.entityRef}</Typography>\n {team.displayName && (\n <Typography variant=\"caption\" color=\"text.secondary\">\n {team.displayName}\n </Typography>\n )}\n </Box>\n </Box>\n )}\n renderInput={params => (\n <TextField\n {...params}\n id=\"team\"\n label={t('announcementForm.onBehalfOf')}\n InputProps={{\n ...params.InputProps,\n endAdornment: (\n <>\n {teamsLoading ? (\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,100 +0,0 @@
1
- import { jsx, jsxs, Fragment } from 'react/jsx-runtime';
2
- import { useMemo } from 'react';
3
- import Autocomplete, { createFilterOptions } from '@mui/material/Autocomplete';
4
- import TextField from '@mui/material/TextField';
5
- import Chip from '@mui/material/Chip';
6
- import CircularProgress from '@mui/material/CircularProgress';
7
- import { useTags, useAnnouncementsTranslation } from '@backstage-community/plugin-announcements-react';
8
-
9
- const filter = createFilterOptions();
10
- function prepareTagFromInput(input) {
11
- if (typeof input === "string") {
12
- return input.toLocaleLowerCase("en-US");
13
- }
14
- if (input.inputValue) {
15
- return input.inputValue.toLocaleLowerCase("en-US");
16
- }
17
- return input.title.toLocaleLowerCase("en-US");
18
- }
19
- function TagsInput({ setForm, form }) {
20
- const { tags, loading } = useTags();
21
- const { t } = useAnnouncementsTranslation();
22
- const createLabel = t("announcementForm.tagsInput.create");
23
- const existingTags = useMemo(() => {
24
- if (!form.tags || form.tags.length === 0) return [];
25
- return form.tags.map((tagSlug) => {
26
- const foundTag = tags?.find((tag) => tag.slug === tagSlug);
27
- return foundTag || { title: tagSlug, slug: tagSlug };
28
- });
29
- }, [form.tags, tags]);
30
- const handleTagChange = (_event, newValue) => {
31
- if (!newValue || newValue.length === 0) {
32
- setForm({ ...form, tags: [] });
33
- return;
34
- }
35
- const processedTags = newValue.map((item) => prepareTagFromInput(item));
36
- const uniqueTags = Array.from(new Set(processedTags));
37
- setForm({ ...form, tags: uniqueTags });
38
- };
39
- return /* @__PURE__ */ jsx(
40
- Autocomplete,
41
- {
42
- fullWidth: true,
43
- multiple: true,
44
- freeSolo: true,
45
- clearOnBlur: true,
46
- value: existingTags,
47
- onChange: handleTagChange,
48
- options: tags || [],
49
- loading,
50
- getOptionLabel: (option) => typeof option === "string" ? option : option.title,
51
- filterOptions: (options, params) => {
52
- const filtered = filter(options, params);
53
- const { inputValue } = params;
54
- if (inputValue.trim() !== "" && !options.some(
55
- (option) => typeof option !== "string" && inputValue.toLocaleLowerCase("en-US") === option.title.toLocaleLowerCase("en-US")
56
- )) {
57
- filtered.push({
58
- title: `${createLabel} "${inputValue}"`,
59
- slug: inputValue.toLocaleLowerCase("en-US"),
60
- inputValue,
61
- isNew: true
62
- });
63
- }
64
- return filtered;
65
- },
66
- renderTags: (value, getTagProps) => value.map((option, index) => {
67
- const tag = typeof option === "string" ? { title: option, slug: option.toLocaleLowerCase("en-US") } : option;
68
- const tagProps = getTagProps({ index });
69
- const { key, ...chipProps } = tagProps;
70
- return /* @__PURE__ */ jsx(
71
- Chip,
72
- {
73
- variant: "outlined",
74
- label: tag.title,
75
- ...chipProps
76
- },
77
- key
78
- );
79
- }),
80
- renderInput: (params) => /* @__PURE__ */ jsx(
81
- TextField,
82
- {
83
- ...params,
84
- label: "Tags",
85
- variant: "outlined",
86
- InputProps: {
87
- ...params.InputProps,
88
- endAdornment: /* @__PURE__ */ jsxs(Fragment, { children: [
89
- loading ? /* @__PURE__ */ jsx(CircularProgress, { color: "inherit", size: 20 }) : null,
90
- params.InputProps.endAdornment
91
- ] })
92
- }
93
- }
94
- )
95
- }
96
- );
97
- }
98
-
99
- export { TagsInput as default };
100
- //# sourceMappingURL=TagsInput.esm.js.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"TagsInput.esm.js","sources":["../../../../../src/components/Admin/AnnouncementsContent/AnnouncementForm/TagsInput.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 { useMemo } from 'react';\nimport Autocomplete, { createFilterOptions } from '@mui/material/Autocomplete';\nimport TextField from '@mui/material/TextField';\nimport Chip from '@mui/material/Chip';\nimport CircularProgress from '@mui/material/CircularProgress';\nimport {\n useTags,\n useAnnouncementsTranslation,\n} from '@backstage-community/plugin-announcements-react';\nimport { Tag } from '@backstage-community/plugin-announcements-common';\n\nexport interface TagOption extends Tag {\n inputValue?: string;\n isNew?: boolean;\n}\n\nconst filter = createFilterOptions<TagOption>();\n\nfunction prepareTagFromInput(input: string | TagOption): string {\n if (typeof input === 'string') {\n return input.toLocaleLowerCase('en-US');\n }\n if (input.inputValue) {\n return input.inputValue.toLocaleLowerCase('en-US');\n }\n return input.title.toLocaleLowerCase('en-US');\n}\n\ninterface TagsInputProps {\n setForm: (form: any) => void;\n form: any;\n}\n\nexport default function TagsInput({ setForm, form }: TagsInputProps) {\n const { tags, loading } = useTags();\n const { t } = useAnnouncementsTranslation();\n const createLabel = t('announcementForm.tagsInput.create');\n\n const existingTags = useMemo(() => {\n if (!form.tags || form.tags.length === 0) return [];\n return form.tags.map((tagSlug: string) => {\n const foundTag = tags?.find(tag => tag.slug === tagSlug);\n return foundTag || { title: tagSlug, slug: tagSlug };\n });\n }, [form.tags, tags]);\n\n const handleTagChange = (_event: any, newValue: (string | TagOption)[]) => {\n if (!newValue || newValue.length === 0) {\n setForm({ ...form, tags: [] });\n return;\n }\n\n const processedTags = newValue.map(item => prepareTagFromInput(item));\n const uniqueTags = Array.from(new Set(processedTags));\n\n setForm({ ...form, tags: uniqueTags });\n };\n\n return (\n <Autocomplete\n fullWidth\n multiple\n freeSolo\n clearOnBlur\n value={existingTags}\n onChange={handleTagChange}\n options={tags || []}\n loading={loading}\n getOptionLabel={(option: string | TagOption) =>\n typeof option === 'string' ? option : option.title\n }\n filterOptions={(options, params) => {\n const filtered = filter(options as TagOption[], params);\n const { inputValue } = params;\n if (\n inputValue.trim() !== '' &&\n !options.some(\n option =>\n typeof option !== 'string' &&\n inputValue.toLocaleLowerCase('en-US') ===\n option.title.toLocaleLowerCase('en-US'),\n )\n ) {\n filtered.push({\n title: `${createLabel} \"${inputValue}\"`,\n slug: inputValue.toLocaleLowerCase('en-US'),\n inputValue,\n isNew: true,\n });\n }\n return filtered;\n }}\n renderTags={(value: (string | TagOption)[], getTagProps) =>\n value.map((option, index) => {\n const tag =\n typeof option === 'string'\n ? { title: option, slug: option.toLocaleLowerCase('en-US') }\n : option;\n\n const tagProps = getTagProps({ index });\n const { key, ...chipProps } = tagProps;\n\n return (\n <Chip\n key={key}\n variant=\"outlined\"\n label={tag.title}\n {...chipProps}\n />\n );\n })\n }\n renderInput={params => (\n <TextField\n {...params}\n label=\"Tags\"\n variant=\"outlined\"\n InputProps={{\n ...params.InputProps,\n endAdornment: (\n <>\n {loading ? (\n <CircularProgress color=\"inherit\" size={20} />\n ) : null}\n {params.InputProps.endAdornment}\n </>\n ),\n }}\n />\n )}\n />\n );\n}\n"],"names":[],"mappings":";;;;;;;;AA+BA,MAAM,SAAS,mBAA+B,EAAA;AAE9C,SAAS,oBAAoB,KAAmC,EAAA;AAC9D,EAAI,IAAA,OAAO,UAAU,QAAU,EAAA;AAC7B,IAAO,OAAA,KAAA,CAAM,kBAAkB,OAAO,CAAA;AAAA;AAExC,EAAA,IAAI,MAAM,UAAY,EAAA;AACpB,IAAO,OAAA,KAAA,CAAM,UAAW,CAAA,iBAAA,CAAkB,OAAO,CAAA;AAAA;AAEnD,EAAO,OAAA,KAAA,CAAM,KAAM,CAAA,iBAAA,CAAkB,OAAO,CAAA;AAC9C;AAOA,SAAwB,SAAU,CAAA,EAAE,OAAS,EAAA,IAAA,EAAwB,EAAA;AACnE,EAAA,MAAM,EAAE,IAAA,EAAM,OAAQ,EAAA,GAAI,OAAQ,EAAA;AAClC,EAAM,MAAA,EAAE,CAAE,EAAA,GAAI,2BAA4B,EAAA;AAC1C,EAAM,MAAA,WAAA,GAAc,EAAE,mCAAmC,CAAA;AAEzD,EAAM,MAAA,YAAA,GAAe,QAAQ,MAAM;AACjC,IAAI,IAAA,CAAC,KAAK,IAAQ,IAAA,IAAA,CAAK,KAAK,MAAW,KAAA,CAAA,SAAU,EAAC;AAClD,IAAA,OAAO,IAAK,CAAA,IAAA,CAAK,GAAI,CAAA,CAAC,OAAoB,KAAA;AACxC,MAAA,MAAM,WAAW,IAAM,EAAA,IAAA,CAAK,CAAO,GAAA,KAAA,GAAA,CAAI,SAAS,OAAO,CAAA;AACvD,MAAA,OAAO,QAAY,IAAA,EAAE,KAAO,EAAA,OAAA,EAAS,MAAM,OAAQ,EAAA;AAAA,KACpD,CAAA;AAAA,GACA,EAAA,CAAC,IAAK,CAAA,IAAA,EAAM,IAAI,CAAC,CAAA;AAEpB,EAAM,MAAA,eAAA,GAAkB,CAAC,MAAA,EAAa,QAAqC,KAAA;AACzE,IAAA,IAAI,CAAC,QAAA,IAAY,QAAS,CAAA,MAAA,KAAW,CAAG,EAAA;AACtC,MAAA,OAAA,CAAQ,EAAE,GAAG,IAAA,EAAM,IAAM,EAAA,IAAI,CAAA;AAC7B,MAAA;AAAA;AAGF,IAAA,MAAM,gBAAgB,QAAS,CAAA,GAAA,CAAI,CAAQ,IAAA,KAAA,mBAAA,CAAoB,IAAI,CAAC,CAAA;AACpE,IAAA,MAAM,aAAa,KAAM,CAAA,IAAA,CAAK,IAAI,GAAA,CAAI,aAAa,CAAC,CAAA;AAEpD,IAAA,OAAA,CAAQ,EAAE,GAAG,IAAM,EAAA,IAAA,EAAM,YAAY,CAAA;AAAA,GACvC;AAEA,EACE,uBAAA,GAAA;AAAA,IAAC,YAAA;AAAA,IAAA;AAAA,MACC,SAAS,EAAA,IAAA;AAAA,MACT,QAAQ,EAAA,IAAA;AAAA,MACR,QAAQ,EAAA,IAAA;AAAA,MACR,WAAW,EAAA,IAAA;AAAA,MACX,KAAO,EAAA,YAAA;AAAA,MACP,QAAU,EAAA,eAAA;AAAA,MACV,OAAA,EAAS,QAAQ,EAAC;AAAA,MAClB,OAAA;AAAA,MACA,gBAAgB,CAAC,MAAA,KACf,OAAO,MAAW,KAAA,QAAA,GAAW,SAAS,MAAO,CAAA,KAAA;AAAA,MAE/C,aAAA,EAAe,CAAC,OAAA,EAAS,MAAW,KAAA;AAClC,QAAM,MAAA,QAAA,GAAW,MAAO,CAAA,OAAA,EAAwB,MAAM,CAAA;AACtD,QAAM,MAAA,EAAE,YAAe,GAAA,MAAA;AACvB,QAAA,IACE,UAAW,CAAA,IAAA,EAAW,KAAA,EAAA,IACtB,CAAC,OAAQ,CAAA,IAAA;AAAA,UACP,CAAA,MAAA,KACE,OAAO,MAAA,KAAW,QAClB,IAAA,UAAA,CAAW,iBAAkB,CAAA,OAAO,CAClC,KAAA,MAAA,CAAO,KAAM,CAAA,iBAAA,CAAkB,OAAO;AAAA,SAE5C,EAAA;AACA,UAAA,QAAA,CAAS,IAAK,CAAA;AAAA,YACZ,KAAO,EAAA,CAAA,EAAG,WAAW,CAAA,EAAA,EAAK,UAAU,CAAA,CAAA,CAAA;AAAA,YACpC,IAAA,EAAM,UAAW,CAAA,iBAAA,CAAkB,OAAO,CAAA;AAAA,YAC1C,UAAA;AAAA,YACA,KAAO,EAAA;AAAA,WACR,CAAA;AAAA;AAEH,QAAO,OAAA,QAAA;AAAA,OACT;AAAA,MACA,UAAA,EAAY,CAAC,KAA+B,EAAA,WAAA,KAC1C,MAAM,GAAI,CAAA,CAAC,QAAQ,KAAU,KAAA;AAC3B,QAAA,MAAM,GACJ,GAAA,OAAO,MAAW,KAAA,QAAA,GACd,EAAE,KAAA,EAAO,MAAQ,EAAA,IAAA,EAAM,MAAO,CAAA,iBAAA,CAAkB,OAAO,CAAA,EACvD,GAAA,MAAA;AAEN,QAAA,MAAM,QAAW,GAAA,WAAA,CAAY,EAAE,KAAA,EAAO,CAAA;AACtC,QAAA,MAAM,EAAE,GAAA,EAAK,GAAG,SAAA,EAAc,GAAA,QAAA;AAE9B,QACE,uBAAA,GAAA;AAAA,UAAC,IAAA;AAAA,UAAA;AAAA,YAEC,OAAQ,EAAA,UAAA;AAAA,YACR,OAAO,GAAI,CAAA,KAAA;AAAA,YACV,GAAG;AAAA,WAAA;AAAA,UAHC;AAAA,SAIP;AAAA,OAEH,CAAA;AAAA,MAEH,aAAa,CACX,MAAA,qBAAA,GAAA;AAAA,QAAC,SAAA;AAAA,QAAA;AAAA,UACE,GAAG,MAAA;AAAA,UACJ,KAAM,EAAA,MAAA;AAAA,UACN,OAAQ,EAAA,UAAA;AAAA,UACR,UAAY,EAAA;AAAA,YACV,GAAG,MAAO,CAAA,UAAA;AAAA,YACV,8BAEK,IAAA,CAAA,QAAA,EAAA,EAAA,QAAA,EAAA;AAAA,cAAA,OAAA,uBACE,gBAAiB,EAAA,EAAA,KAAA,EAAM,SAAU,EAAA,IAAA,EAAM,IAAI,CAC1C,GAAA,IAAA;AAAA,cACH,OAAO,UAAW,CAAA;AAAA,aACrB,EAAA;AAAA;AAEJ;AAAA;AACF;AAAA,GAEJ;AAEJ;;;;"}
@@ -1,305 +0,0 @@
1
- import { jsx, jsxs } from 'react/jsx-runtime';
2
- import { useState, useMemo } from 'react';
3
- import { Progress, ErrorPanel, StatusOK, StatusPending, Table } from '@backstage/core-components';
4
- import { useApi, alertApiRef } from '@backstage/core-plugin-api';
5
- import { announcementsApiRef, useCategories, useAnnouncementsTranslation, useAnnouncementsPermissions } from '@backstage-community/plugin-announcements-react';
6
- import { announcementCreatePermission } from '@backstage-community/plugin-announcements-common';
7
- import useAsyncRetry from 'react-use/esm/useAsyncRetry';
8
- import { useDeleteDialogState } from '../shared/DeleteDialog/useDeleteDialogState.esm.js';
9
- import { DeleteDialog } from '../shared/DeleteDialog/DeleteDialog.esm.js';
10
- import { Typography, Box, IconButton, Grid, Button } from '@material-ui/core';
11
- import { useNavigate } from 'react-router-dom';
12
- import { AnnouncementForm } from './AnnouncementForm/AnnouncementForm.esm.js';
13
- import slugify from 'slugify';
14
- import { RequirePermission } from '@backstage/plugin-permission-react';
15
- import DeleteIcon from '@material-ui/icons/Delete';
16
- import EditIcon from '@material-ui/icons/Edit';
17
- import PreviewIcon from '@material-ui/icons/Visibility';
18
- import { DateTime } from 'luxon';
19
-
20
- const AnnouncementsContent = ({
21
- defaultInactive
22
- }) => {
23
- const alertApi = useApi(alertApiRef);
24
- const announcementsApi = useApi(announcementsApiRef);
25
- const navigate = useNavigate();
26
- const { categories } = useCategories();
27
- const { t } = useAnnouncementsTranslation();
28
- const permissions = useAnnouncementsPermissions();
29
- const [showCreateAnnouncementForm, setShowCreateAnnouncementForm] = useState(false);
30
- const [editingAnnouncementId, setEditingAnnouncementId] = useState(null);
31
- const {
32
- loading,
33
- error,
34
- value: announcements,
35
- retry
36
- } = useAsyncRetry(async () => await announcementsApi.announcements({}));
37
- const {
38
- isOpen: isDeleteDialogOpen,
39
- open: openDeleteDialog,
40
- close: closeDeleteDialog,
41
- item: announcementToDelete
42
- } = useDeleteDialogState();
43
- const onCreateButtonClick = () => {
44
- setShowCreateAnnouncementForm(!showCreateAnnouncementForm);
45
- setEditingAnnouncementId(null);
46
- };
47
- const onTitleClick = (announcement) => {
48
- navigate(`/announcements/view/${announcement.id}`);
49
- };
50
- const onEdit = (announcement) => {
51
- setEditingAnnouncementId(announcement.id);
52
- setShowCreateAnnouncementForm(false);
53
- };
54
- const onCancelEdit = () => {
55
- setEditingAnnouncementId(null);
56
- };
57
- const onCancelDelete = () => {
58
- closeDeleteDialog();
59
- };
60
- const onConfirmDelete = async () => {
61
- closeDeleteDialog();
62
- try {
63
- await announcementsApi.deleteAnnouncementByID(announcementToDelete.id);
64
- alertApi.post({ message: "Announcement deleted.", severity: "success" });
65
- } catch (err) {
66
- alertApi.post({ message: err.message, severity: "error" });
67
- }
68
- retry();
69
- };
70
- const onSubmit = async (request) => {
71
- const { category } = request;
72
- const slugs = categories.map((c) => c.slug);
73
- let alertMsg = t("admin.announcementsContent.alertMessage");
74
- try {
75
- if (category) {
76
- const categorySlug = slugify(category, {
77
- lower: true
78
- });
79
- if (slugs.indexOf(categorySlug) === -1) {
80
- alertMsg = alertMsg.replace(".", "");
81
- alertMsg = `${alertMsg} ${t(
82
- "admin.announcementsContent.alertMessageWithNewCategory"
83
- )} ${category}.`;
84
- await announcementsApi.createCategory({
85
- title: category
86
- });
87
- }
88
- }
89
- await announcementsApi.createAnnouncement({
90
- ...request,
91
- category: request.category?.toLocaleLowerCase("en-US")
92
- });
93
- alertApi.post({ message: alertMsg, severity: "success" });
94
- setShowCreateAnnouncementForm(false);
95
- retry();
96
- } catch (err) {
97
- alertApi.post({ message: err.message, severity: "error" });
98
- }
99
- };
100
- const onUpdate = async (request) => {
101
- if (!editingAnnouncementId) {
102
- return;
103
- }
104
- const { category } = request;
105
- const slugs = categories.map((c) => c.slug);
106
- let updateMsg = t("editAnnouncementPage.updatedMessage");
107
- try {
108
- if (category) {
109
- const categorySlug = slugify(category, {
110
- lower: true
111
- });
112
- if (slugs.indexOf(categorySlug) === -1) {
113
- updateMsg = updateMsg.replace(".", "");
114
- updateMsg = `${updateMsg} ${t(
115
- "editAnnouncementPage.updatedMessageWithNewCategory"
116
- )} ${category}.`;
117
- await announcementsApi.createCategory({
118
- title: category
119
- });
120
- }
121
- }
122
- await announcementsApi.updateAnnouncement(editingAnnouncementId, request);
123
- alertApi.post({ message: updateMsg, severity: "success" });
124
- setEditingAnnouncementId(null);
125
- retry();
126
- } catch (err) {
127
- alertApi.post({ message: err.message, severity: "error" });
128
- }
129
- };
130
- const announcementToEdit = useMemo(() => {
131
- if (!editingAnnouncementId || !announcements?.results) {
132
- return null;
133
- }
134
- return announcements.results.find((a) => a.id === editingAnnouncementId) ?? null;
135
- }, [editingAnnouncementId, announcements?.results]);
136
- if (loading) {
137
- return /* @__PURE__ */ jsx(Progress, {});
138
- }
139
- if (error) {
140
- return /* @__PURE__ */ jsx(ErrorPanel, { error });
141
- }
142
- const columns = [
143
- {
144
- title: /* @__PURE__ */ jsx(Typography, { children: t("admin.announcementsContent.table.title") }),
145
- sorting: true,
146
- field: "title",
147
- render: (rowData) => rowData.title
148
- },
149
- {
150
- title: /* @__PURE__ */ jsx(Typography, { children: t("admin.announcementsContent.table.body") }),
151
- sorting: true,
152
- field: "body",
153
- render: (rowData) => rowData.body
154
- },
155
- {
156
- title: /* @__PURE__ */ jsx(Typography, { children: t("admin.announcementsContent.table.publisher") }),
157
- sorting: true,
158
- field: "publisher",
159
- render: (rowData) => rowData.publisher
160
- },
161
- {
162
- title: /* @__PURE__ */ jsx(Typography, { children: t("admin.announcementsContent.table.onBehalfOf") }),
163
- sorting: true,
164
- field: "on_behalf_of",
165
- render: (rowData) => rowData.on_behalf_of
166
- },
167
- {
168
- title: /* @__PURE__ */ jsx(Typography, { children: t("admin.announcementsContent.table.category") }),
169
- sorting: true,
170
- field: "category",
171
- render: (rowData) => rowData.category?.title ?? ""
172
- },
173
- {
174
- title: /* @__PURE__ */ jsx(Typography, { children: t("admin.announcementsContent.table.tags") }),
175
- sorting: true,
176
- field: "tags",
177
- render: (rowData) => rowData.tags?.map((tag) => tag.title).join(", ") || ""
178
- },
179
- {
180
- title: /* @__PURE__ */ jsx(Typography, { children: t("admin.announcementsContent.table.status") }),
181
- sorting: true,
182
- field: "active",
183
- render: (rowData) => rowData.active ? /* @__PURE__ */ jsx(StatusOK, { children: t("admin.announcementsContent.table.active") }) : /* @__PURE__ */ jsx(StatusPending, { children: t("admin.announcementsContent.table.inactive") })
184
- },
185
- {
186
- title: /* @__PURE__ */ jsx(Typography, { children: t("admin.announcementsContent.table.created_at") }),
187
- sorting: true,
188
- field: "created_at",
189
- type: "date",
190
- render: (rowData) => DateTime.fromISO(rowData.created_at).toFormat("M/d/yyyy")
191
- },
192
- {
193
- title: /* @__PURE__ */ jsx(Typography, { children: t("admin.announcementsContent.table.start_at") }),
194
- sorting: true,
195
- field: "start_at",
196
- type: "date",
197
- render: (rowData) => DateTime.fromISO(rowData.start_at).toFormat("M/d/yyyy")
198
- },
199
- {
200
- title: /* @__PURE__ */ jsx(Typography, { children: t("admin.announcementsContent.table.until_date") }),
201
- sorting: true,
202
- field: "until_date",
203
- type: "date",
204
- render: (rowData) => rowData?.until_date ? DateTime.fromISO(rowData.until_date).toFormat("M/d/yyyy") : "-"
205
- },
206
- {
207
- title: /* @__PURE__ */ jsx(Typography, { children: t("admin.announcementsContent.table.actions") }),
208
- render: (rowData) => {
209
- return /* @__PURE__ */ jsxs(Box, { display: "flex", flexDirection: "row", children: [
210
- /* @__PURE__ */ jsx(
211
- IconButton,
212
- {
213
- "aria-label": "preview",
214
- onClick: () => onTitleClick(rowData),
215
- size: "small",
216
- children: /* @__PURE__ */ jsx(PreviewIcon, { fontSize: "small", "data-testid": "preview" })
217
- }
218
- ),
219
- /* @__PURE__ */ jsx(
220
- IconButton,
221
- {
222
- "aria-label": "edit",
223
- disabled: permissions.update.loading || !permissions.update.allowed || editingAnnouncementId === rowData.id,
224
- onClick: () => onEdit(rowData),
225
- size: "small",
226
- children: /* @__PURE__ */ jsx(EditIcon, { fontSize: "small", "data-testid": "edit-icon" })
227
- }
228
- ),
229
- /* @__PURE__ */ jsx(
230
- IconButton,
231
- {
232
- "aria-label": "delete",
233
- disabled: permissions.delete.loading || !permissions.delete.allowed,
234
- onClick: () => openDeleteDialog(rowData),
235
- size: "small",
236
- children: /* @__PURE__ */ jsx(DeleteIcon, { fontSize: "small", "data-testid": "delete-icon" })
237
- }
238
- )
239
- ] });
240
- }
241
- }
242
- ];
243
- return /* @__PURE__ */ jsx(RequirePermission, { permission: announcementCreatePermission, children: /* @__PURE__ */ jsxs(Grid, { container: true, children: [
244
- !editingAnnouncementId && /* @__PURE__ */ jsx(Grid, { item: true, xs: 12, children: /* @__PURE__ */ jsx(
245
- Button,
246
- {
247
- disabled: permissions.create.loading || !permissions.create.allowed,
248
- variant: "contained",
249
- onClick: () => onCreateButtonClick(),
250
- children: showCreateAnnouncementForm ? t("admin.announcementsContent.cancelButton") : t("admin.announcementsContent.createButton")
251
- }
252
- ) }),
253
- showCreateAnnouncementForm && /* @__PURE__ */ jsx(Grid, { item: true, xs: 12, children: /* @__PURE__ */ jsx(
254
- AnnouncementForm,
255
- {
256
- initialData: { active: !defaultInactive },
257
- onSubmit
258
- }
259
- ) }),
260
- editingAnnouncementId && announcementToEdit && /* @__PURE__ */ jsx(Grid, { item: true, xs: 12, children: /* @__PURE__ */ jsxs(Box, { children: [
261
- /* @__PURE__ */ jsxs(
262
- Box,
263
- {
264
- display: "flex",
265
- justifyContent: "space-between",
266
- style: { marginBottom: 16 },
267
- children: [
268
- /* @__PURE__ */ jsx(Typography, { variant: "h6", children: t("announcementForm.editAnnouncement") }),
269
- /* @__PURE__ */ jsx(Button, { variant: "outlined", onClick: onCancelEdit, size: "small", children: t("admin.announcementsContent.cancelButton") })
270
- ]
271
- }
272
- ),
273
- /* @__PURE__ */ jsx(
274
- AnnouncementForm,
275
- {
276
- initialData: announcementToEdit,
277
- onSubmit: onUpdate
278
- }
279
- )
280
- ] }) }),
281
- /* @__PURE__ */ jsxs(Grid, { item: true, xs: 12, children: [
282
- /* @__PURE__ */ jsx(
283
- Table,
284
- {
285
- title: t("admin.announcementsContent.announcements"),
286
- options: { pageSize: 20, search: true },
287
- columns,
288
- data: announcements?.results ?? [],
289
- emptyContent: /* @__PURE__ */ jsx(Typography, { style: { padding: 2, textAlign: "center" }, children: t("admin.announcementsContent.noAnnouncementsFound") })
290
- }
291
- ),
292
- /* @__PURE__ */ jsx(
293
- DeleteDialog,
294
- {
295
- isOpen: isDeleteDialogOpen,
296
- onCancel: onCancelDelete,
297
- onConfirm: onConfirmDelete
298
- }
299
- )
300
- ] })
301
- ] }) });
302
- };
303
-
304
- export { AnnouncementsContent };
305
- //# sourceMappingURL=AnnouncementsContent.esm.js.map