Skip to content
Snippets Groups Projects

Compare revisions

Changes are shown as if the source revision was being merged into the target revision. Learn more about comparing revisions.

Source

Select target project
No results found

Target

Select target project
  • graphpolaris/frontend-v2
  • rijkheere/frontend-v-2-reordering-paoh
2 results
Show changes
Showing
with 100 additions and 52 deletions
......@@ -20,7 +20,7 @@ export type MockDataI = typeof mockDataArray[number];
export const loadMockData = async (fileName: MockDataI) => {
const json = await import(`./${fileName.replace('_', '/')}.json`);
const json = await import(`./${fileName.replace('_', '/')}.json` /* @vite-ignore */);
const { nodes, edges, metaData } = graphQueryBackend2graphQuery(json.default);
return {
data: {
......
......@@ -4,8 +4,7 @@
* © Copyright Utrecht University (Department of Information and Computing Sciences)
*/
import { type } from 'os';
import { AllLogicStatement, AnyStatement, InputNodeType } from './logic/general';
import { AllLogicStatement, AnyStatement } from './logic/general';
import { MLTypes } from '../../data-access/store/mlSlice';
/** JSON query format used to send a query to the backend. */
......
import { SchemaAttribute } from '../../..';
import { Handles, QueryElementTypes } from '../reactflow';
import { QueryGraphEdgeAttribute, QueryGraphEdgeHandle, QueryGraphNodes } from './model';
......
// import { setQuerybuilderNodes, store } from '@graphpolaris/shared/lib/data-access/store';
import Graph, { MultiGraph } from 'graphology';
import Graph from 'graphology';
import { Attributes as GAttributes, Attributes, SerializedGraph } from 'graphology-types';
import {
EntityNodeAttributes,
......@@ -232,8 +231,6 @@ export class QueryMultiGraphology extends Graph<QueryGraphNodes, QueryGraphEdges
handleType: targetHandleType,
};
// console.log('newEdge', attributes, source, target);
// Add/edit an edge to the graphology object
const edgeResult = this.mergeEdge(source.id, target.id, attributes as QueryGraphEdges);
......
import { NodeAttribute, QueryGraphEdgeHandle, QueryGraphNodes } from './graphology';
import { NodeAttribute, QueryGraphEdgeHandle } from './graphology';
import { InputNodeType } from './logic/general';
import { Handles, QueryElementTypes, SchemaReactflowNode } from './reactflow';
......
import {
GeneralDescription,
InputNodeType,
NumberFunctionTypes,
NumberFilterTypes,
StringFilterTypes,
......
......@@ -4,7 +4,6 @@
* © Copyright Utrecht University (Department of Information and Computing Sciences)
*/
import { Position } from 'reactflow';
import { GeneralDescription, NumberAggregationTypes } from './general';
export const MathAggregations: Record<NumberAggregationTypes, GeneralDescription<NumberAggregationTypes>> = {
......
......@@ -4,8 +4,7 @@
* © Copyright Utrecht University (Department of Information and Computing Sciences)
*/
import { Position } from 'reactflow';
import { GeneralDescription, InputNode, NumberFilterTypes } from './general';
import { GeneralDescription, NumberFilterTypes } from './general';
export const MathFilters: Record<NumberFilterTypes, GeneralDescription<NumberFilterTypes>> = {
[NumberFilterTypes.EQUAL]: {
......
......@@ -4,7 +4,6 @@
* © Copyright Utrecht University (Department of Information and Computing Sciences)
*/
import { Position } from 'reactflow';
import { GeneralDescription, NumberFunctionTypes } from './general';
export const NumberFunctions: Record<NumberFunctionTypes, GeneralDescription<NumberFunctionTypes>> = {
......
......@@ -4,7 +4,6 @@
* © Copyright Utrecht University (Department of Information and Computing Sciences)
*/
import { Position } from 'reactflow';
import { GeneralDescription, StringFilterTypes } from './general';
export const StringFilters: Record<StringFilterTypes, GeneralDescription<StringFilterTypes>> = {
......
......@@ -4,7 +4,6 @@
* © Copyright Utrecht University (Department of Information and Computing Sciences)
*/
import { Position } from 'reactflow';
import { GeneralDescription, StringFunctionTypes } from './general';
export const StringFunctions: Record<StringFunctionTypes, GeneralDescription<StringFunctionTypes>> = {
......
......@@ -9,7 +9,6 @@
* Possible handles for an entity node.
*/
// import { FunctionArgTypes } from '../logic/graphFunctions';
import { SchemaReactflowNode, QueryElementTypes } from './model';
/** Links need handles to what they are connected to (and which side) */
export enum Handles {
......
import {
useConfig,
useGraphQueryResult,
useQuerybuilderGraph,
useQuerybuilderHash,
useQuerybuilderSettings,
......@@ -8,7 +7,8 @@ import {
useSchemaInference,
useSearchResultQB,
} from '@graphpolaris/shared/lib/data-access/store';
import { clearQB, setQuerybuilderGraphology, toQuerybuilderGraphology } from '@graphpolaris/shared/lib/data-access/store/querybuilderSlice';
import { useCheckPermissionPolicy } from '@graphpolaris/shared/lib/data-access';
import { setQuerybuilderGraphology, toQuerybuilderGraphology } from '@graphpolaris/shared/lib/data-access/store/querybuilderSlice';
import React, { useCallback, useEffect, useMemo, useRef, useState } from 'react';
import { useDispatch } from 'react-redux';
import ReactFlow, {
......@@ -20,7 +20,6 @@ import ReactFlow, {
NodeChange,
NodePositionChange,
OnConnectStartParams,
OnEdgesChange,
ReactFlowInstance,
ReactFlowProvider,
isNode,
......@@ -83,6 +82,28 @@ export const QueryBuilderInner = (props: QueryBuilderProps) => {
const searchResults = useSearchResultQB();
const reactFlowInstanceRef = useRef<ReactFlowInstance | null>(null);
const [allowZoom, setAllowZoom] = useState(true);
const { canRead, canWrite } = useCheckPermissionPolicy();
const [readAllowed, setReadAllowed] = useState(false);
const [writeAllowed, setWriteAllowed] = useState(false);
const resource = 'query';
const checkReadPermission = useCallback(async () => {
const result = await canRead(resource);
setReadAllowed(result);
}, [canRead]);
const checkWritePermission = useCallback(async () => {
const result = await canWrite(resource);
setWriteAllowed(result);
}, [canWrite]);
useEffect(() => {
checkReadPermission();
}, [checkReadPermission]);
useEffect(() => {
checkWritePermission();
}, [checkWritePermission]);
useEffect(() => {
const searchResultKeys = new Set([...searchResults.nodes.map((node) => node.key), ...searchResults.edges.map((edge) => edge.key)]);
......@@ -120,9 +141,7 @@ export const QueryBuilderInner = (props: QueryBuilderProps) => {
* TODO?
*/
function onNodesChange(nodes: NodeChange[]) {
// console.log(nodes);
nodes.forEach((n) => {
// console.log(graphologyGraph.findNode((gn) => gn === n?.id));
if (n.type !== 'position') {
// updated = true;
// graphologyGraph.updateAttributes(n.id, n.data);
......@@ -223,6 +242,12 @@ export const QueryBuilderInner = (props: QueryBuilderProps) => {
* @param event Drag event.
*/
const onDrop = (event: React.DragEvent<HTMLDivElement>): void => {
if (!writeAllowed) {
console.debug('User blocked from editing query due to being a viewer');
event.preventDefault();
return;
}
event.preventDefault();
// The dropped element should be a valid reactflow element
......@@ -240,7 +265,6 @@ export const QueryBuilderInner = (props: QueryBuilderProps) => {
switch (dragData.type) {
case QueryElementTypes.Entity:
// console.log('entity drop', dragData, schema.getNodeAttribute(dragData.name, 'attributes'));
graphologyGraph.addPill2Graphology(
{
type: QueryElementTypes.Entity,
......@@ -347,20 +371,15 @@ export const QueryBuilderInner = (props: QueryBuilderProps) => {
};
const onNodeMouseEnter = (event: React.MouseEvent, node: Node) => {
// console.log(node);
// console.log(schema.getNodeAttribute(node.type, 'attributes'));
setAllowZoom(false);
};
const onNodeMouseLeave = (event: React.MouseEvent, node: Node) => {
// console.log(node);
setAllowZoom(true);
};
const onConnect = useCallback(
(connection: Connection) => {
console.log(connection);
if (!isEdgeUpdating.current) {
isOnConnect.current = true;
if (!connection.sourceHandle || !connection.targetHandle) throw new Error('Connection has no source or target');
......@@ -380,7 +399,6 @@ export const QueryBuilderInner = (props: QueryBuilderProps) => {
const onConnectStart = useCallback(
(event: React.MouseEvent | React.TouchEvent, params: OnConnectStartParams) => {
// console.log('onConnectStart', params);
if (!params?.handleId) return;
let node = graphologyGraph.getNodeAttributes(params.nodeId);
......@@ -456,10 +474,6 @@ export const QueryBuilderInner = (props: QueryBuilderProps) => {
[graph],
);
const onEdgesChange = (params: OnEdgesChange) => {
console.log('edges change', params);
};
const onEdgeUpdateEnd = useCallback(
(event: MouseEvent | TouchEvent, edge: Edge, handleType: HandleType) => {
if (isEdgeUpdating.current) {
......
import React, { createContext } from 'react';
import { SchemaReactflowEntityNode, SchemaReactflowLogicNode } from '../model';
import { SchemaReactflowLogicNode } from '../model';
import { OnConnectStartParams } from 'reactflow';
export const QueryBuilderDispatcherContext = createContext<{
......
import React, { useMemo, useState } from 'react';
import React, { useMemo } from 'react';
import { ControlContainer, TooltipProvider, Tooltip, TooltipTrigger, Button, TooltipContent, Input } from '../../components';
import { Popover, PopoverTrigger, PopoverContent } from '../../components/layout/Popover';
import { useAppDispatch, useGraphQueryResult, useQuerybuilderSettings, useSchemaStats } from '../../data-access';
import { clearQB, QueryBuilderSettings, setQuerybuilderSettings } from '../../data-access/store/querybuilderSlice';
import { useAppDispatch, useGraphQueryResult, useML, useQuerybuilderSettings } from '../../data-access';
import { clearQB, setQuerybuilderSettings } from '../../data-access/store/querybuilderSlice';
import { QueryMLDialog } from './querysidepanel/QueryMLDialog';
import { QuerySettings } from './querysidepanel/QuerySettings';
......@@ -22,6 +22,7 @@ export const QueryBuilderNav = (props: QueryBuilderNavProps) => {
const qb = useQuerybuilderSettings();
const result = useGraphQueryResult();
const resultSize = useMemo(() => (result ? result.edges.length : 0), [result]);
const ml = useML();
/**
* Clears all nodes in the graph.
......@@ -30,6 +31,8 @@ export const QueryBuilderNav = (props: QueryBuilderNavProps) => {
dispatch(clearQB());
}
const mlEnabled = ml.linkPrediction.enabled || ml.centrality.enabled || ml.communityDetection.enabled || ml.shortestPath.enabled;
return (
<div className="sticky shrink-0 top-0 flex items-stretch justify-between h-7 bg-secondary-100 border-b border-secondary-200 max-w-full">
<div className="flex items-center">
......@@ -147,10 +150,58 @@ export const QueryBuilderNav = (props: QueryBuilderNavProps) => {
<PopoverTrigger>
<Tooltip>
<TooltipTrigger>
<Button variantType="secondary" variant="ghost" size="xs" iconComponent="icon-[ic--baseline-lightbulb]" />
<Button
variantType={mlEnabled ? 'primary' : 'secondary'}
variant="ghost"
size="xs"
iconComponent="icon-[ic--baseline-lightbulb]"
className={mlEnabled ? 'border-primary-600' : ''}
/>
</TooltipTrigger>
<TooltipContent disabled={props.toggleSettings === 'ml'}>
<p>Machine learning</p>
{mlEnabled ? (
<>
<p className="font-bold text-base">Machine learning</p>
<p className="mb-2">Algorithms detected the following results:</p>
{ml.linkPrediction.enabled && ml.linkPrediction.result && (
<>
<p className="mt-2 font-semibold">Link prediction</p>
<p>{ml.linkPrediction.result.length} links</p>{' '}
</>
)}
{ml.centrality.enabled && Object.values(ml.centrality.result).length > 0 && (
<>
<p className="mt-2 font-semibold">Centrality</p>
<p>
{Object.values(ml.centrality.result)
.reduce((a, b) => b + a)
.toFixed(2)}{' '}
sum of centers
</p>
</>
)}
{ml.communityDetection.enabled && ml.communityDetection.result && (
<>
<p className="mt-2 font-semibold">Community detection</p>
<p>{ml.communityDetection.result.length} communities</p>
</>
)}
{ml.shortestPath.enabled && (
<>
<p className="mt-2 font-semibold">Shortest path</p>
{ml.shortestPath.result?.length > 0 && <p># of hops: {ml.shortestPath.result.length}</p>}
{!ml.shortestPath.srcNode ? (
<p>Please select source node</p>
) : (
!ml.shortestPath.trtNode && <p>Please select target node</p>
)}
</>
)}
</>
) : (
<p>Machine learning</p>
)}
</TooltipContent>
</Tooltip>
</PopoverTrigger>
......@@ -177,18 +228,18 @@ export const QueryBuilderNav = (props: QueryBuilderNavProps) => {
<Tooltip>
<TooltipTrigger>
<Button
variantType={qb.limit <= resultSize ? 'danger' : 'secondary'}
variantType={qb.limit <= resultSize ? 'primary' : 'secondary'}
variant="ghost"
size="xs"
iconComponent="icon-[ic--baseline-filter-alt]"
className={qb.limit <= resultSize ? 'border-danger-600' : ''}
className={qb.limit <= resultSize ? 'border-primary-600' : ''}
/>
</TooltipTrigger>
<TooltipContent disabled={props.toggleSettings === 'ml'}>
<p className="font-bold text-base">Limit</p>
<p>Limits the number of edges retrieved from the database.</p>
<p>Required to manage performance.</p>
<p className={`font-semibold${qb.limit <= resultSize ? ' text-danger-800' : ''}`}>
<p className={`font-semibold${qb.limit <= resultSize ? ' text-primary-800' : ''}`}>
Fetched {resultSize} of a maximum of {qb.limit} edges
</p>
</TooltipContent>
......
......@@ -5,8 +5,7 @@ import { useQuerybuilderGraph, useQuerybuilderSettings, useSchemaGraph } from '@
import { toQuerybuilderGraphology, setQuerybuilderGraphology } from '@graphpolaris/shared/lib/data-access/store/querybuilderSlice';
import { useDispatch } from 'react-redux';
import { toSchemaGraphology } from '@graphpolaris/shared/lib/data-access/store/schemaSlice';
import { SchemaAttribute, SchemaEdge, SchemaNode } from '@graphpolaris/shared/lib/schema';
import { schemaExpandRelation } from '@graphpolaris/shared/lib/schema/schema-utils';
import { SchemaEdge, SchemaNode } from '@graphpolaris/shared/lib/schema';
export const QueryBuilderRelatedNodesPanel = (props: {
reactFlowWrapper: HTMLDivElement | null;
......
......@@ -6,7 +6,7 @@ import {
setLinkPredictionEnabled,
setShortestPathEnabled,
} from '@graphpolaris/shared/lib/data-access/store/mlSlice';
import { FormDiv, FormCard, FormBody, FormTitle, FormHBar } from '@graphpolaris/shared/lib/components/forms';
import { FormCard, FormBody, FormTitle, FormHBar } from '@graphpolaris/shared/lib/components/forms';
import { Input } from '@graphpolaris/shared/lib/components/inputs';
export const QueryMLDialog = () => {
......
......@@ -3,7 +3,7 @@ import React from 'react';
import { useAppDispatch, useQuerybuilderSettings } from '../../../data-access';
import { QueryBuilderSettings, setQuerybuilderSettings } from '../../../data-access/store/querybuilderSlice';
import { addWarning } from '../../../data-access/store/configSlice';
import { FormActions, FormBody, FormCard, FormControl, FormDiv, FormHBar, FormTitle } from '../../../components/forms';
import { FormActions } from '../../../components/forms';
import { Layouts } from '@graphpolaris/shared/lib/graph-layout';
import { Input } from '@graphpolaris/shared/lib/components/inputs';
......
......@@ -2,7 +2,6 @@ import React from 'react';
import { Meta } from '@storybook/react';
import { Provider } from 'react-redux';
import { setQuerybuilderGraph, setSchema, store } from '@graphpolaris/shared/lib/data-access/store';
import { SchemaUtils } from '@graphpolaris/shared/lib/schema/schema-utils';
import { Schema } from '@graphpolaris/shared/lib/schema/panel';
import { movieSchemaRaw } from '@graphpolaris/shared/lib/mock-data';
......
import React from 'react';
import { querybuilderSlice, setQuerybuilderGraph, setSchema, store } from '@graphpolaris/shared/lib/data-access/store';
import { configureStore } from '@reduxjs/toolkit';
import { setQuerybuilderGraph, setSchema, store } from '@graphpolaris/shared/lib/data-access/store';
import { Meta } from '@storybook/react';
import { Provider } from 'react-redux';
import QueryBuilder from '../QueryBuilder';
import { Handles, NodeAttribute, QueryElementTypes, QueryMultiGraphology } from '../../model';
import { QueryElementTypes, QueryMultiGraphology } from '../../model';
import { SchemaUtils } from '../../../schema/schema-utils';
const Component: Meta<typeof QueryBuilder> = {
......@@ -137,7 +135,6 @@ export const Simple = {
// );
graph.addEdge2Graphology(entity1, relation1);
graph.addEdge2Graphology(relation1, entity2);
// console.log(graph.getNodeAttributes('2'));
// graph.addEdge('2', '1', { type: 'attribute_connection' });
// graph.addEdge('3', '1', { type: 'attribute_connection' });
// graph.addEdge('4', '0', { type: 'attribute_connection' });
......