Skip to content

Commit

Permalink
mapping page
Browse files Browse the repository at this point in the history
  • Loading branch information
ensaremirerol committed Dec 15, 2024
1 parent 236ce4e commit 123b85c
Show file tree
Hide file tree
Showing 19 changed files with 419 additions and 67 deletions.
14 changes: 14 additions & 0 deletions app/package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

3 changes: 2 additions & 1 deletion app/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@
"react-icons": "^5.4.0",
"react-resizable-panels": "^2.1.7",
"react-router-dom": "7.0.2",
"uuid": "^11.0.3",
"zustand": "5.0.2"
},
"devDependencies": {
Expand All @@ -37,4 +38,4 @@
"typescript-eslint": "8.18.0",
"vite": "6.0.3"
}
}
}
13 changes: 10 additions & 3 deletions app/src/consts/toast.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,14 @@
import { OverlayToaster } from '@blueprintjs/core';
import { createRoot } from 'react-dom/client';

const toast = OverlayToaster.create({
position: 'top',
});
const toast = await OverlayToaster.createAsync(
{
position: 'top',
},
{
domRenderer: (toaster, containerElement) =>
createRoot(containerElement).render(toaster),
},
);

export default toast;
21 changes: 21 additions & 0 deletions app/src/lib/api/mapping_service/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,27 @@ class MappingService {
);
}

public static async getMappingInWorkspace(
workspaceUuid: string,
mappingUuid: string,
): Promise<MappingGraph> {
const result = await this.getApiClient().callApi<MappingGraph>(
`/workspaces/${workspaceUuid}/mapping/${mappingUuid}`,
{
method: 'GET',
parser: data => data as MappingGraph,
},
);

if (result.type === 'success') {
return result.data;
}

throw new Error(
`Failed to get mapping: ${result.message} (status: ${result.status})`,
);
}

