mirror of https://github.com/buster-so/buster.git
update some grid layout stuff
This commit is contained in:
parent
8c43ca0403
commit
f7b8d87c01
|
@ -62,7 +62,7 @@
|
||||||
"prettier-plugin-tailwindcss": "^0.6.11",
|
"prettier-plugin-tailwindcss": "^0.6.11",
|
||||||
"react": "^18",
|
"react": "^18",
|
||||||
"react-color": "^2.19.3",
|
"react-color": "^2.19.3",
|
||||||
"react-data-grid": "^7.0.0-beta.48",
|
"react-data-grid": "^7.0.0-beta.47",
|
||||||
"react-day-picker": "^9.6.1",
|
"react-day-picker": "^9.6.1",
|
||||||
"react-dom": "^18",
|
"react-dom": "^18",
|
||||||
"react-hotkeys-hook": "^4.6.1",
|
"react-hotkeys-hook": "^4.6.1",
|
||||||
|
|
|
@ -70,7 +70,7 @@
|
||||||
"prettier-plugin-tailwindcss": "^0.6.11",
|
"prettier-plugin-tailwindcss": "^0.6.11",
|
||||||
"react": "^18",
|
"react": "^18",
|
||||||
"react-color": "^2.19.3",
|
"react-color": "^2.19.3",
|
||||||
"react-data-grid": "^7.0.0-beta.48",
|
"react-data-grid": "^7.0.0-beta.47",
|
||||||
"react-day-picker": "^9.6.1",
|
"react-day-picker": "^9.6.1",
|
||||||
"react-dom": "^18",
|
"react-dom": "^18",
|
||||||
"react-hotkeys-hook": "^4.6.1",
|
"react-hotkeys-hook": "^4.6.1",
|
||||||
|
|
|
@ -14,24 +14,34 @@ import type { GetMetricParams, ListMetricsParams } from './interfaces';
|
||||||
import { upgradeMetricToIMetric } from '@/lib/chat';
|
import { upgradeMetricToIMetric } from '@/lib/chat';
|
||||||
import { queryKeys } from '@/api/query_keys';
|
import { queryKeys } from '@/api/query_keys';
|
||||||
import { useMemo } from 'react';
|
import { useMemo } from 'react';
|
||||||
|
import { useBusterAssetsContextSelector } from '@/context/Assets/BusterAssetsProvider';
|
||||||
|
|
||||||
export const useGetMetric = (params: GetMetricParams) => {
|
export const useGetMetric = (params: GetMetricParams) => {
|
||||||
|
const queryClient = useQueryClient();
|
||||||
|
const setAssetPasswordError = useBusterAssetsContextSelector(
|
||||||
|
(state) => state.setAssetPasswordError
|
||||||
|
);
|
||||||
|
|
||||||
const queryFn = useMemoizedFn(async () => {
|
const queryFn = useMemoizedFn(async () => {
|
||||||
const result = await getMetric(params);
|
const result = await getMetric(params);
|
||||||
return upgradeMetricToIMetric(result, null);
|
const oldMetric = queryClient.getQueryData(queryKeys.metricsGetMetric(params.id).queryKey);
|
||||||
|
return upgradeMetricToIMetric(result, oldMetric || null);
|
||||||
});
|
});
|
||||||
|
|
||||||
return useQuery({
|
return useQuery({
|
||||||
...queryKeys.useMetricsGetMetric(params.id),
|
...queryKeys.metricsGetMetric(params.id),
|
||||||
queryFn,
|
throwOnError: (error, query) => {
|
||||||
enabled: false //this is handle via a socket query? maybe it should not be?
|
setAssetPasswordError(params.id, error.message || 'An error occurred');
|
||||||
|
return false;
|
||||||
|
},
|
||||||
|
queryFn
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
export const prefetchGetMetric = async (params: GetMetricParams, queryClientProp?: QueryClient) => {
|
export const prefetchGetMetric = async (params: GetMetricParams, queryClientProp?: QueryClient) => {
|
||||||
const queryClient = queryClientProp || new QueryClient();
|
const queryClient = queryClientProp || new QueryClient();
|
||||||
await queryClient.prefetchQuery({
|
await queryClient.prefetchQuery({
|
||||||
...queryKeys.useMetricsGetMetric(params.id),
|
...queryKeys.metricsGetMetric(params.id),
|
||||||
queryFn: async () => {
|
queryFn: async () => {
|
||||||
const result = await getMetric_server(params);
|
const result = await getMetric_server(params);
|
||||||
return upgradeMetricToIMetric(result, null);
|
return upgradeMetricToIMetric(result, null);
|
||||||
|
|
|
@ -9,18 +9,22 @@ import type {
|
||||||
|
|
||||||
export const getMetric = async ({ id, password }: GetMetricParams) => {
|
export const getMetric = async ({ id, password }: GetMetricParams) => {
|
||||||
return mainApi
|
return mainApi
|
||||||
.get<BusterMetric>(`/metrics/get`, {
|
.get<BusterMetric>(`/metrics/${id}`, {
|
||||||
params: { id, ...(password && { password }) }
|
params: { id, ...(password && { password }) }
|
||||||
})
|
})
|
||||||
.then((res) => res.data);
|
.then((res) => res.data);
|
||||||
};
|
};
|
||||||
|
|
||||||
export const getMetric_server = async ({ id, password }: GetMetricParams) => {
|
export const getMetric_server = async ({ id, password }: GetMetricParams) => {
|
||||||
return await serverFetch<BusterMetric>(`/metrics/get`, {
|
return await serverFetch<BusterMetric>(`/metrics/${id}`, {
|
||||||
params: { id, ...(password && { password }) }
|
params: { ...(password && { password }) }
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
|
export const getMetricData = async ({ id }: { id: string }) => {
|
||||||
|
return mainApi.get<BusterMetricData>(`/metrics/${id}/data`).then((res) => res.data);
|
||||||
|
};
|
||||||
|
|
||||||
export const listMetrics = async (params: ListMetricsParams) => {
|
export const listMetrics = async (params: ListMetricsParams) => {
|
||||||
return mainApi.get<BusterMetricListItem[]>('/metrics/list', { params }).then((res) => res.data);
|
return mainApi.get<BusterMetricListItem[]>('/metrics/list', { params }).then((res) => res.data);
|
||||||
};
|
};
|
||||||
|
@ -29,10 +33,6 @@ export const listMetrics_server = async (params: ListMetricsParams) => {
|
||||||
return await serverFetch<BusterMetricListItem[]>('/metrics/list', { params });
|
return await serverFetch<BusterMetricListItem[]>('/metrics/list', { params });
|
||||||
};
|
};
|
||||||
|
|
||||||
export const getMetricData = async ({ id }: { id: string }) => {
|
|
||||||
return mainApi.get<BusterMetricData>(`/metrics/${id}/data`).then((res) => res.data);
|
|
||||||
};
|
|
||||||
|
|
||||||
export const updateMetric = async (params: UpdateMetricParams) => {
|
export const updateMetric = async (params: UpdateMetricParams) => {
|
||||||
return mainApi
|
return mainApi
|
||||||
.put<BusterMetric>(`/metrics/update/${params.id}`, { params })
|
.put<BusterMetric>(`/metrics/update/${params.id}`, { params })
|
||||||
|
|
|
@ -36,7 +36,7 @@ const chatsBlackBoxMessages = (messageId: string) =>
|
||||||
queryOptions<string | null>({
|
queryOptions<string | null>({
|
||||||
queryKey: ['chats', 'messages', messageId, 'black-box'] as const,
|
queryKey: ['chats', 'messages', messageId, 'black-box'] as const,
|
||||||
staleTime: Infinity,
|
staleTime: Infinity,
|
||||||
enabled: false,
|
enabled: false, //this is local
|
||||||
queryFn: () => Promise.resolve(null)
|
queryFn: () => Promise.resolve(null)
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
|
@ -2,32 +2,16 @@
|
||||||
|
|
||||||
import { queryOptions } from '@tanstack/react-query';
|
import { queryOptions } from '@tanstack/react-query';
|
||||||
import type {
|
import type {
|
||||||
BusterMetricData,
|
|
||||||
BusterMetricListItem,
|
BusterMetricListItem,
|
||||||
IBusterMetric,
|
IBusterMetric,
|
||||||
IBusterMetricData
|
IBusterMetricData
|
||||||
} from '@/api/asset_interfaces/metric';
|
} from '@/api/asset_interfaces/metric';
|
||||||
import { useBusterAssetsContextSelector } from '@/context/Assets/BusterAssetsProvider';
|
|
||||||
import { ListMetricsParams } from '../buster_rest/metrics';
|
import { ListMetricsParams } from '../buster_rest/metrics';
|
||||||
|
|
||||||
export const metricsGetMetric = (metricId: string) => {
|
export const metricsGetMetric = (metricId: string) => {
|
||||||
return queryOptions<IBusterMetric>({
|
return queryOptions<IBusterMetric>({
|
||||||
queryKey: ['metrics', 'get', metricId] as const,
|
queryKey: ['metrics', 'get', metricId] as const,
|
||||||
staleTime: 30 * 60 * 1000,
|
staleTime: 30 * 60 * 1000
|
||||||
enabled: false
|
|
||||||
});
|
|
||||||
};
|
|
||||||
|
|
||||||
export const useMetricsGetMetric = (metricId: string) => {
|
|
||||||
const setAssetPasswordError = useBusterAssetsContextSelector(
|
|
||||||
(state) => state.setAssetPasswordError
|
|
||||||
);
|
|
||||||
return queryOptions<IBusterMetric>({
|
|
||||||
...metricsGetMetric(metricId),
|
|
||||||
throwOnError: (error, query) => {
|
|
||||||
setAssetPasswordError(metricId, error.message || 'An error occurred');
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -45,7 +29,6 @@ export const metricsGetData = (id: string) =>
|
||||||
|
|
||||||
export const metricsQueryKeys = {
|
export const metricsQueryKeys = {
|
||||||
metricsGetMetric,
|
metricsGetMetric,
|
||||||
useMetricsGetMetric,
|
|
||||||
metricsGetList,
|
metricsGetList,
|
||||||
metricsGetData
|
metricsGetData
|
||||||
};
|
};
|
||||||
|
|
|
@ -8,9 +8,6 @@ export default async function Page(props: {
|
||||||
|
|
||||||
const { chatId, metricId } = params;
|
const { chatId, metricId } = params;
|
||||||
|
|
||||||
console.log('chatId', chatId);
|
|
||||||
console.log('metricId', metricId);
|
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<AppAssetCheckLayout metricId={metricId} type="metric">
|
<AppAssetCheckLayout metricId={metricId} type="metric">
|
||||||
<MetricController metricId={metricId} />
|
<MetricController metricId={metricId} />
|
||||||
|
|
|
@ -13,10 +13,11 @@ export default function Page(params: { params: { chatId: string } }) {
|
||||||
}
|
}
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<StatusCard
|
<div className="p-5">
|
||||||
className="text-red-500"
|
<StatusCard
|
||||||
title="Error"
|
title="Error"
|
||||||
message="If you are seeing this, tell Nate and screenshot this whole page including the URL and logs..."
|
message="If you are seeing this, tell Nate and screenshot this whole page including the URL and logs..."
|
||||||
/>
|
/>
|
||||||
|
</div>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
|
@ -38,7 +38,7 @@ export const NewUserController = () => {
|
||||||
});
|
});
|
||||||
|
|
||||||
onChangePage({
|
onChangePage({
|
||||||
route: BusterRoutes.APP_METRIC
|
route: BusterRoutes.APP_HOME
|
||||||
});
|
});
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
//
|
//
|
||||||
|
@ -81,7 +81,6 @@ export const NewUserController = () => {
|
||||||
placeholder="What is your full name"
|
placeholder="What is your full name"
|
||||||
className="w-full"
|
className="w-full"
|
||||||
value={name || ''}
|
value={name || ''}
|
||||||
defaultValue={user?.name || ''}
|
|
||||||
name="name"
|
name="name"
|
||||||
onChange={(e) => setName(e.target.value)}
|
onChange={(e) => setName(e.target.value)}
|
||||||
/>
|
/>
|
||||||
|
|
|
@ -38,6 +38,7 @@ export const SidebarUserFooterComponent: React.FC<{
|
||||||
email: string;
|
email: string;
|
||||||
signOut: () => Promise<{ error: string }>;
|
signOut: () => Promise<{ error: string }>;
|
||||||
}> = React.memo(({ name, avatarUrl, email, signOut }) => {
|
}> = React.memo(({ name, avatarUrl, email, signOut }) => {
|
||||||
|
if (!name || !email) return null;
|
||||||
return (
|
return (
|
||||||
<SidebarUserDropdown signOut={signOut}>
|
<SidebarUserDropdown signOut={signOut}>
|
||||||
<div className="flex w-full">
|
<div className="flex w-full">
|
||||||
|
|
|
@ -0,0 +1,112 @@
|
||||||
|
import type { Meta, StoryObj } from '@storybook/react';
|
||||||
|
import { AppDataGrid } from './AppDataGrid';
|
||||||
|
|
||||||
|
const meta: Meta<typeof AppDataGrid> = {
|
||||||
|
title: 'UI/table/AppDataGrid',
|
||||||
|
component: AppDataGrid,
|
||||||
|
parameters: {
|
||||||
|
layout: 'centered'
|
||||||
|
},
|
||||||
|
tags: ['autodocs']
|
||||||
|
};
|
||||||
|
|
||||||
|
export default meta;
|
||||||
|
type Story = StoryObj<typeof AppDataGrid>;
|
||||||
|
|
||||||
|
const sampleData = [
|
||||||
|
{ id: 1, name: 'John Doe', age: 30, email: 'john@example.com', joinDate: new Date('2023-01-15') },
|
||||||
|
{
|
||||||
|
id: 2,
|
||||||
|
name: 'Jane Smith',
|
||||||
|
age: 25,
|
||||||
|
email: 'jane@example.com',
|
||||||
|
joinDate: new Date('2023-02-20')
|
||||||
|
},
|
||||||
|
{
|
||||||
|
id: 3,
|
||||||
|
name: 'Bob Johnson',
|
||||||
|
age: 35,
|
||||||
|
email: 'bob@example.com',
|
||||||
|
joinDate: new Date('2023-03-10')
|
||||||
|
},
|
||||||
|
{
|
||||||
|
id: 4,
|
||||||
|
name: 'Alice Brown',
|
||||||
|
age: 28,
|
||||||
|
email: 'alice@example.com',
|
||||||
|
joinDate: new Date('2023-04-05')
|
||||||
|
}
|
||||||
|
];
|
||||||
|
|
||||||
|
export const Default: Story = {
|
||||||
|
args: {
|
||||||
|
rows: sampleData,
|
||||||
|
animate: true,
|
||||||
|
resizable: true,
|
||||||
|
draggable: true,
|
||||||
|
sortable: true
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
export const NonResizable: Story = {
|
||||||
|
args: {
|
||||||
|
rows: sampleData,
|
||||||
|
resizable: false,
|
||||||
|
draggable: true,
|
||||||
|
sortable: true
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
export const NonDraggable: Story = {
|
||||||
|
args: {
|
||||||
|
rows: sampleData,
|
||||||
|
resizable: true,
|
||||||
|
draggable: false,
|
||||||
|
sortable: true
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
export const NonSortable: Story = {
|
||||||
|
args: {
|
||||||
|
rows: sampleData,
|
||||||
|
resizable: true,
|
||||||
|
draggable: true,
|
||||||
|
sortable: false
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
export const CustomColumnOrder: Story = {
|
||||||
|
args: {
|
||||||
|
rows: sampleData,
|
||||||
|
columnOrder: ['name', 'email', 'age', 'id', 'joinDate'],
|
||||||
|
resizable: true,
|
||||||
|
draggable: true,
|
||||||
|
sortable: true
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
export const CustomFormatting: Story = {
|
||||||
|
args: {
|
||||||
|
rows: sampleData,
|
||||||
|
headerFormat: (value, columnName) => columnName.toUpperCase(),
|
||||||
|
cellFormat: (value, columnName) => {
|
||||||
|
if (columnName === 'joinDate' && value instanceof Date) {
|
||||||
|
return value.toLocaleDateString();
|
||||||
|
}
|
||||||
|
return String(value);
|
||||||
|
},
|
||||||
|
resizable: true,
|
||||||
|
draggable: true,
|
||||||
|
sortable: true
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
export const WithInitialWidth: Story = {
|
||||||
|
args: {
|
||||||
|
rows: sampleData,
|
||||||
|
initialWidth: 800,
|
||||||
|
resizable: true,
|
||||||
|
draggable: true,
|
||||||
|
sortable: true
|
||||||
|
}
|
||||||
|
};
|
|
@ -35,7 +35,6 @@ import {
|
||||||
defaultHeaderFormat,
|
defaultHeaderFormat,
|
||||||
MIN_WIDTH
|
MIN_WIDTH
|
||||||
} from './helpers';
|
} from './helpers';
|
||||||
import styles from './AppDataGrid.module.css';
|
|
||||||
|
|
||||||
type Row = Record<string, string | number | null | Date>;
|
type Row = Record<string, string | number | null | Date>;
|
||||||
|
|
||||||
|
@ -339,6 +338,19 @@ export const AppDataGrid: React.FC<AppDataGridProps> = React.memo(
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
const columnsX = [
|
||||||
|
{ key: 'id', name: 'ID' },
|
||||||
|
{ key: 'title', name: 'Title' }
|
||||||
|
];
|
||||||
|
|
||||||
|
const rowsX = [
|
||||||
|
{ id: 0, title: 'Example' },
|
||||||
|
{ id: 1, title: 'Demo' }
|
||||||
|
];
|
||||||
|
|
||||||
|
console.log('columnsX', columnsX);
|
||||||
|
console.log('rowsX', rowsX);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<React.Fragment key={forceRenderId}>
|
<React.Fragment key={forceRenderId}>
|
||||||
<ErrorBoundary onError={handleErrorBoundary}>
|
<ErrorBoundary onError={handleErrorBoundary}>
|
||||||
|
@ -348,7 +360,9 @@ export const AppDataGrid: React.FC<AppDataGridProps> = React.memo(
|
||||||
style={{
|
style={{
|
||||||
transition: animate ? 'opacity 0.25s' : undefined
|
transition: animate ? 'opacity 0.25s' : undefined
|
||||||
}}>
|
}}>
|
||||||
<DataGrid
|
<DataGrid columns={columnsX} rows={rowsX} />
|
||||||
|
|
||||||
|
{/* <DataGrid
|
||||||
className={styles.dataGrid}
|
className={styles.dataGrid}
|
||||||
columns={reorderedColumns}
|
columns={reorderedColumns}
|
||||||
rows={sortedRows}
|
rows={sortedRows}
|
||||||
|
@ -363,7 +377,7 @@ export const AppDataGrid: React.FC<AppDataGridProps> = React.memo(
|
||||||
onColumnsReorder={onColumnsReorder}
|
onColumnsReorder={onColumnsReorder}
|
||||||
defaultColumnOptions={DEFAULT_COLUMN_WIDTH}
|
defaultColumnOptions={DEFAULT_COLUMN_WIDTH}
|
||||||
direction={'ltr'}
|
direction={'ltr'}
|
||||||
/>
|
/> */}
|
||||||
<div style={{ width: '100%' }}></div>
|
<div style={{ width: '100%' }}></div>
|
||||||
</div>
|
</div>
|
||||||
</ErrorBoundary>
|
</ErrorBoundary>
|
||||||
|
|
|
@ -20,7 +20,7 @@ export const AppNoPageAccess: React.FC<{
|
||||||
<div className="flex h-[85vh] w-full flex-col items-center justify-center space-y-6">
|
<div className="flex h-[85vh] w-full flex-col items-center justify-center space-y-6">
|
||||||
<BusterLogo className="h-16 w-16" />
|
<BusterLogo className="h-16 w-16" />
|
||||||
|
|
||||||
<div className="max-w-[340px] text-center">
|
<div className="max-w-[440px] text-center">
|
||||||
<Title
|
<Title
|
||||||
as="h2"
|
as="h2"
|
||||||
className="text-center">{`It looks like you don’t have access to this file...`}</Title>
|
className="text-center">{`It looks like you don’t have access to this file...`}</Title>
|
||||||
|
|
|
@ -63,7 +63,7 @@ const AppPasswordInputComponent: React.FC<{
|
||||||
style={{
|
style={{
|
||||||
marginTop: '25vh'
|
marginTop: '25vh'
|
||||||
}}>
|
}}>
|
||||||
<div className="flex max-w-[340px] flex-col items-center space-y-6">
|
<div className="flex max-w-[440px] flex-col items-center space-y-6">
|
||||||
<BusterLogo className="h-16 w-16" />
|
<BusterLogo className="h-16 w-16" />
|
||||||
|
|
||||||
<div className="text-center">
|
<div className="text-center">
|
||||||
|
|
|
@ -53,7 +53,7 @@ export const MetricTitle: React.FC<{
|
||||||
<Title
|
<Title
|
||||||
{...titleConfig}
|
{...titleConfig}
|
||||||
as="h4"
|
as="h4"
|
||||||
className="text-md! max-w-[calc(100%_-_22px)]"
|
className="text-md! max-w-[calc(100%_-_22px)] whitespace-nowrap"
|
||||||
style={{ fontSize: '14px' }}>
|
style={{ fontSize: '14px' }}>
|
||||||
{`${title}`}
|
{`${title}`}
|
||||||
</Title>
|
</Title>
|
||||||
|
@ -96,11 +96,7 @@ const ThreeDotPlaceholder: React.FC<{
|
||||||
}> = React.memo(({ className }) => {
|
}> = React.memo(({ className }) => {
|
||||||
return (
|
return (
|
||||||
<div className={`relative h-[24px] w-[24px] ${className}`}>
|
<div className={`relative h-[24px] w-[24px] ${className}`}>
|
||||||
{/* <Button
|
<Button variant="link" prefix={<DotsVertical />} />
|
||||||
className="absolute top-[-2px] hidden"
|
|
||||||
type="text"
|
|
||||||
icon={<AppMaterialIcons icon="more_vert" />}
|
|
||||||
/> */}
|
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
});
|
});
|
||||||
|
|
|
@ -19,7 +19,7 @@ export const MetricViewChartHeader: React.FC<{
|
||||||
<EditableTitle level={4} className="mb-0" inputClassName="text-md!" onChange={onSetTitle}>
|
<EditableTitle level={4} className="mb-0" inputClassName="text-md!" onChange={onSetTitle}>
|
||||||
{title}
|
{title}
|
||||||
</EditableTitle>
|
</EditableTitle>
|
||||||
<div className="flex items-center space-x-1">
|
<div className="flex items-center space-x-1 whitespace-nowrap">
|
||||||
{!!timeFrame && (
|
{!!timeFrame && (
|
||||||
<>
|
<>
|
||||||
<Text size={'sm'} variant="secondary">
|
<Text size={'sm'} variant="secondary">
|
||||||
|
|
|
@ -1,7 +1,7 @@
|
||||||
'use client';
|
'use client';
|
||||||
|
|
||||||
import { createContext, useContextSelector } from 'use-context-selector';
|
import { createContext, useContextSelector } from 'use-context-selector';
|
||||||
import React, { PropsWithChildren, useTransition } from 'react';
|
import React, { PropsWithChildren } from 'react';
|
||||||
import { useMemoizedFn } from '@/hooks';
|
import { useMemoizedFn } from '@/hooks';
|
||||||
import type { AppSplitterRef } from '@/components/ui/layouts';
|
import type { AppSplitterRef } from '@/components/ui/layouts';
|
||||||
import { DEFAULT_CHAT_OPTION_SIDEBAR_SIZE } from './config';
|
import { DEFAULT_CHAT_OPTION_SIDEBAR_SIZE } from './config';
|
||||||
|
@ -38,9 +38,7 @@ export const useChatLayout = ({ appSplitterRef }: UseChatSplitterProps) => {
|
||||||
renderViewLayoutKey
|
renderViewLayoutKey
|
||||||
} = useSelectedFileAndLayout({ animateOpenSplitter });
|
} = useSelectedFileAndLayout({ animateOpenSplitter });
|
||||||
|
|
||||||
const { collapseDirection, isCollapseOpen, onCollapseFileClick } = useLayoutCollapse({
|
const { onCollapseFileClick } = useLayoutCollapse({
|
||||||
selectedFile,
|
|
||||||
selectedLayout,
|
|
||||||
animateOpenSplitter
|
animateOpenSplitter
|
||||||
});
|
});
|
||||||
|
|
||||||
|
@ -56,8 +54,6 @@ export const useChatLayout = ({ appSplitterRef }: UseChatSplitterProps) => {
|
||||||
renderViewLayoutKey,
|
renderViewLayoutKey,
|
||||||
selectedFileType: selectedFile?.type,
|
selectedFileType: selectedFile?.type,
|
||||||
selectedFile,
|
selectedFile,
|
||||||
collapseDirection,
|
|
||||||
isCollapseOpen,
|
|
||||||
onCollapseFileClick,
|
onCollapseFileClick,
|
||||||
onSetSelectedFile,
|
onSetSelectedFile,
|
||||||
animateOpenSplitter
|
animateOpenSplitter
|
||||||
|
|
|
@ -1,4 +1,4 @@
|
||||||
import { DoubleChevronRight, DoubleChevronLeft } from '@/components/ui/icons';
|
import { DoubleChevronRight } from '@/components/ui/icons';
|
||||||
import { Button } from '@/components/ui/buttons';
|
import { Button } from '@/components/ui/buttons';
|
||||||
import React, { useMemo } from 'react';
|
import React, { useMemo } from 'react';
|
||||||
import { AnimatePresence, motion } from 'framer-motion';
|
import { AnimatePresence, motion } from 'framer-motion';
|
||||||
|
@ -6,30 +6,21 @@ import { useMemoizedFn } from '@/hooks';
|
||||||
|
|
||||||
export const CollapseFileButton: React.FC<{
|
export const CollapseFileButton: React.FC<{
|
||||||
showCollapseButton: boolean;
|
showCollapseButton: boolean;
|
||||||
isOpen: boolean;
|
|
||||||
collapseDirection: 'left' | 'right';
|
|
||||||
onCollapseFileClick: (value?: boolean) => void;
|
onCollapseFileClick: (value?: boolean) => void;
|
||||||
}> = React.memo(({ showCollapseButton, isOpen, collapseDirection, onCollapseFileClick }) => {
|
}> = React.memo(({ showCollapseButton, onCollapseFileClick }) => {
|
||||||
const icon = useMemo(() => {
|
const icon = <DoubleChevronRight />;
|
||||||
if (collapseDirection === 'left') {
|
|
||||||
return isOpen ? <DoubleChevronLeft /> : <DoubleChevronRight />;
|
|
||||||
}
|
|
||||||
return isOpen ? <DoubleChevronRight /> : <DoubleChevronLeft />;
|
|
||||||
}, [isOpen, collapseDirection]);
|
|
||||||
|
|
||||||
const onClick = useMemoizedFn(() => {
|
const onClick = useMemoizedFn(() => {
|
||||||
onCollapseFileClick();
|
onCollapseFileClick();
|
||||||
});
|
});
|
||||||
|
|
||||||
const animation = useMemo(() => {
|
const animation = useMemo(() => {
|
||||||
const baseAnimation = {
|
return {
|
||||||
initial: { opacity: 0 },
|
initial: { opacity: 0 },
|
||||||
animate: { opacity: 1 },
|
animate: { opacity: 1 },
|
||||||
exit: { opacity: 0 }
|
exit: { opacity: 0 }
|
||||||
};
|
};
|
||||||
|
}, []);
|
||||||
return baseAnimation;
|
|
||||||
}, [collapseDirection, isOpen]);
|
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<AnimatePresence mode="wait" initial={false}>
|
<AnimatePresence mode="wait" initial={false}>
|
||||||
|
|
|
@ -2,7 +2,7 @@
|
||||||
|
|
||||||
import React from 'react';
|
import React from 'react';
|
||||||
import { CollapseFileButton } from './CollapseFileButton';
|
import { CollapseFileButton } from './CollapseFileButton';
|
||||||
import type { FileType } from '@/api/asset_interfaces';
|
import type { FileType } from '@/api/asset_interfaces/chat';
|
||||||
import { FileContainerSegmentProps, FileContainerButtonsProps } from './interfaces';
|
import { FileContainerSegmentProps, FileContainerButtonsProps } from './interfaces';
|
||||||
import { DashboardContainerHeaderButtons } from './DashboardContainerHeaderButtons';
|
import { DashboardContainerHeaderButtons } from './DashboardContainerHeaderButtons';
|
||||||
import { DashboardContainerHeaderSegment } from './DashboardContainerHeaderSegment';
|
import { DashboardContainerHeaderSegment } from './DashboardContainerHeaderSegment';
|
||||||
|
@ -15,8 +15,6 @@ export const FileContainerHeader: React.FC = React.memo(() => {
|
||||||
const selectedFileType = useChatLayoutContextSelector((x) => x.selectedFileType);
|
const selectedFileType = useChatLayoutContextSelector((x) => x.selectedFileType);
|
||||||
const selectedFileView = useChatLayoutContextSelector((x) => x.selectedFileView);
|
const selectedFileView = useChatLayoutContextSelector((x) => x.selectedFileView);
|
||||||
const onCollapseFileClick = useChatLayoutContextSelector((state) => state.onCollapseFileClick);
|
const onCollapseFileClick = useChatLayoutContextSelector((state) => state.onCollapseFileClick);
|
||||||
const collapseDirection = useChatLayoutContextSelector((state) => state.collapseDirection);
|
|
||||||
const isCollapseOpen = useChatLayoutContextSelector((state) => state.isCollapseOpen);
|
|
||||||
const renderViewLayoutKey = useChatLayoutContextSelector((state) => state.renderViewLayoutKey);
|
const renderViewLayoutKey = useChatLayoutContextSelector((state) => state.renderViewLayoutKey);
|
||||||
|
|
||||||
const showCollapseButton = renderViewLayoutKey !== 'file';
|
const showCollapseButton = renderViewLayoutKey !== 'file';
|
||||||
|
@ -33,7 +31,7 @@ export const FileContainerHeader: React.FC = React.memo(() => {
|
||||||
() =>
|
() =>
|
||||||
selectedFileType && SelectedFileButtonsRecord[selectedFileType]
|
selectedFileType && SelectedFileButtonsRecord[selectedFileType]
|
||||||
? SelectedFileButtonsRecord[selectedFileType]
|
? SelectedFileButtonsRecord[selectedFileType]
|
||||||
: () => <>no buttons</>,
|
: () => null,
|
||||||
[selectedFileType]
|
[selectedFileType]
|
||||||
);
|
);
|
||||||
|
|
||||||
|
@ -41,9 +39,7 @@ export const FileContainerHeader: React.FC = React.memo(() => {
|
||||||
<>
|
<>
|
||||||
<div className="flex items-center gap-1.5">
|
<div className="flex items-center gap-1.5">
|
||||||
<CollapseFileButton
|
<CollapseFileButton
|
||||||
collapseDirection={collapseDirection}
|
|
||||||
showCollapseButton={showCollapseButton}
|
showCollapseButton={showCollapseButton}
|
||||||
isOpen={isCollapseOpen}
|
|
||||||
onCollapseFileClick={onCollapseFileClick}
|
onCollapseFileClick={onCollapseFileClick}
|
||||||
/>
|
/>
|
||||||
{selectedFileView && <SelectedFileSegment selectedFileView={selectedFileView} />}
|
{selectedFileView && <SelectedFileSegment selectedFileView={selectedFileView} />}
|
||||||
|
|
|
@ -11,6 +11,7 @@ import { useMetricIndividual } from '@/context/Metrics';
|
||||||
import { SaveMetricToCollectionButton } from '../../../../components/features/buttons/SaveMetricToCollectionButton';
|
import { SaveMetricToCollectionButton } from '../../../../components/features/buttons/SaveMetricToCollectionButton';
|
||||||
import { SaveMetricToDashboardButton } from '../../../../components/features/buttons/SaveMetricToDashboardButton';
|
import { SaveMetricToDashboardButton } from '../../../../components/features/buttons/SaveMetricToDashboardButton';
|
||||||
import { ShareMetricButton } from '../../../../components/features/buttons/ShareMetricButton';
|
import { ShareMetricButton } from '../../../../components/features/buttons/ShareMetricButton';
|
||||||
|
import { Code3, SquareChartPen } from '@/components/ui/icons';
|
||||||
|
|
||||||
export const MetricContainerHeaderButtons: React.FC<FileContainerButtonsProps> = React.memo(() => {
|
export const MetricContainerHeaderButtons: React.FC<FileContainerButtonsProps> = React.memo(() => {
|
||||||
const renderViewLayoutKey = useChatLayoutContextSelector((x) => x.renderViewLayoutKey);
|
const renderViewLayoutKey = useChatLayoutContextSelector((x) => x.renderViewLayoutKey);
|
||||||
|
@ -52,7 +53,7 @@ const EditChartButton = React.memo(() => {
|
||||||
return (
|
return (
|
||||||
<SelectableButton
|
<SelectableButton
|
||||||
tooltipText="Edit chart"
|
tooltipText="Edit chart"
|
||||||
icon="chart_edit"
|
icon={<SquareChartPen />}
|
||||||
onClick={onClickButton}
|
onClick={onClickButton}
|
||||||
selected={isSelectedView}
|
selected={isSelectedView}
|
||||||
/>
|
/>
|
||||||
|
@ -76,7 +77,7 @@ const EditSQLButton = React.memo(() => {
|
||||||
return (
|
return (
|
||||||
<SelectableButton
|
<SelectableButton
|
||||||
tooltipText="Edit SQL"
|
tooltipText="Edit SQL"
|
||||||
icon="code_blocks"
|
icon={<Code3 />}
|
||||||
onClick={onClickButton}
|
onClick={onClickButton}
|
||||||
selected={isSelectedView}
|
selected={isSelectedView}
|
||||||
/>
|
/>
|
||||||
|
|
|
@ -2,7 +2,6 @@ import React from 'react';
|
||||||
|
|
||||||
import { Button } from '@/components/ui/buttons';
|
import { Button } from '@/components/ui/buttons';
|
||||||
import { AppTooltip } from '@/components/ui/tooltip';
|
import { AppTooltip } from '@/components/ui/tooltip';
|
||||||
import {} from '@/components/ui/icons';
|
|
||||||
|
|
||||||
export const SelectableButton = React.memo<{
|
export const SelectableButton = React.memo<{
|
||||||
tooltipText: string;
|
tooltipText: string;
|
||||||
|
|
|
@ -6,50 +6,25 @@ import { SelectedFileParams } from './useSelectedFileAndLayout';
|
||||||
import { ChatLayoutView } from '../interfaces';
|
import { ChatLayoutView } from '../interfaces';
|
||||||
|
|
||||||
export const useLayoutCollapse = ({
|
export const useLayoutCollapse = ({
|
||||||
selectedFile,
|
animateOpenSplitter
|
||||||
animateOpenSplitter,
|
|
||||||
selectedLayout
|
|
||||||
}: {
|
}: {
|
||||||
selectedFile: SelectedFileParams['selectedFile'];
|
|
||||||
selectedLayout: ChatLayoutView;
|
|
||||||
animateOpenSplitter: (side: 'left' | 'right' | 'both') => void;
|
animateOpenSplitter: (side: 'left' | 'right' | 'both') => void;
|
||||||
}) => {
|
}) => {
|
||||||
const isReasoningFile = selectedFile?.type === 'reasoning';
|
|
||||||
|
|
||||||
const [isCollapseOpen, setIsCollapseOpen] = useState(isReasoningFile ? true : false);
|
|
||||||
|
|
||||||
const collapseDirection: 'left' | 'right' = useMemo(() => {
|
|
||||||
if (selectedFile?.type === 'reasoning') return 'right';
|
|
||||||
|
|
||||||
return selectedLayout === 'file' ? 'left' : 'right';
|
|
||||||
}, [selectedLayout, selectedFile?.type]);
|
|
||||||
|
|
||||||
const onCollapseFileClick = useMemoizedFn((close?: boolean) => {
|
const onCollapseFileClick = useMemoizedFn((close?: boolean) => {
|
||||||
const isCloseAction = close ?? isCollapseOpen;
|
// if (selectedFile && selectedFile.type === 'reasoning') {
|
||||||
const isFileLayout = selectedLayout === 'file';
|
// animateOpenSplitter(!isCloseAction ? 'both' : 'left');
|
||||||
|
// } else if (isFileLayout) {
|
||||||
|
// // For file layout, toggle between 'both' and 'right'
|
||||||
|
// animateOpenSplitter(!isCloseAction && selectedFile ? 'both' : 'right');
|
||||||
|
// } else {
|
||||||
|
// // For other layouts, toggle between 'right' and 'both'
|
||||||
|
// animateOpenSplitter(isCloseAction ? 'left' : 'both');
|
||||||
|
// }
|
||||||
|
|
||||||
setIsCollapseOpen(!isCloseAction);
|
animateOpenSplitter('left');
|
||||||
|
|
||||||
if (selectedFile && selectedFile.type === 'reasoning') {
|
|
||||||
animateOpenSplitter(!isCloseAction ? 'both' : 'left');
|
|
||||||
} else if (isFileLayout) {
|
|
||||||
// For file layout, toggle between 'both' and 'right'
|
|
||||||
animateOpenSplitter(!isCloseAction && selectedFile ? 'both' : 'right');
|
|
||||||
} else {
|
|
||||||
// For other layouts, toggle between 'right' and 'both'
|
|
||||||
animateOpenSplitter(isCloseAction ? 'left' : 'both');
|
|
||||||
}
|
|
||||||
});
|
});
|
||||||
|
|
||||||
useEffect(() => {
|
|
||||||
if (isReasoningFile && !isCollapseOpen) {
|
|
||||||
setIsCollapseOpen(true);
|
|
||||||
}
|
|
||||||
}, [isReasoningFile]);
|
|
||||||
|
|
||||||
return {
|
return {
|
||||||
collapseDirection,
|
|
||||||
isCollapseOpen,
|
|
||||||
onCollapseFileClick
|
onCollapseFileClick
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|
Loading…
Reference in New Issue