diff --git a/.changeset/vacation-calendar-nfs-support.md b/.changeset/vacation-calendar-nfs-support.md new file mode 100644 index 0000000..cc48836 --- /dev/null +++ b/.changeset/vacation-calendar-nfs-support.md @@ -0,0 +1,5 @@ +--- +'@axis-backstage/plugin-vacation-calendar': patch +--- + +Added new frontend system (NFS) support via a new `./alpha` entry point. The plugin exposes an `EntityContentBlueprint` that renders the Out of Office calendar as an entity content tab, and an `ApiBlueprint` for the vacation calendar API. diff --git a/packages/app-next/package.json b/packages/app-next/package.json index 7eb1882..2134ae4 100644 --- a/packages/app-next/package.json +++ b/packages/app-next/package.json @@ -17,7 +17,7 @@ "@axis-backstage/plugin-jira-dashboard": "workspace:^", "@axis-backstage/plugin-jira-dashboard-common": "workspace:^", "@axis-backstage/plugin-readme": "workspace:^", - "@axis-backstage/plugin-statuspage": "workspace:^", + "@axis-backstage/plugin-vacation-calendar": "workspace:^", "@backstage/app-defaults": "^1.7.7", "@backstage/catalog-model": "^1.8.0", "@backstage/cli": "^0.36.1", diff --git a/plugins/vacation-calendar/README.md b/plugins/vacation-calendar/README.md index 90db3f6..eb5dcda 100644 --- a/plugins/vacation-calendar/README.md +++ b/plugins/vacation-calendar/README.md @@ -100,6 +100,45 @@ const userPage = ( ); ``` +## New Frontend System + +The plugin supports the [new Backstage frontend system](https://backstage.io/docs/frontend-system/) via the `./alpha` entry point. + +1. Install the plugin into your app: + +```bash +# From your Backstage root directory +yarn --cwd packages/app add @axis-backstage/plugin-vacation-calendar +``` + +2. Add the plugin to your app: + +```ts +// packages/app/src/App.tsx (or wherever you configure your frontend features) +import vacationCalendarPlugin from '@axis-backstage/plugin-vacation-calendar/alpha'; + +export const app = createApp({ + features: [ + // ... + vacationCalendarPlugin, + ], +}); +``` + +The plugin will automatically add an "Out Of Office" tab to Group and User entity pages. No manual route or API setup is required — the plugin handles both the API factory and the entity content tab as extensions. + +### Overriding the entity filter + +By default the tab is shown on `Group` and `User` entities. You can override this in your `app-config.yaml`: + +```yaml +app: + extensions: + - entity-content:vacation-calendar/entity: + config: + filter: 'kind: Component' +``` + ## Development The plugin has been added to the example app in this repository, meaning you'll be able to access it by running `yarn start` in the root directory, and then navigating to [/vacation-calendar](http://localhost:3000/vacation-calendar). diff --git a/plugins/vacation-calendar/package.json b/plugins/vacation-calendar/package.json index 61c7c22..b7d473e 100644 --- a/plugins/vacation-calendar/package.json +++ b/plugins/vacation-calendar/package.json @@ -1,8 +1,23 @@ { "name": "@axis-backstage/plugin-vacation-calendar", "version": "0.9.2", + "exports": { + ".": "./src/index.ts", + "./alpha": "./src/alpha.ts", + "./package.json": "./package.json" + }, "main": "src/index.ts", "types": "src/index.ts", + "typesVersions": { + "*": { + "alpha": [ + "src/alpha.ts" + ], + "package.json": [ + "package.json" + ] + } + }, "license": "Apache-2.0", "publishConfig": { "access": "public", @@ -32,6 +47,7 @@ "@backstage/core-components": "^0.18.9", "@backstage/core-plugin-api": "^1.12.5", "@backstage/errors": "^1.3.0", + "@backstage/frontend-plugin-api": "^0.16.2", "@backstage/plugin-catalog-react": "^2.1.4", "@backstage/theme": "^0.7.3", "@date-io/luxon": "3.0.0", diff --git a/plugins/vacation-calendar/report-alpha.api.md b/plugins/vacation-calendar/report-alpha.api.md new file mode 100644 index 0000000..8bd7753 --- /dev/null +++ b/plugins/vacation-calendar/report-alpha.api.md @@ -0,0 +1,125 @@ +## API Report File for "@axis-backstage/plugin-vacation-calendar" + +> Do not edit this file. It is a report generated by [API Extractor](https://api-extractor.com/). + +```ts +import { AnyApiFactory } from '@backstage/frontend-plugin-api'; +import { AnyRouteRefParams } from '@backstage/frontend-plugin-api'; +import { ApiFactory } from '@backstage/frontend-plugin-api'; +import { Entity } from '@backstage/catalog-model'; +import { ExtensionBlueprintParams } from '@backstage/frontend-plugin-api'; +import { ExtensionDataRef } from '@backstage/frontend-plugin-api'; +import { FilterPredicate } from '@backstage/filter-predicates'; +import { JSX as JSX_2 } from 'react'; +import { JSXElementConstructor } from 'react'; +import { OverridableExtensionDefinition } from '@backstage/frontend-plugin-api'; +import { OverridableFrontendPlugin } from '@backstage/frontend-plugin-api'; +import { ReactElement } from 'react'; +import { RouteRef } from '@backstage/core-plugin-api'; +import { RouteRef as RouteRef_2 } from '@backstage/frontend-plugin-api'; + +// @alpha +const _default: OverridableFrontendPlugin< + { + root: RouteRef; + }, + {}, + { + 'api:vacation-calendar': OverridableExtensionDefinition<{ + kind: 'api'; + name: undefined; + config: {}; + configInput: {}; + output: ExtensionDataRef; + inputs: {}; + params: < + TApi, + TImpl extends TApi, + TDeps extends { [name in string]: unknown }, + >( + params: ApiFactory, + ) => ExtensionBlueprintParams; + }>; + 'entity-content:vacation-calendar/entity': OverridableExtensionDefinition<{ + kind: 'entity-content'; + name: 'entity'; + config: { + path: string | undefined; + title: string | undefined; + filter: FilterPredicate | undefined; + group: string | false | undefined; + icon: string | undefined; + }; + configInput: { + path?: string | undefined; + title?: string | undefined; + filter?: FilterPredicate | undefined; + group?: string | false | undefined; + icon?: string | undefined; + }; + output: + | ExtensionDataRef + | ExtensionDataRef + | ExtensionDataRef< + RouteRef_2, + 'core.routing.ref', + { + optional: true; + } + > + | ExtensionDataRef< + (entity: Entity) => boolean, + 'catalog.entity-filter-function', + { + optional: true; + } + > + | ExtensionDataRef< + string, + 'catalog.entity-filter-expression', + { + optional: true; + } + > + | ExtensionDataRef + | ExtensionDataRef< + string, + 'catalog.entity-content-group', + { + optional: true; + } + > + | ExtensionDataRef< + string | ReactElement>, + 'catalog.entity-content-icon', + { + optional: true; + } + >; + inputs: {}; + params: { + defaultPath?: [Error: `Use the 'path' param instead`]; + path: string; + defaultTitle?: [Error: `Use the 'title' param instead`]; + title: string; + defaultGroup?: [Error: `Use the 'group' param instead`]; + group?: + | ( + | 'development' + | 'deployment' + | 'overview' + | 'documentation' + | 'operation' + | 'observability' + ) + | (string & {}); + icon?: string | ReactElement; + loader: () => Promise; + routeRef?: RouteRef_2; + filter?: string | FilterPredicate | ((entity: Entity) => boolean); + }; + }>; + } +>; +export default _default; +``` diff --git a/plugins/vacation-calendar/src/alpha.ts b/plugins/vacation-calendar/src/alpha.ts new file mode 100644 index 0000000..fb71c23 --- /dev/null +++ b/plugins/vacation-calendar/src/alpha.ts @@ -0,0 +1,20 @@ +import { createFrontendPlugin } from '@backstage/frontend-plugin-api'; +import { + entityVacationCalendarContent, + vacationCalendarApi, +} from './alpha/index'; +import { rootRouteRef } from './routes'; + +/** + * Frontend plugin that fetches and displays out of office events for entities. + * + * @alpha + * @packageDocumentation + */ +export default createFrontendPlugin({ + pluginId: 'vacation-calendar', + extensions: [entityVacationCalendarContent, vacationCalendarApi], + routes: { + root: rootRouteRef, + }, +}); diff --git a/plugins/vacation-calendar/src/alpha/apis.ts b/plugins/vacation-calendar/src/alpha/apis.ts new file mode 100644 index 0000000..df44949 --- /dev/null +++ b/plugins/vacation-calendar/src/alpha/apis.ts @@ -0,0 +1,17 @@ +import { ApiBlueprint, fetchApiRef } from '@backstage/frontend-plugin-api'; +import { microsoftAuthApiRef } from '@backstage/core-plugin-api'; +import { vacationCalendarApiRef } from '../api'; +import { VacationCalendarApiClient } from '../api'; + +/** + * @alpha + */ +export const vacationCalendarApi = ApiBlueprint.make({ + params: defineParams => + defineParams({ + api: vacationCalendarApiRef, + deps: { authApi: microsoftAuthApiRef, fetchApi: fetchApiRef }, + factory: ({ authApi, fetchApi }) => + new VacationCalendarApiClient({ authApi, fetchApi }), + }), +}); diff --git a/plugins/vacation-calendar/src/alpha/entityContent.tsx b/plugins/vacation-calendar/src/alpha/entityContent.tsx new file mode 100644 index 0000000..8a15eb9 --- /dev/null +++ b/plugins/vacation-calendar/src/alpha/entityContent.tsx @@ -0,0 +1,18 @@ +import { isGroupEntity, isUserEntity } from '@backstage/catalog-model'; +import { EntityContentBlueprint } from '@backstage/plugin-catalog-react/alpha'; +import { rootRouteRef } from '../routes'; + +/** + * @alpha + */ +export const entityVacationCalendarContent = EntityContentBlueprint.make({ + name: 'entity', + params: { + path: '/vacation-calendar', + title: 'Out Of Office', + routeRef: rootRouteRef, + filter: entity => isGroupEntity(entity) || isUserEntity(entity), + loader: async () => + import('../components').then(m => ), + }, +}); diff --git a/plugins/vacation-calendar/src/alpha/index.ts b/plugins/vacation-calendar/src/alpha/index.ts new file mode 100644 index 0000000..d3bf0e6 --- /dev/null +++ b/plugins/vacation-calendar/src/alpha/index.ts @@ -0,0 +1,2 @@ +export * from './apis'; +export * from './entityContent'; diff --git a/plugins/vacation-calendar/src/components/CalendarCard/CalendarCard.tsx b/plugins/vacation-calendar/src/components/CalendarCard/CalendarCard.tsx index 7be46b8..8fd1df3 100644 --- a/plugins/vacation-calendar/src/components/CalendarCard/CalendarCard.tsx +++ b/plugins/vacation-calendar/src/components/CalendarCard/CalendarCard.tsx @@ -1,3 +1,4 @@ +import 'react-calendar-timeline/lib/Timeline.css'; import { ReactNode, useState } from 'react'; import useAsync from 'react-use/lib/useAsync'; import Timeline, { diff --git a/yarn.lock b/yarn.lock index 769321f..7558a5c 100644 --- a/yarn.lock +++ b/yarn.lock @@ -2200,6 +2200,7 @@ __metadata: "@backstage/core-plugin-api": "npm:^1.12.5" "@backstage/dev-utils": "npm:^1.1.22" "@backstage/errors": "npm:^1.3.0" + "@backstage/frontend-plugin-api": "npm:^0.16.2" "@backstage/plugin-catalog-react": "npm:^2.1.4" "@backstage/test-utils": "npm:^1.7.17" "@backstage/theme": "npm:^0.7.3" @@ -15308,7 +15309,7 @@ __metadata: "@axis-backstage/plugin-jira-dashboard": "workspace:^" "@axis-backstage/plugin-jira-dashboard-common": "workspace:^" "@axis-backstage/plugin-readme": "workspace:^" - "@axis-backstage/plugin-statuspage": "workspace:^" + "@axis-backstage/plugin-vacation-calendar": "workspace:^" "@backstage/app-defaults": "npm:^1.7.7" "@backstage/catalog-model": "npm:^1.8.0" "@backstage/cli": "npm:^0.36.1"