public static async createMappingInWorkspace(
workspaceUuid: string,
name: string,
Expand Down
32 changes: 14 additions & 18 deletions app/src/lib/api/mapping_service/types.ts
Original file line number Diff line number Diff line change
@@ -1,43 +1,39 @@
export enum MappingNodeType {
ENTITY = 'entity',
LITERAL = 'literal',
URIRef = 'uri_ref',
}
export type MappingNodeType = 'entity' | 'literal' | 'uri_ref';

export interface MappingNode {
export type MappingNode = {
id: string;
type: MappingNodeType.ENTITY;
type: 'entity';
label: string;
uri_pattern: string;
rdf_type: string[];
}
};

export interface MappingLiteral {
export type MappingLiteral = {
id: string;
type: MappingNodeType.LITERAL;
type: 'literal';
label: string;
value: string;
literal_type: string;
}
};

export interface MappingURIRef {
export type MappingURIRef = {
id: string;
type: MappingNodeType.URIRef;
type: 'uri_ref';
uri_pattern: string;
}
};

export interface MappingEdge {
export type MappingEdge = {
id: string;
source: string;
target: string;
predicate_uri: string;
}
};

export interface MappingGraph {
export type MappingGraph = {
uuid: string;
name: string;
description: string;
source_id: string;
nodes: (MappingNode | MappingLiteral | MappingURIRef)[];
edges: MappingEdge[];
}
};
9 changes: 8 additions & 1 deletion app/src/main.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -2,9 +2,16 @@ import { createRoot } from 'react-dom/client';

import './index.scss';

import React from 'react';
import App from './app';
import ApiService from './lib/services/api_service';

ApiService.registerWithNamespace('default', 'http://localhost:8000/api/');

createRoot(document.getElementById('root')!).render(<App />);
const root = createRoot(document.getElementById('root')!);

root.render(
<React.StrictMode>
<App />
</React.StrictMode>,
);
Empty file.
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
import { Card, H3 } from '@blueprintjs/core';
import { Handle, NodeProps, NodeResizer, Position } from '@xyflow/react';
import { EntityNodeType } from '../types';

const EntityNode: React.FC<NodeProps<EntityNodeType>> = node => {
return (
<>
<NodeResizer
color='blue'
isVisible={node.selected}
nodeId={node.id}
minWidth={200}
minHeight={100}
/>
<Handle type='source' position={Position.Top} id='top' />
<Handle type='target' position={Position.Bottom} id='bottom' />
<Card
style={{
width: node.width,
height: node.height,
display: 'flex',
flexDirection: 'column',
justifyContent: 'center',
alignItems: 'center',
}}
>
<H3>{node.data.label}</H3>
</Card>
<Handle type='source' position={Position.Left} id='left' />
<Handle type='target' position={Position.Right} id='right' />
</>
);
};

export default EntityNode;
100 changes: 96 additions & 4 deletions app/src/pages/mapping_page/components/MainPanel/index.tsx
Original file line number Diff line number Diff line change
@@ -1,10 +1,102 @@
const MainPanel = () => {
import { Menu, MenuItem, showContextMenu } from '@blueprintjs/core';

import {
addEdge,
Background,
Connection,
Controls,
NodeTypes,
ReactFlow,
useEdgesState,
useNodesState,
useReactFlow,
} from '@xyflow/react';

import '@xyflow/react/dist/style.css';
import { useCallback } from 'react';
import { MappingGraph } from '../../../../lib/api/mapping_service/types';
import EntityNode from './components/EntityNode';
import { XYEdgeType, XYNodeTypes } from './types';

type MainPanelProps = {
initialGraph: MappingGraph | null;
};
const nodeTypes: NodeTypes = {
entity: EntityNode,
};

const MainPanel = ({ initialGraph }: MainPanelProps) => {
const reactflow = useReactFlow();

const [nodes, setNodes, onNodesChange] = useNodesState<XYNodeTypes>([]);
const [edges, setEdges, onEdgesChange] = useEdgesState<XYEdgeType>([]);

const onConnect = useCallback(
(params: Connection) => {
setEdges(edges => addEdge(params, edges));
},
[setEdges],
);

const handleAddEntityNode = useCallback(
(e: React.MouseEvent<HTMLAnchorElement, MouseEvent>) => {
setNodes(nodes => [
...nodes,
{
id: `node-${nodes.length}`,
data: {
id: `node-${nodes.length}`,
label: 'New Entity',
rdf_type: [''],
uri_pattern: '',
type: 'entity',
},
width: 200,
height: 100,
position: reactflow.screenToFlowPosition({
x: e.clientX,
y: e.clientY,
}),
type: 'entity',
},
]);
},
[setNodes, reactflow],
);

const openMenu = (e: React.MouseEvent) => {
e.preventDefault();
showContextMenu({
content: (
<Menu>
<MenuItem text='Create Node' onClick={handleAddEntityNode} />
<MenuItem text='Create URI Reference' />
<MenuItem text='Create Literal' />
</Menu>
),
targetOffset: { left: e.clientX, top: e.clientY },
isDarkTheme: true,
});
};

return (
// disable default right click menu
<div className='main-panel'>
<h2>Main Panel</h2>
<p>This area expands fully when the side panel is collapsed.</p>
<ReactFlow
nodes={nodes}
edges={edges}
nodeTypes={nodeTypes}
onConnect={onConnect}
onNodesChange={onNodesChange}
onEdgesChange={onEdgesChange}
colorMode='dark'
onContextMenu={openMenu}
>
<Background bgColor='#1C2127' gap={16} size={1} />
<Controls />
</ReactFlow>
</div>
);
};

export default MainPanel;
export default MainPanel;
13 changes: 13 additions & 0 deletions app/src/pages/mapping_page/components/MainPanel/types.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
import { Edge, Node } from '@xyflow/react';
import {
MappingEdge,
MappingNode,
} from '../../../../lib/api/mapping_service/types';

export type EntityNodeType = Node<MappingNode, 'entity'>;
export type LiteralNodeType = Node<MappingNode, 'literal'>;
export type URIRefNodeType = Node<MappingNode, 'uri_ref'>;

export type XYNodeTypes = EntityNodeType | LiteralNodeType | URIRefNodeType;

export type XYEdgeType = Edge<MappingEdge, 'edge'>;
16 changes: 11 additions & 5 deletions app/src/pages/mapping_page/components/Navbar/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -3,10 +3,12 @@ import { useNavigate } from 'react-router-dom';

type NavbarProps = {
uuid: string | undefined;
mapping_uuid: string | undefined;
name: string | undefined;
isLoading: string | null;
onSave: () => void;
};

const Navbar = ({ uuid, mapping_uuid }: NavbarProps) => {
const Navbar = ({ uuid, name, isLoading, onSave }: NavbarProps) => {
const navigation = useNavigate();

return (
Expand All @@ -20,13 +22,17 @@ const Navbar = ({ uuid, mapping_uuid }: NavbarProps) => {
}}
/>
<div style={{ width: 10 }} />
<BPNavbar.Heading>Workspace: {mapping_uuid}</BPNavbar.Heading>
<BPNavbar.Heading>Mapping: {name}</BPNavbar.Heading>
<BPNavbar.Divider />
<BPNavbar.Heading></BPNavbar.Heading>
<BPNavbar.Heading>
{isLoading ? <>{isLoading}</> : null}
</BPNavbar.Heading>
</BPNavbar.Group>
<BPNavbar.Group align='right'>
<ButtonGroup>
<Button icon='floppy-disk'>Save</Button>
<Button icon='floppy-disk' onClick={onSave}>
Save
</Button>
<Button icon='rocket'>Map</Button>
</ButtonGroup>
</BPNavbar.Group>
Expand Down
2 changes: 2 additions & 0 deletions app/src/pages/mapping_page/components/SidePanel/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,8 @@ const SidePanel = ({ selectedTab }: SidePanelProps) => {
return <div>AI Panel Content</div>;
case 'references':
return <div>Source References Panel Content</div>;
case 'search':
return <div>Search Panel Content</div>;
case 'settings':
return <div>Settings Panel Content</div>;
default:
Expand Down
Loading

0 comments on commit 123b85c

Please sign in to comment.