mirror of https://github.com/buster-so/buster.git
fast fix for typechecks
This commit is contained in:
parent
0a247421c6
commit
51344c527f
|
@ -1,7 +1,7 @@
|
|||
import type { ShareAssetType, ShareRole, WorkspaceShareRole } from '@buster/server-shared/share';
|
||||
import last from 'lodash/last';
|
||||
import React, { useMemo } from 'react';
|
||||
import type { DropdownItem } from '@/components/ui/dropdown';
|
||||
import type { IDropdownItem } from '@/components/ui/dropdown';
|
||||
import { Dropdown } from '@/components/ui/dropdown';
|
||||
import { ChevronDown } from '@/components/ui/icons/NucleoIconFilled';
|
||||
import { Paragraph, Text } from '@/components/ui/typography';
|
||||
|
@ -39,7 +39,7 @@ export const AccessDropdown: React.FC<AccessDropdownProps> = React.memo(
|
|||
const items = useMemo(() => {
|
||||
const isWorkspace = props.type === 'workspace';
|
||||
|
||||
const baseItems: DropdownItem<DropdownValue>[] = [
|
||||
const baseItems: IDropdownItem<DropdownValue>[] = [
|
||||
...(isWorkspace ? workspaceItems : itemsRecord[assetType] || []),
|
||||
];
|
||||
|
||||
|
@ -133,7 +133,7 @@ export const AccessDropdown: React.FC<AccessDropdownProps> = React.memo(
|
|||
|
||||
AccessDropdown.displayName = 'AccessDropdown';
|
||||
|
||||
const metricItems: DropdownItem<ShareRole>[] = [
|
||||
const metricItems: IDropdownItem<ShareRole>[] = [
|
||||
{
|
||||
value: 'full_access',
|
||||
label: 'Full access',
|
||||
|
@ -151,7 +151,7 @@ const metricItems: DropdownItem<ShareRole>[] = [
|
|||
},
|
||||
];
|
||||
|
||||
const dashboardItems: DropdownItem<ShareRole>[] = [
|
||||
const dashboardItems: IDropdownItem<ShareRole>[] = [
|
||||
{
|
||||
value: 'full_access',
|
||||
label: 'Full access',
|
||||
|
@ -169,7 +169,7 @@ const dashboardItems: DropdownItem<ShareRole>[] = [
|
|||
},
|
||||
];
|
||||
|
||||
const collectionItems: DropdownItem<ShareRole>[] = [
|
||||
const collectionItems: IDropdownItem<ShareRole>[] = [
|
||||
{
|
||||
value: 'full_access',
|
||||
label: 'Full access',
|
||||
|
@ -187,7 +187,7 @@ const collectionItems: DropdownItem<ShareRole>[] = [
|
|||
},
|
||||
];
|
||||
|
||||
const reportItems: DropdownItem<ShareRole>[] = [
|
||||
const reportItems: IDropdownItem<ShareRole>[] = [
|
||||
{
|
||||
value: 'full_access',
|
||||
label: 'Full access',
|
||||
|
@ -205,7 +205,7 @@ const reportItems: DropdownItem<ShareRole>[] = [
|
|||
},
|
||||
];
|
||||
|
||||
const workspaceItems: DropdownItem<WorkspaceShareRole>[] = [
|
||||
const workspaceItems: IDropdownItem<WorkspaceShareRole>[] = [
|
||||
{
|
||||
value: 'full_access',
|
||||
label: 'Full access',
|
||||
|
@ -228,7 +228,7 @@ const workspaceItems: DropdownItem<WorkspaceShareRole>[] = [
|
|||
},
|
||||
];
|
||||
|
||||
const itemsRecord: Record<ShareAssetType, DropdownItem<ShareRole>[]> = {
|
||||
const itemsRecord: Record<ShareAssetType, IDropdownItem<ShareRole>[]> = {
|
||||
dashboard: dashboardItems,
|
||||
metric: metricItems,
|
||||
collection: collectionItems,
|
||||
|
@ -236,15 +236,15 @@ const itemsRecord: Record<ShareAssetType, DropdownItem<ShareRole>[]> = {
|
|||
report: reportItems,
|
||||
};
|
||||
|
||||
const OWNER_ITEM: DropdownItem<DropdownValue> = {
|
||||
const OWNER_ITEM: IDropdownItem<DropdownValue> = {
|
||||
value: 'owner',
|
||||
label: 'Owner',
|
||||
secondaryLabel: 'Owner of the asset.',
|
||||
};
|
||||
|
||||
const WORKSPACE_NOT_SHARED_ITEM: DropdownItem<DropdownValue> = last(
|
||||
const WORKSPACE_NOT_SHARED_ITEM: IDropdownItem<DropdownValue> = last(
|
||||
workspaceItems
|
||||
) as DropdownItem<DropdownValue>;
|
||||
) as IDropdownItem<DropdownValue>;
|
||||
|
||||
const FooterContent = React.memo(() => {
|
||||
return (
|
||||
|
|
|
@ -8,40 +8,61 @@ import { Link } from '@/components/ui/icons';
|
|||
import { Input } from '@/components/ui/inputs';
|
||||
import { Text } from '@/components/ui/typography';
|
||||
import { useBusterNotifications } from '@/context/BusterNotifications';
|
||||
import { useMemoizedFn } from '@/hooks';
|
||||
import { cn } from '@/lib/classMerge';
|
||||
import { BusterRoutes, createBusterRoute } from '@/routes';
|
||||
import { useBuildLocation } from '../../../context/Routes/useRouteBuilder';
|
||||
import type { ShareMenuContentBodyProps } from './ShareMenuContentBody';
|
||||
|
||||
export const ShareMenuContentEmbed: React.FC<ShareMenuContentBodyProps> = React.memo(
|
||||
({ className, assetType, assetId }) => {
|
||||
const buildLocation = useBuildLocation();
|
||||
const { openSuccessMessage } = useBusterNotifications();
|
||||
|
||||
const embedURL = useMemo(() => {
|
||||
let url = '';
|
||||
|
||||
if (assetType === 'metric') {
|
||||
url = createBusterRoute({
|
||||
route: BusterRoutes.EMBED_METRIC_ID,
|
||||
metricId: assetId,
|
||||
});
|
||||
return buildLocation({
|
||||
to: '/embed/metric/$metricId',
|
||||
params: {
|
||||
metricId: assetId,
|
||||
},
|
||||
}).href;
|
||||
}
|
||||
|
||||
if (assetType === 'dashboard') {
|
||||
url = createBusterRoute({
|
||||
route: BusterRoutes.EMBED_DASHBOARD_ID,
|
||||
dashboardId: assetId,
|
||||
});
|
||||
return buildLocation({
|
||||
to: '/embed/dashboard/$dashboardId',
|
||||
params: {
|
||||
dashboardId: assetId,
|
||||
},
|
||||
}).href;
|
||||
}
|
||||
|
||||
return url;
|
||||
}, [assetType, assetId]);
|
||||
if (assetType === 'report') {
|
||||
return buildLocation({
|
||||
to: '/embed/report/$reportId',
|
||||
params: {
|
||||
reportId: assetId,
|
||||
},
|
||||
}).href;
|
||||
}
|
||||
|
||||
const onCopyLink = useMemoizedFn(() => {
|
||||
if (assetType === 'chat') {
|
||||
return '';
|
||||
}
|
||||
|
||||
if (assetType === 'collection') {
|
||||
return '';
|
||||
}
|
||||
|
||||
const _exhaustiveCheck: never = assetType;
|
||||
|
||||
return '';
|
||||
}, [assetType, assetId, buildLocation]);
|
||||
|
||||
const onCopyLink = () => {
|
||||
const url = window.location.origin + embedURL;
|
||||
navigator.clipboard.writeText(url);
|
||||
openSuccessMessage('Link copied to clipboard');
|
||||
});
|
||||
};
|
||||
|
||||
return (
|
||||
<div className={cn('flex flex-col', className)}>
|
||||
|
@ -73,7 +94,7 @@ export const ShareMenuContentEmbedFooter = ({
|
|||
const { mutateAsync: onShareCollection } = useUpdateCollectionShare();
|
||||
const { openSuccessMessage } = useBusterNotifications();
|
||||
|
||||
const onPublish = useMemoizedFn(async () => {
|
||||
const onPublish = async () => {
|
||||
const payload: Parameters<typeof onShareMetric>[0] = {
|
||||
id: assetId,
|
||||
params: {
|
||||
|
@ -88,7 +109,7 @@ export const ShareMenuContentEmbedFooter = ({
|
|||
await onShareCollection(payload);
|
||||
}
|
||||
openSuccessMessage('Succuessfully published');
|
||||
});
|
||||
};
|
||||
|
||||
return (
|
||||
<div className="bg-item-hover flex justify-start overflow-hidden rounded-b px-3 py-2.5">
|
||||
|
|
|
@ -2,7 +2,6 @@ import type { ShareAssetType } from '@buster/server-shared/share';
|
|||
import React, { useMemo } from 'react';
|
||||
import type { SegmentedItem } from '@/components/ui/segmented';
|
||||
import { AppSegmented } from '@/components/ui/segmented';
|
||||
import { useMemoizedFn } from '@/hooks';
|
||||
import { CopyLinkButton } from './CopyLinkButton';
|
||||
|
||||
export enum ShareMenuTopBarOptions {
|
||||
|
@ -41,9 +40,9 @@ export const ShareMenuTopBar: React.FC<{
|
|||
.map((o) => ({ ...o, show: undefined }));
|
||||
}, [assetType, canEditPermissions]);
|
||||
|
||||
const onChange = useMemoizedFn((v: SegmentedItem<ShareMenuTopBarOptions>) => {
|
||||
const onChange = (v: SegmentedItem<ShareMenuTopBarOptions>) => {
|
||||
onChangeSelectedOption(v.value);
|
||||
});
|
||||
};
|
||||
|
||||
return (
|
||||
<div className="flex items-center justify-between">
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
import type { Meta, StoryObj } from '@storybook/nextjs';
|
||||
import type { Meta, StoryObj } from '@storybook/react-vite';
|
||||
import { WorkspaceAvatar } from './WorkspaceAvatar';
|
||||
|
||||
const meta: Meta<typeof WorkspaceAvatar> = {
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
import type { VerificationStatus } from '@buster/server-shared/share';
|
||||
import { useMemo } from 'react';
|
||||
import type { DropdownItem, DropdownProps } from '@/components/ui/dropdown';
|
||||
import type { DropdownProps, IDropdownItem } from '@/components/ui/dropdown';
|
||||
import { getTooltipText } from './helpers';
|
||||
import { StatusBadgeIndicator } from './StatusBadgeIndicator';
|
||||
|
||||
|
@ -27,7 +27,7 @@ export const useStatusDropdownContent = ({
|
|||
'showIndex' | 'items' | 'emptyStateText' | 'menuHeader' | 'selectType'
|
||||
> => {
|
||||
const items = useMemo(() => {
|
||||
return statuses.map<DropdownItem<VerificationStatus>>((status) => {
|
||||
return statuses.map<IDropdownItem<VerificationStatus>>((status) => {
|
||||
const requiresAdmin = requiresAdminItems.includes(status);
|
||||
return {
|
||||
label: getTooltipText(status),
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
import React, { useMemo } from 'react';
|
||||
import { useGetMetric } from '@/api/buster_rest/metrics';
|
||||
import { DropdownContent, type IDropdownItem } from '@/components/ui/dropdown';
|
||||
import { DropdownContent, type IDropdownItem, type IDropdownItems } from '@/components/ui/dropdown';
|
||||
import { History, Star, WandSparkle } from '@/components/ui/icons';
|
||||
import { Star as StarFilled } from '@/components/ui/icons/NucleoIconFilled';
|
||||
import { FollowUpWithAssetContent } from '../assets/FollowUpWithAsset';
|
||||
|
@ -51,7 +51,7 @@ export const useFavoriteMetricSelectMenu = ({ metricId }: { metricId: string })
|
|||
name: name || '',
|
||||
});
|
||||
|
||||
const item: DropdownItem = useMemo(
|
||||
const item: IDropdownItem = useMemo(
|
||||
() => ({
|
||||
label: isFavorited ? 'Remove from favorites' : 'Add to favorites',
|
||||
value: 'add-to-favorites',
|
||||
|
@ -65,7 +65,7 @@ export const useFavoriteMetricSelectMenu = ({ metricId }: { metricId: string })
|
|||
return item;
|
||||
};
|
||||
|
||||
export const useMetricDrilldownItem = ({ metricId }: { metricId: string }): DropdownItem => {
|
||||
export const useMetricDrilldownItem = ({ metricId }: { metricId: string }): IDropdownItem => {
|
||||
return useMemo(
|
||||
() => ({
|
||||
value: 'drilldown',
|
||||
|
|
|
@ -1,45 +1,53 @@
|
|||
import type { UserFavorite } from '@buster/server-shared/user';
|
||||
import type { OptionsTo } from '@/types/routes';
|
||||
import type { RegisteredRouter } from '@tanstack/react-router';
|
||||
import { defineLink } from '@/lib/routes';
|
||||
|
||||
export const getFavoriteRoute = (favorite: Pick<UserFavorite, 'asset_type' | 'id'>): OptionsTo => {
|
||||
export const getFavoriteRoute = <
|
||||
TRouter extends RegisteredRouter = RegisteredRouter,
|
||||
TOptions = unknown,
|
||||
TFrom extends string = string,
|
||||
>(
|
||||
favorite: Pick<UserFavorite, 'asset_type' | 'id'>
|
||||
) => {
|
||||
if (favorite.asset_type === 'chat') {
|
||||
return {
|
||||
return defineLink({
|
||||
to: '/app/chats/$chatId',
|
||||
params: { chatId: favorite.id },
|
||||
} as const satisfies OptionsTo;
|
||||
params: { chatId: '123' },
|
||||
});
|
||||
}
|
||||
|
||||
if (favorite.asset_type === 'metric') {
|
||||
return {
|
||||
return defineLink({
|
||||
to: '/app/metrics/$metricId',
|
||||
params: { metricId: favorite.id },
|
||||
} as const satisfies OptionsTo;
|
||||
});
|
||||
}
|
||||
|
||||
if (favorite.asset_type === 'dashboard') {
|
||||
return {
|
||||
return defineLink({
|
||||
to: '/app/dashboards/$dashboardId',
|
||||
params: { dashboardId: favorite.id },
|
||||
} as const satisfies OptionsTo;
|
||||
});
|
||||
}
|
||||
|
||||
if (favorite.asset_type === 'collection') {
|
||||
return {
|
||||
return defineLink({
|
||||
to: '/app/collections/$collectionId',
|
||||
params: { collectionId: favorite.id },
|
||||
} as const satisfies OptionsTo;
|
||||
});
|
||||
}
|
||||
|
||||
if (favorite.asset_type === 'report') {
|
||||
return {
|
||||
return defineLink({
|
||||
to: '/app/reports/$reportId',
|
||||
params: { reportId: favorite.id },
|
||||
} as const satisfies OptionsTo;
|
||||
});
|
||||
}
|
||||
|
||||
const _exhaustiveCheck: never = favorite.asset_type;
|
||||
return {
|
||||
|
||||
return defineLink({
|
||||
to: '/app/chats/$chatId',
|
||||
params: { chatId: favorite.id },
|
||||
} as const satisfies OptionsTo;
|
||||
});
|
||||
};
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
import { type LinkProps, useMatchRoute } from '@tanstack/react-router';
|
||||
import { useMatchRoute } from '@tanstack/react-router';
|
||||
import { useMemo } from 'react';
|
||||
import {
|
||||
useDeleteUserFavorite,
|
||||
|
@ -6,9 +6,11 @@ import {
|
|||
useUpdateUserFavorites,
|
||||
} from '@/api/buster_rest/users';
|
||||
import { assetTypeToIcon } from '@/components/features/icons/assetIcons';
|
||||
import type { ISidebarGroup, ISidebarItem } from '@/components/ui/sidebar';
|
||||
import type { ISidebarGroup } from '@/components/ui/sidebar';
|
||||
import { useMount } from '@/hooks/useMount';
|
||||
import { assetParamsToRoute } from '@/lib/assets/assetParamsToRoute';
|
||||
|
||||
import { createSidebarItem } from '../../../ui/sidebar/create-sidebar-item';
|
||||
import { getFavoriteRoute } from './getFavoriteRoute';
|
||||
|
||||
export const useFavoriteSidebarPanel = (): ISidebarGroup | null => {
|
||||
|
@ -29,18 +31,17 @@ export const useFavoriteSidebarPanel = (): ISidebarGroup | null => {
|
|||
id: 'favorites',
|
||||
isSortable: true,
|
||||
onItemsReorder: updateUserFavorites,
|
||||
items: favorites.map<ISidebarItem>((favorite) => {
|
||||
items: favorites.map((favorite) => {
|
||||
const Icon = assetTypeToIcon(favorite.asset_type);
|
||||
const route = getFavoriteRoute(favorite);
|
||||
// const isActive = !!matcher({ ...route, fuzzy: true } as Parameters<typeof matcher>[0]);
|
||||
return {
|
||||
const link = getFavoriteRoute(favorite);
|
||||
|
||||
return createSidebarItem({
|
||||
label: favorite.name,
|
||||
icon: <Icon />,
|
||||
route,
|
||||
link,
|
||||
id: favorite.id,
|
||||
preload: 'intent',
|
||||
onRemove: () => deleteUserFavorite([favorite.id]),
|
||||
};
|
||||
});
|
||||
}),
|
||||
} satisfies ISidebarGroup;
|
||||
}, [favorites, matcher, deleteUserFavorite]);
|
||||
|
|
|
@ -1,35 +1,31 @@
|
|||
import Link from 'next/link';
|
||||
import { Link } from '@tanstack/react-router';
|
||||
import React, { useCallback, useMemo, useRef } from 'react';
|
||||
import { Button } from '@/components/ui/buttons';
|
||||
import { History, Xmark } from '@/components/ui/icons';
|
||||
import { Check3 } from '@/components/ui/icons/NucleoIconFilled';
|
||||
import { AppPageLayout } from '@/components/ui/layouts';
|
||||
import Check3 from '@/components/ui/icons/NucleoIconFilled/check-3';
|
||||
import { AppPageLayout } from '@/components/ui/layouts/AppPageLayout';
|
||||
import { CircleSpinnerLoader } from '@/components/ui/loaders';
|
||||
import { AppTooltip } from '@/components/ui/tooltip';
|
||||
import { Text } from '@/components/ui/typography';
|
||||
import { useGetFileLink } from '@/context/Assets/useGetFileLink';
|
||||
import { useMemoizedFn, useMount } from '@/hooks';
|
||||
import { useChatLayoutContextSelector } from '@/layouts/ChatLayout';
|
||||
import { useCloseVersionHistory } from '@/layouts/ChatLayout/FileContainer/FileContainerHeader/FileContainerHeaderVersionHistory';
|
||||
import { timeFromNow, timeout } from '@/lib';
|
||||
import { useMemoizedFn } from '@/hooks/useMemoizedFn';
|
||||
import { useMount } from '@/hooks/useMount';
|
||||
import { cn } from '@/lib/classMerge';
|
||||
import { useListVersionHistories } from './useListVersionHistories';
|
||||
import { timeFromNow } from '@/lib/date';
|
||||
import { timeout } from '@/lib/timeout';
|
||||
|
||||
export const VersionHistoryPanel = React.memo(
|
||||
({ assetId, type }: { assetId: string; type: 'metric' | 'dashboard' }) => {
|
||||
const chatId = useChatLayoutContextSelector((x) => x.chatId);
|
||||
const {
|
||||
listItems,
|
||||
onPrefetchAsset,
|
||||
restoringVersion,
|
||||
currentVersionNumber,
|
||||
selectedQueryVersion,
|
||||
onClickRestoreVersion,
|
||||
} = useListVersionHistories({
|
||||
assetId,
|
||||
type,
|
||||
});
|
||||
const { getFileLink } = useGetFileLink();
|
||||
// const {
|
||||
// listItems,
|
||||
// onPrefetchAsset,
|
||||
// restoringVersion,
|
||||
// currentVersionNumber,
|
||||
// selectedQueryVersion,
|
||||
// onClickRestoreVersion,
|
||||
// } = useListVersionHistories({
|
||||
// assetId,
|
||||
// type,
|
||||
// });
|
||||
|
||||
const bodyRef = useRef<HTMLDivElement>(null);
|
||||
|
||||
|
@ -51,7 +47,8 @@ export const VersionHistoryPanel = React.memo(
|
|||
headerBorderVariant="ghost"
|
||||
>
|
||||
<div ref={bodyRef} className="mx-2 mb-1.5 flex flex-col">
|
||||
{listItems?.map((item) => (
|
||||
TODO
|
||||
{/* {listItems?.map((item) => (
|
||||
<ListItem
|
||||
key={item.version_number}
|
||||
{...item}
|
||||
|
@ -70,109 +67,107 @@ export const VersionHistoryPanel = React.memo(
|
|||
}) || ''
|
||||
}
|
||||
/>
|
||||
))}
|
||||
))} */}
|
||||
</div>
|
||||
</AppPageLayout>
|
||||
);
|
||||
}
|
||||
);
|
||||
|
||||
const ListItem = React.memo(
|
||||
({
|
||||
version_number,
|
||||
updated_at,
|
||||
selected,
|
||||
showRestoreButton,
|
||||
link,
|
||||
restoringVersion,
|
||||
onClickRestoreVersion,
|
||||
onPrefetchAsset,
|
||||
}: {
|
||||
version_number: number;
|
||||
updated_at: string;
|
||||
selected: boolean;
|
||||
showRestoreButton: boolean;
|
||||
restoringVersion: number | null;
|
||||
onClickRestoreVersion: (versionNumber: number) => void;
|
||||
onPrefetchAsset: (versionNumber: number, link: string) => Promise<void>;
|
||||
link: string;
|
||||
}) => {
|
||||
const routePrefetchTimeoutRef = useRef<NodeJS.Timeout>();
|
||||
// const ListItem = React.memo(
|
||||
// ({
|
||||
// version_number,
|
||||
// updated_at,
|
||||
// selected,
|
||||
// showRestoreButton,
|
||||
// link,
|
||||
// restoringVersion,
|
||||
// onClickRestoreVersion,
|
||||
// onPrefetchAsset,
|
||||
// }: {
|
||||
// version_number: number;
|
||||
// updated_at: string;
|
||||
// selected: boolean;
|
||||
// showRestoreButton: boolean;
|
||||
// restoringVersion: number | null;
|
||||
// onClickRestoreVersion: (versionNumber: number) => void;
|
||||
// onPrefetchAsset: (versionNumber: number, link: string) => Promise<void>;
|
||||
// link: string;
|
||||
// }) => {
|
||||
// const routePrefetchTimeoutRef = useRef<NodeJS.Timeout>(null);
|
||||
|
||||
const onHoverLink = useMemoizedFn(() => {
|
||||
// Prefetch route after 50ms
|
||||
routePrefetchTimeoutRef.current = setTimeout(() => {
|
||||
onPrefetchAsset(version_number, link);
|
||||
}, 125);
|
||||
});
|
||||
// const onHoverLink = useMemoizedFn(() => {
|
||||
// // Prefetch route after 50ms
|
||||
// routePrefetchTimeoutRef.current = setTimeout(() => {
|
||||
// onPrefetchAsset(version_number, link);
|
||||
// }, 125);
|
||||
// });
|
||||
|
||||
const onHoverEnd = useCallback(() => {
|
||||
if (routePrefetchTimeoutRef.current) {
|
||||
clearTimeout(routePrefetchTimeoutRef.current);
|
||||
}
|
||||
}, []);
|
||||
// const onHoverEnd = useCallback(() => {
|
||||
// if (routePrefetchTimeoutRef.current) {
|
||||
// clearTimeout(routePrefetchTimeoutRef.current);
|
||||
// }
|
||||
// }, []);
|
||||
|
||||
const isRestoringVersion = restoringVersion === version_number;
|
||||
// const isRestoringVersion = restoringVersion === version_number;
|
||||
|
||||
return (
|
||||
<Link prefetch={false} href={link} onMouseEnter={onHoverLink} onMouseLeave={onHoverEnd}>
|
||||
<div
|
||||
className={cn(
|
||||
'group hover:bg-item-hover flex cursor-pointer items-center justify-between space-x-2 rounded px-2.5 py-1.5',
|
||||
selected && 'bg-item-select hover:bg-item-select selected-version'
|
||||
)}
|
||||
>
|
||||
<div className="flex flex-col justify-center space-y-0.5">
|
||||
<Text>{`Version ${version_number}`}</Text>
|
||||
<Text size={'xs'} variant={'secondary'}>
|
||||
{timeFromNow(updated_at, false)}
|
||||
</Text>
|
||||
</div>
|
||||
// return (
|
||||
// <Link prefetch={false} href={link} onMouseEnter={onHoverLink} onMouseLeave={onHoverEnd}>
|
||||
// <div
|
||||
// className={cn(
|
||||
// 'group hover:bg-item-hover flex cursor-pointer items-center justify-between space-x-2 rounded px-2.5 py-1.5',
|
||||
// selected && 'bg-item-select hover:bg-item-select selected-version'
|
||||
// )}
|
||||
// >
|
||||
// <div className="flex flex-col justify-center space-y-0.5">
|
||||
// <Text>{`Version ${version_number}`}</Text>
|
||||
// <Text size={'xs'} variant={'secondary'}>
|
||||
// {timeFromNow(updated_at, false)}
|
||||
// </Text>
|
||||
// </div>
|
||||
|
||||
<div className="text-icon-color animate-in fade-in-0 flex items-center space-x-2 duration-200">
|
||||
{showRestoreButton && (
|
||||
<AppTooltip title={restoringVersion ? 'Restoring...' : 'Restore version'}>
|
||||
<button
|
||||
type="button"
|
||||
onClick={(e) => {
|
||||
if (restoringVersion) return;
|
||||
// <div className="text-icon-color animate-in fade-in-0 flex items-center space-x-2 duration-200">
|
||||
// {showRestoreButton && (
|
||||
// <AppTooltip title={restoringVersion ? 'Restoring...' : 'Restore version'}>
|
||||
// <button
|
||||
// type="button"
|
||||
// onClick={(e) => {
|
||||
// if (restoringVersion) return;
|
||||
|
||||
e.stopPropagation();
|
||||
e.preventDefault();
|
||||
onClickRestoreVersion(version_number);
|
||||
}}
|
||||
className={cn(
|
||||
'hover:bg-gray-light/20 hover:text-foreground -mr-1 cursor-pointer rounded p-1 opacity-0 group-hover:block group-hover:opacity-100',
|
||||
isRestoringVersion && 'cursor-not-allowed opacity-100!'
|
||||
)}
|
||||
>
|
||||
{isRestoringVersion ? <CircleSpinnerLoader size={12} /> : <History />}
|
||||
</button>
|
||||
</AppTooltip>
|
||||
)}
|
||||
// e.stopPropagation();
|
||||
// e.preventDefault();
|
||||
// onClickRestoreVersion(version_number);
|
||||
// }}
|
||||
// className={cn(
|
||||
// 'hover:bg-gray-light/20 hover:text-foreground -mr-1 cursor-pointer rounded p-1 opacity-0 group-hover:block group-hover:opacity-100',
|
||||
// isRestoringVersion && 'cursor-not-allowed opacity-100!'
|
||||
// )}
|
||||
// >
|
||||
// {isRestoringVersion ? <CircleSpinnerLoader size={12} /> : <History />}
|
||||
// </button>
|
||||
// </AppTooltip>
|
||||
// )}
|
||||
|
||||
{selected && (
|
||||
<div className="group-hover:opacity-100">
|
||||
<Check3 />
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
</Link>
|
||||
);
|
||||
}
|
||||
);
|
||||
ListItem.displayName = 'ListItem';
|
||||
// {selected && (
|
||||
// <div className="group-hover:opacity-100">
|
||||
// <Check3 />
|
||||
// </div>
|
||||
// )}
|
||||
// </div>
|
||||
// </div>
|
||||
// </Link>
|
||||
// );
|
||||
// }
|
||||
// );
|
||||
// ListItem.displayName = 'ListItem';
|
||||
|
||||
const PanelHeader = React.memo(() => {
|
||||
const { href } = useCloseVersionHistory();
|
||||
|
||||
return (
|
||||
<div className="flex w-full items-center justify-between">
|
||||
<Text>Version history</Text>
|
||||
<Link href={href} prefetch className="-mr-1.5">
|
||||
{/* <Link href={href} className="-mr-1.5">
|
||||
<Button variant="ghost" prefix={<Xmark />} />
|
||||
</Link>
|
||||
</Link> */}
|
||||
</div>
|
||||
);
|
||||
});
|
||||
|
|
|
@ -1,2 +1 @@
|
|||
export * from './useListVersionHistories';
|
||||
export * from './VersionHistoryPanel';
|
||||
|
|
|
@ -1,5 +1,6 @@
|
|||
import type { Meta, StoryObj } from '@storybook/react-vite';
|
||||
import { Breadcrumb } from './Breadcrumb';
|
||||
import { createBreadcrumbItem } from './create-breadcrumb';
|
||||
|
||||
const meta: Meta<typeof Breadcrumb> = {
|
||||
title: 'UI/Breadcrumb',
|
||||
|
@ -16,8 +17,8 @@ type Story = StoryObj<typeof Breadcrumb>;
|
|||
export const Default: Story = {
|
||||
args: {
|
||||
items: [
|
||||
{ label: 'Home', route: { to: '/app/home' } },
|
||||
{ label: 'Datasets', route: { to: '/app/datasets' } },
|
||||
{ label: 'Home', link: { to: '/app/home' } },
|
||||
{ label: 'Datasets', link: { to: '/app/datasets' } },
|
||||
{ label: 'Current Dataset' },
|
||||
],
|
||||
},
|
||||
|
@ -26,13 +27,13 @@ export const Default: Story = {
|
|||
export const WithDropdown: Story = {
|
||||
args: {
|
||||
items: [
|
||||
{ label: 'Home', route: { to: '/app/home' } },
|
||||
{ label: 'Home', link: { to: '/app/home' } },
|
||||
{
|
||||
label: null,
|
||||
dropdown: [
|
||||
{ label: 'Dataset A', route: { to: '/app/datasets' } },
|
||||
{ label: 'Dataset B', route: { to: '/app/datasets' } },
|
||||
{ label: 'Dataset C', route: { to: '/app/datasets' } },
|
||||
{ label: 'Dataset A', link: { to: '/app/datasets' } },
|
||||
{ label: 'Dataset B', link: { to: '/app/datasets' } },
|
||||
{ label: 'Dataset C', link: { to: '/app/datasets' } },
|
||||
],
|
||||
},
|
||||
{ label: 'Current Dataset' },
|
||||
|
@ -43,9 +44,12 @@ export const WithDropdown: Story = {
|
|||
export const CustomActiveIndex: Story = {
|
||||
args: {
|
||||
items: [
|
||||
{ label: 'Home', route: { to: '/app/home' } },
|
||||
{ label: 'Datasets', route: { to: '/app/datasets' } },
|
||||
{ label: 'Settings', route: { to: '/app/chats/$chatId', params: { chatId: '123' } } },
|
||||
{ label: 'Home', link: { to: '/app/home' } },
|
||||
{ label: 'Datasets', link: { to: '/app/datasets' } },
|
||||
createBreadcrumbItem({
|
||||
label: 'Settings',
|
||||
link: { to: '/app/chats/$chatId', params: { chatId: '123' } },
|
||||
}),
|
||||
{ label: 'Profile' },
|
||||
],
|
||||
activeIndex: 2,
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
import { Link, type LinkProps } from '@tanstack/react-router';
|
||||
import * as React from 'react';
|
||||
import { cn } from '@/lib/classMerge';
|
||||
import type { OptionsTo } from '@/types/routes';
|
||||
import type { ILinkProps } from '@/types/routes';
|
||||
import { ChevronLeft } from '../icons';
|
||||
import { Button } from './Button';
|
||||
|
||||
|
@ -11,7 +11,7 @@ interface BackButtonProps {
|
|||
size?: 'medium' | 'large';
|
||||
className?: string;
|
||||
style?: React.CSSProperties;
|
||||
linkUrl?: OptionsTo;
|
||||
linkUrl?: ILinkProps;
|
||||
activeProps?: LinkProps['activeProps'];
|
||||
activeOptions?: LinkProps['activeOptions'];
|
||||
}
|
||||
|
|
|
@ -6,7 +6,8 @@ import React from 'react';
|
|||
import { fn } from 'storybook/test';
|
||||
import { Button } from '../buttons/Button';
|
||||
import { PaintRoller, Star, Storage } from '../icons';
|
||||
import { Dropdown, type DropdownItems } from './Dropdown';
|
||||
import { Dropdown } from './Dropdown';
|
||||
import type { IDropdownItems } from './dropdown-items.types';
|
||||
|
||||
const meta: Meta<typeof Dropdown> = {
|
||||
title: 'UI/Dropdowns/Dropdown',
|
||||
|
@ -249,7 +250,7 @@ export const WithSelectionMultiple: Story = {
|
|||
render: () => {
|
||||
const [selectedIds, setSelectedIds] = React.useState<Set<string>>(new Set(['3']));
|
||||
|
||||
const items: DropdownItems = [
|
||||
const items: IDropdownItems = [
|
||||
{
|
||||
value: '1',
|
||||
label: 'Option 1',
|
||||
|
@ -464,7 +465,7 @@ export const WithLinksAndMultipleSelection: Story = {
|
|||
render: () => {
|
||||
const [selectedIds, setSelectedIds] = React.useState<Set<string>>(new Set(['2']));
|
||||
|
||||
const items: DropdownItems = [
|
||||
const items: IDropdownItems = [
|
||||
{
|
||||
value: '1',
|
||||
label: 'Documentation Home',
|
||||
|
|
|
@ -3,7 +3,7 @@ import get from 'lodash/get';
|
|||
import React, { useMemo } from 'react';
|
||||
import { useMemoizedFn } from '@/hooks/useMemoizedFn';
|
||||
import { cn } from '@/lib/classMerge';
|
||||
import type { OptionsTo } from '@/types/routes';
|
||||
import type { ILinkProps } from '@/types/routes';
|
||||
import { CheckboxColumn } from './CheckboxColumn';
|
||||
import { HEIGHT_OF_ROW } from './config';
|
||||
import type { BusterListColumn, BusterListProps, BusterListRowItem } from './interfaces';
|
||||
|
@ -160,7 +160,7 @@ const BusterListCellComponent = <T,>({
|
|||
|
||||
const LinkWrapper: React.FC<
|
||||
{
|
||||
link?: OptionsTo;
|
||||
link?: ILinkProps;
|
||||
children: React.ReactNode;
|
||||
} & LinkProps
|
||||
> = ({ link, children, preload, preloadDelay, activeOptions }) => {
|
||||
|
|
|
@ -3,7 +3,7 @@ import React from 'react';
|
|||
import { Button } from '@/components/ui/buttons';
|
||||
import { Plus } from '@/components/ui/icons';
|
||||
import { Paragraph, Title } from '@/components/ui/typography';
|
||||
import type { OptionsTo } from '@/types/routes';
|
||||
import type { ILinkProps } from '@/types/routes';
|
||||
|
||||
export const ListEmptyStateWithButton: React.FC<{
|
||||
isAdmin?: boolean;
|
||||
|
@ -14,7 +14,7 @@ export const ListEmptyStateWithButton: React.FC<{
|
|||
buttonPrefix?: React.ReactNode;
|
||||
buttonSuffix?: React.ReactNode;
|
||||
loading?: boolean;
|
||||
link?: OptionsTo | string;
|
||||
link?: ILinkProps | string;
|
||||
linkButtonTarget?: '_blank' | '_self';
|
||||
}> = React.memo(
|
||||
({
|
||||
|
@ -66,7 +66,7 @@ export const ListEmptyStateWithButton: React.FC<{
|
|||
|
||||
const ButtonWrapper: React.FC<{
|
||||
children: React.ReactNode;
|
||||
link?: OptionsTo | string;
|
||||
link?: ILinkProps | string;
|
||||
target?: '_blank' | '_self';
|
||||
}> = ({ children, link, target }) => {
|
||||
if (!link) return <>{children}</>;
|
||||
|
|
|
@ -1,14 +1,13 @@
|
|||
import type { RegisteredRouter } from '@tanstack/react-router';
|
||||
import { defineLink } from '../../../lib/routes';
|
||||
import type { ISidebarGroup, ISidebarItem, ISidebarList } from './interfaces';
|
||||
|
||||
export function createSidebarItem<
|
||||
TRouter extends RegisteredRouter,
|
||||
TOptions,
|
||||
TFrom extends string = string,
|
||||
>(
|
||||
item: ISidebarItem<TRouter, TOptions, TFrom> & { show?: boolean }
|
||||
): ISidebarItem<TRouter, TOptions, TFrom> & { show?: boolean } {
|
||||
return item;
|
||||
>(item: ISidebarItem<TRouter, TOptions, TFrom> & { show?: boolean }) {
|
||||
return item as ISidebarItem & { show?: boolean };
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -40,3 +39,21 @@ export function createSidebarGroup<
|
|||
>(items: ISidebarGroup<TRouter, TOptions, TFrom>): ISidebarGroup<TRouter, TOptions, TFrom> {
|
||||
return items;
|
||||
}
|
||||
|
||||
const test = createSidebarItem({
|
||||
label: 'Test',
|
||||
id: '123',
|
||||
link: {
|
||||
to: '/app/chats/$chatId',
|
||||
params: { chatId: '123' },
|
||||
},
|
||||
});
|
||||
|
||||
const test123 = createSidebarItem({
|
||||
label: 'Test',
|
||||
id: '123',
|
||||
link: defineLink({
|
||||
to: '/app/chats/$chatId',
|
||||
params: { chatId: '123' },
|
||||
}),
|
||||
});
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
import type { RegisteredRouter, ValidateLinkOptions } from '@tanstack/react-router';
|
||||
import type { RegisteredRouter } from '@tanstack/react-router';
|
||||
import type React from 'react';
|
||||
import type { ILinkOptions } from '@/types/routes';
|
||||
import type { ILinkProps } from '@/types/routes';
|
||||
|
||||
// Base properties shared by all sidebar items
|
||||
type ISidebarItemBase = {
|
||||
|
@ -22,7 +22,7 @@ export type ISidebarItem<
|
|||
> = ISidebarItemBase &
|
||||
(
|
||||
| {
|
||||
link: ValidateLinkOptions<TRouter, TOptions, TFrom> & ILinkOptions;
|
||||
link: ILinkProps<TRouter, TOptions, TFrom>;
|
||||
}
|
||||
| {
|
||||
link?: never;
|
||||
|
|
|
@ -1,12 +1,11 @@
|
|||
import type { RegisteredRouter, ValidateLinkOptions } from '@tanstack/react-router';
|
||||
import type { ILinkProps } from '@/types/routes';
|
||||
|
||||
export function defineLink<
|
||||
TRouter extends RegisteredRouter,
|
||||
TOptions,
|
||||
TFrom extends string = string,
|
||||
>(
|
||||
link: ValidateLinkOptions<TRouter, TOptions, TFrom>
|
||||
): ValidateLinkOptions<TRouter, TOptions, TFrom> {
|
||||
>(link: ILinkProps<TRouter, TOptions, TFrom>): ILinkProps<TRouter, TOptions, TFrom> {
|
||||
return link;
|
||||
}
|
||||
|
||||
|
|
Loading…
Reference in New Issue