diff --git a/ui/src/app/Onboarding.tsx b/ui/src/app/Onboarding.tsx
index 90115786ac..ef17158568 100644
--- a/ui/src/app/Onboarding.tsx
+++ b/ui/src/app/Onboarding.tsx
@@ -3,6 +3,7 @@ import {
AcademicCapIcon,
BookOpenIcon,
ChatBubbleLeftIcon,
+ CloudIcon,
CodeBracketIcon,
CommandLineIcon,
PuzzlePieceIcon,
@@ -20,28 +21,34 @@ const gettingStartedTiles = [
icon: AcademicCapIcon,
name: 'Get Started',
description: 'Learn how to create your first feature flag',
+ href: 'https://docs.flipt.io/introduction'
+ },
+ {
+ icon: CloudIcon,
+ name: 'Introducing Flipt Hybrid Cloud',
className: 'sm:col-span-2',
- href: 'https://www.flipt.io/docs/introduction'
+ description:
+ 'Learn about our managed offering with enhanced security and support',
+ href: 'https://docs.flipt.io/cloud/overview'
},
{
icon: CommandLineIcon,
name: 'Try the CLI',
description: 'Use the Flipt CLI to manage your feature flags and more',
- href: 'https://www.flipt.io/docs/cli/overview'
+ href: 'https://docs.flipt.io/cli/overview'
},
{
icon: BookOpenIcon,
name: 'Checkout a Guide',
description:
'Use Flipt to its full potential. Read our guides including using Flipt with GitOps',
- href: 'https://www.flipt.io/docs/guides'
+ href: 'https://docs.flipt.io/guides'
},
{
icon: PuzzlePieceIcon,
name: 'Integrate Your Application',
description: 'Use our SDKs to integrate your applications in your language',
- className: 'sm:col-span-2',
- href: 'https://www.flipt.io/docs/integration/overview'
+ href: 'https://docs.flipt.io/integration/overview'
}
];
diff --git a/ui/src/app/events/eventSlice.ts b/ui/src/app/events/eventSlice.ts
index c273209011..59703d4eec 100644
--- a/ui/src/app/events/eventSlice.ts
+++ b/ui/src/app/events/eventSlice.ts
@@ -4,10 +4,12 @@ import { RootState } from '~/store';
export const eventKey = 'event';
interface IEventState {
completedOnboarding: boolean;
+ dismissedBanner: boolean;
}
const initialState: IEventState = {
- completedOnboarding: false
+ completedOnboarding: false,
+ dismissedBanner: false
};
export const eventSlice = createSlice({
@@ -16,15 +18,23 @@ export const eventSlice = createSlice({
reducers: {
onboardingCompleted: (state) => {
state.completedOnboarding = true;
+ },
+ bannerDismissed: (state) => {
+ state.dismissedBanner = true;
}
}
});
-export const { onboardingCompleted } = eventSlice.actions;
+export const { onboardingCompleted, bannerDismissed } = eventSlice.actions;
export const selectCompletedOnboarding = createSelector(
[(state: RootState) => state.user],
(user) => user.completedOnboarding
);
+export const selectDismissedBanner = createSelector(
+ [(state: RootState) => state.user],
+ (user) => user.dismissedBanner
+);
+
export default eventSlice.reducer;
diff --git a/ui/src/components/Banner.tsx b/ui/src/components/Banner.tsx
new file mode 100644
index 0000000000..3f0db5a8da
--- /dev/null
+++ b/ui/src/components/Banner.tsx
@@ -0,0 +1,44 @@
+import { XMarkIcon } from '@heroicons/react/20/solid';
+import { useDispatch } from 'react-redux';
+import { bannerDismissed } from '~/app/events/eventSlice';
+
+type BannerProps = {
+ title: string;
+ description: string;
+ href: string;
+};
+
+export default function Banner(props: BannerProps) {
+ const { title, description, href } = props;
+
+ const dispatch = useDispatch();
+
+ return (
+
+ );
+}
diff --git a/ui/src/store.ts b/ui/src/store.ts
index 421d228779..e61f963734 100644
--- a/ui/src/store.ts
+++ b/ui/src/store.ts
@@ -28,7 +28,10 @@ import { eventSlice, eventKey } from './app/events/eventSlice';
const listenerMiddleware = createListenerMiddleware();
listenerMiddleware.startListening({
- matcher: isAnyOf(eventSlice.actions.onboardingCompleted),
+ matcher: isAnyOf(
+ eventSlice.actions.onboardingCompleted,
+ eventSlice.actions.bannerDismissed
+ ),
effect: (_action, api) => {
// save to local storage
localStorage.setItem(
diff --git a/ui/tests/onboarding.spec.ts b/ui/tests/onboarding.spec.ts
index cdbf47edfb..b6c953d5fc 100644
--- a/ui/tests/onboarding.spec.ts
+++ b/ui/tests/onboarding.spec.ts
@@ -7,24 +7,29 @@ test.describe('Onboarding', () => {
await expect(page.locator('h1')).toContainText('Onboarding');
await expect(
- page.getByText('Get Started', { exact: true })
+ page.getByRole('heading', { name: 'Get Started' })
).toBeVisible();
await expect(
- page.getByText('Try the CLI', { exact: true })
+ page.getByRole('heading', { name: 'Introducing Flipt Hybrid Cloud' })
).toBeVisible();
await expect(
- page.getByText('Checkout a Guide', { exact: true })
+ page.getByRole('heading', { name: 'Try the CLI' })
).toBeVisible();
await expect(
- page.getByText('Integrate Your Application', { exact: true })
+ page.getByRole('heading', { name: 'Checkout a Guide' })
).toBeVisible();
await expect(
- page.getByText('Join the Community', { exact: true })
+ page.getByRole('heading', { name: 'Integrate Your Application' })
).toBeVisible();
await expect(
- page.getByText('View API Reference', { exact: true })
+ page.getByRole('heading', { name: 'Join the Community' })
+ ).toBeVisible();
+ await expect(
+ page.getByRole('heading', { name: 'View API Reference' })
+ ).toBeVisible();
+ await expect(
+ page.getByRole('heading', { name: 'Support Us' })
).toBeVisible();
- await expect(page.getByText('Support Us', { exact: true })).toBeVisible();
await expect(
page.getByRole('button', { name: 'Continue to Dashboard' })
).toBeVisible();
@@ -41,7 +46,7 @@ test.describe('Onboarding', () => {
test.describe('user navigates to the onboarding page', () => {
test.beforeEach(async ({ page }) => {
await page.goto('/');
- await page.getByRole('link', { name: 'Support' }).click();
+ await page.getByRole('link', { name: 'Support', exact: true }).click();
await page.getByRole('heading', { name: 'Onboarding' }).click();
await page.getByRole('link', { name: "Let's Go" }).click();
});