@red-hat-developer-hub/backstage-plugin-quickstart 1.3.0 → 1.5.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.
package/CHANGELOG.md CHANGED
@@ -1,5 +1,21 @@
1
1
  # @red-hat-developer-hub/backstage-plugin-quickstart
2
2
 
3
+ ## 1.5.0
4
+
5
+ ### Minor Changes
6
+
7
+ - d49b252: Backstage version bump to v1.42.5
8
+
9
+ ### Patch Changes
10
+
11
+ - e8cc528: Updated dependency `@red-hat-developer-hub/backstage-plugin-theme` to `^0.10.0` and move it from plugin dependencies to devDependencies.
12
+
13
+ ## 1.4.0
14
+
15
+ ### Minor Changes
16
+
17
+ - 47fd25f: Enabled Quickstart items for developer role.
18
+
3
19
  ## 1.3.0
4
20
 
5
21
  ### Minor Changes
package/README.md CHANGED
@@ -9,6 +9,7 @@ The Quickstart plugin provides a guided onboarding experience for new users of R
9
9
  - **Configurable Content**: Define custom quickstart items through app configuration
10
10
  - **Visual Progress Indicator**: Shows overall completion progress with a progress bar
11
11
  - **Call-to-Action Support**: Each step can include clickable action buttons
12
+ - **Role-Based Access Control**: Show different quickstart items based on user roles (admin/developer)
12
13
 
13
14
  ## Installation
14
15
 
@@ -44,18 +45,28 @@ app:
44
45
  - title: 'Welcome to Developer Hub'
45
46
  description: 'Learn the basics of navigating the Developer Hub interface'
46
47
  icon: 'home'
48
+ roles: ['admin', 'developer'] # Show to both roles
47
49
  cta:
48
50
  text: 'Get Started'
49
51
  link: '/catalog'
52
+ - title: 'Configure RBAC Policies'
53
+ description: 'Set up role-based access control for your organization'
54
+ icon: 'security'
55
+ roles: ['admin'] # Admin-only quickstart item
56
+ cta:
57
+ text: 'Configure RBAC'
58
+ link: '/rbac'
50
59
  - title: 'Create Your First Component'
51
60
  description: 'Follow our guide to register your first software component'
52
61
  icon: 'code'
62
+ # No roles specified - defaults to 'admin'
53
63
  cta:
54
64
  text: 'Create Component'
55
65
  link: '/catalog-import'
56
66
  - title: 'Explore Templates'
57
67
  description: 'Discover available software templates to bootstrap new projects'
58
68
  icon: 'template'
69
+ roles: ['developer'] # Developer-only quickstart item
59
70
  cta:
60
71
  text: 'Browse Templates'
61
72
  link: '/create'
@@ -68,10 +79,56 @@ Each quickstart item supports the following properties:
68
79
  - `title` (required): The display title for the quickstart step
69
80
  - `description` (required): A brief description of what the step covers
70
81
  - `icon` (optional): Icon identifier (supports Material UI icons)
82
+ - `roles` (optional): Array of user roles that should see this quickstart item. Supported values: `['admin', 'developer']`. If not specified, defaults to `['admin']`
71
83
  - `cta` (optional): Call-to-action object with:
72
84
  - `text`: Button text
73
85
  - `link`: Target URL or route
74
86
 
87
+ ## Role-Based Access Control (RBAC)
88
+
89
+ The quickstart plugin integrates with Backstage's RBAC system to show different quickstart items based on user roles.
90
+
91
+ ### User Role Determination
92
+
93
+ The plugin determines user roles using the following logic:
94
+
95
+ - **When RBAC is disabled** (`permission.enabled: false` or not configured): Users are assumed to be platform engineers setting up RHDH and are assigned the `admin` role
96
+ - **When RBAC is enabled** (`permission.enabled: true`): User roles are determined based on permissions:
97
+ - Users with `policy.entity.create` permission are assigned the `admin` role
98
+ - Users without this permission are assigned the `developer` role
99
+
100
+ ### Supported Roles
101
+
102
+ - **`admin`**: Platform engineers, administrators, and users with elevated permissions
103
+ - **`developer`**: Regular developers and users with standard permissions
104
+
105
+ ### Role Assignment Behavior
106
+
107
+ - Quickstart items without a `roles` property default to `['admin']`
108
+ - Items can specify multiple roles: `roles: ['admin', 'developer']`
109
+ - Users only see quickstart items that match their assigned role
110
+
111
+ ### Configuration Example
112
+
113
+ Enable RBAC in your `app-config.yaml`:
114
+
115
+ ```yaml
116
+ permission:
117
+ enabled: true
118
+
119
+ app:
120
+ quickstart:
121
+ - title: 'Platform Configuration'
122
+ roles: ['admin']
123
+ # Only admins see this
124
+ - title: 'Getting Started as Developer'
125
+ roles: ['developer']
126
+ # Only developers see this
127
+ - title: 'Universal Welcome Guide'
128
+ roles: ['admin', 'developer']
129
+ # Both roles see this
130
+ ```
131
+
75
132
  ## Usage
76
133
 
77
134
  ### Using the Context Hook
@@ -8,7 +8,8 @@ import { useState, useCallback, useEffect } from 'react';
8
8
 
9
9
  const Quickstart = ({
10
10
  quickstartItems,
11
- handleDrawerClose
11
+ handleDrawerClose,
12
+ isLoading
12
13
  }) => {
13
14
  const itemCount = quickstartItems.length;
14
15
  const [progress, setProgress] = useState(0);
@@ -54,7 +55,8 @@ const Quickstart = ({
54
55
  setProgress: () => {
55
56
  const newProgress = calculateProgress();
56
57
  setProgress(newProgress);
57
- }
58
+ },
59
+ isLoading
58
60
  }
59
61
  )
60
62
  ]
