mirror of
https://github.com/colanode/colanode.git
synced 2025-12-16 11:47:47 +01:00
Refactor container and breadcrumb
This commit is contained in:
@@ -1,8 +1,10 @@
|
||||
import { useNavigate } from '@tanstack/react-router';
|
||||
import { LogOut } from 'lucide-react';
|
||||
import { toast } from 'sonner';
|
||||
|
||||
import { Breadcrumb } from '@colanode/ui/components/layouts/breadcrumbs/breadcrumb';
|
||||
import { BreadcrumbItem } from '@colanode/ui/components/layouts/breadcrumbs/breadcrumb-item';
|
||||
import { Button } from '@colanode/ui/components/ui/button';
|
||||
import { Container, ContainerBody } from '@colanode/ui/components/ui/container';
|
||||
import { Separator } from '@colanode/ui/components/ui/separator';
|
||||
import { Spinner } from '@colanode/ui/components/ui/spinner';
|
||||
import { useAccount } from '@colanode/ui/contexts/account';
|
||||
@@ -14,8 +16,14 @@ export const AccountLogoutScreen = () => {
|
||||
const { mutate, isPending } = useMutation();
|
||||
|
||||
return (
|
||||
<Container>
|
||||
<ContainerBody className="max-w-4xl space-y-8">
|
||||
<>
|
||||
<Breadcrumb>
|
||||
<BreadcrumbItem
|
||||
icon={(className) => <LogOut className={className} />}
|
||||
name="Logout"
|
||||
/>
|
||||
</Breadcrumb>
|
||||
<div className="max-w-4xl space-y-8">
|
||||
<div className="space-y-6">
|
||||
<div>
|
||||
<h2 className="text-2xl font-semibold tracking-tight">Logout</h2>
|
||||
@@ -59,7 +67,7 @@ export const AccountLogoutScreen = () => {
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</ContainerBody>
|
||||
</Container>
|
||||
</div>
|
||||
</>
|
||||
);
|
||||
};
|
||||
|
||||
@@ -1,12 +1,21 @@
|
||||
import { Settings } from 'lucide-react';
|
||||
|
||||
import { AccountDelete } from '@colanode/ui/components/accounts/account-delete';
|
||||
import { AccountUpdate } from '@colanode/ui/components/accounts/account-update';
|
||||
import { Container, ContainerBody } from '@colanode/ui/components/ui/container';
|
||||
import { Breadcrumb } from '@colanode/ui/components/layouts/breadcrumbs/breadcrumb';
|
||||
import { BreadcrumbItem } from '@colanode/ui/components/layouts/breadcrumbs/breadcrumb-item';
|
||||
import { Separator } from '@colanode/ui/components/ui/separator';
|
||||
|
||||
export const AccountSettingsScreen = () => {
|
||||
return (
|
||||
<Container>
|
||||
<ContainerBody className="max-w-4xl space-y-8">
|
||||
<>
|
||||
<Breadcrumb>
|
||||
<BreadcrumbItem
|
||||
icon={(className) => <Settings className={className} />}
|
||||
name="Settings"
|
||||
/>
|
||||
</Breadcrumb>
|
||||
<div className="max-w-4xl space-y-8">
|
||||
<div className="space-y-6">
|
||||
<div>
|
||||
<h2 className="text-2xl font-semibold tracking-tight">General</h2>
|
||||
@@ -24,7 +33,7 @@ export const AccountSettingsScreen = () => {
|
||||
</div>
|
||||
<AccountDelete />
|
||||
</div>
|
||||
</ContainerBody>
|
||||
</Container>
|
||||
</div>
|
||||
</>
|
||||
);
|
||||
};
|
||||
|
||||
@@ -1,8 +1,9 @@
|
||||
import { Check, Laptop, Moon, Sun } from 'lucide-react';
|
||||
import { Check, Laptop, Moon, Palette, Sun } from 'lucide-react';
|
||||
|
||||
import { ThemeColor, ThemeMode } from '@colanode/client/types';
|
||||
import { Breadcrumb } from '@colanode/ui/components/layouts/breadcrumbs/breadcrumb';
|
||||
import { BreadcrumbItem } from '@colanode/ui/components/layouts/breadcrumbs/breadcrumb-item';
|
||||
import { Button } from '@colanode/ui/components/ui/button';
|
||||
import { Container, ContainerBody } from '@colanode/ui/components/ui/container';
|
||||
import { Separator } from '@colanode/ui/components/ui/separator';
|
||||
import { useMutation } from '@colanode/ui/hooks/use-mutation';
|
||||
import { cn } from '@colanode/ui/lib/utils';
|
||||
@@ -59,8 +60,14 @@ export const AppAppearanceSettingsScreen = () => {
|
||||
const themeColor = useAppStore((state) => state.metadata.theme.color);
|
||||
|
||||
return (
|
||||
<Container>
|
||||
<ContainerBody className="max-w-4xl space-y-8">
|
||||
<>
|
||||
<Breadcrumb>
|
||||
<BreadcrumbItem
|
||||
icon={(className) => <Palette className={className} />}
|
||||
name="Appearance"
|
||||
/>
|
||||
</Breadcrumb>
|
||||
<div className="max-w-4xl space-y-8">
|
||||
<div>
|
||||
<h2 className="text-2xl font-semibold tracking-tight">Appearance</h2>
|
||||
<Separator className="mt-3" />
|
||||
@@ -171,7 +178,7 @@ export const AppAppearanceSettingsScreen = () => {
|
||||
);
|
||||
})}
|
||||
</div>
|
||||
</ContainerBody>
|
||||
</Container>
|
||||
</div>
|
||||
</>
|
||||
);
|
||||
};
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
import { LocalChannelNode } from '@colanode/client/types';
|
||||
import { Avatar } from '@colanode/ui/components/avatars/avatar';
|
||||
import { BreadcrumbItem } from '@colanode/ui/components/layouts/breadcrumbs/breadcrumb-item';
|
||||
|
||||
interface ChannelBreadcrumbItemProps {
|
||||
channel: LocalChannelNode;
|
||||
@@ -9,14 +10,16 @@ export const ChannelBreadcrumbItem = ({
|
||||
channel,
|
||||
}: ChannelBreadcrumbItemProps) => {
|
||||
return (
|
||||
<div className="flex items-center space-x-2">
|
||||
<Avatar
|
||||
id={channel.id}
|
||||
name={channel.attributes.name}
|
||||
avatar={channel.attributes.avatar}
|
||||
className="size-4"
|
||||
/>
|
||||
<span>{channel.attributes.name}</span>
|
||||
</div>
|
||||
<BreadcrumbItem
|
||||
icon={(className) => (
|
||||
<Avatar
|
||||
id={channel.id}
|
||||
name={channel.attributes.name}
|
||||
avatar={channel.attributes.avatar}
|
||||
className={className}
|
||||
/>
|
||||
)}
|
||||
name={channel.attributes.name}
|
||||
/>
|
||||
);
|
||||
};
|
||||
|
||||
@@ -1,14 +1,10 @@
|
||||
import { LocalChannelNode } from '@colanode/client/types';
|
||||
import { ChannelNotFound } from '@colanode/ui/components/channels/channel-not-found';
|
||||
import { ChannelSettings } from '@colanode/ui/components/channels/channel-settings';
|
||||
import { ContainerBreadcrumb } from '@colanode/ui/components/layouts/containers/container-breadrumb';
|
||||
import { Breadcrumb } from '@colanode/ui/components/layouts/breadcrumbs/breadcrumb';
|
||||
import { ContainerSettings } from '@colanode/ui/components/layouts/containers/container-settings';
|
||||
import { Conversation } from '@colanode/ui/components/messages/conversation';
|
||||
import {
|
||||
Container,
|
||||
ContainerBody,
|
||||
ContainerHeader,
|
||||
ContainerSettings,
|
||||
} from '@colanode/ui/components/ui/container';
|
||||
import { NodeBreadcrumb } from '@colanode/ui/components/nodes/node-breadcrumb';
|
||||
import { useNodeContainer } from '@colanode/ui/hooks/use-node-container';
|
||||
import { useNodeRadar } from '@colanode/ui/hooks/use-node-radar';
|
||||
|
||||
@@ -32,20 +28,18 @@ export const ChannelContainer = ({ channelId }: ChannelContainerProps) => {
|
||||
const { node: channel, role } = data;
|
||||
|
||||
return (
|
||||
<Container>
|
||||
<ContainerHeader>
|
||||
<ContainerBreadcrumb breadcrumb={data.breadcrumb} />
|
||||
<ContainerSettings>
|
||||
<ChannelSettings channel={channel} role={role} />
|
||||
</ContainerSettings>
|
||||
</ContainerHeader>
|
||||
<ContainerBody>
|
||||
<Conversation
|
||||
conversationId={channel.id}
|
||||
rootId={channel.rootId}
|
||||
role={role}
|
||||
/>
|
||||
</ContainerBody>
|
||||
</Container>
|
||||
<>
|
||||
<Breadcrumb>
|
||||
<NodeBreadcrumb breadcrumb={data.breadcrumb} />
|
||||
</Breadcrumb>
|
||||
<ContainerSettings>
|
||||
<ChannelSettings channel={channel} role={role} />
|
||||
</ContainerSettings>
|
||||
<Conversation
|
||||
conversationId={channel.id}
|
||||
rootId={channel.rootId}
|
||||
role={role}
|
||||
/>
|
||||
</>
|
||||
);
|
||||
};
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
import { LocalChatNode } from '@colanode/client/types';
|
||||
import { Avatar } from '@colanode/ui/components/avatars/avatar';
|
||||
import { BreadcrumbItem } from '@colanode/ui/components/layouts/breadcrumbs/breadcrumb-item';
|
||||
import { useWorkspace } from '@colanode/ui/contexts/workspace';
|
||||
import { useLiveQuery } from '@colanode/ui/hooks/use-live-query';
|
||||
|
||||
@@ -29,14 +30,16 @@ export const ChatBreadcrumbItem = ({ chat }: ChatBreadcrumbItemProps) => {
|
||||
}
|
||||
|
||||
return (
|
||||
<div className="flex items-center space-x-2">
|
||||
<Avatar
|
||||
id={userGetQuery.data.id}
|
||||
name={userGetQuery.data.name}
|
||||
avatar={userGetQuery.data.avatar}
|
||||
className="size-4"
|
||||
/>
|
||||
<span>{userGetQuery.data.name}</span>
|
||||
</div>
|
||||
<BreadcrumbItem
|
||||
icon={(className) => (
|
||||
<Avatar
|
||||
id={userGetQuery.data!.id}
|
||||
name={userGetQuery.data!.name}
|
||||
avatar={userGetQuery.data!.avatar}
|
||||
className={className}
|
||||
/>
|
||||
)}
|
||||
name={userGetQuery.data!.name}
|
||||
/>
|
||||
);
|
||||
};
|
||||
|
||||
@@ -1,14 +1,10 @@
|
||||
import { LocalChatNode } from '@colanode/client/types';
|
||||
import { ChatNotFound } from '@colanode/ui/components/chats/chat-not-found';
|
||||
import { NodeCollaboratorsPopover } from '@colanode/ui/components/collaborators/node-collaborators-popover';
|
||||
import { ContainerBreadcrumb } from '@colanode/ui/components/layouts/containers/container-breadrumb';
|
||||
import { Breadcrumb } from '@colanode/ui/components/layouts/breadcrumbs/breadcrumb';
|
||||
import { ContainerSettings } from '@colanode/ui/components/layouts/containers/container-settings';
|
||||
import { Conversation } from '@colanode/ui/components/messages/conversation';
|
||||
import {
|
||||
Container,
|
||||
ContainerBody,
|
||||
ContainerHeader,
|
||||
ContainerSettings,
|
||||
} from '@colanode/ui/components/ui/container';
|
||||
import { NodeBreadcrumb } from '@colanode/ui/components/nodes/node-breadcrumb';
|
||||
import { useNodeContainer } from '@colanode/ui/hooks/use-node-container';
|
||||
import { useNodeRadar } from '@colanode/ui/hooks/use-node-radar';
|
||||
|
||||
@@ -32,20 +28,14 @@ export const ChatContainer = ({ chatId }: ChatContainerProps) => {
|
||||
const { node, role } = data;
|
||||
|
||||
return (
|
||||
<Container>
|
||||
<ContainerHeader>
|
||||
<ContainerBreadcrumb breadcrumb={data.breadcrumb} />
|
||||
<ContainerSettings>
|
||||
<NodeCollaboratorsPopover node={node} nodes={[node]} role={role} />
|
||||
</ContainerSettings>
|
||||
</ContainerHeader>
|
||||
<ContainerBody>
|
||||
<Conversation
|
||||
conversationId={node.id}
|
||||
rootId={node.rootId}
|
||||
role={role}
|
||||
/>
|
||||
</ContainerBody>
|
||||
</Container>
|
||||
<>
|
||||
<Breadcrumb>
|
||||
<NodeBreadcrumb breadcrumb={data.breadcrumb} />
|
||||
</Breadcrumb>
|
||||
<ContainerSettings>
|
||||
<NodeCollaboratorsPopover node={node} nodes={[node]} role={role} />
|
||||
</ContainerSettings>
|
||||
<Conversation conversationId={node.id} rootId={node.rootId} role={role} />
|
||||
</>
|
||||
);
|
||||
};
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
import { LocalDatabaseNode } from '@colanode/client/types';
|
||||
import { Avatar } from '@colanode/ui/components/avatars/avatar';
|
||||
import { BreadcrumbItem } from '@colanode/ui/components/layouts/breadcrumbs/breadcrumb-item';
|
||||
|
||||
interface DatabaseBreadcrumbItemProps {
|
||||
database: LocalDatabaseNode;
|
||||
@@ -9,14 +10,16 @@ export const DatabaseBreadcrumbItem = ({
|
||||
database,
|
||||
}: DatabaseBreadcrumbItemProps) => {
|
||||
return (
|
||||
<div className="flex items-center space-x-2">
|
||||
<Avatar
|
||||
id={database.id}
|
||||
name={database.attributes.name}
|
||||
avatar={database.attributes.avatar}
|
||||
className="size-4"
|
||||
/>
|
||||
<span>{database.attributes.name}</span>
|
||||
</div>
|
||||
<BreadcrumbItem
|
||||
icon={(className) => (
|
||||
<Avatar
|
||||
id={database.id}
|
||||
name={database.attributes.name}
|
||||
avatar={database.attributes.avatar}
|
||||
className={className}
|
||||
/>
|
||||
)}
|
||||
name={database.attributes.name}
|
||||
/>
|
||||
);
|
||||
};
|
||||
|
||||
@@ -3,13 +3,9 @@ import { Database } from '@colanode/ui/components/databases/database';
|
||||
import { DatabaseNotFound } from '@colanode/ui/components/databases/database-not-found';
|
||||
import { DatabaseSettings } from '@colanode/ui/components/databases/database-settings';
|
||||
import { DatabaseViews } from '@colanode/ui/components/databases/database-views';
|
||||
import { ContainerBreadcrumb } from '@colanode/ui/components/layouts/containers/container-breadrumb';
|
||||
import {
|
||||
Container,
|
||||
ContainerBody,
|
||||
ContainerHeader,
|
||||
ContainerSettings,
|
||||
} from '@colanode/ui/components/ui/container';
|
||||
import { Breadcrumb } from '@colanode/ui/components/layouts/breadcrumbs/breadcrumb';
|
||||
import { ContainerSettings } from '@colanode/ui/components/layouts/containers/container-settings';
|
||||
import { NodeBreadcrumb } from '@colanode/ui/components/nodes/node-breadcrumb';
|
||||
import { useNodeContainer } from '@colanode/ui/hooks/use-node-container';
|
||||
import { useNodeRadar } from '@colanode/ui/hooks/use-node-radar';
|
||||
|
||||
@@ -33,18 +29,16 @@ export const DatabaseContainer = ({ databaseId }: DatabaseContainerProps) => {
|
||||
const { node: database, role } = data;
|
||||
|
||||
return (
|
||||
<Container>
|
||||
<ContainerHeader>
|
||||
<ContainerBreadcrumb breadcrumb={data.breadcrumb} />
|
||||
<ContainerSettings>
|
||||
<DatabaseSettings database={database} role={role} />
|
||||
</ContainerSettings>
|
||||
</ContainerHeader>
|
||||
<ContainerBody>
|
||||
<Database database={database} role={role}>
|
||||
<DatabaseViews />
|
||||
</Database>
|
||||
</ContainerBody>
|
||||
</Container>
|
||||
<>
|
||||
<Breadcrumb>
|
||||
<NodeBreadcrumb breadcrumb={data.breadcrumb} />
|
||||
</Breadcrumb>
|
||||
<ContainerSettings>
|
||||
<DatabaseSettings database={database} role={role} />
|
||||
</ContainerSettings>
|
||||
<Database database={database} role={role}>
|
||||
<DatabaseViews />
|
||||
</Database>
|
||||
</>
|
||||
);
|
||||
};
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
import { LocalFileNode } from '@colanode/client/types';
|
||||
import { FileThumbnail } from '@colanode/ui/components/files/file-thumbnail';
|
||||
import { BreadcrumbItem } from '@colanode/ui/components/layouts/breadcrumbs/breadcrumb-item';
|
||||
|
||||
interface FileBreadcrumbItemProps {
|
||||
file: LocalFileNode;
|
||||
@@ -7,12 +8,9 @@ interface FileBreadcrumbItemProps {
|
||||
|
||||
export const FileBreadcrumbItem = ({ file }: FileBreadcrumbItemProps) => {
|
||||
return (
|
||||
<div className="flex items-center space-x-2">
|
||||
<FileThumbnail
|
||||
file={file}
|
||||
className="size-4 overflow-hidden rounded object-contain"
|
||||
/>
|
||||
<span>{file.attributes.name}</span>
|
||||
</div>
|
||||
<BreadcrumbItem
|
||||
icon={(className) => <FileThumbnail file={file} className={className} />}
|
||||
name={file.attributes.name}
|
||||
/>
|
||||
);
|
||||
};
|
||||
|
||||
@@ -2,13 +2,9 @@ import { LocalFileNode } from '@colanode/client/types';
|
||||
import { FileBody } from '@colanode/ui/components/files/file-body';
|
||||
import { FileNotFound } from '@colanode/ui/components/files/file-not-found';
|
||||
import { FileSettings } from '@colanode/ui/components/files/file-settings';
|
||||
import { ContainerBreadcrumb } from '@colanode/ui/components/layouts/containers/container-breadrumb';
|
||||
import {
|
||||
Container,
|
||||
ContainerBody,
|
||||
ContainerHeader,
|
||||
ContainerSettings,
|
||||
} from '@colanode/ui/components/ui/container';
|
||||
import { Breadcrumb } from '@colanode/ui/components/layouts/breadcrumbs/breadcrumb';
|
||||
import { ContainerSettings } from '@colanode/ui/components/layouts/containers/container-settings';
|
||||
import { NodeBreadcrumb } from '@colanode/ui/components/nodes/node-breadcrumb';
|
||||
import { useNodeContainer } from '@colanode/ui/hooks/use-node-container';
|
||||
import { useNodeRadar } from '@colanode/ui/hooks/use-node-radar';
|
||||
|
||||
@@ -29,16 +25,14 @@ export const FileContainer = ({ fileId }: FileContainerProps) => {
|
||||
}
|
||||
|
||||
return (
|
||||
<Container>
|
||||
<ContainerHeader>
|
||||
<ContainerBreadcrumb breadcrumb={data.breadcrumb} />
|
||||
<ContainerSettings>
|
||||
<FileSettings file={data.node} role={data.role} />
|
||||
</ContainerSettings>
|
||||
</ContainerHeader>
|
||||
<ContainerBody>
|
||||
<FileBody file={data.node} />
|
||||
</ContainerBody>
|
||||
</Container>
|
||||
<>
|
||||
<Breadcrumb>
|
||||
<NodeBreadcrumb breadcrumb={data.breadcrumb} />
|
||||
</Breadcrumb>
|
||||
<ContainerSettings>
|
||||
<FileSettings file={data.node} role={data.role} />
|
||||
</ContainerSettings>
|
||||
<FileBody file={data.node} />
|
||||
</>
|
||||
);
|
||||
};
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
import { LocalFolderNode } from '@colanode/client/types';
|
||||
import { Avatar } from '@colanode/ui/components/avatars/avatar';
|
||||
import { BreadcrumbItem } from '@colanode/ui/components/layouts/breadcrumbs/breadcrumb-item';
|
||||
|
||||
interface FolderBreadcrumbItemProps {
|
||||
folder: LocalFolderNode;
|
||||
@@ -7,14 +8,16 @@ interface FolderBreadcrumbItemProps {
|
||||
|
||||
export const FolderBreadcrumbItem = ({ folder }: FolderBreadcrumbItemProps) => {
|
||||
return (
|
||||
<div className="flex items-center space-x-2">
|
||||
<Avatar
|
||||
id={folder.id}
|
||||
name={folder.attributes.name}
|
||||
avatar={folder.attributes.avatar}
|
||||
className="size-4"
|
||||
/>
|
||||
<span>{folder.attributes.name}</span>
|
||||
</div>
|
||||
<BreadcrumbItem
|
||||
icon={(className) => (
|
||||
<Avatar
|
||||
id={folder.id}
|
||||
name={folder.attributes.name}
|
||||
avatar={folder.attributes.avatar}
|
||||
className={className}
|
||||
/>
|
||||
)}
|
||||
name={folder.attributes.name}
|
||||
/>
|
||||
);
|
||||
};
|
||||
|
||||
@@ -2,13 +2,9 @@ import { LocalFolderNode } from '@colanode/client/types';
|
||||
import { FolderBody } from '@colanode/ui/components/folders/folder-body';
|
||||
import { FolderNotFound } from '@colanode/ui/components/folders/folder-not-found';
|
||||
import { FolderSettings } from '@colanode/ui/components/folders/folder-settings';
|
||||
import { ContainerBreadcrumb } from '@colanode/ui/components/layouts/containers/container-breadrumb';
|
||||
import {
|
||||
Container,
|
||||
ContainerBody,
|
||||
ContainerHeader,
|
||||
ContainerSettings,
|
||||
} from '@colanode/ui/components/ui/container';
|
||||
import { Breadcrumb } from '@colanode/ui/components/layouts/breadcrumbs/breadcrumb';
|
||||
import { ContainerSettings } from '@colanode/ui/components/layouts/containers/container-settings';
|
||||
import { NodeBreadcrumb } from '@colanode/ui/components/nodes/node-breadcrumb';
|
||||
import { useNodeContainer } from '@colanode/ui/hooks/use-node-container';
|
||||
import { useNodeRadar } from '@colanode/ui/hooks/use-node-radar';
|
||||
|
||||
@@ -32,16 +28,14 @@ export const FolderContainer = ({ folderId }: FolderContainerProps) => {
|
||||
const { node: folder, role } = data;
|
||||
|
||||
return (
|
||||
<Container>
|
||||
<ContainerHeader>
|
||||
<ContainerBreadcrumb breadcrumb={data.breadcrumb} />
|
||||
<ContainerSettings>
|
||||
<FolderSettings folder={folder} role={role} />
|
||||
</ContainerSettings>
|
||||
</ContainerHeader>
|
||||
<ContainerBody>
|
||||
<FolderBody folder={folder} role={role} />
|
||||
</ContainerBody>
|
||||
</Container>
|
||||
<>
|
||||
<Breadcrumb>
|
||||
<NodeBreadcrumb breadcrumb={data.breadcrumb} />
|
||||
</Breadcrumb>
|
||||
<ContainerSettings>
|
||||
<FolderSettings folder={folder} role={role} />
|
||||
</ContainerSettings>
|
||||
<FolderBody folder={folder} role={role} />
|
||||
</>
|
||||
);
|
||||
};
|
||||
|
||||
@@ -0,0 +1,25 @@
|
||||
import { cn } from '@colanode/ui/lib/utils';
|
||||
|
||||
interface BreadcrumbItemProps {
|
||||
icon: (className: string) => React.ReactNode;
|
||||
name: string;
|
||||
className?: string;
|
||||
}
|
||||
|
||||
export const BreadcrumbItem = ({
|
||||
icon,
|
||||
name,
|
||||
className,
|
||||
}: BreadcrumbItemProps) => {
|
||||
return (
|
||||
<div
|
||||
className={cn(
|
||||
'text-muted-foreground flex items-center space-x-2 hover:text-foreground cursor-pointer text-sm',
|
||||
className
|
||||
)}
|
||||
>
|
||||
{icon('size-4')}
|
||||
<span>{name}</span>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
@@ -0,0 +1,21 @@
|
||||
import { useEffect } from 'react';
|
||||
|
||||
import { useContainer } from '@colanode/ui/contexts/container';
|
||||
|
||||
interface BreadcrumbProps {
|
||||
children: React.ReactNode;
|
||||
}
|
||||
|
||||
export const Breadcrumb = ({ children }: BreadcrumbProps) => {
|
||||
const container = useContainer();
|
||||
|
||||
useEffect(() => {
|
||||
container.setBreadcrumb(children);
|
||||
|
||||
return () => {
|
||||
container.resetBreadcrumb();
|
||||
};
|
||||
}, [children]);
|
||||
|
||||
return null;
|
||||
};
|
||||
@@ -0,0 +1,21 @@
|
||||
import { useEffect } from 'react';
|
||||
|
||||
import { useContainer } from '@colanode/ui/contexts/container';
|
||||
|
||||
interface ContainerSettingsProps {
|
||||
children: React.ReactNode;
|
||||
}
|
||||
|
||||
export const ContainerSettings = ({ children }: ContainerSettingsProps) => {
|
||||
const container = useContainer();
|
||||
|
||||
useEffect(() => {
|
||||
container.setSettings(children);
|
||||
|
||||
return () => {
|
||||
container.resetSettings();
|
||||
};
|
||||
}, [children]);
|
||||
|
||||
return null;
|
||||
};
|
||||
34
packages/ui/src/components/layouts/containers/container.tsx
Normal file
34
packages/ui/src/components/layouts/containers/container.tsx
Normal file
@@ -0,0 +1,34 @@
|
||||
import { Outlet } from '@tanstack/react-router';
|
||||
import { useState } from 'react';
|
||||
|
||||
import { SidebarMobile } from '@colanode/ui/components/layouts/sidebars/sidebar-mobile';
|
||||
import { ContainerContext } from '@colanode/ui/contexts/container';
|
||||
import { useIsMobile } from '@colanode/ui/hooks/use-is-mobile';
|
||||
|
||||
export const Container = () => {
|
||||
const isMobile = useIsMobile();
|
||||
const [settings, setSettings] = useState<React.ReactNode>(null);
|
||||
const [breadcrumb, setBreadcrumb] = useState<React.ReactNode>(null);
|
||||
|
||||
return (
|
||||
<ContainerContext.Provider
|
||||
value={{
|
||||
setSettings,
|
||||
setBreadcrumb,
|
||||
resetSettings: () => setSettings(null),
|
||||
resetBreadcrumb: () => setBreadcrumb(null),
|
||||
}}
|
||||
>
|
||||
<div className="flex flex-col w-full h-full min-w-full min-h-full">
|
||||
<div className="flex flex-row w-full items-center gap-2 p-3">
|
||||
{isMobile && <SidebarMobile />}
|
||||
{breadcrumb && <div className="flex-1">{breadcrumb}</div>}
|
||||
{settings}
|
||||
</div>
|
||||
<div className="lg:px-10 px-4 lg:py-4 py-2 flex-grow max-h-full h-full overflow-hidden">
|
||||
<Outlet />
|
||||
</div>
|
||||
</div>
|
||||
</ContainerContext.Provider>
|
||||
);
|
||||
};
|
||||
@@ -1,5 +1,4 @@
|
||||
import { Outlet } from '@tanstack/react-router';
|
||||
|
||||
import { Container } from '@colanode/ui/components/layouts/containers/container';
|
||||
import { SidebarDesktop } from '@colanode/ui/components/layouts/sidebars/sidebar-desktop';
|
||||
import { useIsMobile } from '@colanode/ui/hooks/use-is-mobile';
|
||||
|
||||
@@ -10,7 +9,7 @@ export const Layout = () => {
|
||||
<div className="w-screen min-w-screen h-screen min-h-screen flex flex-row bg-background">
|
||||
{!isMobile && <SidebarDesktop />}
|
||||
<div className="h-full max-h-screen w-full flex-grow overflow-hidden">
|
||||
<Outlet />
|
||||
<Container />
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
|
||||
@@ -1,18 +1,19 @@
|
||||
import { MessageCircle } from 'lucide-react';
|
||||
|
||||
import { LocalMessageNode } from '@colanode/client/types';
|
||||
import { BreadcrumbItem } from '@colanode/ui/components/layouts/breadcrumbs/breadcrumb-item';
|
||||
|
||||
interface MessageBreadcrumbItemProps {
|
||||
message: LocalMessageNode;
|
||||
}
|
||||
|
||||
export const MessageBreadcrumbItem = ({
|
||||
message,
|
||||
message: _,
|
||||
}: MessageBreadcrumbItemProps) => {
|
||||
return (
|
||||
<div className="flex items-center space-x-2" id={message.id}>
|
||||
<MessageCircle className="size-4" />
|
||||
<span>Message</span>
|
||||
</div>
|
||||
<BreadcrumbItem
|
||||
icon={(className) => <MessageCircle className={className} />}
|
||||
name="Message"
|
||||
/>
|
||||
);
|
||||
};
|
||||
|
||||
@@ -1,12 +1,8 @@
|
||||
import { LocalMessageNode } from '@colanode/client/types';
|
||||
import { ContainerBreadcrumb } from '@colanode/ui/components/layouts/containers/container-breadrumb';
|
||||
import { Breadcrumb } from '@colanode/ui/components/layouts/breadcrumbs/breadcrumb';
|
||||
import { Message } from '@colanode/ui/components/messages/message';
|
||||
import { MessageNotFound } from '@colanode/ui/components/messages/message-not-found';
|
||||
import {
|
||||
Container,
|
||||
ContainerBody,
|
||||
ContainerHeader,
|
||||
} from '@colanode/ui/components/ui/container';
|
||||
import { NodeBreadcrumb } from '@colanode/ui/components/nodes/node-breadcrumb';
|
||||
import { ConversationContext } from '@colanode/ui/contexts/conversation';
|
||||
import { useNodeContainer } from '@colanode/ui/hooks/use-node-container';
|
||||
import { useNodeRadar } from '@colanode/ui/hooks/use-node-radar';
|
||||
@@ -29,25 +25,23 @@ export const MessageContainer = ({ messageId }: MessageContainerProps) => {
|
||||
}
|
||||
|
||||
return (
|
||||
<Container>
|
||||
<ContainerHeader>
|
||||
<ContainerBreadcrumb breadcrumb={data.breadcrumb} />
|
||||
</ContainerHeader>
|
||||
<ContainerBody>
|
||||
<ConversationContext.Provider
|
||||
value={{
|
||||
id: data.node.id,
|
||||
role: data.role,
|
||||
rootId: data.node.rootId,
|
||||
canCreateMessage: true,
|
||||
onReply: () => {},
|
||||
onLastMessageIdChange: () => {},
|
||||
canDeleteMessage: () => false,
|
||||
}}
|
||||
>
|
||||
<Message message={data.node} />
|
||||
</ConversationContext.Provider>
|
||||
</ContainerBody>
|
||||
</Container>
|
||||
<>
|
||||
<Breadcrumb>
|
||||
<NodeBreadcrumb breadcrumb={data.breadcrumb} />
|
||||
</Breadcrumb>
|
||||
<ConversationContext.Provider
|
||||
value={{
|
||||
id: data.node.id,
|
||||
role: data.role,
|
||||
rootId: data.node.rootId,
|
||||
canCreateMessage: true,
|
||||
onReply: () => {},
|
||||
onLastMessageIdChange: () => {},
|
||||
canDeleteMessage: () => false,
|
||||
}}
|
||||
>
|
||||
<Message message={data.node} />
|
||||
</ConversationContext.Provider>
|
||||
</>
|
||||
);
|
||||
};
|
||||
|
||||
@@ -9,13 +9,11 @@ import { PageBreadcrumbItem } from '@colanode/ui/components/pages/page-breadcrum
|
||||
import { RecordBreadcrumbItem } from '@colanode/ui/components/records/record-breadcrumb-item';
|
||||
import { SpaceBreadcrumbItem } from '@colanode/ui/components/spaces/space-breadcrumb-item';
|
||||
|
||||
interface ContainerBreadcrumbItemProps {
|
||||
interface NodeBreadcrumbItemProps {
|
||||
node: LocalNode;
|
||||
}
|
||||
|
||||
export const ContainerBreadcrumbItem = ({
|
||||
node,
|
||||
}: ContainerBreadcrumbItemProps) => {
|
||||
export const NodeBreadcrumbItem = ({ node }: NodeBreadcrumbItemProps) => {
|
||||
switch (node.type) {
|
||||
case 'space':
|
||||
return <SpaceBreadcrumbItem space={node} />;
|
||||
@@ -2,7 +2,7 @@ import { Link } from '@tanstack/react-router';
|
||||
import { Fragment } from 'react';
|
||||
|
||||
import { LocalNode } from '@colanode/client/types';
|
||||
import { ContainerBreadcrumbItem } from '@colanode/ui/components/layouts/containers/container-breadcrumb-item';
|
||||
import { NodeBreadcrumbItem } from '@colanode/ui/components/nodes/node-breadcrumb-item';
|
||||
import {
|
||||
Breadcrumb,
|
||||
BreadcrumbEllipsis,
|
||||
@@ -17,13 +17,11 @@ import {
|
||||
DropdownMenuTrigger,
|
||||
} from '@colanode/ui/components/ui/dropdown-menu';
|
||||
|
||||
interface ContainerBreadcrumbProps {
|
||||
interface NodeBreadcrumbProps {
|
||||
breadcrumb: LocalNode[];
|
||||
}
|
||||
|
||||
export const ContainerBreadcrumb = ({
|
||||
breadcrumb,
|
||||
}: ContainerBreadcrumbProps) => {
|
||||
export const NodeBreadcrumb = ({ breadcrumb }: NodeBreadcrumbProps) => {
|
||||
// Show ellipsis if we have more than 3 nodes (first + last two)
|
||||
const showEllipsis = breadcrumb.length > 3;
|
||||
|
||||
@@ -54,7 +52,7 @@ export const ContainerBreadcrumb = ({
|
||||
to="$nodeId"
|
||||
params={{ nodeId: item.id }}
|
||||
>
|
||||
<ContainerBreadcrumbItem node={item} />
|
||||
<NodeBreadcrumbItem node={item} />
|
||||
</Link>
|
||||
</BreadcrumbItem>
|
||||
{showEllipsis && isFirst && (
|
||||
@@ -75,9 +73,7 @@ export const ContainerBreadcrumb = ({
|
||||
params={{ nodeId: ellipsisItem.id }}
|
||||
>
|
||||
<BreadcrumbItem className="cursor-pointer hover:text-foreground">
|
||||
<ContainerBreadcrumbItem
|
||||
node={ellipsisItem}
|
||||
/>
|
||||
<NodeBreadcrumbItem node={ellipsisItem} />
|
||||
</BreadcrumbItem>
|
||||
</Link>
|
||||
</DropdownMenuItem>
|
||||
@@ -1,25 +1,25 @@
|
||||
import { BadgeAlert } from 'lucide-react';
|
||||
|
||||
import {
|
||||
Container,
|
||||
ContainerBody,
|
||||
ContainerHeader,
|
||||
} from '@colanode/ui/components/ui/container';
|
||||
import { Breadcrumb } from '@colanode/ui/components/layouts/breadcrumbs/breadcrumb';
|
||||
import { BreadcrumbItem } from '@colanode/ui/components/layouts/breadcrumbs/breadcrumb-item';
|
||||
|
||||
export const NodeErrorScreen = () => {
|
||||
return (
|
||||
<Container>
|
||||
<ContainerHeader />
|
||||
<ContainerBody>
|
||||
<div className="flex flex-col items-center justify-center h-full p-6 text-center">
|
||||
<BadgeAlert className="size-12 mb-4" />
|
||||
<h1 className="text-2xl font-semibold tracking-tight">Node error</h1>
|
||||
<p className="mt-2 text-sm font-medium text-muted-foreground">
|
||||
The node you are looking for does not exist. It may have been
|
||||
deleted or your access has been removed.
|
||||
</p>
|
||||
</div>
|
||||
</ContainerBody>
|
||||
</Container>
|
||||
<>
|
||||
<Breadcrumb>
|
||||
<BreadcrumbItem
|
||||
icon={(className) => <BadgeAlert className={className} />}
|
||||
name="Node error"
|
||||
/>
|
||||
</Breadcrumb>
|
||||
<div className="flex flex-col items-center justify-center h-full p-6 text-center">
|
||||
<BadgeAlert className="size-12 mb-4" />
|
||||
<h1 className="text-2xl font-semibold tracking-tight">Node error</h1>
|
||||
<p className="mt-2 text-sm font-medium text-muted-foreground">
|
||||
The node you are looking for does not exist. It may have been deleted
|
||||
or your access has been removed.
|
||||
</p>
|
||||
</div>
|
||||
</>
|
||||
);
|
||||
};
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
import { LocalPageNode } from '@colanode/client/types';
|
||||
import { Avatar } from '@colanode/ui/components/avatars/avatar';
|
||||
import { BreadcrumbItem } from '@colanode/ui/components/layouts/breadcrumbs/breadcrumb-item';
|
||||
|
||||
interface PageBreadcrumbItemProps {
|
||||
page: LocalPageNode;
|
||||
@@ -7,14 +8,16 @@ interface PageBreadcrumbItemProps {
|
||||
|
||||
export const PageBreadcrumbItem = ({ page }: PageBreadcrumbItemProps) => {
|
||||
return (
|
||||
<div className="flex items-center space-x-2">
|
||||
<Avatar
|
||||
id={page.id}
|
||||
name={page.attributes.name}
|
||||
avatar={page.attributes.avatar}
|
||||
className="size-4"
|
||||
/>
|
||||
<span>{page.attributes.name}</span>
|
||||
</div>
|
||||
<BreadcrumbItem
|
||||
icon={(className) => (
|
||||
<Avatar
|
||||
id={page.id}
|
||||
name={page.attributes.name}
|
||||
avatar={page.attributes.avatar}
|
||||
className={className}
|
||||
/>
|
||||
)}
|
||||
name={page.attributes.name}
|
||||
/>
|
||||
);
|
||||
};
|
||||
|
||||
@@ -1,14 +1,10 @@
|
||||
import { LocalPageNode } from '@colanode/client/types';
|
||||
import { ContainerBreadcrumb } from '@colanode/ui/components/layouts/containers/container-breadrumb';
|
||||
import { Breadcrumb } from '@colanode/ui/components/layouts/breadcrumbs/breadcrumb';
|
||||
import { ContainerSettings } from '@colanode/ui/components/layouts/containers/container-settings';
|
||||
import { NodeBreadcrumb } from '@colanode/ui/components/nodes/node-breadcrumb';
|
||||
import { PageBody } from '@colanode/ui/components/pages/page-body';
|
||||
import { PageNotFound } from '@colanode/ui/components/pages/page-not-found';
|
||||
import { PageSettings } from '@colanode/ui/components/pages/page-settings';
|
||||
import {
|
||||
Container,
|
||||
ContainerBody,
|
||||
ContainerHeader,
|
||||
ContainerSettings,
|
||||
} from '@colanode/ui/components/ui/container';
|
||||
import { useNodeContainer } from '@colanode/ui/hooks/use-node-container';
|
||||
import { useNodeRadar } from '@colanode/ui/hooks/use-node-radar';
|
||||
|
||||
@@ -31,16 +27,14 @@ export const PageContainer = ({ pageId }: PageContainerProps) => {
|
||||
const { node: page, role } = data;
|
||||
|
||||
return (
|
||||
<Container>
|
||||
<ContainerHeader>
|
||||
<ContainerBreadcrumb breadcrumb={data.breadcrumb} />
|
||||
<ContainerSettings>
|
||||
<PageSettings page={page} role={role} />
|
||||
</ContainerSettings>
|
||||
</ContainerHeader>
|
||||
<ContainerBody>
|
||||
<PageBody page={page} role={role} />
|
||||
</ContainerBody>
|
||||
</Container>
|
||||
<>
|
||||
<Breadcrumb>
|
||||
<NodeBreadcrumb breadcrumb={data.breadcrumb} />
|
||||
</Breadcrumb>
|
||||
<ContainerSettings>
|
||||
<PageSettings page={page} role={role} />
|
||||
</ContainerSettings>
|
||||
<PageBody page={page} role={role} />
|
||||
</>
|
||||
);
|
||||
};
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
import { LocalRecordNode } from '@colanode/client/types';
|
||||
import { Avatar } from '@colanode/ui/components/avatars/avatar';
|
||||
import { BreadcrumbItem } from '@colanode/ui/components/layouts/breadcrumbs/breadcrumb-item';
|
||||
|
||||
interface RecordBreadcrumbItemProps {
|
||||
record: LocalRecordNode;
|
||||
@@ -7,14 +8,16 @@ interface RecordBreadcrumbItemProps {
|
||||
|
||||
export const RecordBreadcrumbItem = ({ record }: RecordBreadcrumbItemProps) => {
|
||||
return (
|
||||
<div className="flex items-center space-x-2">
|
||||
<Avatar
|
||||
id={record.id}
|
||||
name={record.attributes.name}
|
||||
avatar={record.attributes.avatar}
|
||||
className="size-4"
|
||||
/>
|
||||
<span>{record.attributes.name}</span>
|
||||
</div>
|
||||
<BreadcrumbItem
|
||||
icon={(className) => (
|
||||
<Avatar
|
||||
id={record.id}
|
||||
name={record.attributes.name}
|
||||
avatar={record.attributes.avatar}
|
||||
className={className}
|
||||
/>
|
||||
)}
|
||||
name={record.attributes.name}
|
||||
/>
|
||||
);
|
||||
};
|
||||
|
||||
@@ -1,14 +1,10 @@
|
||||
import { LocalRecordNode } from '@colanode/client/types';
|
||||
import { ContainerBreadcrumb } from '@colanode/ui/components/layouts/containers/container-breadrumb';
|
||||
import { Breadcrumb } from '@colanode/ui/components/layouts/breadcrumbs/breadcrumb';
|
||||
import { ContainerSettings } from '@colanode/ui/components/layouts/containers/container-settings';
|
||||
import { NodeBreadcrumb } from '@colanode/ui/components/nodes/node-breadcrumb';
|
||||
import { RecordBody } from '@colanode/ui/components/records/record-body';
|
||||
import { RecordNotFound } from '@colanode/ui/components/records/record-not-found';
|
||||
import { RecordSettings } from '@colanode/ui/components/records/record-settings';
|
||||
import {
|
||||
Container,
|
||||
ContainerBody,
|
||||
ContainerHeader,
|
||||
ContainerSettings,
|
||||
} from '@colanode/ui/components/ui/container';
|
||||
import { useNodeContainer } from '@colanode/ui/hooks/use-node-container';
|
||||
import { useNodeRadar } from '@colanode/ui/hooks/use-node-radar';
|
||||
|
||||
@@ -32,16 +28,14 @@ export const RecordContainer = ({ recordId }: RecordContainerProps) => {
|
||||
const { node: record, role } = data;
|
||||
|
||||
return (
|
||||
<Container>
|
||||
<ContainerHeader>
|
||||
<ContainerBreadcrumb breadcrumb={data.breadcrumb} />
|
||||
<ContainerSettings>
|
||||
<RecordSettings record={record} role={role} />
|
||||
</ContainerSettings>
|
||||
</ContainerHeader>
|
||||
<ContainerBody>
|
||||
<RecordBody record={record} role={role} />
|
||||
</ContainerBody>
|
||||
</Container>
|
||||
<>
|
||||
<Breadcrumb>
|
||||
<NodeBreadcrumb breadcrumb={data.breadcrumb} />
|
||||
</Breadcrumb>
|
||||
<ContainerSettings>
|
||||
<RecordSettings record={record} role={role} />
|
||||
</ContainerSettings>
|
||||
<RecordBody record={record} role={role} />
|
||||
</>
|
||||
);
|
||||
};
|
||||
|
||||
@@ -6,7 +6,6 @@ import { NodeRole, hasNodeRole } from '@colanode/core';
|
||||
import { NodeCollaborators } from '@colanode/ui/components/collaborators/node-collaborators';
|
||||
import { SpaceDelete } from '@colanode/ui/components/spaces/space-delete';
|
||||
import { SpaceForm } from '@colanode/ui/components/spaces/space-form';
|
||||
import { Container, ContainerBody } from '@colanode/ui/components/ui/container';
|
||||
import {
|
||||
ScrollArea,
|
||||
ScrollBar,
|
||||
@@ -30,83 +29,77 @@ export const SpaceBody = ({ space, role }: SpaceBodyProps) => {
|
||||
const canDelete = hasNodeRole(role, 'admin');
|
||||
|
||||
return (
|
||||
<Container>
|
||||
<ContainerBody>
|
||||
<ScrollArea className="relative overflow-hidden">
|
||||
<ScrollViewport className="h-full max-h-[calc(100vh-100px)] w-full overflow-y-auto rounded-[inherit]">
|
||||
<div className="max-w-4xl space-y-8 w-full pb-10">
|
||||
<div className="space-y-6">
|
||||
<div>
|
||||
<h2 className="text-2xl font-semibold tracking-tight">
|
||||
General
|
||||
</h2>
|
||||
<Separator className="mt-3" />
|
||||
</div>
|
||||
<SpaceForm
|
||||
values={{
|
||||
name: space.attributes.name,
|
||||
description: space.attributes.description ?? '',
|
||||
avatar: space.attributes.avatar ?? null,
|
||||
}}
|
||||
readOnly={!canEdit}
|
||||
onSubmit={(values) => {
|
||||
mutate({
|
||||
input: {
|
||||
type: 'space.update',
|
||||
accountId: workspace.accountId,
|
||||
workspaceId: workspace.id,
|
||||
spaceId: space.id,
|
||||
name: values.name,
|
||||
description: values.description,
|
||||
avatar: values.avatar,
|
||||
},
|
||||
onSuccess() {
|
||||
toast.success('Space updated');
|
||||
},
|
||||
onError(error) {
|
||||
toast.error(error.message);
|
||||
},
|
||||
});
|
||||
}}
|
||||
isSaving={isPending}
|
||||
saveText="Update"
|
||||
/>
|
||||
</div>
|
||||
|
||||
<div className="space-y-6">
|
||||
<div>
|
||||
<h2 className="text-2xl font-semibold tracking-tight">
|
||||
Collaborators
|
||||
</h2>
|
||||
<Separator className="mt-3" />
|
||||
</div>
|
||||
<NodeCollaborators node={space} nodes={[space]} role={role} />
|
||||
</div>
|
||||
|
||||
{canDelete && (
|
||||
<div className="space-y-6">
|
||||
<div>
|
||||
<h2 className="text-2xl font-semibold tracking-tight">
|
||||
Danger Zone
|
||||
</h2>
|
||||
<Separator className="mt-3" />
|
||||
</div>
|
||||
<SpaceDelete
|
||||
id={space.id}
|
||||
onDeleted={() => {
|
||||
navigate({
|
||||
to: '/',
|
||||
});
|
||||
}}
|
||||
/>
|
||||
</div>
|
||||
)}
|
||||
<ScrollArea className="relative overflow-hidden">
|
||||
<ScrollViewport className="h-full max-h-[calc(100vh-100px)] w-full overflow-y-auto rounded-[inherit]">
|
||||
<div className="max-w-4xl space-y-8 w-full pb-10">
|
||||
<div className="space-y-6">
|
||||
<div>
|
||||
<h2 className="text-2xl font-semibold tracking-tight">General</h2>
|
||||
<Separator className="mt-3" />
|
||||
</div>
|
||||
</ScrollViewport>
|
||||
<ScrollBar orientation="horizontal" />
|
||||
<ScrollBar orientation="vertical" />
|
||||
</ScrollArea>
|
||||
</ContainerBody>
|
||||
</Container>
|
||||
<SpaceForm
|
||||
values={{
|
||||
name: space.attributes.name,
|
||||
description: space.attributes.description ?? '',
|
||||
avatar: space.attributes.avatar ?? null,
|
||||
}}
|
||||
readOnly={!canEdit}
|
||||
onSubmit={(values) => {
|
||||
mutate({
|
||||
input: {
|
||||
type: 'space.update',
|
||||
accountId: workspace.accountId,
|
||||
workspaceId: workspace.id,
|
||||
spaceId: space.id,
|
||||
name: values.name,
|
||||
description: values.description,
|
||||
avatar: values.avatar,
|
||||
},
|
||||
onSuccess() {
|
||||
toast.success('Space updated');
|
||||
},
|
||||
onError(error) {
|
||||
toast.error(error.message);
|
||||
},
|
||||
});
|
||||
}}
|
||||
isSaving={isPending}
|
||||
saveText="Update"
|
||||
/>
|
||||
</div>
|
||||
|
||||
<div className="space-y-6">
|
||||
<div>
|
||||
<h2 className="text-2xl font-semibold tracking-tight">
|
||||
Collaborators
|
||||
</h2>
|
||||
<Separator className="mt-3" />
|
||||
</div>
|
||||
<NodeCollaborators node={space} nodes={[space]} role={role} />
|
||||
</div>
|
||||
|
||||
{canDelete && (
|
||||
<div className="space-y-6">
|
||||
<div>
|
||||
<h2 className="text-2xl font-semibold tracking-tight">
|
||||
Danger Zone
|
||||
</h2>
|
||||
<Separator className="mt-3" />
|
||||
</div>
|
||||
<SpaceDelete
|
||||
id={space.id}
|
||||
onDeleted={() => {
|
||||
navigate({
|
||||
to: '/',
|
||||
});
|
||||
}}
|
||||
/>
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
</ScrollViewport>
|
||||
<ScrollBar orientation="horizontal" />
|
||||
<ScrollBar orientation="vertical" />
|
||||
</ScrollArea>
|
||||
);
|
||||
};
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
import { LocalSpaceNode } from '@colanode/client/types';
|
||||
import { Avatar } from '@colanode/ui/components/avatars/avatar';
|
||||
import { BreadcrumbItem } from '@colanode/ui/components/layouts/breadcrumbs/breadcrumb-item';
|
||||
|
||||
interface SpaceBreadcrumbItemProps {
|
||||
space: LocalSpaceNode;
|
||||
@@ -7,14 +8,16 @@ interface SpaceBreadcrumbItemProps {
|
||||
|
||||
export const SpaceBreadcrumbItem = ({ space }: SpaceBreadcrumbItemProps) => {
|
||||
return (
|
||||
<div className="flex items-center space-x-2">
|
||||
<Avatar
|
||||
id={space.id}
|
||||
name={space.attributes.name}
|
||||
avatar={space.attributes.avatar}
|
||||
className="size-4"
|
||||
/>
|
||||
<span>{space.attributes.name}</span>
|
||||
</div>
|
||||
<BreadcrumbItem
|
||||
icon={(className) => (
|
||||
<Avatar
|
||||
id={space.id}
|
||||
name={space.attributes.name}
|
||||
avatar={space.attributes.avatar}
|
||||
className={className}
|
||||
/>
|
||||
)}
|
||||
name={space.attributes.name}
|
||||
/>
|
||||
);
|
||||
};
|
||||
|
||||
@@ -1,12 +1,8 @@
|
||||
import { LocalSpaceNode } from '@colanode/client/types';
|
||||
import { ContainerBreadcrumb } from '@colanode/ui/components/layouts/containers/container-breadrumb';
|
||||
import { Breadcrumb } from '@colanode/ui/components/layouts/breadcrumbs/breadcrumb';
|
||||
import { NodeBreadcrumb } from '@colanode/ui/components/nodes/node-breadcrumb';
|
||||
import { SpaceBody } from '@colanode/ui/components/spaces/space-body';
|
||||
import { SpaceNotFound } from '@colanode/ui/components/spaces/space-not-found';
|
||||
import {
|
||||
Container,
|
||||
ContainerBody,
|
||||
ContainerHeader,
|
||||
} from '@colanode/ui/components/ui/container';
|
||||
import { useNodeContainer } from '@colanode/ui/hooks/use-node-container';
|
||||
import { useNodeRadar } from '@colanode/ui/hooks/use-node-radar';
|
||||
|
||||
@@ -30,13 +26,11 @@ export const SpaceContainer = ({ spaceId }: SpaceContainerProps) => {
|
||||
const { node, role } = data;
|
||||
|
||||
return (
|
||||
<Container>
|
||||
<ContainerHeader>
|
||||
<ContainerBreadcrumb breadcrumb={data.breadcrumb} />
|
||||
</ContainerHeader>
|
||||
<ContainerBody>
|
||||
<SpaceBody space={node} role={role} />
|
||||
</ContainerBody>
|
||||
</Container>
|
||||
<>
|
||||
<Breadcrumb>
|
||||
<NodeBreadcrumb breadcrumb={data.breadcrumb} />
|
||||
</Breadcrumb>
|
||||
<SpaceBody space={node} role={role} />
|
||||
</>
|
||||
);
|
||||
};
|
||||
|
||||
@@ -1,69 +0,0 @@
|
||||
import { SidebarMobile } from '@colanode/ui/components/layouts/sidebars/sidebar-mobile';
|
||||
import { useIsMobile } from '@colanode/ui/hooks/use-is-mobile';
|
||||
import { cn } from '@colanode/ui/lib/utils';
|
||||
|
||||
export const Container = ({
|
||||
children,
|
||||
className,
|
||||
...props
|
||||
}: React.HTMLAttributes<HTMLDivElement>) => {
|
||||
return (
|
||||
<div
|
||||
className={cn(
|
||||
'flex flex-col w-full h-full min-w-full min-h-full',
|
||||
className
|
||||
)}
|
||||
{...props}
|
||||
>
|
||||
{children}
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
export const ContainerHeader = ({
|
||||
children,
|
||||
className,
|
||||
...props
|
||||
}: React.HTMLAttributes<HTMLDivElement>) => {
|
||||
const isMobile = useIsMobile();
|
||||
|
||||
return (
|
||||
<div
|
||||
className={cn('flex flex-row w-full items-center gap-2 p-3', className)}
|
||||
{...props}
|
||||
>
|
||||
{isMobile && <SidebarMobile />}
|
||||
{children}
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
export const ContainerBody = ({
|
||||
children,
|
||||
className,
|
||||
...props
|
||||
}: React.HTMLAttributes<HTMLDivElement>) => {
|
||||
return (
|
||||
<div
|
||||
className={cn(
|
||||
'lg:px-10 px-4 lg:py-4 py-2 flex-grow max-h-full h-full overflow-hidden',
|
||||
className
|
||||
)}
|
||||
{...props}
|
||||
>
|
||||
{children}
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
export const ContainerSettings = ({
|
||||
children,
|
||||
className,
|
||||
...props
|
||||
}: React.HTMLAttributes<HTMLDivElement>) => {
|
||||
return (
|
||||
<div className={cn('flex items-center gap-2', className)} {...props}>
|
||||
{children}
|
||||
</div>
|
||||
);
|
||||
};
|
||||
@@ -1,8 +1,10 @@
|
||||
import { Download } from 'lucide-react';
|
||||
import { useState } from 'react';
|
||||
import { InView } from 'react-intersection-observer';
|
||||
|
||||
import { DownloadListManualQueryInput } from '@colanode/client/queries';
|
||||
import { Container, ContainerBody } from '@colanode/ui/components/ui/container';
|
||||
import { Breadcrumb } from '@colanode/ui/components/layouts/breadcrumbs/breadcrumb';
|
||||
import { BreadcrumbItem } from '@colanode/ui/components/layouts/breadcrumbs/breadcrumb-item';
|
||||
import { Separator } from '@colanode/ui/components/ui/separator';
|
||||
import { WorkspaceDownloadFile } from '@colanode/ui/components/workspaces/downloads/workspace-download-file';
|
||||
import { useWorkspace } from '@colanode/ui/contexts/workspace';
|
||||
@@ -32,8 +34,14 @@ export const WorkspaceDownloadsScreen = () => {
|
||||
!isPending && downloads.length === lastPage * DOWNLOADS_PER_PAGE;
|
||||
|
||||
return (
|
||||
<Container>
|
||||
<ContainerBody className="overflow-y-auto">
|
||||
<>
|
||||
<Breadcrumb>
|
||||
<BreadcrumbItem
|
||||
icon={(className) => <Download className={className} />}
|
||||
name="Downloads"
|
||||
/>
|
||||
</Breadcrumb>
|
||||
<div className="overflow-y-auto">
|
||||
<div className="max-w-4xl space-y-10">
|
||||
<div>
|
||||
<h2 className="text-2xl font-semibold tracking-tight">Downloads</h2>
|
||||
@@ -53,7 +61,7 @@ export const WorkspaceDownloadsScreen = () => {
|
||||
}}
|
||||
/>
|
||||
</div>
|
||||
</ContainerBody>
|
||||
</Container>
|
||||
</div>
|
||||
</>
|
||||
);
|
||||
};
|
||||
|
||||
@@ -1,4 +1,7 @@
|
||||
import { Container, ContainerBody } from '@colanode/ui/components/ui/container';
|
||||
import { Cylinder } from 'lucide-react';
|
||||
|
||||
import { Breadcrumb } from '@colanode/ui/components/layouts/breadcrumbs/breadcrumb';
|
||||
import { BreadcrumbItem } from '@colanode/ui/components/layouts/breadcrumbs/breadcrumb-item';
|
||||
import { UserStorageStats } from '@colanode/ui/components/workspaces/storage/user-storage-stats';
|
||||
import { WorkspaceStorageStats } from '@colanode/ui/components/workspaces/storage/workspace-storage-stats';
|
||||
import { useWorkspace } from '@colanode/ui/contexts/workspace';
|
||||
@@ -9,11 +12,17 @@ export const WorkspaceStorageScreen = () => {
|
||||
workspace.role === 'owner' || workspace.role === 'admin';
|
||||
|
||||
return (
|
||||
<Container>
|
||||
<ContainerBody className="max-w-4xl space-y-10">
|
||||
<>
|
||||
<Breadcrumb>
|
||||
<BreadcrumbItem
|
||||
icon={(className) => <Cylinder className={className} />}
|
||||
name="Storage"
|
||||
/>
|
||||
</Breadcrumb>
|
||||
<div className="max-w-4xl space-y-10">
|
||||
<UserStorageStats />
|
||||
{canManageStorage && <WorkspaceStorageStats />}
|
||||
</ContainerBody>
|
||||
</Container>
|
||||
</div>
|
||||
</>
|
||||
);
|
||||
};
|
||||
|
||||
@@ -1,8 +1,10 @@
|
||||
import { Upload } from 'lucide-react';
|
||||
import { useState } from 'react';
|
||||
import { InView } from 'react-intersection-observer';
|
||||
|
||||
import { UploadListQueryInput } from '@colanode/client/queries';
|
||||
import { Container, ContainerBody } from '@colanode/ui/components/ui/container';
|
||||
import { Breadcrumb } from '@colanode/ui/components/layouts/breadcrumbs/breadcrumb';
|
||||
import { BreadcrumbItem } from '@colanode/ui/components/layouts/breadcrumbs/breadcrumb-item';
|
||||
import { Separator } from '@colanode/ui/components/ui/separator';
|
||||
import { WorkspaceUploadFile } from '@colanode/ui/components/workspaces/uploads/workspace-upload-file';
|
||||
import { useWorkspace } from '@colanode/ui/contexts/workspace';
|
||||
@@ -31,8 +33,14 @@ export const WorkspaceUploadsScreen = () => {
|
||||
const hasMore = !isPending && uploads.length === lastPage * UPLOADS_PER_PAGE;
|
||||
|
||||
return (
|
||||
<Container>
|
||||
<ContainerBody className="overflow-y-auto">
|
||||
<>
|
||||
<Breadcrumb>
|
||||
<BreadcrumbItem
|
||||
icon={(className) => <Upload className={className} />}
|
||||
name="Uploads"
|
||||
/>
|
||||
</Breadcrumb>
|
||||
<div className="overflow-y-auto">
|
||||
<div className="max-w-4xl space-y-10">
|
||||
<div>
|
||||
<h2 className="text-2xl font-semibold tracking-tight">Uploads</h2>
|
||||
@@ -52,7 +60,7 @@ export const WorkspaceUploadsScreen = () => {
|
||||
}}
|
||||
/>
|
||||
</div>
|
||||
</ContainerBody>
|
||||
</Container>
|
||||
</div>
|
||||
</>
|
||||
);
|
||||
};
|
||||
|
||||
@@ -1,23 +1,25 @@
|
||||
import {
|
||||
Container,
|
||||
ContainerBody,
|
||||
ContainerHeader,
|
||||
} from '@colanode/ui/components/ui/container';
|
||||
import { Home } from 'lucide-react';
|
||||
|
||||
import { Breadcrumb } from '@colanode/ui/components/layouts/breadcrumbs/breadcrumb';
|
||||
import { BreadcrumbItem } from '@colanode/ui/components/layouts/breadcrumbs/breadcrumb-item';
|
||||
|
||||
export const WorkspaceHomeScreen = () => {
|
||||
return (
|
||||
<Container>
|
||||
<ContainerHeader />
|
||||
<ContainerBody>
|
||||
<div className="h-full w-full flex flex-col gap-1">
|
||||
<div className="h-10 app-drag-region"></div>
|
||||
<div className="flex-grow flex items-center justify-center">
|
||||
<p className="text-sm text-muted-foreground">
|
||||
What did you get done this week?
|
||||
</p>
|
||||
</div>
|
||||
<>
|
||||
<Breadcrumb>
|
||||
<BreadcrumbItem
|
||||
icon={(className) => <Home className={className} />}
|
||||
name="Home"
|
||||
/>
|
||||
</Breadcrumb>
|
||||
<div className="h-full w-full flex flex-col gap-1">
|
||||
<div className="h-10 app-drag-region"></div>
|
||||
<div className="flex-grow flex items-center justify-center">
|
||||
<p className="text-sm text-muted-foreground">
|
||||
What did you get done this week?
|
||||
</p>
|
||||
</div>
|
||||
</ContainerBody>
|
||||
</Container>
|
||||
</div>
|
||||
</>
|
||||
);
|
||||
};
|
||||
|
||||
@@ -1,6 +1,8 @@
|
||||
import { Settings } from 'lucide-react';
|
||||
import { toast } from 'sonner';
|
||||
|
||||
import { Container, ContainerBody } from '@colanode/ui/components/ui/container';
|
||||
import { Breadcrumb } from '@colanode/ui/components/layouts/breadcrumbs/breadcrumb';
|
||||
import { BreadcrumbItem } from '@colanode/ui/components/layouts/breadcrumbs/breadcrumb-item';
|
||||
import { Separator } from '@colanode/ui/components/ui/separator';
|
||||
import { WorkspaceDelete } from '@colanode/ui/components/workspaces/workspace-delete';
|
||||
import { WorkspaceForm } from '@colanode/ui/components/workspaces/workspace-form';
|
||||
@@ -23,8 +25,14 @@ export const WorkspaceSettingsScreen = () => {
|
||||
}
|
||||
|
||||
return (
|
||||
<Container>
|
||||
<ContainerBody className="max-w-4xl space-y-8">
|
||||
<>
|
||||
<Breadcrumb>
|
||||
<BreadcrumbItem
|
||||
icon={(className) => <Settings className={className} />}
|
||||
name="Settings"
|
||||
/>
|
||||
</Breadcrumb>
|
||||
<div className="max-w-4xl space-y-8">
|
||||
<div className="space-y-6">
|
||||
<div>
|
||||
<h2 className="text-2xl font-semibold tracking-tight">General</h2>
|
||||
@@ -69,7 +77,7 @@ export const WorkspaceSettingsScreen = () => {
|
||||
</div>
|
||||
<WorkspaceDelete />
|
||||
</div>
|
||||
</ContainerBody>
|
||||
</Container>
|
||||
</div>
|
||||
</>
|
||||
);
|
||||
};
|
||||
|
||||
@@ -1,10 +1,12 @@
|
||||
import { Users } from 'lucide-react';
|
||||
import { useState } from 'react';
|
||||
import { InView } from 'react-intersection-observer';
|
||||
|
||||
import { UserListQueryInput } from '@colanode/client/queries';
|
||||
import { WorkspaceRole } from '@colanode/core';
|
||||
import { Avatar } from '@colanode/ui/components/avatars/avatar';
|
||||
import { Container, ContainerBody } from '@colanode/ui/components/ui/container';
|
||||
import { Breadcrumb } from '@colanode/ui/components/layouts/breadcrumbs/breadcrumb';
|
||||
import { BreadcrumbItem } from '@colanode/ui/components/layouts/breadcrumbs/breadcrumb-item';
|
||||
import { Separator } from '@colanode/ui/components/ui/separator';
|
||||
import { Spinner } from '@colanode/ui/components/ui/spinner';
|
||||
import { WorkspaceUserInvite } from '@colanode/ui/components/workspaces/workspace-user-invite';
|
||||
@@ -35,8 +37,14 @@ export const WorkspaceUsersScreen = () => {
|
||||
const hasMore = !isPending && users.length === lastPage * USERS_PER_PAGE;
|
||||
|
||||
return (
|
||||
<Container>
|
||||
<ContainerBody className="max-w-4xl space-y-8">
|
||||
<>
|
||||
<Breadcrumb>
|
||||
<BreadcrumbItem
|
||||
icon={(className) => <Users className={className} />}
|
||||
name="Users"
|
||||
/>
|
||||
</Breadcrumb>
|
||||
<div className="max-w-4xl space-y-8">
|
||||
{canEditUsers && (
|
||||
<div className="space-y-6">
|
||||
<div>
|
||||
@@ -94,7 +102,7 @@ export const WorkspaceUsersScreen = () => {
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
</ContainerBody>
|
||||
</Container>
|
||||
</div>
|
||||
</>
|
||||
);
|
||||
};
|
||||
|
||||
14
packages/ui/src/contexts/container.ts
Normal file
14
packages/ui/src/contexts/container.ts
Normal file
@@ -0,0 +1,14 @@
|
||||
import { createContext, useContext } from 'react';
|
||||
|
||||
interface ContainerContext {
|
||||
setSettings: (settings: React.ReactNode) => void;
|
||||
resetSettings: () => void;
|
||||
setBreadcrumb: (breadcrumb: React.ReactNode) => void;
|
||||
resetBreadcrumb: () => void;
|
||||
}
|
||||
|
||||
export const ContainerContext = createContext<ContainerContext>(
|
||||
{} as ContainerContext
|
||||
);
|
||||
|
||||
export const useContainer = () => useContext(ContainerContext);
|
||||
Reference in New Issue
Block a user