Implement workspace switching

This commit is contained in:
2024-10-26 23:15:23 +02:00
parent fd313c1d7f
commit 12312137b7
8 changed files with 189 additions and 91 deletions

View File

@@ -1,6 +1,6 @@
import React from 'react';
import { Group, Text, Avatar } from '@mantine/core';
import WorkspaceMenu from './WorkspaceMenu';
import WorkspaceSwitcher from './WorkspaceSwitcher';
import Settings from './Settings';
const Header = () => {
@@ -10,8 +10,8 @@ const Header = () => {
NovaMD
</Text>
<Group>
<WorkspaceSwitcher />
<Avatar src="https://via.placeholder.com/40" radius="xl" />
<WorkspaceMenu />
</Group>
<Settings />
</Group>

View File

@@ -1,77 +0,0 @@
import React from 'react';
import { Menu, ActionIcon, Text } from '@mantine/core';
import {
IconFolders,
IconFolderPlus,
IconSwitchHorizontal,
IconSettings,
IconTrash,
IconPencil,
IconFileExport,
IconFileImport,
} from '@tabler/icons-react';
import { useModalContext } from '../contexts/ModalContext';
import { useWorkspace } from '../contexts/WorkspaceContext';
const WorkspaceMenu = () => {
const { setSettingsModalVisible } = useModalContext();
const { currentWorkspace } = useWorkspace();
const openSettings = () => setSettingsModalVisible(true);
return (
<Menu shadow="md" width={200} position="bottom-end">
<Menu.Target>
<ActionIcon variant="subtle" size="lg">
<IconFolders size={24} />
</ActionIcon>
</Menu.Target>
<Menu.Dropdown>
<Menu.Label>Current Workspace</Menu.Label>
<Menu.Item disabled>
<Text size="sm" truncate>
{currentWorkspace?.name || 'No workspace selected'}
</Text>
</Menu.Item>
<Menu.Divider />
<Menu.Label>Workspace Actions</Menu.Label>
<Menu.Item leftSection={<IconFolderPlus size={14} />}>
Create Workspace
</Menu.Item>
<Menu.Item leftSection={<IconSwitchHorizontal size={14} />}>
Switch Workspace
</Menu.Item>
<Menu.Item leftSection={<IconPencil size={14} />}>
Rename Workspace
</Menu.Item>
<Menu.Item leftSection={<IconTrash size={14} />} color="red">
Delete Workspace
</Menu.Item>
<Menu.Divider />
<Menu.Label>Data Management</Menu.Label>
<Menu.Item leftSection={<IconFileExport size={14} />}>
Export Workspace
</Menu.Item>
<Menu.Item leftSection={<IconFileImport size={14} />}>
Import Workspace
</Menu.Item>
<Menu.Divider />
<Menu.Item
leftSection={<IconSettings size={14} />}
onClick={openSettings}
>
Workspace Settings
</Menu.Item>
</Menu.Dropdown>
</Menu>
);
};
export default WorkspaceMenu;

View File

@@ -0,0 +1,136 @@
import React, { useState } from 'react';
import {
Box,
Popover,
Stack,
Paper,
ScrollArea,
Group,
UnstyledButton,
Text,
Loader,
Center,
Button,
ActionIcon,
} from '@mantine/core';
import { IconFolders, IconSettings, IconFolderPlus } from '@tabler/icons-react';
import { useWorkspace } from '../contexts/WorkspaceContext';
import { useModalContext } from '../contexts/ModalContext';
import { listWorkspaces } from '../services/api';
const WorkspaceSwitcher = () => {
const { currentWorkspace, switchWorkspace } = useWorkspace();
const { setSettingsModalVisible } = useModalContext();
const [workspaces, setWorkspaces] = useState([]);
const [loading, setLoading] = useState(false);
const [popoverOpened, setPopoverOpened] = useState(false);
const loadWorkspaces = async () => {
setLoading(true);
try {
const list = await listWorkspaces();
setWorkspaces(list);
} catch (error) {
console.error('Failed to load workspaces:', error);
}
setLoading(false);
};
return (
<Popover
width={300}
position="bottom-start"
shadow="md"
opened={popoverOpened}
onChange={setPopoverOpened}
>
<Popover.Target>
<UnstyledButton
onClick={() => {
setPopoverOpened((o) => !o);
if (!popoverOpened) {
loadWorkspaces();
}
}}
>
<Group gap="xs">
<IconFolders size={20} />
<div>
<Text size="sm" fw={500}>
{currentWorkspace?.name || 'No workspace'}
</Text>
</div>
</Group>
</UnstyledButton>
</Popover.Target>
<Popover.Dropdown>
<Text size="sm" fw={600} mb="md">
Switch Workspace
</Text>
<ScrollArea.Autosize mah={400} mb="md" offsetScrollbars>
<Stack gap="xs">
{loading ? (
<Center p="md">
<Loader size="sm" />
</Center>
) : (
workspaces.map((workspace) => (
<UnstyledButton
key={workspace.id}
onClick={() => {
switchWorkspace(workspace.id);
setPopoverOpened(false);
}}
>
<Paper
p="xs"
withBorder={workspace.id === currentWorkspace?.id}
bg={
workspace.id === currentWorkspace?.id
? 'var(--mantine-color-blue-light)'
: undefined
}
>
<Group justify="space-between" wrap="nowrap">
<Box>
<Text size="sm" fw={500} truncate>
{workspace.name}
</Text>
<Text size="xs" c="dimmed">
{new Date(workspace.createdAt).toLocaleDateString()}
</Text>
</Box>
{workspace.id === currentWorkspace?.id && (
<ActionIcon
variant="subtle"
size="sm"
onClick={(e) => {
e.stopPropagation();
setSettingsModalVisible(true);
setPopoverOpened(false);
}}
>
<IconSettings size={14} />
</ActionIcon>
)}
</Group>
</Paper>
</UnstyledButton>
))
)}
</Stack>
</ScrollArea.Autosize>
<Button
variant="light"
leftSection={<IconFolderPlus size={14} />}
fullWidth
>
Create Workspace
</Button>
</Popover.Dropdown>
</Popover>
);
};
export default WorkspaceSwitcher;