mirror of https://github.com/buster-so/buster.git
make page layout better
This commit is contained in:
parent
92fe355f47
commit
b883ffc31c
|
@ -10,17 +10,16 @@ import { BusterListColumn, BusterListRow } from '@/components/ui/list';
|
|||
import { ChatSelectedOptionPopup } from './ChatItemsSelectedPopup';
|
||||
import { BusterList, ListEmptyStateWithButton } from '@/components/ui/list';
|
||||
import { useCreateListByDate } from '@/components/ui/list/useCreateListByDate';
|
||||
import { cn } from '@/lib/utils';
|
||||
|
||||
export const ChatItemsContainer: React.FC<{
|
||||
chats: BusterChatListItem[];
|
||||
className?: string;
|
||||
openNewMetricModal: () => void;
|
||||
loading: boolean;
|
||||
}> = ({ chats = [], className = '', loading, openNewMetricModal }) => {
|
||||
}> = ({ chats = [], className = '', loading }) => {
|
||||
const [selectedRowKeys, setSelectedRowKeys] = useState<string[]>([]);
|
||||
const renderedDates = useRef<Record<string, string>>({});
|
||||
const renderedOwners = useRef<Record<string, React.ReactNode>>({});
|
||||
const tableContainerRef = useRef<HTMLDivElement>(null);
|
||||
|
||||
const onSelectChange = useMemoizedFn((selectedRowKeys: string[]) => {
|
||||
setSelectedRowKeys(selectedRowKeys);
|
||||
|
@ -106,15 +105,13 @@ export const ChatItemsContainer: React.FC<{
|
|||
);
|
||||
|
||||
return (
|
||||
<div
|
||||
ref={tableContainerRef}
|
||||
className={`${className} relative flex h-full flex-col items-center`}>
|
||||
<>
|
||||
<BusterList
|
||||
rows={chatsByDate}
|
||||
columns={columns}
|
||||
onSelectChange={onSelectChange}
|
||||
selectedRowKeys={selectedRowKeys}
|
||||
emptyState={<EmptyState loading={loading} openNewMetricModal={openNewMetricModal} />}
|
||||
emptyState={<EmptyState loading={loading} />}
|
||||
/>
|
||||
|
||||
<ChatSelectedOptionPopup
|
||||
|
@ -122,31 +119,27 @@ export const ChatItemsContainer: React.FC<{
|
|||
onSelectChange={onSelectChange}
|
||||
hasSelected={hasSelected}
|
||||
/>
|
||||
</div>
|
||||
</>
|
||||
);
|
||||
};
|
||||
|
||||
const EmptyState: React.FC<{
|
||||
loading: boolean;
|
||||
openNewMetricModal: () => void;
|
||||
}> = React.memo(({ loading, openNewMetricModal }) => {
|
||||
}> = React.memo(({ loading }) => {
|
||||
if (loading) {
|
||||
return <></>;
|
||||
}
|
||||
|
||||
return <ChatsEmptyState openNewMetricModal={openNewMetricModal} />;
|
||||
return <ChatsEmptyState />;
|
||||
});
|
||||
EmptyState.displayName = 'EmptyState';
|
||||
|
||||
const ChatsEmptyState: React.FC<{
|
||||
openNewMetricModal: () => void;
|
||||
}> = ({ openNewMetricModal }) => {
|
||||
const ChatsEmptyState: React.FC<{}> = ({}) => {
|
||||
return (
|
||||
<ListEmptyStateWithButton
|
||||
title="You don't have any chats yet."
|
||||
description="You don't have any chats. As soon as you do, they will start to appear here."
|
||||
buttonText="New chat"
|
||||
onClick={openNewMetricModal}
|
||||
/>
|
||||
);
|
||||
};
|
||||
|
|
|
@ -1,44 +1,15 @@
|
|||
'use client';
|
||||
|
||||
import React, { useState } from 'react';
|
||||
import { AppContentPage } from '@/components/ui/layouts/AppContentPage';
|
||||
import { useAppLayoutContextSelector } from '@/context/BusterAppLayout';
|
||||
import { useMemoizedFn, useMount } from 'ahooks';
|
||||
import { useBusterChatListByFilter } from '@/context/Chats';
|
||||
import { ChatListHeader } from './ChatListHeader';
|
||||
import { ChatItemsContainer } from './ChatItemsContainer';
|
||||
|
||||
export const ChatListContainer: React.FC<{
|
||||
className?: string;
|
||||
}> = ({ className = '' }) => {
|
||||
const onToggleChatsModal = useAppLayoutContextSelector((s) => s.onToggleChatsModal);
|
||||
export const ChatListContainer: React.FC<{}> = ({}) => {
|
||||
const [filters, setFilters] = useState<Parameters<typeof useBusterChatListByFilter>[0]>({
|
||||
admin_view: false
|
||||
});
|
||||
|
||||
const { list, isFetched } = useBusterChatListByFilter(filters);
|
||||
|
||||
const onSetFilters = useMemoizedFn(
|
||||
(newFilters: Parameters<typeof useBusterChatListByFilter>[0]) => {
|
||||
setFilters(newFilters);
|
||||
}
|
||||
);
|
||||
|
||||
useMount(async () => {
|
||||
onSetFilters({ admin_view: false });
|
||||
});
|
||||
|
||||
return (
|
||||
<div className={`${className} flex h-full flex-col`}>
|
||||
<ChatListHeader />
|
||||
<AppContentPage>
|
||||
<ChatItemsContainer
|
||||
chats={list}
|
||||
loading={!isFetched}
|
||||
openNewMetricModal={onToggleChatsModal}
|
||||
className="flex-col overflow-hidden"
|
||||
/>
|
||||
</AppContentPage>
|
||||
</div>
|
||||
);
|
||||
return <ChatItemsContainer chats={list} loading={!isFetched} />;
|
||||
};
|
||||
|
|
|
@ -1,17 +1,10 @@
|
|||
'use client';
|
||||
|
||||
import React from 'react';
|
||||
import { AppContentHeader } from '@/components/ui/layouts/AppContentHeader';
|
||||
import { Text } from '@/components/ui';
|
||||
import { Text } from '@/components/ui/typography';
|
||||
|
||||
export const ChatListHeader: React.FC<{}> = ({}) => {
|
||||
return (
|
||||
<AppContentHeader>
|
||||
<div className="flex w-full items-center justify-between">
|
||||
<div className="flex items-center space-x-2">
|
||||
<Text>{'Chats'}</Text>
|
||||
</div>
|
||||
</div>
|
||||
</AppContentHeader>
|
||||
<>
|
||||
<Text>{'Chats'}</Text>
|
||||
</>
|
||||
);
|
||||
};
|
||||
|
|
|
@ -1,5 +1,11 @@
|
|||
import { AppPageLayout } from '@/components/ui/layouts/AppPageLayout';
|
||||
import { ChatListContainer } from './_ChatsListContainer';
|
||||
import { ChatListHeader } from './_ChatsListContainer/ChatListHeader';
|
||||
|
||||
export default function ChatsPage() {
|
||||
return <ChatListContainer />;
|
||||
return (
|
||||
<AppPageLayout headerVariant="list" header={<ChatListHeader />}>
|
||||
<ChatListContainer />
|
||||
</AppPageLayout>
|
||||
);
|
||||
}
|
||||
|
|
|
@ -14,6 +14,9 @@ import { Tooltip } from '@/components/ui/tooltip/Tooltip';
|
|||
import Link from 'next/link';
|
||||
import { useUserConfigContextSelector } from '@/context/Users';
|
||||
import { useAppLayoutContextSelector } from '@/context/BusterAppLayout';
|
||||
import { SupportModal } from '../modal/SupportModal';
|
||||
import { InvitePeopleModal } from '../modal/InvitePeopleModal';
|
||||
import { useMemoizedFn } from 'ahooks';
|
||||
|
||||
const topItems: ISidebarList = {
|
||||
items: [
|
||||
|
@ -126,7 +129,11 @@ export const SidebarPrimary = React.memo(() => {
|
|||
}, [isAdmin, favorites, currentRoute]);
|
||||
|
||||
return (
|
||||
<Sidebar content={sidebarItems} header={<SidebarPrimaryHeader />} activeItem={currentRoute} />
|
||||
<>
|
||||
<Sidebar content={sidebarItems} header={<SidebarPrimaryHeader />} activeItem={currentRoute} />
|
||||
|
||||
<GlobalModals />
|
||||
</>
|
||||
);
|
||||
});
|
||||
|
||||
|
@ -148,7 +155,7 @@ const SidebarPrimaryHeader = React.memo(() => {
|
|||
size="tall"
|
||||
rounding={'large'}
|
||||
prefix={
|
||||
<div className="translate-x-[-1px] translate-y-[-1px]">
|
||||
<div className="translate-x-[0px] translate-y-[-1px]">
|
||||
<PencilSquareIcon />
|
||||
</div>
|
||||
}
|
||||
|
@ -162,6 +169,25 @@ const SidebarPrimaryHeader = React.memo(() => {
|
|||
|
||||
SidebarPrimaryHeader.displayName = 'SidebarPrimaryHeader';
|
||||
|
||||
const GlobalModals = React.memo(() => {
|
||||
const onToggleSupportModal = useAppLayoutContextSelector((s) => s.onToggleSupportModal);
|
||||
const onToggleInviteModal = useAppLayoutContextSelector((s) => s.onToggleInviteModal);
|
||||
const openSupportModal = useAppLayoutContextSelector((s) => s.openSupportModal);
|
||||
const openInviteModal = useAppLayoutContextSelector((s) => s.openInviteModal);
|
||||
const isAnonymousUser = useUserConfigContextSelector((state) => state.isAnonymousUser);
|
||||
|
||||
const onCloseInviteModal = useMemoizedFn(() => onToggleInviteModal(false));
|
||||
const onCloseSupportModal = useMemoizedFn(() => onToggleSupportModal(false));
|
||||
|
||||
if (isAnonymousUser) return null;
|
||||
|
||||
return (
|
||||
<>
|
||||
<InvitePeopleModal open={openInviteModal} onClose={onCloseInviteModal} />
|
||||
<SupportModal open={openSupportModal} onClose={onCloseSupportModal} />
|
||||
</>
|
||||
);
|
||||
});
|
||||
const favoritesDropdown = (favorites: BusterUserFavorite[]): ISidebarGroup => {
|
||||
return {
|
||||
label: 'Favorites',
|
||||
|
|
|
@ -1,6 +1,5 @@
|
|||
import React, { PropsWithChildren } from 'react';
|
||||
import { cn } from '@/lib/utils';
|
||||
import { cva, type VariantProps } from 'class-variance-authority';
|
||||
|
||||
export const AppContentPage: React.FC<
|
||||
PropsWithChildren<{
|
||||
|
@ -11,7 +10,8 @@ export const AppContentPage: React.FC<
|
|||
return (
|
||||
<main
|
||||
className={cn(
|
||||
'bg-background app-content max-h-[100%] overflow-hidden p-0',
|
||||
'app-content-page',
|
||||
'bg-background app-content h-full max-h-[100%] overflow-hidden p-0',
|
||||
scrollable && 'overflow-y-auto',
|
||||
className
|
||||
)}>
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
import { cn } from '@/lib/utils';
|
||||
import React from 'react';
|
||||
import { cn } from '@/lib/utils';
|
||||
import { AppContentHeader } from './AppContentHeader';
|
||||
import { AppContentPage } from './AppContentPage';
|
||||
|
||||
|
@ -18,7 +18,7 @@ export const AppPageLayout: React.FC<
|
|||
className?: string;
|
||||
headerVariant?: 'default' | 'list';
|
||||
}>
|
||||
> = ({ children, header, scrollable = true, className = '', headerVariant = 'default' }) => {
|
||||
> = ({ children, header, scrollable = false, className = '', headerVariant = 'default' }) => {
|
||||
return (
|
||||
<div
|
||||
className={cn(
|
||||
|
|
|
@ -1,5 +1,9 @@
|
|||
export * from './AppSplitter';
|
||||
export * from './PreventNavigation';
|
||||
export * from './AppContentHeader_Old';
|
||||
export * from './AppContentPage';
|
||||
export * from './ClientRedirect';
|
||||
|
||||
//keepers
|
||||
// export * from './AppPageLayout';
|
||||
// export * from './AppSplitter';
|
||||
// export * from './AppLayout';
|
||||
|
|
|
@ -1,14 +1,16 @@
|
|||
import React from 'react';
|
||||
import { AppMaterialIcons, Text, Title } from '@/components/ui';
|
||||
import { Button } from 'antd';
|
||||
import { Button } from '../buttons/Button';
|
||||
import Link from 'next/link';
|
||||
|
||||
export const ListEmptyStateWithButton: React.FC<{
|
||||
isAdmin?: boolean;
|
||||
title: string;
|
||||
description: string;
|
||||
onClick: () => void;
|
||||
onClick?: () => void;
|
||||
buttonText: string;
|
||||
loading?: boolean;
|
||||
linkButton?: string;
|
||||
}> = React.memo(({ isAdmin = true, title, buttonText, description, onClick, loading = false }) => {
|
||||
return (
|
||||
<div className="flex h-full w-full flex-col">
|
||||
|
@ -27,8 +29,8 @@ export const ListEmptyStateWithButton: React.FC<{
|
|||
|
||||
{isAdmin && (
|
||||
<Button
|
||||
type="default"
|
||||
icon={<AppMaterialIcons icon="add" />}
|
||||
variant="default"
|
||||
prefix={<AppMaterialIcons icon="add" />}
|
||||
loading={loading}
|
||||
onClick={onClick}>
|
||||
{buttonText}
|
||||
|
|
|
@ -30,7 +30,7 @@ type ParagraphProps = {
|
|||
} & VariantProps<typeof textColorVariants> &
|
||||
VariantProps<typeof paragraphVariants>;
|
||||
|
||||
const Paragraph: React.FC<ParagraphProps> = ({
|
||||
export const Paragraph: React.FC<ParagraphProps> = ({
|
||||
onClick,
|
||||
variant = 'default',
|
||||
size = 'base',
|
||||
|
|
|
@ -33,7 +33,7 @@ type TextProps = {
|
|||
} & VariantProps<typeof textVariants> &
|
||||
VariantProps<typeof textColorVariants>;
|
||||
|
||||
const Text: React.FC<TextProps> = ({
|
||||
export const Text: React.FC<TextProps> = ({
|
||||
variant = 'default',
|
||||
size = 'base',
|
||||
truncate,
|
||||
|
@ -53,5 +53,3 @@ const Text: React.FC<TextProps> = ({
|
|||
</TextNode>
|
||||
);
|
||||
};
|
||||
|
||||
export default Text;
|
||||
|
|
|
@ -33,7 +33,7 @@ type TitleProps = {
|
|||
} & VariantProps<typeof titleVariants> &
|
||||
VariantProps<typeof textColorVariants>;
|
||||
|
||||
const Title: React.FC<TitleProps> = ({
|
||||
export const Title: React.FC<TitleProps> = ({
|
||||
as = 'h1',
|
||||
variant = 'default',
|
||||
size,
|
||||
|
|
|
@ -0,0 +1,3 @@
|
|||
export * from './Text';
|
||||
export * from './Title';
|
||||
export * from './Paragraph';
|
Loading…
Reference in New Issue