@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 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<react.JSX.Element, "core.reactElement", {}> | _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", {
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, Link, Avatar } from '@backstage/core-components';
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(availability, isUserEntity, users);
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
- return /* @__PURE__ */ jsx(
155
- Link,
156
- {
157
- to: `/catalog/default/user/${group.entity?.metadata.name}`,
158
- children: /* @__PURE__ */ jsxs("div", { className: "custom-group", children: [
159
- /* @__PURE__ */ jsx(
160
- Avatar,
161
- {
162
- displayName: group.entity?.metadata.displayName,
163
- picture: group.entity?.spec.profile?.picture,
164
- customStyles: {
165
- width: 30,
166
- height: 30
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
- const { items } = await catalogApi.getEntities({ filter });
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 (await catalogApi.getEntities({ filter })).items;
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 const { items } = await catalogApi.getEntities({ filter });\n return items as UserEntity[];\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 (await catalogApi.getEntities({ filter })).items as UserEntity[];\n};\n"],"names":[],"mappings":";;AAQA,MAAM,kBAAA,GAAqB,SAAA;AAEpB,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,MAAM,EAAE,OAAM,GAAI,MAAM,WAAW,WAAA,CAAY,EAAE,QAAQ,CAAA;AACzD,EAAA,OAAO,KAAA;AACT;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,OAAA,CAAQ,MAAM,UAAA,CAAW,WAAA,CAAY,EAAE,MAAA,EAAQ,CAAA,EAAG,KAAA;AACpD;;;;"}
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 getFullName = (users, userId) => {
7
- const [name] = userId.split("@");
8
- const user = users.find((u) => u.metadata.name === name);
9
- if (user) return user.spec.profile?.displayName;
10
- return userId;
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 = (users, userId) => {
13
- const [name] = userId.split("@");
14
- return users.find((u) => u.metadata.name === name);
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, isUserEntity, users) => {
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(users || [], a.scheduleId || "")
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 = isUserEntity && item.schedule.scheduleId && isUserEntity === item.schedule.scheduleId?.split("@")[0];
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(users || [], item.schedule.scheduleId || ""),
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 getFullName = (users: UserEntity[], userId: string) => {\n const [name] = userId.split('@');\n const user = users.find(u => u.metadata.name === name);\n\n if (user) return user.spec.profile?.displayName;\n return userId;\n};\n\nconst getUser = (users: UserEntity[], userId: string) => {\n const [name] = userId.split('@');\n\n return users.find(u => u.metadata.name === name);\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 isUserEntity: 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 flattenedSchedules = availability.pages\n .flatMap(p => p)\n .map((a, originalIndex) => ({\n originalIndex,\n schedule: a,\n title: getFullName(users || [], 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 isUserEntity &&\n item.schedule.scheduleId &&\n isUserEntity === item.schedule.scheduleId?.split('@')[0];\n\n return {\n id: newIndex,\n highlight: !!isHighlighted,\n title: item.title || '',\n entity: getUser(users || [], item.schedule.scheduleId || ''),\n height: 30,\n };\n });\n\n return [groups, groupIndexMap];\n};\n"],"names":[],"mappings":"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"}
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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"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@axis-backstage/plugin-vacation-calendar",
3
- "version": "0.11.1",
3
+ "version": "0.11.2",
4
4
  "exports": {
5
5
  ".": {
6
6
  "import": "./dist/index.esm.js",