added segments

This commit is contained in:
Nate Kelley 2025-09-04 00:20:42 -06:00
parent 87d4bd3dc3
commit 73f68fd504
No known key found for this signature in database
GPG Key ID: FD90372AB8D98B4F
20 changed files with 674 additions and 78 deletions

View File

@ -1,6 +1,6 @@
import type { UserResponse } from '@buster/server-shared/user';
import {
QueryClient,
type QueryClient,
type UseQueryOptions,
useMutation,
useQuery,
@ -61,13 +61,12 @@ export const useUpdateUser = () => {
});
};
export const prefetchGetUser = async (userId: string, queryClientProp?: QueryClient) => {
const queryClient = queryClientProp || new QueryClient();
export const prefetchGetUser = async (userId: string, queryClient: QueryClient) => {
await queryClient.prefetchQuery({
...userQueryKeys.userGetUser(userId),
queryFn: () => getUser({ userId }),
});
return queryClient;
return queryClient.getQueryData(userQueryKeys.userGetUser(userId).queryKey);
};
export const useInviteUser = () => {

View File

@ -1,4 +1,3 @@
import { useMatchRoute } from '@tanstack/react-router';
import { useMemo } from 'react';
import {
useDeleteUserFavorite,
@ -14,7 +13,6 @@ export const useFavoriteSidebarPanel = (): ISidebarGroup | null => {
const { data: favorites } = useGetUserFavorites();
const { mutateAsync: updateUserFavorites } = useUpdateUserFavorites();
const { mutateAsync: deleteUserFavorite } = useDeleteUserFavorite();
const matcher = useMatchRoute();
return useMemo(() => {
if (!favorites || favorites.length === 0) return null;
@ -37,5 +35,5 @@ export const useFavoriteSidebarPanel = (): ISidebarGroup | null => {
});
}),
} satisfies ISidebarGroup;
}, [favorites, matcher, deleteUserFavorite]);
}, [favorites, deleteUserFavorite]);
};

View File

@ -320,3 +320,23 @@ SegmentedTriggerComponent.displayName = 'SegmentedTrigger';
const SegmentedTrigger = React.memo(SegmentedTriggerComponent) as typeof SegmentedTriggerComponent;
SegmentedTrigger.displayName = 'SegmentedTrigger';
export function createSegmentedItem<T extends string | number = string>() {
return <
TRouter extends RegisteredRouter = RegisteredRouter,
TOptions = Record<string, unknown>,
TFrom extends string = string,
>(
item: SegmentedItem<T, TRouter, TOptions, TFrom>
) => item as SegmentedItem<T, TRouter, TOptions, TFrom>;
}
export function createSegmentedItems<T extends string | number = string>() {
return <
TRouter extends RegisteredRouter = RegisteredRouter,
TOptions = Record<string, unknown>,
TFrom extends string = string,
>(
items: SegmentedItem<T, TRouter, TOptions, TFrom>[]
) => items as SegmentedItem<T, TRouter, TOptions, TFrom>[];
}

View File