@@ -1 +1 @@
1
- {"version":3,"file":"Quickstart.esm.js","sources":["../../src/components/Quickstart.tsx"],"sourcesContent":["/*\n * Copyright Red Hat, Inc.\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 */\n\nimport Box from '@mui/material/Box';\nimport { QuickstartHeader } from './QuickstartHeader';\nimport Divider from '@mui/material/Divider';\nimport { QuickstartContent } from './QuickstartContent/QuickstartContent';\nimport { QuickstartFooter } from './QuickstartFooter';\nimport { useEffect, useState, useCallback } from 'react';\nimport { QuickstartItemData } from '../types';\n\ntype QuickstartProps = {\n quickstartItems: QuickstartItemData[];\n handleDrawerClose: () => void;\n};\n\nexport const Quickstart = ({\n quickstartItems,\n handleDrawerClose,\n}: QuickstartProps) => {\n const itemCount = quickstartItems.length;\n const [progress, setProgress] = useState<number>(0);\n\n const calculateProgress = useCallback(() => {\n const completedCount = quickstartItems.filter((item, index) => {\n const itemKey = `${item.title}-${index}`;\n const stepState = localStorage.getItem(itemKey);\n return stepState === 'true';\n }).length;\n\n const percentage = (completedCount / itemCount) * 100;\n return Math.round(percentage * 100) / 100;\n }, [quickstartItems, itemCount]);\n\n useEffect(() => {\n const calculatedProgress = calculateProgress();\n setProgress(calculatedProgress);\n localStorage.setItem('progressState', calculatedProgress.toString());\n }, [quickstartItems, itemCount, calculateProgress]);\n\n return (\n <Box\n sx={{\n height: '100%',\n display: 'flex',\n flexDirection: 'column',\n }}\n >\n <Box\n sx={{\n flex: 1,\n padding: theme => theme.spacing(3),\n overflowY: 'auto',\n }}\n >\n <QuickstartHeader />\n <Divider />\n <QuickstartContent\n quickstartItems={quickstartItems}\n itemCount={itemCount}\n setProgress={() => {\n const newProgress = calculateProgress();\n setProgress(newProgress);\n }}\n />\n </Box>\n <QuickstartFooter\n handleDrawerClose={handleDrawerClose}\n progress={progress}\n />\n </Box>\n );\n};\n"],"names":[],"mappings":";;;;;;;;AA6BO,MAAM,aAAa,CAAC;AAAA,EACzB,eAAA;AAAA,EACA;AACF,CAAuB,KAAA;AACrB,EAAA,MAAM,YAAY,eAAgB,CAAA,MAAA;AAClC,EAAA,MAAM,CAAC,QAAA,EAAU,WAAW,CAAA,GAAI,SAAiB,CAAC,CAAA;AAElD,EAAM,MAAA,iBAAA,GAAoB,YAAY,MAAM;AAC1C,IAAA,MAAM,cAAiB,GAAA,eAAA,CAAgB,MAAO,CAAA,CAAC,MAAM,KAAU,KAAA;AAC7D,MAAA,MAAM,OAAU,GAAA,CAAA,EAAG,IAAK,CAAA,KAAK,IAAI,KAAK,CAAA,CAAA;AACtC,MAAM,MAAA,SAAA,GAAY,YAAa,CAAA,OAAA,CAAQ,OAAO,CAAA;AAC9C,MAAA,OAAO,SAAc,KAAA,MAAA;AAAA,KACtB,CAAE,CAAA,MAAA;AAEH,IAAM,MAAA,UAAA,GAAc,iBAAiB,SAAa,GAAA,GAAA;AAClD,IAAA,OAAO,IAAK,CAAA,KAAA,CAAM,UAAa,GAAA,GAAG,CAAI,GAAA,GAAA;AAAA,GACrC,EAAA,CAAC,eAAiB,EAAA,SAAS,CAAC,CAAA;AAE/B,EAAA,SAAA,CAAU,MAAM;AACd,IAAA,MAAM,qBAAqB,iBAAkB,EAAA;AAC7C,IAAA,WAAA,CAAY,kBAAkB,CAAA;AAC9B,IAAA,YAAA,CAAa,OAAQ,CAAA,eAAA,EAAiB,kBAAmB,CAAA,QAAA,EAAU,CAAA;AAAA,GAClE,EAAA,CAAC,eAAiB,EAAA,SAAA,EAAW,iBAAiB,CAAC,CAAA;AAElD,EACE,uBAAA,IAAA;AAAA,IAAC,GAAA;AAAA,IAAA;AAAA,MACC,EAAI,EAAA;AAAA,QACF,MAAQ,EAAA,MAAA;AAAA,QACR,OAAS,EAAA,MAAA;AAAA,QACT,aAAe,EAAA;AAAA,OACjB;AAAA,MAEA,QAAA,EAAA;AAAA,wBAAA,IAAA;AAAA,UAAC,GAAA;AAAA,UAAA;AAAA,YACC,EAAI,EAAA;AAAA,cACF,IAAM,EAAA,CAAA;AAAA,cACN,OAAS,EAAA,CAAA,KAAA,KAAS,KAAM,CAAA,OAAA,CAAQ,CAAC,CAAA;AAAA,cACjC,SAAW,EAAA;AAAA,aACb;AAAA,YAEA,QAAA,EAAA;AAAA,8BAAA,GAAA,CAAC,gBAAiB,EAAA,EAAA,CAAA;AAAA,kCACjB,OAAQ,EAAA,EAAA,CAAA;AAAA,8BACT,GAAA;AAAA,gBAAC,iBAAA;AAAA,gBAAA;AAAA,kBACC,eAAA;AAAA,kBACA,SAAA;AAAA,kBACA,aAAa,MAAM;AACjB,oBAAA,MAAM,cAAc,iBAAkB,EAAA;AACtC,oBAAA,WAAA,CAAY,WAAW,CAAA;AAAA;AACzB;AAAA;AACF;AAAA;AAAA,SACF;AAAA,wBACA,GAAA;AAAA,UAAC,gBAAA;AAAA,UAAA;AAAA,YACC,iBAAA;AAAA,YACA;AAAA;AAAA;AACF;AAAA;AAAA,GACF;AAEJ;;;;"}
1
+ {"version":3,"file":"Quickstart.esm.js","sources":["../../src/components/Quickstart.tsx"],"sourcesContent":["/*\n * Copyright Red Hat, Inc.\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 */\n\nimport Box from '@mui/material/Box';\nimport { QuickstartHeader } from './QuickstartHeader';\nimport Divider from '@mui/material/Divider';\nimport { QuickstartContent } from './QuickstartContent/QuickstartContent';\nimport { QuickstartFooter } from './QuickstartFooter';\nimport { useEffect, useState, useCallback } from 'react';\nimport { QuickstartItemData } from '../types';\n\ntype QuickstartProps = {\n quickstartItems: QuickstartItemData[];\n handleDrawerClose: () => void;\n isLoading: boolean;\n};\n\nexport const Quickstart = ({\n quickstartItems,\n handleDrawerClose,\n isLoading,\n}: QuickstartProps) => {\n const itemCount = quickstartItems.length;\n const [progress, setProgress] = useState<number>(0);\n\n const calculateProgress = useCallback(() => {\n const completedCount = quickstartItems.filter((item, index) => {\n const itemKey = `${item.title}-${index}`;\n const stepState = localStorage.getItem(itemKey);\n return stepState === 'true';\n }).length;\n\n const percentage = (completedCount / itemCount) * 100;\n return Math.round(percentage * 100) / 100;\n }, [quickstartItems, itemCount]);\n\n useEffect(() => {\n const calculatedProgress = calculateProgress();\n setProgress(calculatedProgress);\n localStorage.setItem('progressState', calculatedProgress.toString());\n }, [quickstartItems, itemCount, calculateProgress]);\n\n return (\n <Box\n sx={{\n height: '100%',\n display: 'flex',\n flexDirection: 'column',\n }}\n >\n <Box\n sx={{\n flex: 1,\n padding: theme => theme.spacing(3),\n overflowY: 'auto',\n }}\n >\n <QuickstartHeader />\n <Divider />\n <QuickstartContent\n quickstartItems={quickstartItems}\n itemCount={itemCount}\n setProgress={() => {\n const newProgress = calculateProgress();\n setProgress(newProgress);\n }}\n isLoading={isLoading}\n />\n </Box>\n <QuickstartFooter\n handleDrawerClose={handleDrawerClose}\n progress={progress}\n />\n </Box>\n );\n};\n"],"names":[],"mappings":";;;;;;;;AA8BO,MAAM,aAAa,CAAC;AAAA,EACzB,eAAA;AAAA,EACA,iBAAA;AAAA,EACA;AACF,CAAuB,KAAA;AACrB,EAAA,MAAM,YAAY,eAAgB,CAAA,MAAA;AAClC,EAAA,MAAM,CAAC,QAAA,EAAU,WAAW,CAAA,GAAI,SAAiB,CAAC,CAAA;AAElD,EAAM,MAAA,iBAAA,GAAoB,YAAY,MAAM;AAC1C,IAAA,MAAM,cAAiB,GAAA,eAAA,CAAgB,MAAO,CAAA,CAAC,MAAM,KAAU,KAAA;AAC7D,MAAA,MAAM,OAAU,GAAA,CAAA,EAAG,IAAK,CAAA,KAAK,IAAI,KAAK,CAAA,CAAA;AACtC,MAAM,MAAA,SAAA,GAAY,YAAa,CAAA,OAAA,CAAQ,OAAO,CAAA;AAC9C,MAAA,OAAO,SAAc,KAAA,MAAA;AAAA,KACtB,CAAE,CAAA,MAAA;AAEH,IAAM,MAAA,UAAA,GAAc,iBAAiB,SAAa,GAAA,GAAA;AAClD,IAAA,OAAO,IAAK,CAAA,KAAA,CAAM,UAAa,GAAA,GAAG,CAAI,GAAA,GAAA;AAAA,GACrC,EAAA,CAAC,eAAiB,EAAA,SAAS,CAAC,CAAA;AAE/B,EAAA,SAAA,CAAU,MAAM;AACd,IAAA,MAAM,qBAAqB,iBAAkB,EAAA;AAC7C,IAAA,WAAA,CAAY,kBAAkB,CAAA;AAC9B,IAAA,YAAA,CAAa,OAAQ,CAAA,eAAA,EAAiB,kBAAmB,CAAA,QAAA,EAAU,CAAA;AAAA,GAClE,EAAA,CAAC,eAAiB,EAAA,SAAA,EAAW,iBAAiB,CAAC,CAAA;AAElD,EACE,uBAAA,IAAA;AAAA,IAAC,GAAA;AAAA,IAAA;AAAA,MACC,EAAI,EAAA;AAAA,QACF,MAAQ,EAAA,MAAA;AAAA,QACR,OAAS,EAAA,MAAA;AAAA,QACT,aAAe,EAAA;AAAA,OACjB;AAAA,MAEA,QAAA,EAAA;AAAA,wBAAA,IAAA;AAAA,UAAC,GAAA;AAAA,UAAA;AAAA,YACC,EAAI,EAAA;AAAA,cACF,IAAM,EAAA,CAAA;AAAA,cACN,OAAS,EAAA,CAAA,KAAA,KAAS,KAAM,CAAA,OAAA,CAAQ,CAAC,CAAA;AAAA,cACjC,SAAW,EAAA;AAAA,aACb;AAAA,YAEA,QAAA,EAAA;AAAA,8BAAA,GAAA,CAAC,gBAAiB,EAAA,EAAA,CAAA;AAAA,kCACjB,OAAQ,EAAA,EAAA,CAAA;AAAA,8BACT,GAAA;AAAA,gBAAC,iBAAA;AAAA,gBAAA;AAAA,kBACC,eAAA;AAAA,kBACA,SAAA;AAAA,kBACA,aAAa,MAAM;AACjB,oBAAA,MAAM,cAAc,iBAAkB,EAAA;AACtC,oBAAA,WAAA,CAAY,WAAW,CAAA;AAAA,mBACzB;AAAA,kBACA;AAAA;AAAA;AACF;AAAA;AAAA,SACF;AAAA,wBACA,GAAA;AAAA,UAAC,gBAAA;AAAA,UAAA;AAAA,YACC,iBAAA;AAAA,YACA;AAAA;AAAA;AACF;AAAA;AAAA,GACF;AAEJ;;;;"}
@@ -5,9 +5,11 @@ import Box from '@mui/material/Box';
5
5
  import Typography from '@mui/material/Typography';
6
6
  import { useTheme } from '@mui/material/styles';
7
7
  import WavingHandIcon from '@mui/icons-material/WavingHandOutlined';
8
- import { useQuickstartPermission } from '../../hooks/useQuickstartPermission.esm.js';
9
8
  import { useQuickstartDrawerContext } from '../../hooks/useQuickstartDrawerContext.esm.js';
10
9
  import { useTranslation } from '../../hooks/useTranslation.esm.js';
10
+ import { useApiHolder, configApiRef } from '@backstage/core-plugin-api';
11
+ import { filterQuickstartItemsByRole } from '../../utils/filterQuickstartItems.esm.js';
12
+ import { useQuickstartRole } from '../../hooks/useQuickstartRole.esm.js';
11
13
 
