Skip to content

Commit a94a9d4

Browse files
upcoming: [UIE-9398] - Private Image Sharing Tabs New Layout (v2) (linode#13407)
* Split PR, and add more changes * Added changeset: Private Image Sharing tabs new layout (v2) * Few changes * Update few remaining references * Update filename * Change custom to owned (query params) * Update sharegroups to share-groups * Update remaining share-groups references * Few updates * Minor clean up * Make parent tabs routed and use routeparams for nested tabs per UX * Update small code comment * Update few more comments
1 parent 039f29c commit a94a9d4

18 files changed

+392
-6
lines changed
Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
---
2+
"@linode/manager": Upcoming Features
3+
---
4+
5+
Private Image Sharing tabs new layout (v2) ([#13407](https://github.com/linode/manager/pull/13407))

packages/manager/src/Router.tsx

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@ import { useGlobalErrors } from 'src/hooks/useGlobalErrors';
1010
import { useIsACLPEnabled } from './features/CloudPulse/Utils/utils';
1111
import { useIsDatabasesEnabled } from './features/Databases/utilities';
1212
import { ErrorBoundaryFallback } from './features/ErrorBoundary/ErrorBoundaryFallback';
13+
import { useIsPrivateImageSharingEnabled } from './features/Images/utils';
1314
import { useIsPlacementGroupsEnabled } from './features/PlacementGroups/utils';
1415
import { router } from './routes';
1516

@@ -22,6 +23,7 @@ export const Router = () => {
2223
const { isDatabasesEnabled } = useIsDatabasesEnabled();
2324
const { isPlacementGroupsEnabled } = useIsPlacementGroupsEnabled();
2425
const { isACLPEnabled } = useIsACLPEnabled();
26+
const { isPrivateImageSharingEnabled } = useIsPrivateImageSharingEnabled();
2527
const flags = useFlags();
2628

2729
// Update the router's context
@@ -31,6 +33,7 @@ export const Router = () => {
3133
flags,
3234
globalErrors,
3335
isACLPEnabled,
36+
isPrivateImageSharingEnabled,
3437
isDatabasesEnabled,
3538
isPlacementGroupsEnabled,
3639
profile,

packages/manager/src/features/Images/ImagesLanding/ImagesLanding.test.tsx

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@ import { makeResourcePage } from 'src/mocks/serverHandlers';
77
import { http, HttpResponse, server } from 'src/mocks/testServer';
88
import { mockMatchMedia, renderWithTheme } from 'src/utilities/testHelpers';
99

10-
import ImagesLanding from './ImagesLanding';
10+
import { ImagesLanding } from './ImagesLanding';
1111

1212
const queryMocks = vi.hoisted(() => ({
1313
usePermissions: vi.fn().mockReturnValue({ data: { create_image: false } }),

packages/manager/src/features/Images/ImagesLanding/ImagesLanding.tsx

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -568,5 +568,3 @@ export const ImagesLanding = () => {
568568
</>
569569
);
570570
};
571-
572-
export default ImagesLanding;

packages/manager/src/features/Images/ImagesLanding/imagesLandingLazyRoute.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
import { createLazyRoute } from '@tanstack/react-router';
22

3-
import { ImagesLanding } from 'src/features/Images/ImagesLanding/ImagesLanding';
3+
import { ImagesLanding } from './ImagesLanding';
44

55
export const imagesLandingLazyRoute = createLazyRoute('/images')({
66
component: ImagesLanding,
Lines changed: 70 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,70 @@
1+
import { BetaChip, Notice, Stack } from '@linode/ui';
2+
import { useNavigate, useParams } from '@tanstack/react-router';
3+
import * as React from 'react';
4+
5+
import { SuspenseLoader } from 'src/components/SuspenseLoader';
6+
import { SafeTabPanel } from 'src/components/Tabs/SafeTabPanel';
7+
import { Tab } from 'src/components/Tabs/Tab';
8+
import { TabList } from 'src/components/Tabs/TabList';
9+
import { TabPanels } from 'src/components/Tabs/TabPanels';
10+
import { Tabs } from 'src/components/Tabs/Tabs';
11+
12+
import { getImageLibrarySubTabIndex } from '../../../utils';
13+
import { imageLibrarySubTabs as subTabs } from './imageLibraryTabsConfig';
14+
15+
export const ImageLibraryTabs = () => {
16+
const navigate = useNavigate();
17+
18+
const params = useParams({
19+
from: '/images/image-library/$imageType',
20+
shouldThrow: false,
21+
});
22+
23+
const subTabIndex = getImageLibrarySubTabIndex(subTabs, params?.imageType);
24+
25+
const onTabChange = (index: number) => {
26+
// - Update the "imageType" param.
27+
// - This switches between "Owned by me", "Shared with me" and "Recovery images" sub-tabs within the Image Library tab.
28+
navigate({
29+
to: `/images/image-library/$imageType`,
30+
params: {
31+
imageType: subTabs[index].type,
32+
},
33+
});
34+
};
35+
36+
return (
37+
<Stack spacing={3}>
38+
<Tabs index={subTabIndex} onChange={onTabChange}>
39+
<TabList>
40+
{subTabs.map((tab) => (
41+
<Tab key={`images-${tab.type}`}>
42+
{tab.title} {tab.isBeta ? <BetaChip /> : null}
43+
</Tab>
44+
))}
45+
</TabList>
46+
<React.Suspense fallback={<SuspenseLoader />}>
47+
<TabPanels>
48+
{subTabs.map((tab, idx) => (
49+
<SafeTabPanel index={idx} key={`images-${tab.type}-content`}>
50+
{tab.type === 'owned-by-me' && (
51+
// <ImagesView handlers={handlers} type="owned-by-me" />
52+
<Notice variant="info">Custom Images</Notice>
53+
)}
54+
{tab.type === 'shared-with-me' && (
55+
<Notice variant="info">
56+
Share with me is coming soon...
57+
</Notice>
58+
)}
59+
{tab.type === 'recovery-images' && (
60+
// <ImagesView handlers={handlers} type="recovery-images" />
61+
<Notice variant="info">Recovery Images</Notice>
62+
)}
63+
</SafeTabPanel>
64+
))}
65+
</TabPanels>
66+
</React.Suspense>
67+
</Tabs>
68+
</Stack>
69+
);
70+
};
Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
import type { ImageLibrarySubTab } from '../../../utils';
2+
3+
export const imageLibrarySubTabs: ImageLibrarySubTab[] = [
4+
{ type: 'owned-by-me', title: 'Owned by me' },
5+
{
6+
type: 'shared-with-me',
7+
title: 'Shared with me',
8+
isBeta: true,
9+
},
10+
{ type: 'recovery-images', title: 'Recovery images' },
11+
];
Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
import { createLazyRoute } from '@tanstack/react-router';
2+
3+
import { ImageLibraryTabs } from './ImageLibraryTabs';
4+
5+
export const imageLibraryTabsLazyRoute = createLazyRoute(
6+
'/images/image-library'
7+
)({
8+
component: ImageLibraryTabs,
9+
});
Lines changed: 46 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,46 @@
1+
import { BetaChip } from '@linode/ui';
2+
import { Outlet } from '@tanstack/react-router';
3+
import React from 'react';
4+
5+
import { LandingHeader } from 'src/components/LandingHeader';
6+
import { SuspenseLoader } from 'src/components/SuspenseLoader';
7+
import { TabPanels } from 'src/components/Tabs/TabPanels';
8+
import { Tabs } from 'src/components/Tabs/Tabs';
9+
import { TanStackTabLinkList } from 'src/components/Tabs/TanStackTabLinkList';
10+
import { useTabs } from 'src/hooks/useTabs';
11+
12+
export const ImagesLandingV2 = () => {
13+
const { handleTabChange, tabIndex, tabs } = useTabs([
14+
{
15+
title: 'Image Library',
16+
to: '/images/image-library',
17+
},
18+
{
19+
title: 'Share Groups',
20+
to: '/images/share-groups',
21+
chip: <BetaChip />,
22+
},
23+
]);
24+
25+
return (
26+
<>
27+
<LandingHeader
28+
breadcrumbProps={{
29+
pathname: 'Images',
30+
removeCrumbX: 1,
31+
}}
32+
spacingBottom={16}
33+
title="Images"
34+
/>
35+
36+
<Tabs index={tabIndex} onChange={handleTabChange}>
37+
<TanStackTabLinkList tabs={tabs} />
38+
<React.Suspense fallback={<SuspenseLoader />}>
39+
<TabPanels>
40+
<Outlet />
41+
</TabPanels>
42+
</React.Suspense>
43+
</Tabs>
44+
</>
45+
);
46+
};
Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
import { Notice } from '@linode/ui';
2+
import React from 'react';
3+
4+
export const ShareGroupsTabs = () => {
5+
return <Notice variant="info">Share Groups is coming soon...</Notice>;
6+
};

0 commit comments

Comments
 (0)