@ -1,21 +1,22 @@
import React, { useLayoutEffect, useState } from 'react';
import { useLocation, useMatchRoute, useRouter } from '@tanstack/react-router';
import React, { useLayoutEffect, useMemo, useState } from 'react';
import { useGetUser } from '@/api/buster_rest/users';
import { useIsUserAdmin } from '@/api/buster_rest/users/useGetUserInfo';
import { UserHeader } from './UserHeader';
import { SegmentToApp, UserSegments, UserSegmentsApps } from './UserSegments';
import { UserSegments, UserSegmentsApps } from './UserSegments';
export const LayoutHeaderAndSegment = React.memo(
({ children, userId }: { children: React.ReactNode; userId: string }) => {
const { data: user } = useGetUser({ userId });
const isAdmin = useIsUserAdmin();
console.warn('TODO: currentRoute');
//SegmentToApp[currentRoute as keyof typeof SegmentToApp] ||
const [selectedApp, setSelectedApp] = useState<UserSegmentsApps>(UserSegmentsApps.OVERVIEW);
const initialSelectedApp = useRouteToSegments();
useLayoutEffect(() => {
// if (currentRoute && currentRoute in SegmentToApp) {
// setSelectedApp(SegmentToApp[currentRoute as keyof typeof SegmentToApp]);
// }
if (initialSelectedApp) {
setSelectedApp(initialSelectedApp);
}
}, []);
if (!user) return null;
@ -36,3 +37,30 @@ export const LayoutHeaderAndSegment = React.memo(
);
LayoutHeaderAndSegment.displayName = 'LayoutHeaderAndSegment';
const useRouteToSegments = (): UserSegmentsApps => {
const match = useMatchRoute();
return useMemo(() => {
const isPermissionGroups = match({
from: '/app/settings/users/$userId/permission-groups',
});
if (isPermissionGroups) {
return UserSegmentsApps.PERMISSION_GROUPS;
}
const isDatasetGroups = match({
from: '/app/settings/users/$userId/dataset-groups',
});
if (isDatasetGroups) {
return UserSegmentsApps.DATASET_GROUPS;
}
const isDatasets = match({
from: '/app/settings/users/$userId/datasets',
});
if (isDatasets) {
return UserSegmentsApps.DATASETS;
}
return UserSegmentsApps.OVERVIEW;
}, []);
};

View File

@ -1,7 +1,7 @@
import type { LinkProps } from '@tanstack/react-router';
import { Link, type LinkProps } from '@tanstack/react-router';
import React, { useMemo } from 'react';
import type { SegmentedItem } from '@/components/ui/segmented';
import { AppSegmented } from '@/components/ui/segmented';
import { AppSegmented, createSegmentedItem, createSegmentedItems } from '@/components/ui/segmented';
import { Separator } from '@/components/ui/separator';
import { useMemoizedFn } from '@/hooks/useMemoizedFn';
import { defineLink } from '@/lib/routes';
@ -16,75 +16,81 @@ export enum UserSegmentsApps {
TEAMS = 'Teams',
}
export const SegmentToApp = {
// [BusterRoutes.SETTINGS_USERS]: UserSegmentsApps.OVERVIEW,
// [BusterRoutes.SETTINGS_USERS_ID]: UserSegmentsApps.OVERVIEW,
// [BusterRoutes.SETTINGS_USERS_ID_PERMISSION_GROUPS]: UserSegmentsApps.PERMISSION_GROUPS,
// [BusterRoutes.SETTINGS_USERS_ID_DATASET_GROUPS]: UserSegmentsApps.DATASET_GROUPS,
// [BusterRoutes.SETTINGS_USERS_ID_DATASETS]: UserSegmentsApps.DATASETS,
// [BusterRoutes.SETTINGS_USERS_ID_ATTRIBUTES]: UserSegmentsApps.ATTRIBUTES,
// [BusterRoutes.SETTINGS_USERS_ID_TEAMS]: UserSegmentsApps.TEAMS,
};
export const UserSegments: React.FC<{
isAdmin: boolean;
selectedApp: UserSegmentsApps;
onSelectApp: (app: UserSegmentsApps) => void;
userId: string;
}> = React.memo(({ isAdmin, selectedApp, onSelectApp, userId }) => {
}> = React.memo(({ selectedApp, onSelectApp, userId }) => {
const onChange = useMemoizedFn((value: SegmentedItem<UserSegmentsApps>) => {
onSelectApp(value.value);
});
const options: SegmentedItem<UserSegmentsApps>[] = useMemo(
const createUserSegmentedItems = createSegmentedItems<UserSegmentsApps>();
const options = useMemo(
() =>
[
createUserSegmentedItems([
{
label: 'Overview',
value: UserSegmentsApps.OVERVIEW,
link: defineLink({
link: {
to: `/app/settings/users/$userId`,
params: {
userId,
},
}),
hide: false,
},
},
{
label: 'Permissions groups',
value: UserSegmentsApps.PERMISSION_GROUPS,
link: {
to: `/app/settings/users/$userId/permission-groups`,
params: {
userId,
},
},
},
{
label: 'Dataset groups',
value: UserSegmentsApps.DATASET_GROUPS,
link: {
to: `/app/settings/users/$userId/dataset-groups`,
params: {
userId,
},
},
},
{
label: 'Datasets',
value: UserSegmentsApps.DATASETS,
link: {
to: `/app/settings/users/$userId/datasets`,
params: {
userId,
},
},
},
// {
// label: 'Permissions groups',
// value: UserSegmentsApps.PERMISSION_GROUPS,
// link: createBusterRoute({
// route: BusterRoutes.SETTINGS_USERS_ID_PERMISSION_GROUPS,
// userId,
// }),
// hide: !isAdmin,
// },
// {
// label: 'Dataset groups',
// value: UserSegmentsApps.DATASET_GROUPS,
// link: createBusterRoute({
// route: BusterRoutes.SETTINGS_USERS_ID_DATASET_GROUPS,
// userId,
// }),
// },
// {
// label: 'Datasets',
// value: UserSegmentsApps.DATASETS,
// link: createBusterRoute({ route: BusterRoutes.SETTINGS_USERS_ID_DATASETS, userId }),
// },
// {
// label: 'Attributes',
// value: UserSegmentsApps.ATTRIBUTES,
// link: createBusterRoute({ route: BusterRoutes.SETTINGS_USERS_ID_ATTRIBUTES, userId }),
// hide: true,
// link: {
// to: `/app/settings/users/$userId/attributes`,
// params: {
// userId,
// },
// },
// },
// {
// label: 'Teams',
// value: UserSegmentsApps.TEAMS,
// link: createBusterRoute({ route: BusterRoutes.SETTINGS_USERS_ID_TEAMS, userId }),
// },
]
.filter((x) => !x.hide)
.map((x) => ({ ...x, hide: undefined })),
{
label: 'Teams',
value: UserSegmentsApps.TEAMS,
link: {
to: `/app/settings/users/$userId/teams`,
params: {
userId,
},
},
},
]),
[userId]
);

View File

@ -0,0 +1,121 @@
import type { OrganizationUserDataset } from '@buster/server-shared/organization';
import React, { useMemo } from 'react';
import { PermissionLineageBreadcrumb } from '@/components/features/permissions';
import {
type BusterListColumn,
type BusterListRowItem,
EmptyStateList,
InfiniteListContainer,
} from '@/components/ui/list';
import { BusterInfiniteList } from '@/components/ui/list/BusterInfiniteList';
import { Text } from '@/components/ui/typography';
export const UserDatasetListContainer = React.memo(
({ filteredDatasets }: { filteredDatasets: OrganizationUserDataset[] }) => {
const columns: BusterListColumn<OrganizationUserDataset>[] = useMemo(
() => [
{
title: 'Name',
dataIndex: 'name',
render: (_: string, dataset: OrganizationUserDataset) => {
return (
<div className="flex items-center justify-between space-x-2">
<Text>{dataset.name}</Text>
<DatasetLineageCell dataset={dataset} />
</div>
);
},
},
],
[]
);
const { canQuery, cannotQuery, disabled } = useMemo(() => {
const results: {
canQuery: BusterListRowItem<OrganizationUserDataset>[];
cannotQuery: BusterListRowItem<OrganizationUserDataset>[];
disabled: BusterListRowItem<OrganizationUserDataset>[];
} = filteredDatasets.reduce<{
canQuery: BusterListRowItem<OrganizationUserDataset>[];
cannotQuery: BusterListRowItem<OrganizationUserDataset>[];
disabled: BusterListRowItem<OrganizationUserDataset>[];
}>(
(acc, dataset) => {
const datasetItem: BusterListRowItem<OrganizationUserDataset> = {
id: dataset.id,
data: dataset,
};
if (dataset.can_query) {
acc.canQuery.push(datasetItem);
} else {
acc.cannotQuery.push(datasetItem);
}
return acc;
},
{ canQuery: [], cannotQuery: [], disabled: [] }
);
return results;
}, [filteredDatasets]);
const rows: BusterListRowItem<OrganizationUserDataset>[] = useMemo(() => {
return [
{
id: 'header-can-query',
data: null,
hidden: canQuery.length === 0,
rowSection: {
title: 'Can query',
secondaryTitle: canQuery.length.toString(),
},
},
...canQuery,
{
id: 'header-cannot-query',
data: null,
hidden: cannotQuery.length === 0,
rowSection: {
title: 'Cannot Query',
secondaryTitle: cannotQuery.length.toString(),
},
},
...cannotQuery,
{
id: 'header-disabled',
data: null,
hidden: disabled.length === 0,
rowSection: {
title: 'Disabled',
secondaryTitle: disabled.length.toString(),
},
},
...disabled,
].filter((row) => !row.hidden);
}, [canQuery, cannotQuery, disabled]);
return (
<InfiniteListContainer>
<BusterInfiniteList
columns={columns}
rows={rows}
showHeader={false}
showSelectAll={false}
emptyState={useMemo(() => <EmptyStateList text="No datasets found" />, [])}
/>
</InfiniteListContainer>
);
}
);
UserDatasetListContainer.displayName = 'UserDatasetListContainer';
const DatasetLineageCell = React.memo(({ dataset }: { dataset: OrganizationUserDataset }) => {
return (
<div className="flex items-center justify-end">
<PermissionLineageBreadcrumb lineage={dataset.lineage} canQuery={dataset.can_query} />
</div>
);
});
DatasetLineageCell.displayName = 'DatasetLineageCell';

View File

@ -0,0 +1,26 @@
import type { OrganizationUser } from '@buster/server-shared/organization';
import React from 'react';
import { PermissionSearchAndListWrapper } from '@/components/features/permissions/PermissionSearchAndListWrapper';
import { useDebounceSearch } from '@/hooks/useDebounceSearch';
import { UserDatasetListContainer } from './UserDatasetListContainer';
export const UserDatasetSearch = React.memo(({ user }: { user: OrganizationUser }) => {
const { datasets } = user;
const { filteredItems, searchText, handleSearchChange } = useDebounceSearch({
items: datasets,
searchPredicate: (item, searchText) =>
item.name.toLowerCase().includes(searchText.toLowerCase()),
});
return (
<PermissionSearchAndListWrapper
searchText={searchText}
handleSearchChange={handleSearchChange}
searchPlaceholder="Search by dataset name"
>
<UserDatasetListContainer filteredDatasets={filteredItems} />
</PermissionSearchAndListWrapper>
);
});
UserDatasetSearch.displayName = 'UserDatasetSearch';

View File

@ -0,0 +1,90 @@
import type { OrganizationUser } from '@buster/server-shared/organization';
import type { User } from '@buster/server-shared/user';
import React from 'react';
import { useUpdateUser } from '@/api/buster_rest/users';
import { AccessRoleSelect } from '@/components/features/security/AccessRoleSelect';
import {
Card,
CardContent,
CardDescription,
CardHeader,
CardTitle,
} from '@/components/ui/card/CardBase';
import { AppTooltip } from '@/components/ui/tooltip';
import { Text } from '@/components/ui/typography';
import { useMemoizedFn } from '@/hooks/useMemoizedFn';
export const UserDefaultAccess: React.FC<{
user: OrganizationUser;
isAdmin: boolean;
myUser: User;
refetchUser: () => void;
}> = ({ user, isAdmin, myUser, refetchUser }) => {
const { mutateAsync } = useUpdateUser();
const userIsMe = user.id === myUser.id;
const onChange = useMemoizedFn(async (value: string) => {
await mutateAsync({ userId: user.id, role: value as OrganizationUser['role'] });
refetchUser();
});
return (
<DefaultAccessCard
role={user.role}
onChange={onChange}
isAdmin={isAdmin}
userIsMe={userIsMe}
name={user.name}
/>
);
};
const DefaultAccessCard = React.memo(
({
role,
onChange,
isAdmin,
userIsMe,
name,
}: {
role: OrganizationUser['role'];
onChange: (role: OrganizationUser['role']) => void;
isAdmin: boolean;
userIsMe: boolean;
name: string;
}) => {
const isDisabled = !isAdmin || userIsMe;
return (
<Card>
<CardHeader>
<CardTitle>Default Access</CardTitle>
<CardDescription>
This becomes the minimum level of access that {name} will have for all datasets.
</CardDescription>
</CardHeader>
<CardContent className="!pt-0">
<div className="flex items-center justify-between">
<Text variant="secondary">Access level</Text>
<div className="min-w-44">
<AppTooltip
title={
isDisabled
? userIsMe
? 'You cannot change your own access'
: 'Only admins can change access'
: undefined
}
>
<AccessRoleSelect role={role} onChange={onChange} />
</AppTooltip>
</div>
</div>
</CardContent>
</Card>
);
}
);
DefaultAccessCard.displayName = 'DefaultAccessCard';

View File

@ -1,10 +1,16 @@
export const UserIndividualsLayout = () => {
import { LayoutHeaderAndSegment, UsersBackButton } from './LayoutHeaderAndSegment';
export const UserIndividualsLayout = ({
children,
userId,
}: {
children: React.ReactNode;
userId: string;
}) => {
return (
<div className="flex h-full flex-col space-y-5 overflow-y-auto px-12 py-12">
<UsersBackButton />
<HydrationBoundary state={dehydrate(queryClient)}>
{<LayoutHeaderAndSegment userId={params.userId}>{children}</LayoutHeaderAndSegment>}
</HydrationBoundary>
{<LayoutHeaderAndSegment userId={userId}>{children}</LayoutHeaderAndSegment>}
</div>
);
};

View File

@ -0,0 +1,17 @@
import type { OrganizationUser } from '@buster/server-shared/organization';
import React from 'react';
import { HeaderExplanation } from '@/components/features/permissions';
export const UserLineageHeader = React.memo(
({ className = '', user }: { className?: string; user: OrganizationUser }) => {
return (
<HeaderExplanation
className={className}
title={'Dataset access & lineage'}
description={`View ${user.name}s access to all available datasets. Lineage is provided to show where access originates from.`}
/>
);
}
);
UserLineageHeader.displayName = 'UserLineageHeader';

View File

@ -0,0 +1,24 @@
import React from 'react';
import { useGetUser } from '@/api/buster_rest/users';
import { useGetUserBasicInfo, useIsUserAdmin } from '@/api/buster_rest/users/useGetUserInfo';
import { UserDatasetSearch } from './UserDatasetSearch';
import { UserDefaultAccess } from './UserDefaultAccess';
import { UserLineageHeader } from './UserLineageHeader';
export const UserOverviewController = React.memo(({ userId }: { userId: string }) => {
const { data: user, refetch: refetchUser } = useGetUser({ userId });
const isAdmin = useIsUserAdmin();
const myUser = useGetUserBasicInfo();
if (!user || !myUser) return null;
return (
<>
<UserDefaultAccess user={user} myUser={myUser} isAdmin={isAdmin} refetchUser={refetchUser} />
<UserLineageHeader className="mt-12!" user={user} />
<UserDatasetSearch user={user} />
</>
);
});
UserOverviewController.displayName = 'UserController';

View File

@ -54,6 +54,7 @@ import { Route as AppAppAssetReportsReportIdLayoutRouteImport } from './routes/a
import { Route as AppAppAssetMetricsMetricIdLayoutRouteImport } from './routes/app/_app/_asset/metrics.$metricId/_layout'
import { Route as AppAppAssetDashboardsDashboardIdLayoutRouteImport } from './routes/app/_app/_asset/dashboards.$dashboardId/_layout'
import { Route as AppSettingsRestricted_layoutAdmin_onlySettingsDatasourcesIndexRouteImport } from './routes/app/_settings/_restricted_layout/_admin_only/settings.datasources.index'
import { Route as AppSettingsPermissionsSettingsUsersUserIdIndexRouteImport } from './routes/app/_settings/_permissions/settings.users.$userId.index'
import { Route as AppAppAssetReportsReportIdLayoutIndexRouteImport } from './routes/app/_app/_asset/reports.$reportId/_layout/index'
import { Route as AppAppAssetMetricsMetricIdLayoutIndexRouteImport } from './routes/app/_app/_asset/metrics.$metricId/_layout/index'
import { Route as AppAppAssetDashboardsDashboardIdLayoutIndexRouteImport } from './routes/app/_app/_asset/dashboards.$dashboardId/_layout/index'
@ -61,6 +62,11 @@ import { Route as AppSettingsRestricted_layoutAdmin_onlySettingsStorageStorageId
import { Route as AppSettingsRestricted_layoutAdmin_onlySettingsStorageAddRouteImport } from './routes/app/_settings/_restricted_layout/_admin_only/settings.storage.add'
import { Route as AppSettingsRestricted_layoutAdmin_onlySettingsDatasourcesAddRouteImport } from './routes/app/_settings/_restricted_layout/_admin_only/settings.datasources.add'
import { Route as AppSettingsRestricted_layoutAdmin_onlySettingsDatasourcesDatasourceIdRouteImport } from './routes/app/_settings/_restricted_layout/_admin_only/settings.datasources.$datasourceId'
import { Route as AppSettingsPermissionsSettingsUsersUserIdTeamsRouteImport } from './routes/app/_settings/_permissions/settings.users.$userId.teams'
import { Route as AppSettingsPermissionsSettingsUsersUserIdPermissionGroupsRouteImport } from './routes/app/_settings/_permissions/settings.users.$userId.permission-groups'
import { Route as AppSettingsPermissionsSettingsUsersUserIdDatasetsRouteImport } from './routes/app/_settings/_permissions/settings.users.$userId.datasets'
import { Route as AppSettingsPermissionsSettingsUsersUserIdDatasetGroupsRouteImport } from './routes/app/_settings/_permissions/settings.users.$userId.dataset-groups'
import { Route as AppSettingsPermissionsSettingsUsersUserIdAttributesRouteImport } from './routes/app/_settings/_permissions/settings.users.$userId.attributes'
import { Route as AppAppAssetReportsReportIdLayoutContentRouteImport } from './routes/app/_app/_asset/reports.$reportId/_layout/content'
import { Route as AppAppAssetMetricsMetricIdLayoutSqlRouteImport } from './routes/app/_app/_asset/metrics.$metricId/_layout/sql'
import { Route as AppAppAssetMetricsMetricIdLayoutResultsRouteImport } from './routes/app/_app/_asset/metrics.$metricId/_layout/results'
@ -404,6 +410,12 @@ const AppSettingsRestricted_layoutAdmin_onlySettingsDatasourcesIndexRoute =
getParentRoute: () => AppSettingsRestricted_layoutAdmin_onlyRoute,
} as any,
)
const AppSettingsPermissionsSettingsUsersUserIdIndexRoute =
AppSettingsPermissionsSettingsUsersUserIdIndexRouteImport.update({
id: '/',
path: '/',
getParentRoute: () => AppSettingsPermissionsSettingsUsersUserIdRoute,
} as any)
const AppAppAssetReportsReportIdLayoutIndexRoute =
AppAppAssetReportsReportIdLayoutIndexRouteImport.update({
id: '/',
@ -452,6 +464,36 @@ const AppSettingsRestricted_layoutAdmin_onlySettingsDatasourcesDatasourceIdRoute
getParentRoute: () => AppSettingsRestricted_layoutAdmin_onlyRoute,
} as any,
)
const AppSettingsPermissionsSettingsUsersUserIdTeamsRoute =
AppSettingsPermissionsSettingsUsersUserIdTeamsRouteImport.update({
id: '/teams',
path: '/teams',
getParentRoute: () => AppSettingsPermissionsSettingsUsersUserIdRoute,
} as any)
const AppSettingsPermissionsSettingsUsersUserIdPermissionGroupsRoute =
AppSettingsPermissionsSettingsUsersUserIdPermissionGroupsRouteImport.update({
id: '/permission-groups',
path: '/permission-groups',
getParentRoute: () => AppSettingsPermissionsSettingsUsersUserIdRoute,
} as any)
const AppSettingsPermissionsSettingsUsersUserIdDatasetsRoute =
AppSettingsPermissionsSettingsUsersUserIdDatasetsRouteImport.update({
id: '/datasets',
path: '/datasets',
getParentRoute: () => AppSettingsPermissionsSettingsUsersUserIdRoute,
} as any)
const AppSettingsPermissionsSettingsUsersUserIdDatasetGroupsRoute =
AppSettingsPermissionsSettingsUsersUserIdDatasetGroupsRouteImport.update({
id: '/dataset-groups',
path: '/dataset-groups',
getParentRoute: () => AppSettingsPermissionsSettingsUsersUserIdRoute,
} as any)
const AppSettingsPermissionsSettingsUsersUserIdAttributesRoute =
AppSettingsPermissionsSettingsUsersUserIdAttributesRouteImport.update({
id: '/attributes',
path: '/attributes',
getParentRoute: () => AppSettingsPermissionsSettingsUsersUserIdRoute,
} as any)
const AppAppAssetReportsReportIdLayoutContentRoute =
AppAppAssetReportsReportIdLayoutContentRouteImport.update({
id: '/content',
@ -742,7 +784,7 @@ export interface FileRoutesByFullPath {
'/app/dashboards/$dashboardId': typeof AppAppAssetDashboardsDashboardIdLayoutRouteWithChildren
'/app/metrics/$metricId': typeof AppAppAssetMetricsMetricIdLayoutRouteWithChildren
'/app/reports/$reportId': typeof AppAppAssetReportsReportIdLayoutRouteWithChildren
'/app/settings/users/$userId': typeof AppSettingsPermissionsSettingsUsersUserIdRoute
'/app/settings/users/$userId': typeof AppSettingsPermissionsSettingsUsersUserIdRouteWithChildren
'/app/settings/api-keys': typeof AppSettingsRestricted_layoutAdmin_onlySettingsApiKeysRoute
'/app/settings/integrations': typeof AppSettingsRestricted_layoutAdmin_onlySettingsIntegrationsRoute
'/app/settings/security': typeof AppSettingsRestricted_layoutAdmin_onlySettingsSecurityRoute
@ -754,6 +796,11 @@ export interface FileRoutesByFullPath {
'/app/metrics/$metricId/results': typeof AppAppAssetMetricsMetricIdLayoutResultsRoute
'/app/metrics/$metricId/sql': typeof AppAppAssetMetricsMetricIdLayoutSqlRoute
'/app/reports/$reportId/content': typeof AppAppAssetReportsReportIdLayoutContentRoute
'/app/settings/users/$userId/attributes': typeof AppSettingsPermissionsSettingsUsersUserIdAttributesRoute
'/app/settings/users/$userId/dataset-groups': typeof AppSettingsPermissionsSettingsUsersUserIdDatasetGroupsRoute
'/app/settings/users/$userId/datasets': typeof AppSettingsPermissionsSettingsUsersUserIdDatasetsRoute
'/app/settings/users/$userId/permission-groups': typeof AppSettingsPermissionsSettingsUsersUserIdPermissionGroupsRoute
'/app/settings/users/$userId/teams': typeof AppSettingsPermissionsSettingsUsersUserIdTeamsRoute
'/app/settings/datasources/$datasourceId': typeof AppSettingsRestricted_layoutAdmin_onlySettingsDatasourcesDatasourceIdRoute
'/app/settings/datasources/add': typeof AppSettingsRestricted_layoutAdmin_onlySettingsDatasourcesAddRoute
'/app/settings/storage/add': typeof AppSettingsRestricted_layoutAdmin_onlySettingsStorageAddRoute
@ -761,6 +808,7 @@ export interface FileRoutesByFullPath {
'/app/dashboards/$dashboardId/': typeof AppAppAssetDashboardsDashboardIdLayoutIndexRoute
'/app/metrics/$metricId/': typeof AppAppAssetMetricsMetricIdLayoutIndexRoute
'/app/reports/$reportId/': typeof AppAppAssetReportsReportIdLayoutIndexRoute
'/app/settings/users/$userId/': typeof AppSettingsPermissionsSettingsUsersUserIdIndexRoute
'/app/settings/datasources': typeof AppSettingsRestricted_layoutAdmin_onlySettingsDatasourcesIndexRoute
'/app/chats/$chatId/dashboards/$dashboardId': typeof AppAppAssetChatsChatIdDashboardsDashboardIdLayoutRouteWithChildren
'/app/chats/$chatId/metrics/$metricId': typeof AppAppAssetChatsChatIdMetricsMetricIdLayoutRouteWithChildren
@ -820,7 +868,6 @@ export interface FileRoutesByTo {
'/app/dashboards/$dashboardId': typeof AppAppAssetDashboardsDashboardIdLayoutIndexRoute
'/app/metrics/$metricId': typeof AppAppAssetMetricsMetricIdLayoutIndexRoute
'/app/reports/$reportId': typeof AppAppAssetReportsReportIdLayoutIndexRoute
'/app/settings/users/$userId': typeof AppSettingsPermissionsSettingsUsersUserIdRoute
'/app/settings/api-keys': typeof AppSettingsRestricted_layoutAdmin_onlySettingsApiKeysRoute
'/app/settings/integrations': typeof AppSettingsRestricted_layoutAdmin_onlySettingsIntegrationsRoute
'/app/settings/security': typeof AppSettingsRestricted_layoutAdmin_onlySettingsSecurityRoute
@ -832,10 +879,16 @@ export interface FileRoutesByTo {
'/app/metrics/$metricId/results': typeof AppAppAssetMetricsMetricIdLayoutResultsRoute
'/app/metrics/$metricId/sql': typeof AppAppAssetMetricsMetricIdLayoutSqlRoute
'/app/reports/$reportId/content': typeof AppAppAssetReportsReportIdLayoutContentRoute
'/app/settings/users/$userId/attributes': typeof AppSettingsPermissionsSettingsUsersUserIdAttributesRoute
'/app/settings/users/$userId/dataset-groups': typeof AppSettingsPermissionsSettingsUsersUserIdDatasetGroupsRoute
'/app/settings/users/$userId/datasets': typeof AppSettingsPermissionsSettingsUsersUserIdDatasetsRoute
'/app/settings/users/$userId/permission-groups': typeof AppSettingsPermissionsSettingsUsersUserIdPermissionGroupsRoute
'/app/settings/users/$userId/teams': typeof AppSettingsPermissionsSettingsUsersUserIdTeamsRoute
'/app/settings/datasources/$datasourceId': typeof AppSettingsRestricted_layoutAdmin_onlySettingsDatasourcesDatasourceIdRoute
'/app/settings/datasources/add': typeof AppSettingsRestricted_layoutAdmin_onlySettingsDatasourcesAddRoute
'/app/settings/storage/add': typeof AppSettingsRestricted_layoutAdmin_onlySettingsStorageAddRoute
'/app/settings/storage/storageId': typeof AppSettingsRestricted_layoutAdmin_onlySettingsStorageStorageIdRoute
'/app/settings/users/$userId': typeof AppSettingsPermissionsSettingsUsersUserIdIndexRoute
'/app/settings/datasources': typeof AppSettingsRestricted_layoutAdmin_onlySettingsDatasourcesIndexRoute
'/app/chats/$chatId/dashboards/$dashboardId': typeof AppAppAssetChatsChatIdDashboardsDashboardIdLayoutIndexRoute
'/app/chats/$chatId/metrics/$metricId': typeof AppAppAssetChatsChatIdMetricsMetricIdLayoutIndexRoute
@ -900,7 +953,7 @@ export interface FileRoutesById {
'/app/_app/_asset/metrics/$metricId/_layout': typeof AppAppAssetMetricsMetricIdLayoutRouteWithChildren
'/app/_app/_asset/reports/$reportId': typeof AppAppAssetReportsReportIdRouteWithChildren
'/app/_app/_asset/reports/$reportId/_layout': typeof AppAppAssetReportsReportIdLayoutRouteWithChildren
'/app/_settings/_permissions/settings/users/$userId': typeof AppSettingsPermissionsSettingsUsersUserIdRoute
'/app/_settings/_permissions/settings/users/$userId': typeof AppSettingsPermissionsSettingsUsersUserIdRouteWithChildren
'/app/_settings/_restricted_layout/_admin_only/settings/api-keys': typeof AppSettingsRestricted_layoutAdmin_onlySettingsApiKeysRoute
'/app/_settings/_restricted_layout/_admin_only/settings/integrations': typeof AppSettingsRestricted_layoutAdmin_onlySettingsIntegrationsRoute
'/app/_settings/_restricted_layout/_admin_only/settings/security': typeof AppSettingsRestricted_layoutAdmin_onlySettingsSecurityRoute
@ -912,6 +965,11 @@ export interface FileRoutesById {
'/app/_app/_asset/metrics/$metricId/_layout/results': typeof AppAppAssetMetricsMetricIdLayoutResultsRoute
'/app/_app/_asset/metrics/$metricId/_layout/sql': typeof AppAppAssetMetricsMetricIdLayoutSqlRoute
'/app/_app/_asset/reports/$reportId/_layout/content': typeof AppAppAssetReportsReportIdLayoutContentRoute
'/app/_settings/_permissions/settings/users/$userId/attributes': typeof AppSettingsPermissionsSettingsUsersUserIdAttributesRoute
'/app/_settings/_permissions/settings/users/$userId/dataset-groups': typeof AppSettingsPermissionsSettingsUsersUserIdDatasetGroupsRoute
'/app/_settings/_permissions/settings/users/$userId/datasets': typeof AppSettingsPermissionsSettingsUsersUserIdDatasetsRoute
'/app/_settings/_permissions/settings/users/$userId/permission-groups': typeof AppSettingsPermissionsSettingsUsersUserIdPermissionGroupsRoute
'/app/_settings/_permissions/settings/users/$userId/teams': typeof AppSettingsPermissionsSettingsUsersUserIdTeamsRoute
'/app/_settings/_restricted_layout/_admin_only/settings/datasources/$datasourceId': typeof AppSettingsRestricted_layoutAdmin_onlySettingsDatasourcesDatasourceIdRoute
'/app/_settings/_restricted_layout/_admin_only/settings/datasources/add': typeof AppSettingsRestricted_layoutAdmin_onlySettingsDatasourcesAddRoute
'/app/_settings/_restricted_layout/_admin_only/settings/storage/add': typeof AppSettingsRestricted_layoutAdmin_onlySettingsStorageAddRoute
@ -919,6 +977,7 @@ export interface FileRoutesById {
'/app/_app/_asset/dashboards/$dashboardId/_layout/': typeof AppAppAssetDashboardsDashboardIdLayoutIndexRoute
'/app/_app/_asset/metrics/$metricId/_layout/': typeof AppAppAssetMetricsMetricIdLayoutIndexRoute
'/app/_app/_asset/reports/$reportId/_layout/': typeof AppAppAssetReportsReportIdLayoutIndexRoute
'/app/_settings/_permissions/settings/users/$userId/': typeof AppSettingsPermissionsSettingsUsersUserIdIndexRoute
'/app/_settings/_restricted_layout/_admin_only/settings/datasources/': typeof AppSettingsRestricted_layoutAdmin_onlySettingsDatasourcesIndexRoute
'/app/_app/_asset/chats/$chatId/dashboards/$dashboardId': typeof AppAppAssetChatsChatIdDashboardsDashboardIdRouteWithChildren
'/app/_app/_asset/chats/$chatId/dashboards/$dashboardId/_layout': typeof AppAppAssetChatsChatIdDashboardsDashboardIdLayoutRouteWithChildren
@ -1001,6 +1060,11 @@ export interface FileRouteTypes {
| '/app/metrics/$metricId/results'
| '/app/metrics/$metricId/sql'
| '/app/reports/$reportId/content'
| '/app/settings/users/$userId/attributes'
| '/app/settings/users/$userId/dataset-groups'
| '/app/settings/users/$userId/datasets'
| '/app/settings/users/$userId/permission-groups'
| '/app/settings/users/$userId/teams'
| '/app/settings/datasources/$datasourceId'
| '/app/settings/datasources/add'
| '/app/settings/storage/add'
@ -1008,6 +1072,7 @@ export interface FileRouteTypes {
| '/app/dashboards/$dashboardId/'
| '/app/metrics/$metricId/'
| '/app/reports/$reportId/'
| '/app/settings/users/$userId/'
| '/app/settings/datasources'
| '/app/chats/$chatId/dashboards/$dashboardId'
| '/app/chats/$chatId/metrics/$metricId'
@ -1067,7 +1132,6 @@ export interface FileRouteTypes {
| '/app/dashboards/$dashboardId'
| '/app/metrics/$metricId'
| '/app/reports/$reportId'
| '/app/settings/users/$userId'
| '/app/settings/api-keys'
| '/app/settings/integrations'
| '/app/settings/security'
@ -1079,10 +1143,16 @@ export interface FileRouteTypes {
| '/app/metrics/$metricId/results'
| '/app/metrics/$metricId/sql'
| '/app/reports/$reportId/content'
| '/app/settings/users/$userId/attributes'
| '/app/settings/users/$userId/dataset-groups'
| '/app/settings/users/$userId/datasets'
| '/app/settings/users/$userId/permission-groups'
| '/app/settings/users/$userId/teams'
| '/app/settings/datasources/$datasourceId'
| '/app/settings/datasources/add'
| '/app/settings/storage/add'
| '/app/settings/storage/storageId'
| '/app/settings/users/$userId'
| '/app/settings/datasources'
| '/app/chats/$chatId/dashboards/$dashboardId'
| '/app/chats/$chatId/metrics/$metricId'
@ -1158,6 +1228,11 @@ export interface FileRouteTypes {
| '/app/_app/_asset/metrics/$metricId/_layout/results'
| '/app/_app/_asset/metrics/$metricId/_layout/sql'
| '/app/_app/_asset/reports/$reportId/_layout/content'
| '/app/_settings/_permissions/settings/users/$userId/attributes'
| '/app/_settings/_permissions/settings/users/$userId/dataset-groups'
| '/app/_settings/_permissions/settings/users/$userId/datasets'
| '/app/_settings/_permissions/settings/users/$userId/permission-groups'
| '/app/_settings/_permissions/settings/users/$userId/teams'
| '/app/_settings/_restricted_layout/_admin_only/settings/datasources/$datasourceId'
| '/app/_settings/_restricted_layout/_admin_only/settings/datasources/add'
| '/app/_settings/_restricted_layout/_admin_only/settings/storage/add'
@ -1165,6 +1240,7 @@ export interface FileRouteTypes {
| '/app/_app/_asset/dashboards/$dashboardId/_layout/'
| '/app/_app/_asset/metrics/$metricId/_layout/'
| '/app/_app/_asset/reports/$reportId/_layout/'
| '/app/_settings/_permissions/settings/users/$userId/'
| '/app/_settings/_restricted_layout/_admin_only/settings/datasources/'
| '/app/_app/_asset/chats/$chatId/dashboards/$dashboardId'
| '/app/_app/_asset/chats/$chatId/dashboards/$dashboardId/_layout'
@ -1588,6 +1664,13 @@ declare module '@tanstack/react-router' {
preLoaderRoute: typeof AppSettingsRestricted_layoutAdmin_onlySettingsDatasourcesIndexRouteImport
parentRoute: typeof AppSettingsRestricted_layoutAdmin_onlyRoute
}
'/app/_settings/_permissions/settings/users/$userId/': {
id: '/app/_settings/_permissions/settings/users/$userId/'
path: '/'
fullPath: '/app/settings/users/$userId/'
preLoaderRoute: typeof AppSettingsPermissionsSettingsUsersUserIdIndexRouteImport
parentRoute: typeof AppSettingsPermissionsSettingsUsersUserIdRoute
}
'/app/_app/_asset/reports/$reportId/_layout/': {
id: '/app/_app/_asset/reports/$reportId/_layout/'
path: '/'
@ -1637,6 +1720,41 @@ declare module '@tanstack/react-router' {
preLoaderRoute: typeof AppSettingsRestricted_layoutAdmin_onlySettingsDatasourcesDatasourceIdRouteImport
parentRoute: typeof AppSettingsRestricted_layoutAdmin_onlyRoute
}
'/app/_settings/_permissions/settings/users/$userId/teams': {
id: '/app/_settings/_permissions/settings/users/$userId/teams'
path: '/teams'
fullPath: '/app/settings/users/$userId/teams'
preLoaderRoute: typeof AppSettingsPermissionsSettingsUsersUserIdTeamsRouteImport
parentRoute: typeof AppSettingsPermissionsSettingsUsersUserIdRoute
}
'/app/_settings/_permissions/settings/users/$userId/permission-groups': {
id: '/app/_settings/_permissions/settings/users/$userId/permission-groups'
path: '/permission-groups'
fullPath: '/app/settings/users/$userId/permission-groups'
preLoaderRoute: typeof AppSettingsPermissionsSettingsUsersUserIdPermissionGroupsRouteImport
parentRoute: typeof AppSettingsPermissionsSettingsUsersUserIdRoute
}
'/app/_settings/_permissions/settings/users/$userId/datasets': {
id: '/app/_settings/_permissions/settings/users/$userId/datasets'
path: '/datasets'
fullPath: '/app/settings/users/$userId/datasets'
preLoaderRoute: typeof AppSettingsPermissionsSettingsUsersUserIdDatasetsRouteImport
parentRoute: typeof AppSettingsPermissionsSettingsUsersUserIdRoute
}
'/app/_settings/_permissions/settings/users/$userId/dataset-groups': {
id: '/app/_settings/_permissions/settings/users/$userId/dataset-groups'
path: '/dataset-groups'
fullPath: '/app/settings/users/$userId/dataset-groups'
preLoaderRoute: typeof AppSettingsPermissionsSettingsUsersUserIdDatasetGroupsRouteImport
parentRoute: typeof AppSettingsPermissionsSettingsUsersUserIdRoute
}
'/app/_settings/_permissions/settings/users/$userId/attributes': {
id: '/app/_settings/_permissions/settings/users/$userId/attributes'
path: '/attributes'
fullPath: '/app/settings/users/$userId/attributes'
preLoaderRoute: typeof AppSettingsPermissionsSettingsUsersUserIdAttributesRouteImport
parentRoute: typeof AppSettingsPermissionsSettingsUsersUserIdRoute
}
'/app/_app/_asset/reports/$reportId/_layout/content': {
id: '/app/_app/_asset/reports/$reportId/_layout/content'
path: '/content'
@ -2373,15 +2491,45 @@ const AppAppRouteChildren: AppAppRouteChildren = {
const AppAppRouteWithChildren =
AppAppRoute._addFileChildren(AppAppRouteChildren)
interface AppSettingsPermissionsSettingsUsersUserIdRouteChildren {
AppSettingsPermissionsSettingsUsersUserIdAttributesRoute: typeof AppSettingsPermissionsSettingsUsersUserIdAttributesRoute
AppSettingsPermissionsSettingsUsersUserIdDatasetGroupsRoute: typeof AppSettingsPermissionsSettingsUsersUserIdDatasetGroupsRoute
AppSettingsPermissionsSettingsUsersUserIdDatasetsRoute: typeof AppSettingsPermissionsSettingsUsersUserIdDatasetsRoute
AppSettingsPermissionsSettingsUsersUserIdPermissionGroupsRoute: typeof AppSettingsPermissionsSettingsUsersUserIdPermissionGroupsRoute
AppSettingsPermissionsSettingsUsersUserIdTeamsRoute: typeof AppSettingsPermissionsSettingsUsersUserIdTeamsRoute
AppSettingsPermissionsSettingsUsersUserIdIndexRoute: typeof AppSettingsPermissionsSettingsUsersUserIdIndexRoute
}
const AppSettingsPermissionsSettingsUsersUserIdRouteChildren: AppSettingsPermissionsSettingsUsersUserIdRouteChildren =
{
AppSettingsPermissionsSettingsUsersUserIdAttributesRoute:
AppSettingsPermissionsSettingsUsersUserIdAttributesRoute,
AppSettingsPermissionsSettingsUsersUserIdDatasetGroupsRoute:
AppSettingsPermissionsSettingsUsersUserIdDatasetGroupsRoute,
AppSettingsPermissionsSettingsUsersUserIdDatasetsRoute:
AppSettingsPermissionsSettingsUsersUserIdDatasetsRoute,
AppSettingsPermissionsSettingsUsersUserIdPermissionGroupsRoute:
AppSettingsPermissionsSettingsUsersUserIdPermissionGroupsRoute,
AppSettingsPermissionsSettingsUsersUserIdTeamsRoute:
AppSettingsPermissionsSettingsUsersUserIdTeamsRoute,
AppSettingsPermissionsSettingsUsersUserIdIndexRoute:
AppSettingsPermissionsSettingsUsersUserIdIndexRoute,
}
const AppSettingsPermissionsSettingsUsersUserIdRouteWithChildren =
AppSettingsPermissionsSettingsUsersUserIdRoute._addFileChildren(
AppSettingsPermissionsSettingsUsersUserIdRouteChildren,
)
interface AppSettingsPermissionsSettingsUsersRouteChildren {
AppSettingsPermissionsSettingsUsersUserIdRoute: typeof AppSettingsPermissionsSettingsUsersUserIdRoute
AppSettingsPermissionsSettingsUsersUserIdRoute: typeof AppSettingsPermissionsSettingsUsersUserIdRouteWithChildren
AppSettingsPermissionsSettingsUsersIndexRoute: typeof AppSettingsPermissionsSettingsUsersIndexRoute
}
const AppSettingsPermissionsSettingsUsersRouteChildren: AppSettingsPermissionsSettingsUsersRouteChildren =
{
AppSettingsPermissionsSettingsUsersUserIdRoute:
AppSettingsPermissionsSettingsUsersUserIdRoute,
AppSettingsPermissionsSettingsUsersUserIdRouteWithChildren,
AppSettingsPermissionsSettingsUsersIndexRoute:
AppSettingsPermissionsSettingsUsersIndexRoute,
}

View File

@ -0,0 +1,16 @@
import { createFileRoute } from '@tanstack/react-router'
export const Route = createFileRoute(
'/app/_settings/_permissions/settings/users/$userId/attributes',
)({
component: RouteComponent,
})
function RouteComponent() {
return (
<div>
Hello
"/app/_settings/_permissions/settings/users/$userId/permission-groups"!
</div>
)
}

View File

@ -0,0 +1,16 @@
import { createFileRoute } from '@tanstack/react-router'
export const Route = createFileRoute(
'/app/_settings/_permissions/settings/users/$userId/dataset-groups',
)({
component: RouteComponent,
})
function RouteComponent() {
return (
<div>
Hello
"/app/_settings/_permissions/settings/users/$userId/permission-groups"!
</div>
)
}

View File

@ -0,0 +1,16 @@
import { createFileRoute } from '@tanstack/react-router'
export const Route = createFileRoute(
'/app/_settings/_permissions/settings/users/$userId/datasets',
)({
component: RouteComponent,
})
function RouteComponent() {
return (
<div>
Hello
"/app/_settings/_permissions/settings/users/$userId/permission-groups"!
</div>
)
}

View File

@ -0,0 +1,11 @@
import { createFileRoute } from '@tanstack/react-router';
import { UserOverviewController } from '@/controllers/UserIndividualControllers/UserOverviewController';
export const Route = createFileRoute('/app/_settings/_permissions/settings/users/$userId/')({
component: RouteComponent,
});
function RouteComponent() {
const { userId } = Route.useParams();
return <UserOverviewController userId={userId} />;
}

View File

@ -0,0 +1,16 @@
import { createFileRoute } from '@tanstack/react-router'
export const Route = createFileRoute(
'/app/_settings/_permissions/settings/users/$userId/permission-groups',
)({
component: RouteComponent,
})
function RouteComponent() {
return (
<div>
Hello
"/app/_settings/_permissions/settings/users/$userId/permission-groups"!
</div>
)
}

View File

@ -0,0 +1,16 @@
import { createFileRoute } from '@tanstack/react-router'
export const Route = createFileRoute(
'/app/_settings/_permissions/settings/users/$userId/teams',
)({
component: RouteComponent,
})
function RouteComponent() {
return (
<div>
Hello
"/app/_settings/_permissions/settings/users/$userId/permission-groups"!
</div>
)
}

View File

@ -1,9 +1,31 @@
import { createFileRoute } from '@tanstack/react-router';
import { createFileRoute, Outlet } from '@tanstack/react-router';
import { prefetchGetUser } from '@/api/buster_rest/users';
import { UserIndividualsLayout } from '@/controllers/UserIndividualControllers/UserIndividualsLayout';
export const Route = createFileRoute('/app/_settings/_permissions/settings/users/$userId')({
component: RouteComponent,
loader: async ({ context, params }) => {
const { userId } = params;
const user = await prefetchGetUser(userId, context.queryClient);
return {
name: user?.name,
};
},
head: ({ loaderData }) => ({
meta: [
{ title: loaderData?.name || 'User' },
{ name: 'description', content: `View and manage user ${loaderData?.name}` },
{ name: 'og:title', content: loaderData?.name || 'User' },
{ name: 'og:description', content: `View and manage user ${loaderData?.name}` },
],
}),
});
function RouteComponent() {
return <div>Hello "/app/_settings/_permissions/settings/users/$userId"!</div>;
const { userId } = Route.useParams();
return (
<UserIndividualsLayout userId={userId}>
<Outlet />
</UserIndividualsLayout>
);
}

View File

@ -1,4 +1,4 @@
import { createFileRoute } from '@tanstack/react-router';
import { createFileRoute, Outlet } from '@tanstack/react-router';
export const Route = createFileRoute('/app/_settings/_permissions/settings/users')({
head: () => ({
@ -13,5 +13,5 @@ export const Route = createFileRoute('/app/_settings/_permissions/settings/users
});
function RouteComponent() {
return <div>Hello "/app/settings/users"!</div>;
return <Outlet />;
}