12
14
  const QuickstartButton = ({
13
15
  title,
@@ -16,15 +18,22 @@ const QuickstartButton = ({
16
18
  }
17
19
  }) => {
18
20
  const { t } = useTranslation();
19
- const isAllowed = useQuickstartPermission();
20
21
  const { toggleDrawer } = useQuickstartDrawerContext();
21
22
  const theme = useTheme();
23
+ const apiHolder = useApiHolder();
24
+ const config = apiHolder.get(configApiRef);
25
+ const quickstartItems = config?.has("app.quickstart") ? config.get("app.quickstart") : [];
26
+ const { isLoading, userRole } = useQuickstartRole();
27
+ const filteredItems = !isLoading && userRole ? filterQuickstartItemsByRole(quickstartItems, userRole) : [];
22
28
  const defaultTitle = t("button.quickstart");
23
29
  const handleClick = useCallback(() => {
24
30
  toggleDrawer();
25
31
  onClick();
26
32
  }, [toggleDrawer, onClick]);
27
- return isAllowed ? /* @__PURE__ */ jsx(
33
+ if (!isLoading && filteredItems.length === 0) {
34
+ return null;
35
+ }
36
+ return /* @__PURE__ */ jsx(
28
37
  MenuItem,
29
38
  {
30
39
  sx: {
@@ -74,7 +83,7 @@ const QuickstartButton = ({
74
83
  }
75
84
  )
76
85
  }
77
- ) : null;
86
+ );
78
87
  };
79
88
 
80
89
  export { QuickstartButton };
@@ -1 +1 @@
1
- {"version":3,"file":"QuickstartButton.esm.js","sources":["../../../src/components/QuickstartButton/QuickstartButton.tsx"],"sourcesContent":["/*\n * Copyright Red Hat, Inc.\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 */\n\nimport { CSSProperties, useCallback } from 'react';\nimport MenuItem from '@mui/material/MenuItem';\nimport Box from '@mui/material/Box';\nimport Typography from '@mui/material/Typography';\nimport { useTheme } from '@mui/material/styles';\nimport WavingHandIcon from '@mui/icons-material/WavingHandOutlined';\nimport { useQuickstartPermission } from '../../hooks/useQuickstartPermission';\nimport { useQuickstartDrawerContext } from '../../hooks/useQuickstartDrawerContext';\nimport { useTranslation } from '../../hooks/useTranslation';\n\n/**\n * Props for the QuickstartButton component\n * @public\n */\nexport interface QuickstartButtonProps {\n /**\n * The title text to display in the button\n * @defaultValue 'Quick start'\n */\n title?: string;\n /**\n * Custom CSS styles to apply to the button\n */\n style?: CSSProperties;\n /**\n * Optional callback function to execute when the button is clicked\n */\n onClick?: () => void;\n}\n\n/**\n * @public\n */\nexport const QuickstartButton = ({\n title,\n style,\n onClick = () => {},\n}: QuickstartButtonProps) => {\n const { t } = useTranslation();\n const isAllowed = useQuickstartPermission();\n const { toggleDrawer } = useQuickstartDrawerContext();\n const theme = useTheme();\n\n const defaultTitle = t('button.quickstart');\n\n const handleClick = useCallback(() => {\n toggleDrawer();\n onClick();\n }, [toggleDrawer, onClick]);\n\n return isAllowed ? (\n <MenuItem\n sx={{\n width: '100%',\n color: 'inherit',\n ...style,\n }}\n data-testid=\"quickstart-button\"\n onClick={handleClick}\n >\n <Box\n sx={{\n display: 'flex',\n alignItems: 'center',\n justifyContent: 'space-between',\n margin: '8px 0',\n color: 'inherit',\n width: '100%',\n }}\n >\n <Box sx={{ display: 'flex', alignItems: 'center' }}>\n <WavingHandIcon\n sx={{\n fontSize: 20,\n marginRight: '0.5rem',\n flexShrink: 0,\n display: 'flex',\n alignItems: 'center',\n color:\n theme.palette.mode === 'dark'\n ? theme.palette.text.primary\n : theme.palette.text.disabled,\n }}\n />\n <Box\n sx={{\n display: 'flex',\n flexDirection: 'column',\n justifyContent: 'center',\n }}\n >\n <Typography variant=\"body2\" color={theme.palette.text.primary}>\n {title || defaultTitle}\n </Typography>\n </Box>\n </Box>\n </Box>\n </MenuItem>\n ) : null;\n};\n"],"names":[],"mappings":";;;;;;;;;;;AAiDO,MAAM,mBAAmB,CAAC;AAAA,EAC/B,KAAA;AAAA,EACA,KAAA;AAAA,EACA,UAAU,MAAM;AAAA;AAClB,CAA6B,KAAA;AAC3B,EAAM,MAAA,EAAE,CAAE,EAAA,GAAI,cAAe,EAAA;AAC7B,EAAA,MAAM,YAAY,uBAAwB,EAAA;AAC1C,EAAM,MAAA,EAAE,YAAa,EAAA,GAAI,0BAA2B,EAAA;AACpD,EAAA,MAAM,QAAQ,QAAS,EAAA;AAEvB,EAAM,MAAA,YAAA,GAAe,EAAE,mBAAmB,CAAA;AAE1C,EAAM,MAAA,WAAA,GAAc,YAAY,MAAM;AACpC,IAAa,YAAA,EAAA;AACb,IAAQ,OAAA,EAAA;AAAA,GACP,EAAA,CAAC,YAAc,EAAA,OAAO,CAAC,CAAA;AAE1B,EAAA,OAAO,SACL,mBAAA,GAAA;AAAA,IAAC,QAAA;AAAA,IAAA;AAAA,MACC,EAAI,EAAA;AAAA,QACF,KAAO,EAAA,MAAA;AAAA,QACP,KAAO,EAAA,SAAA;AAAA,QACP,GAAG;AAAA,OACL;AAAA,MACA,aAAY,EAAA,mBAAA;AAAA,MACZ,OAAS,EAAA,WAAA;AAAA,MAET,QAAA,kBAAA,GAAA;AAAA,QAAC,GAAA;AAAA,QAAA;AAAA,UACC,EAAI,EAAA;AAAA,YACF,OAAS,EAAA,MAAA;AAAA,YACT,UAAY,EAAA,QAAA;AAAA,YACZ,cAAgB,EAAA,eAAA;AAAA,YAChB,MAAQ,EAAA,OAAA;AAAA,YACR,KAAO,EAAA,SAAA;AAAA,YACP,KAAO,EAAA;AAAA,WACT;AAAA,UAEA,QAAA,kBAAA,IAAA,CAAC,OAAI,EAAI,EAAA,EAAE,SAAS,MAAQ,EAAA,UAAA,EAAY,UACtC,EAAA,QAAA,EAAA;AAAA,4BAAA,GAAA;AAAA,cAAC,cAAA;AAAA,cAAA;AAAA,gBACC,EAAI,EAAA;AAAA,kBACF,QAAU,EAAA,EAAA;AAAA,kBACV,WAAa,EAAA,QAAA;AAAA,kBACb,UAAY,EAAA,CAAA;AAAA,kBACZ,OAAS,EAAA,MAAA;AAAA,kBACT,UAAY,EAAA,QAAA;AAAA,kBACZ,KAAA,EACE,KAAM,CAAA,OAAA,CAAQ,IAAS,KAAA,MAAA,GACnB,KAAM,CAAA,OAAA,CAAQ,IAAK,CAAA,OAAA,GACnB,KAAM,CAAA,OAAA,CAAQ,IAAK,CAAA;AAAA;AAC3B;AAAA,aACF;AAAA,4BACA,GAAA;AAAA,cAAC,GAAA;AAAA,cAAA;AAAA,gBACC,EAAI,EAAA;AAAA,kBACF,OAAS,EAAA,MAAA;AAAA,kBACT,aAAe,EAAA,QAAA;AAAA,kBACf,cAAgB,EAAA;AAAA,iBAClB;AAAA,gBAEA,QAAA,kBAAA,GAAA,CAAC,UAAW,EAAA,EAAA,OAAA,EAAQ,OAAQ,EAAA,KAAA,EAAO,MAAM,OAAQ,CAAA,IAAA,CAAK,OACnD,EAAA,QAAA,EAAA,KAAA,IAAS,YACZ,EAAA;AAAA;AAAA;AACF,WACF,EAAA;AAAA;AAAA;AACF;AAAA,GAEA,GAAA,IAAA;AACN;;;;"}
1
+ {"version":3,"file":"QuickstartButton.esm.js","sources":["../../../src/components/QuickstartButton/QuickstartButton.tsx"],"sourcesContent":["/*\n * Copyright Red Hat, Inc.\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 */\n\nimport { CSSProperties, useCallback } from 'react';\nimport MenuItem from '@mui/material/MenuItem';\nimport Box from '@mui/material/Box';\nimport Typography from '@mui/material/Typography';\nimport { useTheme } from '@mui/material/styles';\nimport WavingHandIcon from '@mui/icons-material/WavingHandOutlined';\nimport { useQuickstartDrawerContext } from '../../hooks/useQuickstartDrawerContext';\nimport { useTranslation } from '../../hooks/useTranslation';\nimport { configApiRef, useApiHolder } from '@backstage/core-plugin-api';\nimport { QuickstartItemData } from '../../types';\nimport { filterQuickstartItemsByRole } from '../../utils';\nimport { useQuickstartRole } from '../../hooks/useQuickstartRole';\n\n/**\n * Props for the QuickstartButton component\n * @public\n */\nexport interface QuickstartButtonProps {\n /**\n * The title text to display in the button\n * @defaultValue 'Quick start'\n */\n title?: string;\n /**\n * Custom CSS styles to apply to the button\n */\n style?: CSSProperties;\n /**\n * Optional callback function to execute when the button is clicked\n */\n onClick?: () => void;\n}\n\n/**\n * @public\n */\nexport const QuickstartButton = ({\n title,\n style,\n onClick = () => {},\n}: QuickstartButtonProps) => {\n // All hooks must be called at the top level, before any early returns\n const { t } = useTranslation();\n const { toggleDrawer } = useQuickstartDrawerContext();\n const theme = useTheme();\n\n // Check if there are any quickstart items available for the current user\n const apiHolder = useApiHolder();\n const config = apiHolder.get(configApiRef);\n const quickstartItems: QuickstartItemData[] = config?.has('app.quickstart')\n ? config.get('app.quickstart')\n : [];\n\n const { isLoading, userRole } = useQuickstartRole();\n const filteredItems =\n !isLoading && userRole\n ? filterQuickstartItemsByRole(quickstartItems, userRole)\n : [];\n\n const defaultTitle = t('button.quickstart');\n\n const handleClick = useCallback(() => {\n toggleDrawer();\n onClick();\n }, [toggleDrawer, onClick]);\n\n // Hide the button if there are no quickstart items for the user\n if (!isLoading && filteredItems.length === 0) {\n return null;\n }\n\n return (\n <MenuItem\n sx={{\n width: '100%',\n color: 'inherit',\n ...style,\n }}\n data-testid=\"quickstart-button\"\n onClick={handleClick}\n >\n <Box\n sx={{\n display: 'flex',\n alignItems: 'center',\n justifyContent: 'space-between',\n margin: '8px 0',\n color: 'inherit',\n width: '100%',\n }}\n >\n <Box sx={{ display: 'flex', alignItems: 'center' }}>\n <WavingHandIcon\n sx={{\n fontSize: 20,\n marginRight: '0.5rem',\n flexShrink: 0,\n display: 'flex',\n alignItems: 'center',\n color:\n theme.palette.mode === 'dark'\n ? theme.palette.text.primary\n : theme.palette.text.disabled,\n }}\n />\n <Box\n sx={{\n display: 'flex',\n flexDirection: 'column',\n justifyContent: 'center',\n }}\n >\n <Typography variant=\"body2\" color={theme.palette.text.primary}>\n {title || defaultTitle}\n </Typography>\n </Box>\n </Box>\n </Box>\n </MenuItem>\n );\n};\n"],"names":[],"mappings":"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"}
@@ -1,27 +1,44 @@
1
1
  import { jsx } from 'react/jsx-runtime';
2
2
  import Box from '@mui/material/Box';
3
3
  import List from '@mui/material/List';
4
+ import CircularProgress from '@mui/material/CircularProgress';
4
5
  import { QuickstartItem } from './QuickstartItem.esm.js';
5
- import { EmptyState } from '@backstage/core-components';
6
- import { useState } from 'react';
7
- import { useTranslation } from '../../hooks/useTranslation.esm.js';
6
+ import { useState, useEffect } from 'react';
8
7
 
9
8
  const QuickstartContent = ({
10
9
  quickstartItems,
11
10
  setProgress,
12
- itemCount
11
+ itemCount,
12
+ isLoading
13
13
  }) => {
14
- const { t } = useTranslation();
15
14
  const [openItems, setOpenItems] = useState(
16
15
  new Array(itemCount).fill(false)
17
16
  );
17
+ useEffect(() => {
18
+ setOpenItems(new Array(itemCount).fill(false));
19
+ }, [itemCount]);
20
+ if (isLoading) {
21
+ return /* @__PURE__ */ jsx(
22
+ Box,
23
+ {
24
+ sx: {
25
+ display: "flex",
26
+ alignItems: "center",
27
+ justifyContent: "center",
28
+ minHeight: "50vh",
29
+ width: "100%"
30
+ },
31
+ children: /* @__PURE__ */ jsx(CircularProgress, {})
32
+ }
33
+ );
34
+ }
18
35
  return /* @__PURE__ */ jsx(
19
36
  Box,
20
37
  {
21
38
  sx: {
22
39
  paddingTop: (theme) => `${theme.spacing(3)}`
23
40
  },
24
- children: quickstartItems.length > 0 ? /* @__PURE__ */ jsx(List, { disablePadding: true, children: quickstartItems.map((item, index) => /* @__PURE__ */ jsx(
41
+ children: /* @__PURE__ */ jsx(List, { disablePadding: true, children: quickstartItems.map((item, index) => /* @__PURE__ */ jsx(
25
42
  QuickstartItem,
26
43
  {
27
44
  item,
@@ -35,7 +52,7 @@ const QuickstartContent = ({
35
52
  setProgress: () => setProgress(index)
36
53
  },
37
54
  `${item.title}-${index}`
38
- )) }) : /* @__PURE__ */ jsx(EmptyState, { title: t("content.emptyState.title"), missing: "data" })
55
+ )) })
39
56
  }
40
57
  );
41
58
  };
@@ -1 +1 @@
1
- {"version":3,"file":"QuickstartContent.esm.js","sources":["../../../src/components/QuickstartContent/QuickstartContent.tsx"],"sourcesContent":["/*\n * Copyright Red Hat, Inc.\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 */\n\nimport Box from '@mui/material/Box';\nimport List from '@mui/material/List';\nimport { QuickstartItem } from './QuickstartItem';\nimport { EmptyState } from '@backstage/core-components';\nimport { useState } from 'react';\nimport { QuickstartItemData } from '../../types';\nimport { useTranslation } from '../../hooks/useTranslation';\n\ntype QuickstartContentProps = {\n quickstartItems: QuickstartItemData[];\n setProgress: (index: number) => void;\n itemCount: number;\n};\n\nexport const QuickstartContent = ({\n quickstartItems,\n setProgress,\n itemCount,\n}: QuickstartContentProps) => {\n const { t } = useTranslation();\n const [openItems, setOpenItems] = useState<boolean[]>(\n new Array(itemCount).fill(false),\n );\n\n return (\n <Box\n sx={{\n paddingTop: theme => `${theme.spacing(3)}`,\n }}\n >\n {quickstartItems.length > 0 ? (\n <List disablePadding>\n {quickstartItems.map((item: QuickstartItemData, index: number) => (\n <QuickstartItem\n key={`${item.title}-${index}`}\n item={item}\n index={index}\n open={openItems[index]}\n handleOpen={() =>\n setOpenItems(oi => {\n return oi.map((val, valIndex) =>\n valIndex === index ? !val : false,\n );\n })\n }\n setProgress={() => setProgress(index)}\n />\n ))}\n </List>\n ) : (\n <EmptyState title={t('content.emptyState.title')} missing=\"data\" />\n )}\n </Box>\n );\n};\n"],"names":[],"mappings":";;;;;;;;AA8BO,MAAM,oBAAoB,CAAC;AAAA,EAChC,eAAA;AAAA,EACA,WAAA;AAAA,EACA;AACF,CAA8B,KAAA;AAC5B,EAAM,MAAA,EAAE,CAAE,EAAA,GAAI,cAAe,EAAA;AAC7B,EAAM,MAAA,CAAC,SAAW,EAAA,YAAY,CAAI,GAAA,QAAA;AAAA,IAChC,IAAI,KAAA,CAAM,SAAS,CAAA,CAAE,KAAK,KAAK;AAAA,GACjC;AAEA,EACE,uBAAA,GAAA;AAAA,IAAC,GAAA;AAAA,IAAA;AAAA,MACC,EAAI,EAAA;AAAA,QACF,YAAY,CAAS,KAAA,KAAA,CAAA,EAAG,KAAM,CAAA,OAAA,CAAQ,CAAC,CAAC,CAAA;AAAA,OAC1C;AAAA,MAEC,QAAA,EAAA,eAAA,CAAgB,MAAS,GAAA,CAAA,mBACvB,GAAA,CAAA,IAAA,EAAA,EAAK,cAAc,EAAA,IAAA,EACjB,QAAgB,EAAA,eAAA,CAAA,GAAA,CAAI,CAAC,IAAA,EAA0B,KAC9C,qBAAA,GAAA;AAAA,QAAC,cAAA;AAAA,QAAA;AAAA,UAEC,IAAA;AAAA,UACA,KAAA;AAAA,UACA,IAAA,EAAM,UAAU,KAAK,CAAA;AAAA,UACrB,UAAA,EAAY,MACV,YAAA,CAAa,CAAM,EAAA,KAAA;AACjB,YAAA,OAAO,EAAG,CAAA,GAAA;AAAA,cAAI,CAAC,GAAK,EAAA,QAAA,KAClB,QAAa,KAAA,KAAA,GAAQ,CAAC,GAAM,GAAA;AAAA,aAC9B;AAAA,WACD,CAAA;AAAA,UAEH,WAAA,EAAa,MAAM,WAAA,CAAY,KAAK;AAAA,SAAA;AAAA,QAX/B,CAAG,EAAA,IAAA,CAAK,KAAK,CAAA,CAAA,EAAI,KAAK,CAAA;AAAA,OAa9B,CACH,EAAA,CAAA,mBAEC,GAAA,CAAA,UAAA,EAAA,EAAW,OAAO,CAAE,CAAA,0BAA0B,CAAG,EAAA,OAAA,EAAQ,MAAO,EAAA;AAAA;AAAA,GAErE;AAEJ;;;;"}
1
+ {"version":3,"file":"QuickstartContent.esm.js","sources":["../../../src/components/QuickstartContent/QuickstartContent.tsx"],"sourcesContent":["/*\n * Copyright Red Hat, Inc.\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 */\n\nimport Box from '@mui/material/Box';\nimport List from '@mui/material/List';\nimport CircularProgress from '@mui/material/CircularProgress';\nimport { QuickstartItem } from './QuickstartItem';\nimport { useState, useEffect } from 'react';\nimport { QuickstartItemData } from '../../types';\n\ntype QuickstartContentProps = {\n quickstartItems: QuickstartItemData[];\n setProgress: (index: number) => void;\n itemCount: number;\n isLoading: boolean;\n};\n\nexport const QuickstartContent = ({\n quickstartItems,\n setProgress,\n itemCount,\n isLoading,\n}: QuickstartContentProps) => {\n const [openItems, setOpenItems] = useState<boolean[]>(\n new Array(itemCount).fill(false),\n );\n\n // Re-initialize openItems when itemCount changes (e.g., after loading)\n useEffect(() => {\n setOpenItems(new Array(itemCount).fill(false));\n }, [itemCount]);\n\n // Show loading spinner when user role is still being determined\n if (isLoading) {\n return (\n <Box\n sx={{\n display: 'flex',\n alignItems: 'center',\n justifyContent: 'center',\n minHeight: '50vh',\n width: '100%',\n }}\n >\n <CircularProgress />\n </Box>\n );\n }\n\n return (\n <Box\n sx={{\n paddingTop: theme => `${theme.spacing(3)}`,\n }}\n >\n <List disablePadding>\n {quickstartItems.map((item: QuickstartItemData, index: number) => (\n <QuickstartItem\n key={`${item.title}-${index}`}\n item={item}\n index={index}\n open={openItems[index]}\n handleOpen={() =>\n setOpenItems(oi => {\n return oi.map((val, valIndex) =>\n valIndex === index ? !val : false,\n );\n })\n }\n setProgress={() => setProgress(index)}\n />\n ))}\n </List>\n </Box>\n );\n};\n"],"names":[],"mappings":";;;;;;;AA8BO,MAAM,oBAAoB,CAAC;AAAA,EAChC,eAAA;AAAA,EACA,WAAA;AAAA,EACA,SAAA;AAAA,EACA;AACF,CAA8B,KAAA;AAC5B,EAAM,MAAA,CAAC,SAAW,EAAA,YAAY,CAAI,GAAA,QAAA;AAAA,IAChC,IAAI,KAAA,CAAM,SAAS,CAAA,CAAE,KAAK,KAAK;AAAA,GACjC;AAGA,EAAA,SAAA,CAAU,MAAM;AACd,IAAA,YAAA,CAAa,IAAI,KAAM,CAAA,SAAS,CAAE,CAAA,IAAA,CAAK,KAAK,CAAC,CAAA;AAAA,GAC/C,EAAG,CAAC,SAAS,CAAC,CAAA;AAGd,EAAA,IAAI,SAAW,EAAA;AACb,IACE,uBAAA,GAAA;AAAA,MAAC,GAAA;AAAA,MAAA;AAAA,QACC,EAAI,EAAA;AAAA,UACF,OAAS,EAAA,MAAA;AAAA,UACT,UAAY,EAAA,QAAA;AAAA,UACZ,cAAgB,EAAA,QAAA;AAAA,UAChB,SAAW,EAAA,MAAA;AAAA,UACX,KAAO,EAAA;AAAA,SACT;AAAA,QAEA,8BAAC,gBAAiB,EAAA,EAAA;AAAA;AAAA,KACpB;AAAA;AAIJ,EACE,uBAAA,GAAA;AAAA,IAAC,GAAA;AAAA,IAAA;AAAA,MACC,EAAI,EAAA;AAAA,QACF,YAAY,CAAS,KAAA,KAAA,CAAA,EAAG,KAAM,CAAA,OAAA,CAAQ,CAAC,CAAC,CAAA;AAAA,OAC1C;AAAA,MAEA,QAAA,kBAAA,GAAA,CAAC,QAAK,cAAc,EAAA,IAAA,EACjB,0BAAgB,GAAI,CAAA,CAAC,MAA0B,KAC9C,qBAAA,GAAA;AAAA,QAAC,cAAA;AAAA,QAAA;AAAA,UAEC,IAAA;AAAA,UACA,KAAA;AAAA,UACA,IAAA,EAAM,UAAU,KAAK,CAAA;AAAA,UACrB,UAAA,EAAY,MACV,YAAA,CAAa,CAAM,EAAA,KAAA;AACjB,YAAA,OAAO,EAAG,CAAA,GAAA;AAAA,cAAI,CAAC,GAAK,EAAA,QAAA,KAClB,QAAa,KAAA,KAAA,GAAQ,CAAC,GAAM,GAAA;AAAA,aAC9B;AAAA,WACD,CAAA;AAAA,UAEH,WAAA,EAAa,MAAM,WAAA,CAAY,KAAK;AAAA,SAAA;AAAA,QAX/B,CAAG,EAAA,IAAA,CAAK,KAAK,CAAA,CAAA,EAAI,KAAK,CAAA;AAAA,OAa9B,CACH,EAAA;AAAA;AAAA,GACF;AAEJ;;;;"}
@@ -4,14 +4,33 @@ import { useApiHolder, configApiRef } from '@backstage/core-plugin-api';
4
4
  import { Quickstart } from './Quickstart.esm.js';
5
5
  import { useQuickstartDrawerContext } from '../hooks/useQuickstartDrawerContext.esm.js';
6
6
  import { filterQuickstartItemsByRole } from '../utils/filterQuickstartItems.esm.js';
7
+ import { useQuickstartRole } from '../hooks/useQuickstartRole.esm.js';
8
+ import { useRef, useEffect } from 'react';
7
9
 
8
10
  const QuickstartDrawer = () => {
9
- const { isDrawerOpen, closeDrawer, drawerWidth } = useQuickstartDrawerContext();
11
+ const { isDrawerOpen, closeDrawer, openDrawer, drawerWidth } = useQuickstartDrawerContext();
12
+ const hasAutoOpened = useRef(false);
10
13
  const apiHolder = useApiHolder();
11
14
  const config = apiHolder.get(configApiRef);
12
15
  const quickstartItems = config?.has("app.quickstart") ? config.get("app.quickstart") : [];
13
- const userRole = "developer";
14
- const filteredItems = filterQuickstartItemsByRole(quickstartItems, userRole);
16
+ const { isLoading, userRole } = useQuickstartRole();
17
+ const filteredItems = !isLoading && userRole ? filterQuickstartItemsByRole(quickstartItems, userRole) : [];
18
+ useEffect(() => {
19
+ if (!isLoading && filteredItems.length > 0 && !isDrawerOpen && !hasAutoOpened.current) {
20
+ openDrawer();
21
+ hasAutoOpened.current = true;
22
+ }
23
+ }, [isLoading, filteredItems.length, isDrawerOpen, openDrawer]);
24
+ if (!isLoading && filteredItems.length === 0) {
25
+ if (isDrawerOpen) {
26
+ closeDrawer();
27
+ }
28
+ return null;
29
+ }
30
+ if (isLoading && isDrawerOpen) {
31
+ closeDrawer();
32
+ hasAutoOpened.current = false;
33
+ }
15
34
  return /* @__PURE__ */ jsx(
16
35
  Drawer,
17
36
  {
@@ -37,7 +56,8 @@ const QuickstartDrawer = () => {
37
56
  Quickstart,
38
57
  {
39
58
  quickstartItems: filteredItems,
40
- handleDrawerClose: closeDrawer
59
+ handleDrawerClose: closeDrawer,
60
+ isLoading
41
61
  }
42
62
  )
43
63
  }
@@ -1 +1 @@
1
- {"version":3,"file":"QuickstartDrawer.esm.js","sources":["../../src/components/QuickstartDrawer.tsx"],"sourcesContent":["/*\n * Copyright Red Hat, Inc.\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 */\n\nimport Drawer from '@mui/material/Drawer';\nimport { ThemeConfig } from '@red-hat-developer-hub/backstage-plugin-theme';\nimport { configApiRef, useApiHolder } from '@backstage/core-plugin-api';\nimport { Quickstart } from './Quickstart';\nimport { useQuickstartDrawerContext } from '../hooks/useQuickstartDrawerContext';\nimport { QuickstartItemData } from '../types';\nimport { filterQuickstartItemsByRole } from '../utils';\n\nexport const QuickstartDrawer = () => {\n const { isDrawerOpen, closeDrawer, drawerWidth } =\n useQuickstartDrawerContext();\n\n const apiHolder = useApiHolder();\n const config = apiHolder.get(configApiRef);\n const quickstartItems: QuickstartItemData[] = config?.has('app.quickstart')\n ? config.get('app.quickstart')\n : [];\n\n // This will be dynamically determined based on the logged-in user\n const userRole = 'developer'; // switch to 'admin', 'developer', or other roles for testing purposes\n\n const filteredItems = filterQuickstartItemsByRole(quickstartItems, userRole);\n\n return (\n <Drawer\n sx={{\n '& .v5-MuiDrawer-paper': {\n width: drawerWidth,\n boxSizing: 'border-box',\n backgroundColor: theme =>\n `${\n (theme as ThemeConfig).palette?.rhdh?.general\n .sidebarBackgroundColor\n }`,\n justifyContent: 'space-between',\n },\n // Only apply header offset when global header exists\n 'body:has(#global-header) &': {\n '& .v5-MuiDrawer-paper': {\n top: '64px !important',\n height: 'calc(100vh - 64px) !important',\n },\n },\n }}\n variant=\"persistent\"\n anchor=\"right\"\n open={isDrawerOpen}\n >\n <Quickstart\n quickstartItems={filteredItems}\n handleDrawerClose={closeDrawer}\n />\n </Drawer>\n );\n};\n"],"names":[],"mappings":";;;;;;;AAwBO,MAAM,mBAAmB,MAAM;AACpC,EAAA,MAAM,EAAE,YAAA,EAAc,WAAa,EAAA,WAAA,KACjC,0BAA2B,EAAA;AAE7B,EAAA,MAAM,YAAY,YAAa,EAAA;AAC/B,EAAM,MAAA,MAAA,GAAS,SAAU,CAAA,GAAA,CAAI,YAAY,CAAA;AACzC,EAAM,MAAA,eAAA,GAAwC,QAAQ,GAAI,CAAA,gBAAgB,IACtE,MAAO,CAAA,GAAA,CAAI,gBAAgB,CAAA,GAC3B,EAAC;AAGL,EAAA,MAAM,QAAW,GAAA,WAAA;AAEjB,EAAM,MAAA,aAAA,GAAgB,2BAA4B,CAAA,eAAA,EAAiB,QAAQ,CAAA;AAE3E,EACE,uBAAA,GAAA;AAAA,IAAC,MAAA;AAAA,IAAA;AAAA,MACC,EAAI,EAAA;AAAA,QACF,uBAAyB,EAAA;AAAA,UACvB,KAAO,EAAA,WAAA;AAAA,UACP,SAAW,EAAA,YAAA;AAAA,UACX,iBAAiB,CACf,KAAA,KAAA,CAAA,EACG,MAAsB,OAAS,EAAA,IAAA,EAAM,QACnC,sBACL,CAAA,CAAA;AAAA,UACF,cAAgB,EAAA;AAAA,SAClB;AAAA;AAAA,QAEA,4BAA8B,EAAA;AAAA,UAC5B,uBAAyB,EAAA;AAAA,YACvB,GAAK,EAAA,iBAAA;AAAA,YACL,MAAQ,EAAA;AAAA;AACV;AACF,OACF;AAAA,MACA,OAAQ,EAAA,YAAA;AAAA,MACR,MAAO,EAAA,OAAA;AAAA,MACP,IAAM,EAAA,YAAA;AAAA,MAEN,QAAA,kBAAA,GAAA;AAAA,QAAC,UAAA;AAAA,QAAA;AAAA,UACC,eAAiB,EAAA,aAAA;AAAA,UACjB,iBAAmB,EAAA;AAAA;AAAA;AACrB;AAAA,GACF;AAEJ;;;;"}
1
+ {"version":3,"file":"QuickstartDrawer.esm.js","sources":["../../src/components/QuickstartDrawer.tsx"],"sourcesContent":["/*\n * Copyright Red Hat, Inc.\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 */\n\nimport Drawer from '@mui/material/Drawer';\nimport { ThemeConfig } from '@red-hat-developer-hub/backstage-plugin-theme';\nimport { configApiRef, useApiHolder } from '@backstage/core-plugin-api';\nimport { Quickstart } from './Quickstart';\nimport { useQuickstartDrawerContext } from '../hooks/useQuickstartDrawerContext';\nimport { QuickstartItemData } from '../types';\nimport { filterQuickstartItemsByRole } from '../utils';\nimport { useQuickstartRole } from '../hooks/useQuickstartRole';\nimport { useEffect, useRef } from 'react';\n\nexport const QuickstartDrawer = () => {\n const { isDrawerOpen, closeDrawer, openDrawer, drawerWidth } =\n useQuickstartDrawerContext();\n\n // Track if we've already auto-opened the drawer to prevent re-opening after manual close\n const hasAutoOpened = useRef(false);\n\n const apiHolder = useApiHolder();\n const config = apiHolder.get(configApiRef);\n const quickstartItems: QuickstartItemData[] = config?.has('app.quickstart')\n ? config.get('app.quickstart')\n : [];\n\n const { isLoading, userRole } = useQuickstartRole();\n const filteredItems =\n !isLoading && userRole\n ? filterQuickstartItemsByRole(quickstartItems, userRole)\n : [];\n\n // Auto-open drawer when user logs in and has quickstart items available\n // Only do this once, and respect user's manual close action\n useEffect(() => {\n if (\n !isLoading &&\n filteredItems.length > 0 &&\n !isDrawerOpen &&\n !hasAutoOpened.current\n ) {\n openDrawer();\n hasAutoOpened.current = true;\n }\n }, [isLoading, filteredItems.length, isDrawerOpen, openDrawer]);\n\n // Hide the drawer entirely if there are no quickstart items for the user\n // Do this check first, before any rendering happens\n if (!isLoading && filteredItems.length === 0) {\n // Also close the drawer context if it's currently open to prevent layout issues\n if (isDrawerOpen) {\n closeDrawer();\n }\n return null;\n }\n\n // During loading, if drawer is open but we don't know if user will have items yet,\n // close it preemptively to prevent flash and reset auto-open tracking\n if (isLoading && isDrawerOpen) {\n closeDrawer();\n hasAutoOpened.current = false; // Reset for new user\n }\n\n return (\n <Drawer\n sx={{\n '& .v5-MuiDrawer-paper': {\n width: drawerWidth,\n boxSizing: 'border-box',\n backgroundColor: theme =>\n `${\n (theme as ThemeConfig).palette?.rhdh?.general\n .sidebarBackgroundColor\n }`,\n justifyContent: 'space-between',\n },\n // Only apply header offset when global header exists\n 'body:has(#global-header) &': {\n '& .v5-MuiDrawer-paper': {\n top: '64px !important',\n height: 'calc(100vh - 64px) !important',\n },\n },\n }}\n variant=\"persistent\"\n anchor=\"right\"\n open={isDrawerOpen}\n >\n <Quickstart\n quickstartItems={filteredItems}\n handleDrawerClose={closeDrawer}\n isLoading={isLoading}\n />\n </Drawer>\n );\n};\n"],"names":[],"mappings":";;;;;;;;;AA0BO,MAAM,mBAAmB,MAAM;AACpC,EAAA,MAAM,EAAE,YAAc,EAAA,WAAA,EAAa,UAAY,EAAA,WAAA,KAC7C,0BAA2B,EAAA;AAG7B,EAAM,MAAA,aAAA,GAAgB,OAAO,KAAK,CAAA;AAElC,EAAA,MAAM,YAAY,YAAa,EAAA;AAC/B,EAAM,MAAA,MAAA,GAAS,SAAU,CAAA,GAAA,CAAI,YAAY,CAAA;AACzC,EAAM,MAAA,eAAA,GAAwC,QAAQ,GAAI,CAAA,gBAAgB,IACtE,MAAO,CAAA,GAAA,CAAI,gBAAgB,CAAA,GAC3B,EAAC;AAEL,EAAA,MAAM,EAAE,SAAA,EAAW,QAAS,EAAA,GAAI,iBAAkB,EAAA;AAClD,EAAM,MAAA,aAAA,GACJ,CAAC,SAAa,IAAA,QAAA,GACV,4BAA4B,eAAiB,EAAA,QAAQ,IACrD,EAAC;AAIP,EAAA,SAAA,CAAU,MAAM;AACd,IACE,IAAA,CAAC,aACD,aAAc,CAAA,MAAA,GAAS,KACvB,CAAC,YAAA,IACD,CAAC,aAAA,CAAc,OACf,EAAA;AACA,MAAW,UAAA,EAAA;AACX,MAAA,aAAA,CAAc,OAAU,GAAA,IAAA;AAAA;AAC1B,KACC,CAAC,SAAA,EAAW,cAAc,MAAQ,EAAA,YAAA,EAAc,UAAU,CAAC,CAAA;AAI9D,EAAA,IAAI,CAAC,SAAA,IAAa,aAAc,CAAA,MAAA,KAAW,CAAG,EAAA;AAE5C,IAAA,IAAI,YAAc,EAAA;AAChB,MAAY,WAAA,EAAA;AAAA;AAEd,IAAO,OAAA,IAAA;AAAA;AAKT,EAAA,IAAI,aAAa,YAAc,EAAA;AAC7B,IAAY,WAAA,EAAA;AACZ,IAAA,aAAA,CAAc,OAAU,GAAA,KAAA;AAAA;AAG1B,EACE,uBAAA,GAAA;AAAA,IAAC,MAAA;AAAA,IAAA;AAAA,MACC,EAAI,EAAA;AAAA,QACF,uBAAyB,EAAA;AAAA,UACvB,KAAO,EAAA,WAAA;AAAA,UACP,SAAW,EAAA,YAAA;AAAA,UACX,iBAAiB,CACf,KAAA,KAAA,CAAA,EACG,MAAsB,OAAS,EAAA,IAAA,EAAM,QACnC,sBACL,CAAA,CAAA;AAAA,UACF,cAAgB,EAAA;AAAA,SAClB;AAAA;AAAA,QAEA,4BAA8B,EAAA;AAAA,UAC5B,uBAAyB,EAAA;AAAA,YACvB,GAAK,EAAA,iBAAA;AAAA,YACL,MAAQ,EAAA;AAAA;AACV;AACF,OACF;AAAA,MACA,OAAQ,EAAA,YAAA;AAAA,MACR,MAAO,EAAA,OAAA;AAAA,MACP,IAAM,EAAA,YAAA;AAAA,MAEN,QAAA,kBAAA,GAAA;AAAA,QAAC,UAAA;AAAA,QAAA;AAAA,UACC,eAAiB,EAAA,aAAA;AAAA,UACjB,iBAAmB,EAAA,WAAA;AAAA,UACnB;AAAA;AAAA;AACF;AAAA,GACF;AAEJ;;;;"}
@@ -5,10 +5,8 @@ import CloseIcon from '@mui/icons-material/Close';
5
5
  import IconButton from '@mui/material/IconButton';
6
6
  import { QuickstartDrawerContext } from './QuickstartDrawerContext.esm.js';
7
7
  import { QuickstartDrawer } from './QuickstartDrawer.esm.js';
8
- import { useQuickstartPermission } from '../hooks/useQuickstartPermission.esm.js';
9
8
 
10
9
  const QuickstartDrawerProvider = ({ children }) => {
11
- const isAllowed = useQuickstartPermission();
12
10
  const [isDrawerOpen, setIsDrawerOpen] = useState(false);
13
11
  const [showNotification, setShowNotification] = useState(false);
14
12
  const [hasShownNotification, setHasShownNotification] = useState(false);
@@ -35,17 +33,15 @@ const QuickstartDrawerProvider = ({ children }) => {
35
33
  const notificationShown = localStorage.getItem(
36
34
  "quickstart-notification-shown"
37
35
  );
38
- if (isAllowed) {
39
- if (!hasVisited) {
40
- setIsDrawerOpen(true);
41
- localStorage.setItem("quickstart-visited", "true");
42
- localStorage.setItem("quickstart-open", "true");
43
- } else if (wasOpen === "true") {
44
- setIsDrawerOpen(true);
45
- }
36
+ if (!hasVisited) {
37
+ setIsDrawerOpen(true);
38
+ localStorage.setItem("quickstart-visited", "true");
39
+ localStorage.setItem("quickstart-open", "true");
40
+ } else if (wasOpen === "true") {
41
+ setIsDrawerOpen(true);
46
42
  }
47
43
  setHasShownNotification(notificationShown === "true");
48
- }, [isAllowed]);
44
+ }, []);
49
45
  const openDrawer = () => {
50
46
  setIsDrawerOpen(true);
51
47
  localStorage.setItem("quickstart-open", "true");
@@ -1 +1 @@
1
- {"version":3,"file":"QuickstartDrawerProvider.esm.js","sources":["../../src/components/QuickstartDrawerProvider.tsx"],"sourcesContent":["/*\n * Copyright Red Hat, Inc.\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 */\n\nimport { useEffect, PropsWithChildren, useState } from 'react';\nimport Snackbar from '@mui/material/Snackbar';\nimport CloseIcon from '@mui/icons-material/Close';\nimport IconButton from '@mui/material/IconButton';\nimport { QuickstartDrawerContext } from './QuickstartDrawerContext';\nimport { QuickstartDrawer } from './QuickstartDrawer';\nimport { useQuickstartPermission } from '../hooks/useQuickstartPermission';\n\nexport const QuickstartDrawerProvider = ({ children }: PropsWithChildren) => {\n const isAllowed = useQuickstartPermission();\n const [isDrawerOpen, setIsDrawerOpen] = useState<boolean>(false);\n const [showNotification, setShowNotification] = useState(false);\n const [hasShownNotification, setHasShownNotification] = useState(false);\n const [drawerWidth, setDrawerWidth] = useState<number>(500);\n\n // Single useEffect - sets class on document.body\n useEffect(() => {\n if (isDrawerOpen) {\n document.body.classList.add('quickstart-drawer-open');\n document.body.style.setProperty(\n '--quickstart-drawer-width',\n `${drawerWidth}px`,\n );\n } else {\n document.body.classList.remove('quickstart-drawer-open');\n document.body.style.removeProperty('--quickstart-drawer-width');\n }\n\n return () => {\n document.body.classList.remove('quickstart-drawer-open');\n document.body.style.removeProperty('--quickstart-drawer-width');\n };\n }, [isDrawerOpen, drawerWidth]);\n\n useEffect(() => {\n const wasOpen = localStorage.getItem('quickstart-open');\n const hasVisited = localStorage.getItem('quickstart-visited');\n const notificationShown = localStorage.getItem(\n 'quickstart-notification-shown',\n );\n\n if (isAllowed) {\n if (!hasVisited) {\n setIsDrawerOpen(true);\n localStorage.setItem('quickstart-visited', 'true');\n localStorage.setItem('quickstart-open', 'true');\n } else if (wasOpen === 'true') {\n setIsDrawerOpen(true);\n }\n }\n\n setHasShownNotification(notificationShown === 'true');\n }, [isAllowed]);\n\n const openDrawer = () => {\n setIsDrawerOpen(true);\n localStorage.setItem('quickstart-open', 'true');\n };\n\n const closeDrawer = () => {\n setIsDrawerOpen(false);\n if (!hasShownNotification) {\n setShowNotification(true);\n setHasShownNotification(true);\n localStorage.setItem('quickstart-notification-shown', 'true');\n }\n localStorage.setItem('quickstart-open', 'false');\n };\n\n const toggleDrawer = () => {\n setIsDrawerOpen(!isDrawerOpen);\n localStorage.setItem('quickstart-open', (!isDrawerOpen).toString());\n };\n\n const handleNotificationClose = () => setShowNotification(false);\n\n return (\n <QuickstartDrawerContext.Provider\n value={{\n isDrawerOpen,\n openDrawer,\n closeDrawer,\n toggleDrawer,\n setDrawerWidth,\n drawerWidth,\n }}\n >\n {children}\n <QuickstartDrawer />\n <Snackbar\n sx={{ top: '80px !important' }}\n open={showNotification}\n autoHideDuration={10000}\n onClose={handleNotificationClose}\n anchorOrigin={{ vertical: 'top', horizontal: 'right' }}\n message=\"Need help? Visit the Quick Start Guide by clicking on this (?) icon in the header!\"\n action={\n <IconButton\n size=\"small\"\n aria-label=\"close\"\n color=\"inherit\"\n onClick={handleNotificationClose}\n >\n <CloseIcon fontSize=\"small\" />\n </IconButton>\n }\n />\n </QuickstartDrawerContext.Provider>\n );\n};\n"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"QuickstartDrawerProvider.esm.js","sources":["../../src/components/QuickstartDrawerProvider.tsx"],"sourcesContent":["/*\n * Copyright Red Hat, Inc.\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 */\n\nimport { useEffect, PropsWithChildren, useState } from 'react';\nimport Snackbar from '@mui/material/Snackbar';\nimport CloseIcon from '@mui/icons-material/Close';\nimport IconButton from '@mui/material/IconButton';\nimport { QuickstartDrawerContext } from './QuickstartDrawerContext';\nimport { QuickstartDrawer } from './QuickstartDrawer';\n\nexport const QuickstartDrawerProvider = ({ children }: PropsWithChildren) => {\n const [isDrawerOpen, setIsDrawerOpen] = useState<boolean>(false);\n const [showNotification, setShowNotification] = useState(false);\n const [hasShownNotification, setHasShownNotification] = useState(false);\n const [drawerWidth, setDrawerWidth] = useState<number>(500);\n\n // Single useEffect - sets class on document.body\n useEffect(() => {\n if (isDrawerOpen) {\n document.body.classList.add('quickstart-drawer-open');\n document.body.style.setProperty(\n '--quickstart-drawer-width',\n `${drawerWidth}px`,\n );\n } else {\n document.body.classList.remove('quickstart-drawer-open');\n document.body.style.removeProperty('--quickstart-drawer-width');\n }\n\n return () => {\n document.body.classList.remove('quickstart-drawer-open');\n document.body.style.removeProperty('--quickstart-drawer-width');\n };\n }, [isDrawerOpen, drawerWidth]);\n\n useEffect(() => {\n const wasOpen = localStorage.getItem('quickstart-open');\n const hasVisited = localStorage.getItem('quickstart-visited');\n const notificationShown = localStorage.getItem(\n 'quickstart-notification-shown',\n );\n\n if (!hasVisited) {\n setIsDrawerOpen(true);\n localStorage.setItem('quickstart-visited', 'true');\n localStorage.setItem('quickstart-open', 'true');\n } else if (wasOpen === 'true') {\n setIsDrawerOpen(true);\n }\n\n setHasShownNotification(notificationShown === 'true');\n }, []);\n\n const openDrawer = () => {\n setIsDrawerOpen(true);\n localStorage.setItem('quickstart-open', 'true');\n };\n\n const closeDrawer = () => {\n setIsDrawerOpen(false);\n if (!hasShownNotification) {\n setShowNotification(true);\n setHasShownNotification(true);\n localStorage.setItem('quickstart-notification-shown', 'true');\n }\n localStorage.setItem('quickstart-open', 'false');\n };\n\n const toggleDrawer = () => {\n setIsDrawerOpen(!isDrawerOpen);\n localStorage.setItem('quickstart-open', (!isDrawerOpen).toString());\n };\n\n const handleNotificationClose = () => setShowNotification(false);\n\n return (\n <QuickstartDrawerContext.Provider\n value={{\n isDrawerOpen,\n openDrawer,\n closeDrawer,\n toggleDrawer,\n setDrawerWidth,\n drawerWidth,\n }}\n >\n {children}\n <QuickstartDrawer />\n <Snackbar\n sx={{ top: '80px !important' }}\n open={showNotification}\n autoHideDuration={10000}\n onClose={handleNotificationClose}\n anchorOrigin={{ vertical: 'top', horizontal: 'right' }}\n message=\"Need help? Visit the Quick Start Guide by clicking on this (?) icon in the header!\"\n action={\n <IconButton\n size=\"small\"\n aria-label=\"close\"\n color=\"inherit\"\n onClick={handleNotificationClose}\n >\n <CloseIcon fontSize=\"small\" />\n </IconButton>\n }\n />\n </QuickstartDrawerContext.Provider>\n );\n};\n"],"names":[],"mappings":"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"}
@@ -0,0 +1,38 @@
1
+ import { usePermission } from '@backstage/plugin-permission-react';
2
+ import { useApi, configApiRef, identityApiRef } from '@backstage/core-plugin-api';
3
+ import { policyEntityCreatePermission } from '@backstage-community/plugin-rbac-common';
4
+ import { useAsync } from 'react-use';
5
+
6
+ const useQuickstartRole = () => {
7
+ const config = useApi(configApiRef);
8
+ const identityApi = useApi(identityApiRef);
9
+ const isRBACEnabled = config.getOptionalBoolean("permission.enabled") ?? false;
10
+ const { loading, allowed } = usePermission({
11
+ permission: policyEntityCreatePermission
12
+ });
13
+ const { value: authResult, loading: authLoading } = useAsync(async () => {
14
+ try {
15
+ const credentials = await identityApi.getCredentials();
16
+ const identity = await identityApi.getBackstageIdentity();
17
+ const hasValidToken = credentials?.token && credentials.token.length > 10;
18
+ const userEntityRef = identity?.userEntityRef || "";
19
+ const ownershipRefs = identity?.ownershipEntityRefs || [];
20
+ const isGuest = userEntityRef.toLowerCase().includes("guest") || userEntityRef === "user:default/guest" || !hasValidToken && ownershipRefs.length === 0;
21
+ const isAuthenticated = !isGuest;
22
+ return { isAuthenticated, identity, credentials };
23
+ } catch (error) {
24
+ return { isAuthenticated: false, identity: null, credentials: null };
25
+ }
26
+ }, [identityApi]);
27
+ if (authLoading || loading) return { isLoading: true, userRole: null };
28
+ const isUserAuthorized = authResult?.isAuthenticated ?? false;
29
+ if (!isUserAuthorized) {
30
+ return { isLoading: false, userRole: "admin" };
31
+ }
32
+ if (!isRBACEnabled) return { isLoading: false, userRole: "admin" };
33
+ if (allowed) return { isLoading: false, userRole: "admin" };
34
+ return { isLoading: false, userRole: "developer" };
35
+ };
36
+
37
+ export { useQuickstartRole };
38
+ //# sourceMappingURL=useQuickstartRole.esm.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"useQuickstartRole.esm.js","sources":["../../src/hooks/useQuickstartRole.ts"],"sourcesContent":["/*\n * Copyright Red Hat, Inc.\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 */\n\nimport { usePermission } from '@backstage/plugin-permission-react';\nimport {\n configApiRef,\n identityApiRef,\n useApi,\n} from '@backstage/core-plugin-api';\nimport { policyEntityCreatePermission } from '@backstage-community/plugin-rbac-common';\nimport { useAsync } from 'react-use';\nimport { UserRole } from '../types';\n\n/**\n * Determines the user's role for quickstart functionality based on RBAC permissions and user authorization.\n *\n * Business Logic:\n * - Guest user(unauthorized): show admin items\n * - Authorized user + NO RBAC enabled: show admin items\n * - Authorized user + RBAC enabled:\n * - if user has admin permission => show configured admin items\n * - if user doesn't have admin permission => show configured developer items\n *\n * @returns Object with isLoading boolean and userRole ('admin' | 'developer' | null)\n */\nexport const useQuickstartRole = (): {\n isLoading: boolean;\n userRole: UserRole | null;\n} => {\n const config = useApi(configApiRef);\n const identityApi = useApi(identityApiRef);\n const isRBACEnabled =\n config.getOptionalBoolean('permission.enabled') ?? false;\n const { loading, allowed } = usePermission({\n permission: policyEntityCreatePermission,\n });\n\n // Check user authorization status by examining identity and credentials\n const { value: authResult, loading: authLoading } = useAsync(async () => {\n try {\n const credentials = await identityApi.getCredentials();\n const identity = await identityApi.getBackstageIdentity();\n\n // Check multiple indicators to determine if user is authenticated (not a guest)\n const hasValidToken = credentials?.token && credentials.token.length > 10; // Real tokens are longer\n const userEntityRef = identity?.userEntityRef || '';\n const ownershipRefs = identity?.ownershipEntityRefs || [];\n\n const isGuest =\n userEntityRef.toLowerCase().includes('guest') ||\n userEntityRef === 'user:default/guest' ||\n (!hasValidToken && ownershipRefs.length === 0);\n\n const isAuthenticated = !isGuest;\n\n return { isAuthenticated, identity, credentials };\n } catch (error) {\n return { isAuthenticated: false, identity: null, credentials: null };\n }\n }, [identityApi]);\n\n // If still loading authorization or permissions, return loading state\n if (authLoading || loading) return { isLoading: true, userRole: null };\n\n // Check if user is authorized (authenticated, not a guest)\n const isUserAuthorized = authResult?.isAuthenticated ?? false;\n\n // Unauthorized user: show admin items\n if (!isUserAuthorized) {\n return { isLoading: false, userRole: 'admin' };\n }\n\n // Authorized user + NO RBAC enabled: show admin items\n if (!isRBACEnabled) return { isLoading: false, userRole: 'admin' };\n\n // Authorized user + RBAC enabled: check permissions\n // If user has admin permission => show configured admin items\n if (allowed) return { isLoading: false, userRole: 'admin' };\n\n // If user doesn't have admin permission => show configured developer items\n return { isLoading: false, userRole: 'developer' };\n};\n"],"names":[],"mappings":";;;;;AAsCO,MAAM,oBAAoB,MAG5B;AACH,EAAM,MAAA,MAAA,GAAS,OAAO,YAAY,CAAA;AAClC,EAAM,MAAA,WAAA,GAAc,OAAO,cAAc,CAAA;AACzC,EAAA,MAAM,aACJ,GAAA,MAAA,CAAO,kBAAmB,CAAA,oBAAoB,CAAK,IAAA,KAAA;AACrD,EAAA,MAAM,EAAE,OAAA,EAAS,OAAQ,EAAA,GAAI,aAAc,CAAA;AAAA,IACzC,UAAY,EAAA;AAAA,GACb,CAAA;AAGD,EAAA,MAAM,EAAE,KAAO,EAAA,UAAA,EAAY,SAAS,WAAY,EAAA,GAAI,SAAS,YAAY;AACvE,IAAI,IAAA;AACF,MAAM,MAAA,WAAA,GAAc,MAAM,WAAA,CAAY,cAAe,EAAA;AACrD,MAAM,MAAA,QAAA,GAAW,MAAM,WAAA,CAAY,oBAAqB,EAAA;AAGxD,MAAA,MAAM,aAAgB,GAAA,WAAA,EAAa,KAAS,IAAA,WAAA,CAAY,MAAM,MAAS,GAAA,EAAA;AACvE,MAAM,MAAA,aAAA,GAAgB,UAAU,aAAiB,IAAA,EAAA;AACjD,MAAM,MAAA,aAAA,GAAgB,QAAU,EAAA,mBAAA,IAAuB,EAAC;AAExD,MAAA,MAAM,OACJ,GAAA,aAAA,CAAc,WAAY,EAAA,CAAE,QAAS,CAAA,OAAO,CAC5C,IAAA,aAAA,KAAkB,oBACjB,IAAA,CAAC,aAAiB,IAAA,aAAA,CAAc,MAAW,KAAA,CAAA;AAE9C,MAAA,MAAM,kBAAkB,CAAC,OAAA;AAEzB,MAAO,OAAA,EAAE,eAAiB,EAAA,QAAA,EAAU,WAAY,EAAA;AAAA,aACzC,KAAO,EAAA;AACd,MAAA,OAAO,EAAE,eAAiB,EAAA,KAAA,EAAO,QAAU,EAAA,IAAA,EAAM,aAAa,IAAK,EAAA;AAAA;AACrE,GACF,EAAG,CAAC,WAAW,CAAC,CAAA;AAGhB,EAAA,IAAI,eAAe,OAAS,EAAA,OAAO,EAAE,SAAW,EAAA,IAAA,EAAM,UAAU,IAAK,EAAA;AAGrE,EAAM,MAAA,gBAAA,GAAmB,YAAY,eAAmB,IAAA,KAAA;AAGxD,EAAA,IAAI,CAAC,gBAAkB,EAAA;AACrB,IAAA,OAAO,EAAE,SAAA,EAAW,KAAO,EAAA,QAAA,EAAU,OAAQ,EAAA;AAAA;AAI/C,EAAA,IAAI,CAAC,aAAe,EAAA,OAAO,EAAE,SAAW,EAAA,KAAA,EAAO,UAAU,OAAQ,EAAA;AAIjE,EAAA,IAAI,SAAgB,OAAA,EAAE,SAAW,EAAA,KAAA,EAAO,UAAU,OAAQ,EAAA;AAG1D,EAAA,OAAO,EAAE,SAAA,EAAW,KAAO,EAAA,QAAA,EAAU,WAAY,EAAA;AACnD;;;;"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@red-hat-developer-hub/backstage-plugin-quickstart",
3
- "version": "1.3.0",
3
+ "version": "1.5.0",
4
4
  "license": "Apache-2.0",
5
5
  "main": "dist/index.esm.js",
6
6
  "types": "dist/index.d.ts",
@@ -32,22 +32,25 @@
32
32
  "postpack": "backstage-cli package postpack"
33
33
  },
34
34
  "dependencies": {
35
- "@backstage/core-components": "^0.17.4",
35
+ "@backstage-community/plugin-rbac-common": "^1.19.0",
36
+ "@backstage/core-components": "^0.17.5",
36
37
  "@backstage/core-plugin-api": "^1.10.9",
37
- "@backstage/theme": "^0.6.7",
38
+ "@backstage/plugin-permission-react": "^0.4.36",
39
+ "@backstage/theme": "^0.6.8",
38
40
  "@mui/icons-material": "5.18.0",
39
41
  "@mui/material": "5.18.0",
40
- "@red-hat-developer-hub/backstage-plugin-theme": "^0.9.0",
41
- "react-use": "^17.2.4"
42
+ "react-use": "^17.6.0"
42
43
  },
43
44
  "peerDependencies": {
44
45
  "react": "^16.13.1 || ^17.0.0 || ^18.0.0"
45
46
  },
46
47
  "devDependencies": {
47
- "@backstage/cli": "^0.33.1",
48
+ "@backstage/cli": "^0.34.1",
48
49
  "@backstage/core-app-api": "^1.18.0",
49
- "@backstage/dev-utils": "^1.1.12",
50
- "@backstage/test-utils": "^1.7.10",
50
+ "@backstage/dev-utils": "^1.1.13",
51
+ "@backstage/plugin-permission-common": "^0.9.1",
52
+ "@backstage/test-utils": "^1.7.11",
53
+ "@red-hat-developer-hub/backstage-plugin-theme": "^0.10.0",
51
54
  "@testing-library/jest-dom": "^6.0.0",
52
55
  "@testing-library/react": "^14.0.0",
53
56
  "@testing-library/user-event": "^14.0.0",
@@ -1,27 +0,0 @@
1
- import { useApi, identityApiRef, configApiRef } from '@backstage/core-plugin-api';
2
- import { useAsync } from 'react-use';
3
-
4
- const useQuickstartPermission = () => {
5
- const identityApi = useApi(identityApiRef);
6
- const configApi = useApi(configApiRef);
7
- const getUserAuthorization = async () => {
8
- const { token: idToken } = await identityApi.getCredentials();
9
- const backendUrl = configApi.getString("backend.baseUrl");
10
- const jsonResponse = await fetch(`${backendUrl}/api/permission/`, {
11
- headers: {
12
- ...idToken && { Authorization: `Bearer ${idToken}` }
13
- }
14
- });
15
- return jsonResponse.json();
16
- };
17
- const { loading: isUserLoading, value: result } = useAsync(
18
- async () => await getUserAuthorization(),
19
- []
20
- );
21
- const isRBACPluginEnabled = configApi.getOptionalBoolean("permission.enabled");
22
- if (!isRBACPluginEnabled) return true;
23
- return !isUserLoading && result.status === "Authorized";
24
- };
25
-
26
- export { useQuickstartPermission };
27
- //# sourceMappingURL=useQuickstartPermission.esm.js.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"useQuickstartPermission.esm.js","sources":["../../src/hooks/useQuickstartPermission.tsx"],"sourcesContent":["/*\n * Copyright Red Hat, Inc.\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 */\n\nimport {\n configApiRef,\n identityApiRef,\n useApi,\n} from '@backstage/core-plugin-api';\nimport { useAsync } from 'react-use';\n\nexport const useQuickstartPermission = () => {\n const identityApi = useApi(identityApiRef);\n const configApi = useApi(configApiRef);\n\n const getUserAuthorization = async () => {\n const { token: idToken } = await identityApi.getCredentials();\n const backendUrl = configApi.getString('backend.baseUrl');\n const jsonResponse = await fetch(`${backendUrl}/api/permission/`, {\n headers: {\n ...(idToken && { Authorization: `Bearer ${idToken}` }),\n },\n });\n return jsonResponse.json();\n };\n const { loading: isUserLoading, value: result } = useAsync(\n async () => await getUserAuthorization(),\n [],\n );\n\n const isRBACPluginEnabled =\n configApi.getOptionalBoolean('permission.enabled');\n\n if (!isRBACPluginEnabled) return true;\n\n return !isUserLoading && result.status === 'Authorized';\n};\n"],"names":[],"mappings":";;;AAuBO,MAAM,0BAA0B,MAAM;AAC3C,EAAM,MAAA,WAAA,GAAc,OAAO,cAAc,CAAA;AACzC,EAAM,MAAA,SAAA,GAAY,OAAO,YAAY,CAAA;AAErC,EAAA,MAAM,uBAAuB,YAAY;AACvC,IAAA,MAAM,EAAE,KAAO,EAAA,OAAA,EAAY,GAAA,MAAM,YAAY,cAAe,EAAA;AAC5D,IAAM,MAAA,UAAA,GAAa,SAAU,CAAA,SAAA,CAAU,iBAAiB,CAAA;AACxD,IAAA,MAAM,YAAe,GAAA,MAAM,KAAM,CAAA,CAAA,EAAG,UAAU,CAAoB,gBAAA,CAAA,EAAA;AAAA,MAChE,OAAS,EAAA;AAAA,QACP,GAAI,OAAW,IAAA,EAAE,aAAe,EAAA,CAAA,OAAA,EAAU,OAAO,CAAG,CAAA;AAAA;AACtD,KACD,CAAA;AACD,IAAA,OAAO,aAAa,IAAK,EAAA;AAAA,GAC3B;AACA,EAAA,MAAM,EAAE,OAAA,EAAS,aAAe,EAAA,KAAA,EAAO,QAAW,GAAA,QAAA;AAAA,IAChD,YAAY,MAAM,oBAAqB,EAAA;AAAA,IACvC;AAAC,GACH;AAEA,EAAM,MAAA,mBAAA,GACJ,SAAU,CAAA,kBAAA,CAAmB,oBAAoB,CAAA;AAEnD,EAAI,IAAA,CAAC,qBAA4B,OAAA,IAAA;AAEjC,EAAO,OAAA,CAAC,aAAiB,IAAA,MAAA,CAAO,MAAW,KAAA,YAAA;AAC7C;;;;"}