@axis-backstage/plugin-vacation-calendar 0.11.1 → 0.11.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +6 -0
- package/dist/alpha.d.ts +2 -2
- package/dist/components/CalendarCard/CalendarCard.esm.js +23 -23
- package/dist/components/CalendarCard/CalendarCard.esm.js.map +1 -1
- package/dist/components/CalendarCard/fetch.esm.js +15 -3
- package/dist/components/CalendarCard/fetch.esm.js.map +1 -1
- package/dist/components/CalendarCard/lib.esm.js +19 -12
- package/dist/components/CalendarCard/lib.esm.js.map +1 -1
- package/package.json +1 -1
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,11 @@
|
|
|
1
1
|
# @axis-backstage/plugin-vacation-calendar
|
|
2
2
|
|
|
3
|
+
## 0.11.2
|
|
4
|
+
|
|
5
|
+
### Patch Changes
|
|
6
|
+
|
|
7
|
+
- e9c8973: Fix broken user links in the vacation calendar. Users are now matched by email instead of metadata.name, and links use EntityRefLink for correct catalog URLs. Unresolved users render without a link instead of navigating to `/user/undefined`. Catalog queries now paginate to support large groups.
|
|
8
|
+
|
|
3
9
|
## 0.11.1
|
|
4
10
|
|
|
5
11
|
### Patch Changes
|
package/dist/alpha.d.ts
CHANGED
|
@@ -39,9 +39,9 @@ declare const _default: _backstage_frontend_plugin_api.OverridableFrontendPlugin
|
|
|
39
39
|
group?: string | false | undefined;
|
|
40
40
|
icon?: string | undefined;
|
|
41
41
|
};
|
|
42
|
-
output: _backstage_frontend_plugin_api.ExtensionDataRef<
|
|
42
|
+
output: _backstage_frontend_plugin_api.ExtensionDataRef<string, "core.routing.path", {}> | _backstage_frontend_plugin_api.ExtensionDataRef<_backstage_frontend_plugin_api.RouteRef<_backstage_frontend_plugin_api.AnyRouteRefParams>, "core.routing.ref", {
|
|
43
43
|
optional: true;
|
|
44
|
-
}> | _backstage_frontend_plugin_api.ExtensionDataRef<(entity: _backstage_catalog_model.Entity) => boolean, "catalog.entity-filter-function", {
|
|
44
|
+
}> | _backstage_frontend_plugin_api.ExtensionDataRef<react.JSX.Element, "core.reactElement", {}> | _backstage_frontend_plugin_api.ExtensionDataRef<(entity: _backstage_catalog_model.Entity) => boolean, "catalog.entity-filter-function", {
|
|
45
45
|
optional: true;
|
|
46
46
|
}> | _backstage_frontend_plugin_api.ExtensionDataRef<string, "catalog.entity-filter-expression", {
|
|
47
47
|
optional: true;
|
|
@@ -5,8 +5,8 @@ import useAsync from 'react-use/lib/useAsync';
|
|
|
5
5
|
import Timeline, { TimelineHeaders, SidebarHeader, DateHeader } from 'react-calendar-timeline';
|
|
6
6
|
import dayjs from 'dayjs';
|
|
7
7
|
import { useApi } from '@backstage/core-plugin-api';
|
|
8
|
-
import { useEntity, catalogApiRef } from '@backstage/plugin-catalog-react';
|
|
9
|
-
import { ErrorPanel, Content, ContentHeader, SupportButton, Progress,
|
|
8
|
+
import { useEntity, catalogApiRef, EntityRefLink } from '@backstage/plugin-catalog-react';
|
|
9
|
+
import { ErrorPanel, Content, ContentHeader, SupportButton, Progress, Avatar } from '@backstage/core-components';
|
|
10
10
|
import { Box, Flex, Button } from '@backstage/ui';
|
|
11
11
|
import { DateSelector } from '../DateSelector/DateSelector.esm.js';
|
|
12
12
|
import { getGroups, getScheduleItems } from './lib.esm.js';
|
|
@@ -30,6 +30,7 @@ const CalendarCard = () => {
|
|
|
30
30
|
const { isSignedIn, isInitialized, signIn } = useSignIn();
|
|
31
31
|
useAsync(async () => signIn(true), [signIn]);
|
|
32
32
|
const isUserEntity = entity.kind.toLowerCase() === "user" && entity.metadata.name;
|
|
33
|
+
const currentUserEmail = isUserEntity && entity.spec?.profile?.email || false;
|
|
33
34
|
const { value: users } = useAsync(async () => {
|
|
34
35
|
return isUserEntity ? fetchUserEntities(catalogApi, entity) : fetchGroupEntities(catalogApi, entity);
|
|
35
36
|
}, [catalogApi, entity]);
|
|
@@ -42,7 +43,11 @@ const CalendarCard = () => {
|
|
|
42
43
|
fetchNextPage
|
|
43
44
|
} = useAvailability(users, startDate, endDate, isSignedIn);
|
|
44
45
|
const showLoader = isAvailabilityLoading || isAvailabilityFetching || !isInitialized;
|
|
45
|
-
const [groups, groupIndexMap] = getGroups(
|
|
46
|
+
const [groups, groupIndexMap] = getGroups(
|
|
47
|
+
availability,
|
|
48
|
+
currentUserEmail,
|
|
49
|
+
users
|
|
50
|
+
);
|
|
46
51
|
const scheduleItems = getScheduleItems(availability, groupIndexMap);
|
|
47
52
|
if (users?.length === 0) {
|
|
48
53
|
return /* @__PURE__ */ jsx(
|
|
@@ -151,26 +156,21 @@ const CalendarCard = () => {
|
|
|
151
156
|
groups,
|
|
152
157
|
sidebarWidth: 250,
|
|
153
158
|
groupRenderer: ({ group }) => {
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
),
|
|
170
|
-
/* @__PURE__ */ jsx("span", { className: "title", children: group.title })
|
|
171
|
-
] })
|
|
172
|
-
}
|
|
173
|
-
);
|
|
159
|
+
const groupContent = /* @__PURE__ */ jsxs("div", { className: "custom-group", children: [
|
|
160
|
+
/* @__PURE__ */ jsx(
|
|
161
|
+
Avatar,
|
|
162
|
+
{
|
|
163
|
+
displayName: group.entity?.metadata.displayName,
|
|
164
|
+
picture: group.entity?.spec.profile?.picture,
|
|
165
|
+
customStyles: {
|
|
166
|
+
width: 30,
|
|
167
|
+
height: 30
|
|
168
|
+
}
|
|
169
|
+
}
|
|
170
|
+
),
|
|
171
|
+
/* @__PURE__ */ jsx("span", { className: "title", children: group.title })
|
|
172
|
+
] });
|
|
173
|
+
return group.entity ? /* @__PURE__ */ jsx(EntityRefLink, { entityRef: group.entity, children: groupContent }) : groupContent;
|
|
174
174
|
},
|
|
175
175
|
items: scheduleItems,
|
|
176
176
|
itemTouchSendsClick: false,
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"CalendarCard.esm.js","sources":["../../../src/components/CalendarCard/CalendarCard.tsx"],"sourcesContent":["import './CalendarCard.css';\nimport { useState } from 'react';\nimport useAsync from 'react-use/lib/useAsync';\nimport Timeline, {\n TimelineHeaders,\n SidebarHeader,\n DateHeader,\n} from 'react-calendar-timeline';\nimport dayjs from 'dayjs';\nimport { useApi } from '@backstage/core-plugin-api';\nimport { useEntity, catalogApiRef } from '@backstage/plugin-catalog-react';\nimport {\n Content,\n ContentHeader,\n SupportButton,\n Link,\n ErrorPanel,\n Progress,\n Avatar,\n} from '@backstage/core-components';\nimport { Box, Button, Flex } from '@backstage/ui';\nimport { DateSelector } from '../DateSelector';\nimport { getGroups, getScheduleItems } from './lib';\nimport { fetchGroupEntities, fetchUserEntities } from './fetch';\nimport { useAvailability } from '../../hooks/useAvailibility';\nimport { useSignIn } from '../../hooks';\nimport { SignInContent } from '../SignInContent';\nimport Typography from '@mui/material/Typography';\n\nconst DEFAULT_NUM_DAYS = 60;\nconst DAY_IN_MILLISECONDS = 24 * 60 * 60 * 1000;\nconst DAY_HEADER_MAX_ZOOM = DEFAULT_NUM_DAYS * DAY_IN_MILLISECONDS;\n\nexport const CalendarCard = () => {\n const { entity } = useEntity();\n const catalogApi = useApi(catalogApiRef);\n\n const [startDate, setStartDate] = useState(dayjs());\n const [endDate, setEndDate] = useState(\n dayjs().endOf('day').add(DEFAULT_NUM_DAYS, 'day'),\n );\n const [headerUnit, setHeaderUnit] = useState<'day' | 'month'>('day');\n\n const { isSignedIn, isInitialized, signIn } = useSignIn();\n\n useAsync(async () => signIn(true), [signIn]);\n\n const isUserEntity =\n entity.kind.toLowerCase() === 'user' && entity.metadata.name;\n\n const { value: users } = useAsync(async () => {\n return isUserEntity\n ? fetchUserEntities(catalogApi, entity)\n : fetchGroupEntities(catalogApi, entity);\n }, [catalogApi, entity]);\n\n const {\n availability,\n error,\n isLoading: isAvailabilityLoading,\n isFetching: isAvailabilityFetching,\n hasNextPage,\n fetchNextPage,\n } = useAvailability(users, startDate, endDate, isSignedIn);\n\n const showLoader =\n isAvailabilityLoading || isAvailabilityFetching || !isInitialized;\n\n const [groups, groupIndexMap] = getGroups(availability, isUserEntity, users);\n const scheduleItems = getScheduleItems(availability, groupIndexMap);\n\n if (users?.length === 0) {\n return (\n <ErrorPanel\n title=\"No users found.\"\n defaultExpanded\n error={Error('No users found for current entity.')}\n />\n );\n }\n if (error instanceof Error) {\n return (\n <ErrorPanel\n title=\"Microsoft Graph Error.\"\n defaultExpanded\n error={error as any}\n />\n );\n }\n\n return (\n <Content>\n <ContentHeader title=\"Out of Office Calendar\">\n <SupportButton title=\"Backstage Out Of Office Calendar\">\n <Box>\n <Typography variant=\"h6\">How it works</Typography>\n <Typography>\n The \"Out of Office\"-calendar shows Away events. If you want your\n calendar events to be seen in the \"Out of Office\"-calendar, be\n sure to mark your presence as \"Away\" in outlook.\n </Typography>\n <Typography variant=\"h6\">Limitations</Typography>\n <Typography>\n Due to limitations in the Microsoft Graph API the maximum range of\n the dates is {DEFAULT_NUM_DAYS} days.\n </Typography>\n </Box>\n </SupportButton>\n </ContentHeader>\n\n <Box>\n <Flex direction=\"row\" gap=\"3\" align=\"end\">\n <Box>\n <DateSelector\n onDateChange={d => {\n if (d) {\n const days = endDate.diff(d, 'day');\n if (days && Math.abs(days) > DEFAULT_NUM_DAYS) {\n setEndDate(d.endOf('day').add(DEFAULT_NUM_DAYS, 'day'));\n }\n setHeaderUnit('day');\n setStartDate(d);\n }\n }}\n initialDate={startDate}\n label=\"Start Date\"\n />\n </Box>\n <Box>\n <DateSelector\n onDateChange={d => {\n if (d) {\n const days = startDate.diff(d, 'day');\n if (days && Math.abs(days) > DEFAULT_NUM_DAYS) {\n setStartDate(\n d.endOf('day').subtract(DEFAULT_NUM_DAYS, 'day'),\n );\n }\n setHeaderUnit('day');\n setEndDate(d);\n }\n }}\n initialDate={endDate}\n label=\"End Date\"\n />\n </Box>\n <Button\n onClick={() => {\n fetchNextPage();\n }}\n isDisabled={showLoader || !hasNextPage}\n variant=\"secondary\"\n >\n Show more Users\n </Button>\n </Flex>\n {showLoader && (\n <Box py=\"2\">\n <Progress variant=\"query\" />\n </Box>\n )}\n {!isSignedIn && isInitialized && (\n <Box\n p=\"1\"\n pb=\"0\"\n minHeight=\"200px\"\n maxHeight=\"602px\"\n style={{ overflow: 'auto' }}\n >\n <SignInContent handleAuthClick={() => signIn(false)} />\n </Box>\n )}\n {!isAvailabilityLoading && isSignedIn && (\n <div\n style={{\n marginTop: 'var(--bui-space-3)',\n minHeight: '200px',\n overflow: 'auto',\n }}\n >\n <Timeline\n key={`${startDate.valueOf()}-${endDate.valueOf()}`}\n groups={groups}\n sidebarWidth={250}\n groupRenderer={({ group }) => {\n return (\n <Link\n to={`/catalog/default/user/${group.entity?.metadata.name}`}\n >\n <div className=\"custom-group\">\n <Avatar\n displayName={\n group.entity?.metadata.displayName as string\n }\n picture={group.entity?.spec.profile?.picture}\n customStyles={{\n width: 30,\n height: 30,\n }}\n />\n <span className=\"title\">{group.title}</span>\n </div>\n </Link>\n );\n }}\n items={scheduleItems as any}\n itemTouchSendsClick={false}\n defaultTimeStart={startDate.valueOf()}\n defaultTimeEnd={endDate.valueOf()}\n onZoom={({ visibleTimeStart, visibleTimeEnd }) => {\n setHeaderUnit(\n visibleTimeEnd - visibleTimeStart <= DAY_HEADER_MAX_ZOOM\n ? 'day'\n : 'month',\n );\n }}\n >\n <TimelineHeaders>\n <SidebarHeader>\n {({ getRootProps }) => {\n return (\n <div\n {...getRootProps()}\n className=\"timeline-sidebar-header\"\n />\n );\n }}\n </SidebarHeader>\n <DateHeader unit={headerUnit === 'day' ? 'month' : 'year'} />\n <DateHeader unit={headerUnit} />\n </TimelineHeaders>\n </Timeline>\n </div>\n )}\n </Box>\n </Content>\n );\n};\n"],"names":[],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"CalendarCard.esm.js","sources":["../../../src/components/CalendarCard/CalendarCard.tsx"],"sourcesContent":["import './CalendarCard.css';\nimport { useState } from 'react';\nimport useAsync from 'react-use/lib/useAsync';\nimport Timeline, {\n TimelineHeaders,\n SidebarHeader,\n DateHeader,\n} from 'react-calendar-timeline';\nimport dayjs from 'dayjs';\nimport { UserEntity } from '@backstage/catalog-model';\nimport { useApi } from '@backstage/core-plugin-api';\nimport {\n useEntity,\n catalogApiRef,\n EntityRefLink,\n} from '@backstage/plugin-catalog-react';\nimport {\n Content,\n ContentHeader,\n SupportButton,\n ErrorPanel,\n Progress,\n Avatar,\n} from '@backstage/core-components';\nimport { Box, Button, Flex } from '@backstage/ui';\nimport { DateSelector } from '../DateSelector';\nimport { getGroups, getScheduleItems } from './lib';\nimport { fetchGroupEntities, fetchUserEntities } from './fetch';\nimport { useAvailability } from '../../hooks/useAvailibility';\nimport { useSignIn } from '../../hooks';\nimport { SignInContent } from '../SignInContent';\nimport Typography from '@mui/material/Typography';\n\nconst DEFAULT_NUM_DAYS = 60;\nconst DAY_IN_MILLISECONDS = 24 * 60 * 60 * 1000;\nconst DAY_HEADER_MAX_ZOOM = DEFAULT_NUM_DAYS * DAY_IN_MILLISECONDS;\n\nexport const CalendarCard = () => {\n const { entity } = useEntity();\n const catalogApi = useApi(catalogApiRef);\n\n const [startDate, setStartDate] = useState(dayjs());\n const [endDate, setEndDate] = useState(\n dayjs().endOf('day').add(DEFAULT_NUM_DAYS, 'day'),\n );\n const [headerUnit, setHeaderUnit] = useState<'day' | 'month'>('day');\n\n const { isSignedIn, isInitialized, signIn } = useSignIn();\n\n useAsync(async () => signIn(true), [signIn]);\n\n const isUserEntity =\n entity.kind.toLowerCase() === 'user' && entity.metadata.name;\n const currentUserEmail =\n (isUserEntity && (entity as UserEntity).spec?.profile?.email) || false;\n\n const { value: users } = useAsync(async () => {\n return isUserEntity\n ? fetchUserEntities(catalogApi, entity)\n : fetchGroupEntities(catalogApi, entity);\n }, [catalogApi, entity]);\n\n const {\n availability,\n error,\n isLoading: isAvailabilityLoading,\n isFetching: isAvailabilityFetching,\n hasNextPage,\n fetchNextPage,\n } = useAvailability(users, startDate, endDate, isSignedIn);\n\n const showLoader =\n isAvailabilityLoading || isAvailabilityFetching || !isInitialized;\n\n const [groups, groupIndexMap] = getGroups(\n availability,\n currentUserEmail,\n users,\n );\n const scheduleItems = getScheduleItems(availability, groupIndexMap);\n\n if (users?.length === 0) {\n return (\n <ErrorPanel\n title=\"No users found.\"\n defaultExpanded\n error={Error('No users found for current entity.')}\n />\n );\n }\n if (error instanceof Error) {\n return (\n <ErrorPanel\n title=\"Microsoft Graph Error.\"\n defaultExpanded\n error={error as any}\n />\n );\n }\n\n return (\n <Content>\n <ContentHeader title=\"Out of Office Calendar\">\n <SupportButton title=\"Backstage Out Of Office Calendar\">\n <Box>\n <Typography variant=\"h6\">How it works</Typography>\n <Typography>\n The \"Out of Office\"-calendar shows Away events. If you want your\n calendar events to be seen in the \"Out of Office\"-calendar, be\n sure to mark your presence as \"Away\" in outlook.\n </Typography>\n <Typography variant=\"h6\">Limitations</Typography>\n <Typography>\n Due to limitations in the Microsoft Graph API the maximum range of\n the dates is {DEFAULT_NUM_DAYS} days.\n </Typography>\n </Box>\n </SupportButton>\n </ContentHeader>\n\n <Box>\n <Flex direction=\"row\" gap=\"3\" align=\"end\">\n <Box>\n <DateSelector\n onDateChange={d => {\n if (d) {\n const days = endDate.diff(d, 'day');\n if (days && Math.abs(days) > DEFAULT_NUM_DAYS) {\n setEndDate(d.endOf('day').add(DEFAULT_NUM_DAYS, 'day'));\n }\n setHeaderUnit('day');\n setStartDate(d);\n }\n }}\n initialDate={startDate}\n label=\"Start Date\"\n />\n </Box>\n <Box>\n <DateSelector\n onDateChange={d => {\n if (d) {\n const days = startDate.diff(d, 'day');\n if (days && Math.abs(days) > DEFAULT_NUM_DAYS) {\n setStartDate(\n d.endOf('day').subtract(DEFAULT_NUM_DAYS, 'day'),\n );\n }\n setHeaderUnit('day');\n setEndDate(d);\n }\n }}\n initialDate={endDate}\n label=\"End Date\"\n />\n </Box>\n <Button\n onClick={() => {\n fetchNextPage();\n }}\n isDisabled={showLoader || !hasNextPage}\n variant=\"secondary\"\n >\n Show more Users\n </Button>\n </Flex>\n {showLoader && (\n <Box py=\"2\">\n <Progress variant=\"query\" />\n </Box>\n )}\n {!isSignedIn && isInitialized && (\n <Box\n p=\"1\"\n pb=\"0\"\n minHeight=\"200px\"\n maxHeight=\"602px\"\n style={{ overflow: 'auto' }}\n >\n <SignInContent handleAuthClick={() => signIn(false)} />\n </Box>\n )}\n {!isAvailabilityLoading && isSignedIn && (\n <div\n style={{\n marginTop: 'var(--bui-space-3)',\n minHeight: '200px',\n overflow: 'auto',\n }}\n >\n <Timeline\n key={`${startDate.valueOf()}-${endDate.valueOf()}`}\n groups={groups}\n sidebarWidth={250}\n groupRenderer={({ group }) => {\n const groupContent = (\n <div className=\"custom-group\">\n <Avatar\n displayName={group.entity?.metadata.displayName as string}\n picture={group.entity?.spec.profile?.picture}\n customStyles={{\n width: 30,\n height: 30,\n }}\n />\n <span className=\"title\">{group.title}</span>\n </div>\n );\n\n return group.entity ? (\n <EntityRefLink entityRef={group.entity}>\n {groupContent}\n </EntityRefLink>\n ) : (\n groupContent\n );\n }}\n items={scheduleItems as any}\n itemTouchSendsClick={false}\n defaultTimeStart={startDate.valueOf()}\n defaultTimeEnd={endDate.valueOf()}\n onZoom={({ visibleTimeStart, visibleTimeEnd }) => {\n setHeaderUnit(\n visibleTimeEnd - visibleTimeStart <= DAY_HEADER_MAX_ZOOM\n ? 'day'\n : 'month',\n );\n }}\n >\n <TimelineHeaders>\n <SidebarHeader>\n {({ getRootProps }) => {\n return (\n <div\n {...getRootProps()}\n className=\"timeline-sidebar-header\"\n />\n );\n }}\n </SidebarHeader>\n <DateHeader unit={headerUnit === 'day' ? 'month' : 'year'} />\n <DateHeader unit={headerUnit} />\n </TimelineHeaders>\n </Timeline>\n </div>\n )}\n </Box>\n </Content>\n );\n};\n"],"names":[],"mappings":"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"}
|
|
@@ -1,6 +1,19 @@
|
|
|
1
1
|
import { DEFAULT_NAMESPACE, stringifyEntityRef } from '@backstage/catalog-model';
|
|
2
2
|
|
|
3
3
|
const MANAGER_ANNOTATION = "manager";
|
|
4
|
+
const CATALOG_PAGE_SIZE = 500;
|
|
5
|
+
const fetchAllUserEntities = async (catalogApi, filter) => {
|
|
6
|
+
const users = [];
|
|
7
|
+
let cursor;
|
|
8
|
+
do {
|
|
9
|
+
const response = await catalogApi.queryEntities(
|
|
10
|
+
cursor ? { cursor, limit: CATALOG_PAGE_SIZE } : { filter, limit: CATALOG_PAGE_SIZE, totalItems: "exclude" }
|
|
11
|
+
);
|
|
12
|
+
users.push(...response.items);
|
|
13
|
+
cursor = response.pageInfo.nextCursor;
|
|
14
|
+
} while (cursor);
|
|
15
|
+
return users;
|
|
16
|
+
};
|
|
4
17
|
const fetchUserEntities = async (catalogApi, entity) => {
|
|
5
18
|
if (!entity.metadata?.annotations?.[MANAGER_ANNOTATION]) {
|
|
6
19
|
return [entity];
|
|
@@ -9,8 +22,7 @@ const fetchUserEntities = async (catalogApi, entity) => {
|
|
|
9
22
|
kind: "User",
|
|
10
23
|
[`metadata.annotations.${MANAGER_ANNOTATION}`]: entity.metadata.annotations[MANAGER_ANNOTATION]
|
|
11
24
|
};
|
|
12
|
-
|
|
13
|
-
return items;
|
|
25
|
+
return fetchAllUserEntities(catalogApi, filter);
|
|
14
26
|
};
|
|
15
27
|
const fetchGroupEntities = async (catalogApi, entity) => {
|
|
16
28
|
const {
|
|
@@ -26,7 +38,7 @@ const fetchGroupEntities = async (catalogApi, entity) => {
|
|
|
26
38
|
})
|
|
27
39
|
]
|
|
28
40
|
};
|
|
29
|
-
return (
|
|
41
|
+
return fetchAllUserEntities(catalogApi, filter);
|
|
30
42
|
};
|
|
31
43
|
|
|
32
44
|
export { fetchGroupEntities, fetchUserEntities };
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"fetch.esm.js","sources":["../../../src/components/CalendarCard/fetch.ts"],"sourcesContent":["import {\n DEFAULT_NAMESPACE,\n Entity,\n UserEntity,\n stringifyEntityRef,\n} from '@backstage/catalog-model';\nimport { CatalogApi } from '@backstage/plugin-catalog-react';\n\nconst MANAGER_ANNOTATION = 'manager';\n\nexport const fetchUserEntities = async (\n catalogApi: CatalogApi,\n entity: Entity,\n) => {\n if (!entity.metadata?.annotations?.[MANAGER_ANNOTATION]) {\n return [entity as UserEntity];\n }\n\n const filter = {\n kind: 'User',\n [`metadata.annotations.${MANAGER_ANNOTATION}`]:\n entity.metadata.annotations[MANAGER_ANNOTATION],\n };\n\n
|
|
1
|
+
{"version":3,"file":"fetch.esm.js","sources":["../../../src/components/CalendarCard/fetch.ts"],"sourcesContent":["import {\n DEFAULT_NAMESPACE,\n Entity,\n UserEntity,\n stringifyEntityRef,\n} from '@backstage/catalog-model';\nimport { CatalogApi } from '@backstage/plugin-catalog-react';\n\nconst MANAGER_ANNOTATION = 'manager';\nconst CATALOG_PAGE_SIZE = 500;\n\nconst fetchAllUserEntities = async (\n catalogApi: CatalogApi,\n filter: Record<string, string | string[]>,\n) => {\n const users: UserEntity[] = [];\n let cursor: string | undefined;\n\n do {\n const response = await catalogApi.queryEntities(\n cursor\n ? { cursor, limit: CATALOG_PAGE_SIZE }\n : { filter, limit: CATALOG_PAGE_SIZE, totalItems: 'exclude' },\n );\n\n users.push(...(response.items as UserEntity[]));\n cursor = response.pageInfo.nextCursor;\n } while (cursor);\n\n return users;\n};\n\nexport const fetchUserEntities = async (\n catalogApi: CatalogApi,\n entity: Entity,\n) => {\n if (!entity.metadata?.annotations?.[MANAGER_ANNOTATION]) {\n return [entity as UserEntity];\n }\n\n const filter = {\n kind: 'User',\n [`metadata.annotations.${MANAGER_ANNOTATION}`]:\n entity.metadata.annotations[MANAGER_ANNOTATION],\n };\n\n return fetchAllUserEntities(catalogApi, filter);\n};\n\nexport const fetchGroupEntities = async (\n catalogApi: CatalogApi,\n entity: Entity,\n) => {\n const {\n metadata: { name: groupName, namespace = DEFAULT_NAMESPACE },\n } = entity;\n\n const filter = {\n kind: 'User',\n 'relations.memberof': [\n stringifyEntityRef({\n kind: 'group',\n namespace: namespace.toLocaleLowerCase('en-US'),\n name: groupName.toLocaleLowerCase('en-US'),\n }),\n ],\n };\n\n return fetchAllUserEntities(catalogApi, filter);\n};\n"],"names":[],"mappings":";;AAQA,MAAM,kBAAA,GAAqB,SAAA;AAC3B,MAAM,iBAAA,GAAoB,GAAA;AAE1B,MAAM,oBAAA,GAAuB,OAC3B,UAAA,EACA,MAAA,KACG;AACH,EAAA,MAAM,QAAsB,EAAC;AAC7B,EAAA,IAAI,MAAA;AAEJ,EAAA,GAAG;AACD,IAAA,MAAM,QAAA,GAAW,MAAM,UAAA,CAAW,aAAA;AAAA,MAChC,MAAA,GACI,EAAE,MAAA,EAAQ,KAAA,EAAO,iBAAA,EAAkB,GACnC,EAAE,MAAA,EAAQ,KAAA,EAAO,iBAAA,EAAmB,UAAA,EAAY,SAAA;AAAU,KAChE;AAEA,IAAA,KAAA,CAAM,IAAA,CAAK,GAAI,QAAA,CAAS,KAAsB,CAAA;AAC9C,IAAA,MAAA,GAAS,SAAS,QAAA,CAAS,UAAA;AAAA,EAC7B,CAAA,QAAS,MAAA;AAET,EAAA,OAAO,KAAA;AACT,CAAA;AAEO,MAAM,iBAAA,GAAoB,OAC/B,UAAA,EACA,MAAA,KACG;AACH,EAAA,IAAI,CAAC,MAAA,CAAO,QAAA,EAAU,WAAA,GAAc,kBAAkB,CAAA,EAAG;AACvD,IAAA,OAAO,CAAC,MAAoB,CAAA;AAAA,EAC9B;AAEA,EAAA,MAAM,MAAA,GAAS;AAAA,IACb,IAAA,EAAM,MAAA;AAAA,IACN,CAAC,wBAAwB,kBAAkB,CAAA,CAAE,GAC3C,MAAA,CAAO,QAAA,CAAS,YAAY,kBAAkB;AAAA,GAClD;AAEA,EAAA,OAAO,oBAAA,CAAqB,YAAY,MAAM,CAAA;AAChD;AAEO,MAAM,kBAAA,GAAqB,OAChC,UAAA,EACA,MAAA,KACG;AACH,EAAA,MAAM;AAAA,IACJ,QAAA,EAAU,EAAE,IAAA,EAAM,SAAA,EAAW,YAAY,iBAAA;AAAkB,GAC7D,GAAI,MAAA;AAEJ,EAAA,MAAM,MAAA,GAAS;AAAA,IACb,IAAA,EAAM,MAAA;AAAA,IACN,oBAAA,EAAsB;AAAA,MACpB,kBAAA,CAAmB;AAAA,QACjB,IAAA,EAAM,OAAA;AAAA,QACN,SAAA,EAAW,SAAA,CAAU,iBAAA,CAAkB,OAAO,CAAA;AAAA,QAC9C,IAAA,EAAM,SAAA,CAAU,iBAAA,CAAkB,OAAO;AAAA,OAC1C;AAAA;AACH,GACF;AAEA,EAAA,OAAO,oBAAA,CAAqB,YAAY,MAAM,CAAA;AAChD;;;;"}
|
|
@@ -3,15 +3,21 @@ import dayjs from 'dayjs';
|
|
|
3
3
|
const isTimeLineItem = (item) => {
|
|
4
4
|
return item !== void 0;
|
|
5
5
|
};
|
|
6
|
-
const
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
6
|
+
const createUsersByEmail = (users) => {
|
|
7
|
+
return users.reduce((usersByEmail, user) => {
|
|
8
|
+
const email = user.spec.profile?.email;
|
|
9
|
+
if (email) {
|
|
10
|
+
usersByEmail.set(email.toLowerCase(), user);
|
|
11
|
+
}
|
|
12
|
+
return usersByEmail;
|
|
13
|
+
}, /* @__PURE__ */ new Map());
|
|
11
14
|
};
|
|
12
|
-
const getUser = (
|
|
13
|
-
|
|
14
|
-
|
|
15
|
+
const getUser = (usersByEmail, userId) => {
|
|
16
|
+
return usersByEmail.get(userId.toLowerCase());
|
|
17
|
+
};
|
|
18
|
+
const getFullName = (usersByEmail, userId) => {
|
|
19
|
+
const user = getUser(usersByEmail, userId);
|
|
20
|
+
return user?.spec.profile?.displayName ?? userId;
|
|
15
21
|
};
|
|
16
22
|
const getScheduleItems = (availability, groupIndexMap) => {
|
|
17
23
|
return availability ? availability.pages.flatMap((p) => p).flatMap(
|
|
@@ -28,14 +34,15 @@ const getScheduleItems = (availability, groupIndexMap) => {
|
|
|
28
34
|
)
|
|
29
35
|
).filter(isTimeLineItem) : [];
|
|
30
36
|
};
|
|
31
|
-
const getGroups = (availability,
|
|
37
|
+
const getGroups = (availability, currentUserEmail, users) => {
|
|
32
38
|
if (!availability) {
|
|
33
39
|
return [[], /* @__PURE__ */ new Map()];
|
|
34
40
|
}
|
|
41
|
+
const usersByEmail = createUsersByEmail(users || []);
|
|
35
42
|
const flattenedSchedules = availability.pages.flatMap((p) => p).map((a, originalIndex) => ({
|
|
36
43
|
originalIndex,
|
|
37
44
|
schedule: a,
|
|
38
|
-
title: getFullName(
|
|
45
|
+
title: getFullName(usersByEmail, a.scheduleId || "")
|
|
39
46
|
}));
|
|
40
47
|
const sortedSchedules = [...flattenedSchedules].sort((a, b) => {
|
|
41
48
|
const titleA = a.title || "";
|
|
@@ -46,12 +53,12 @@ const getGroups = (availability, isUserEntity, users) => {
|
|
|
46
53
|
sortedSchedules.map((item, newIndex) => [item.originalIndex, newIndex])
|
|
47
54
|
);
|
|
48
55
|
const groups = sortedSchedules.map((item, newIndex) => {
|
|
49
|
-
const isHighlighted =
|
|
56
|
+
const isHighlighted = currentUserEmail && item.schedule.scheduleId?.toLowerCase() === currentUserEmail.toLowerCase();
|
|
50
57
|
return {
|
|
51
58
|
id: newIndex,
|
|
52
59
|
highlight: !!isHighlighted,
|
|
53
60
|
title: item.title || "",
|
|
54
|
-
entity: getUser(
|
|
61
|
+
entity: getUser(usersByEmail, item.schedule.scheduleId || ""),
|
|
55
62
|
height: 30
|
|
56
63
|
};
|
|
57
64
|
});
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"lib.esm.js","sources":["../../../src/components/CalendarCard/lib.ts"],"sourcesContent":["import { TimelineItemBase } from 'react-calendar-timeline';\nimport { UserEntity, UserEntityV1alpha1 } from '@backstage/catalog-model';\nimport { ScheduleInformation } from '@microsoft/microsoft-graph-types';\nimport { InfiniteData } from '@tanstack/react-query';\nimport dayjs from 'dayjs';\n\nconst isTimeLineItem = (\n item: TimelineItemBase<any> | undefined,\n): item is TimelineItemBase<any> => {\n return item !== undefined;\n};\n\nconst
|
|
1
|
+
{"version":3,"file":"lib.esm.js","sources":["../../../src/components/CalendarCard/lib.ts"],"sourcesContent":["import { TimelineItemBase } from 'react-calendar-timeline';\nimport { UserEntity, UserEntityV1alpha1 } from '@backstage/catalog-model';\nimport { ScheduleInformation } from '@microsoft/microsoft-graph-types';\nimport { InfiniteData } from '@tanstack/react-query';\nimport dayjs from 'dayjs';\n\nconst isTimeLineItem = (\n item: TimelineItemBase<any> | undefined,\n): item is TimelineItemBase<any> => {\n return item !== undefined;\n};\n\nconst createUsersByEmail = (users: UserEntity[]) => {\n return users.reduce((usersByEmail, user) => {\n const email = user.spec.profile?.email;\n\n if (email) {\n usersByEmail.set(email.toLowerCase(), user);\n }\n\n return usersByEmail;\n }, new Map<string, UserEntity>());\n};\n\nconst getUser = (usersByEmail: Map<string, UserEntity>, userId: string) => {\n return usersByEmail.get(userId.toLowerCase());\n};\n\nconst getFullName = (usersByEmail: Map<string, UserEntity>, userId: string) => {\n const user = getUser(usersByEmail, userId);\n\n return user?.spec.profile?.displayName ?? userId;\n};\n\nexport const getScheduleItems = (\n availability: InfiniteData<ScheduleInformation[], unknown> | undefined,\n groupIndexMap?: Map<number, number>,\n) => {\n return availability\n ? availability.pages\n .flatMap(p => p)\n // @ts-ignore\n .flatMap((a, i) =>\n // @ts-ignore\n a.scheduleItems\n // @ts-ignore\n ?.filter(scheduleItem => scheduleItem.status === 'oof')\n // @ts-ignore\n .map(s => ({\n id: Date.now().toString(36) + Math.random().toString(36).slice(2),\n group: groupIndexMap?.get(i) ?? i,\n start_time: dayjs(s.start?.dateTime!),\n end_time: dayjs(s.end?.dateTime!),\n canMove: false,\n canResize: false,\n })),\n )\n .filter(isTimeLineItem)\n : [];\n};\n\nexport const getGroups = (\n availability: InfiniteData<ScheduleInformation[], unknown> | undefined,\n currentUserEmail: string | false,\n users: UserEntityV1alpha1[] | undefined,\n): [\n groups: Array<{\n id: number;\n highlight: boolean;\n title: string;\n entity?: UserEntity;\n height: number;\n }>,\n groupIndexMap: Map<number, number>,\n] => {\n if (!availability) {\n return [[], new Map()];\n }\n\n const usersByEmail = createUsersByEmail(users || []);\n const flattenedSchedules = availability.pages\n .flatMap(p => p)\n .map((a, originalIndex) => ({\n originalIndex,\n schedule: a,\n title: getFullName(usersByEmail, a.scheduleId || ''),\n }));\n\n // Sort by title (display name) alphabetically\n const sortedSchedules = [...flattenedSchedules].sort((a, b) => {\n const titleA = a.title || '';\n const titleB = b.title || '';\n return titleA.localeCompare(titleB);\n });\n\n // Create mapping from original index to new sorted index\n const groupIndexMap = new Map(\n sortedSchedules.map((item, newIndex) => [item.originalIndex, newIndex]),\n );\n\n const groups = sortedSchedules.map((item, newIndex) => {\n const isHighlighted =\n currentUserEmail &&\n item.schedule.scheduleId?.toLowerCase() ===\n currentUserEmail.toLowerCase();\n\n return {\n id: newIndex,\n highlight: !!isHighlighted,\n title: item.title || '',\n entity: getUser(usersByEmail, item.schedule.scheduleId || ''),\n height: 30,\n };\n });\n\n return [groups, groupIndexMap];\n};\n"],"names":[],"mappings":"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"}
|