({
typeof item === 'object' &&
item !== null &&
key in item &&
- String(item).toLowerCase().includes(searchValue)
+ String(item[key]).toLowerCase().includes(searchValue)
);
})
)
diff --git a/packages/manager/src/routes/index.tsx b/packages/manager/src/routes/index.tsx
index b31bfac802c..b608e3949b9 100644
--- a/packages/manager/src/routes/index.tsx
+++ b/packages/manager/src/routes/index.tsx
@@ -1,3 +1,4 @@
+import { QueryClient } from '@tanstack/react-query';
import { createRoute, createRouter, redirect } from '@tanstack/react-router';
import React from 'react';
@@ -62,8 +63,11 @@ export const routeTree = rootRoute.addChildren([
]);
export const router = createRouter({
- context: {},
+ context: {
+ queryClient: new QueryClient(),
+ },
defaultNotFoundComponent: () => ,
+ defaultPreload: 'intent',
routeTree,
});
@@ -82,12 +86,17 @@ declare module '@tanstack/react-router' {
*/
export const migrationRouteTree = migrationRootRoute.addChildren([
betaRouteTree,
+ volumesRouteTree,
]);
+export type MigrationRouteTree = typeof migrationRouteTree;
export const migrationRouter = createRouter({
Wrap: ({ children }) => {
return {children}
;
},
- context: {},
+ context: {
+ queryClient: new QueryClient(),
+ },
defaultNotFoundComponent: () => ,
+ defaultPreload: 'intent',
routeTree: migrationRouteTree,
});
diff --git a/packages/manager/src/routes/types.ts b/packages/manager/src/routes/types.ts
index f41099a58e7..80848e64bea 100644
--- a/packages/manager/src/routes/types.ts
+++ b/packages/manager/src/routes/types.ts
@@ -1,4 +1,5 @@
import type { AccountSettings } from '@linode/api-v4';
+import type { QueryClient } from '@tanstack/react-query';
export type RouterContext = {
accountSettings?: AccountSettings;
@@ -8,4 +9,12 @@ export type RouterContext = {
isACLPEnabled?: boolean;
isDatabasesEnabled?: boolean;
isPlacementGroupsEnabled?: boolean;
+ queryClient: QueryClient;
};
+
+export interface TableSearchParams {
+ order?: 'asc' | 'desc';
+ orderBy?: string;
+ page?: number;
+ pageSize?: number;
+}
diff --git a/packages/manager/src/routes/volumes/VolumesRoute.tsx b/packages/manager/src/routes/volumes/VolumesRoot.tsx
similarity index 92%
rename from packages/manager/src/routes/volumes/VolumesRoute.tsx
rename to packages/manager/src/routes/volumes/VolumesRoot.tsx
index 8aa2bf30d58..0745f7346c9 100644
--- a/packages/manager/src/routes/volumes/VolumesRoute.tsx
+++ b/packages/manager/src/routes/volumes/VolumesRoot.tsx
@@ -4,7 +4,7 @@ import React from 'react';
import { ProductInformationBanner } from 'src/components/ProductInformationBanner/ProductInformationBanner';
import { SuspenseLoader } from 'src/components/SuspenseLoader';
-export const VolumesRoute = () => {
+export const VolumesRoot = () => {
return (
}>
diff --git a/packages/manager/src/routes/volumes/constants.ts b/packages/manager/src/routes/volumes/constants.ts
new file mode 100644
index 00000000000..738d9d16f76
--- /dev/null
+++ b/packages/manager/src/routes/volumes/constants.ts
@@ -0,0 +1,3 @@
+export const VOLUME_TABLE_DEFAULT_ORDER = 'asc';
+export const VOLUME_TABLE_DEFAULT_ORDER_BY = 'label';
+export const VOLUME_TABLE_PREFERENCE_KEY = 'volumes';
diff --git a/packages/manager/src/routes/volumes/index.ts b/packages/manager/src/routes/volumes/index.ts
index 9379b004f5b..87db6e3ce86 100644
--- a/packages/manager/src/routes/volumes/index.ts
+++ b/packages/manager/src/routes/volumes/index.ts
@@ -1,10 +1,29 @@
-import { createRoute } from '@tanstack/react-router';
+import { createRoute, redirect } from '@tanstack/react-router';
import { rootRoute } from '../root';
-import { VolumesRoute } from './VolumesRoute';
+import { VolumesRoot } from './VolumesRoot';
+
+import type { TableSearchParams } from '../types';
+
+const volumeAction = {
+ attach: 'attach',
+ clone: 'clone',
+ delete: 'delete',
+ detach: 'detach',
+ details: 'details',
+ edit: 'edit',
+ resize: 'resize',
+ upgrade: 'upgrade',
+} as const;
+
+export type VolumeAction = typeof volumeAction[keyof typeof volumeAction];
+
+export interface VolumesSearchParams extends TableSearchParams {
+ query?: string;
+}
const volumesRoute = createRoute({
- component: VolumesRoute,
+ component: VolumesRoot,
getParentRoute: () => rootRoute,
path: 'volumes',
});
@@ -12,8 +31,9 @@ const volumesRoute = createRoute({
const volumesIndexRoute = createRoute({
getParentRoute: () => volumesRoute,
path: '/',
+ validateSearch: (search: VolumesSearchParams) => search,
}).lazy(() =>
- import('src/features/Volumes/VolumesLanding').then(
+ import('src/routes/volumes/volumesLazyRoutes').then(
(m) => m.volumesLandingLazyRoute
)
);
@@ -22,12 +42,55 @@ const volumesCreateRoute = createRoute({
getParentRoute: () => volumesRoute,
path: 'create',
}).lazy(() =>
- import('src/features/Volumes/VolumeCreate').then(
- (m) => m.volumeCreateLazyRoute
+ import('./volumesLazyRoutes').then((m) => m.volumeCreateLazyRoute)
+);
+
+type VolumeActionRouteParams = {
+ action: VolumeAction;
+ volumeId: P;
+};
+
+const volumeActionRoute = createRoute({
+ beforeLoad: async ({ params }) => {
+ if (!(params.action in volumeAction)) {
+ throw redirect({
+ search: () => ({}),
+ to: '/volumes',
+ });
+ }
+ },
+ getParentRoute: () => volumesRoute,
+ params: {
+ parse: ({ action, volumeId }: VolumeActionRouteParams) => ({
+ action,
+ volumeId: Number(volumeId),
+ }),
+ stringify: ({ action, volumeId }: VolumeActionRouteParams) => ({
+ action,
+ volumeId: String(volumeId),
+ }),
+ },
+ path: '$volumeId/$action',
+ validateSearch: (search: VolumesSearchParams) => search,
+}).lazy(() =>
+ import('src/routes/volumes/volumesLazyRoutes').then(
+ (m) => m.volumesLandingLazyRoute
)
);
+const volumesCatchAllRoute = createRoute({
+ beforeLoad: () => {
+ throw redirect({
+ search: () => ({}),
+ to: '/volumes',
+ });
+ },
+ getParentRoute: () => volumesRoute,
+ path: '*',
+});
+
export const volumesRouteTree = volumesRoute.addChildren([
- volumesIndexRoute,
+ volumesIndexRoute.addChildren([volumeActionRoute]),
volumesCreateRoute,
+ volumesCatchAllRoute,
]);
diff --git a/packages/manager/src/routes/volumes/volumesLazyRoutes.ts b/packages/manager/src/routes/volumes/volumesLazyRoutes.ts
new file mode 100644
index 00000000000..d78731c1d1b
--- /dev/null
+++ b/packages/manager/src/routes/volumes/volumesLazyRoutes.ts
@@ -0,0 +1,12 @@
+import { createLazyRoute } from '@tanstack/react-router';
+
+import { VolumeCreate } from 'src/features/Volumes/VolumeCreate';
+import { VolumesLanding } from 'src/features/Volumes/VolumesLanding';
+
+export const volumesLandingLazyRoute = createLazyRoute('/')({
+ component: VolumesLanding,
+});
+
+export const volumeCreateLazyRoute = createLazyRoute('/volumes/create')({
+ component: VolumeCreate,
+});
diff --git a/packages/manager/src/types/ManagerPreferences.ts b/packages/manager/src/types/ManagerPreferences.ts
index 5c89f454313..42f76b0fc23 100644
--- a/packages/manager/src/types/ManagerPreferences.ts
+++ b/packages/manager/src/types/ManagerPreferences.ts
@@ -7,6 +7,10 @@ export interface OrderSet {
orderBy: string;
}
+export type OrderSetWithPrefix = {
+ [K in `${P}-order` | `${P}-orderBy`]: K extends `${P}-order` ? Order : string;
+};
+
export interface DismissedNotification {
created: string;
expiry?: string;