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
Commits on Source (12)
Showing
with 211 additions and 66 deletions
......@@ -7,6 +7,7 @@ import {
useQuerybuilderSettings,
useSessionCache,
} from '@graphpolaris/shared/lib/data-access';
import { setCurrentTheme } from '@graphpolaris/shared/lib/data-access/store/configSlice';
import { resetGraphQueryResults, queryingBackend } from '@graphpolaris/shared/lib/data-access/store/graphQueryResultSlice';
import { Query2BackendQuery, QueryMultiGraph } from '@graphpolaris/shared/lib/querybuilder';
import { Navbar } from '../components/navbar/navbar';
......@@ -52,6 +53,10 @@ export function App(props: App) {
}
}, [props]);
window.matchMedia('(prefers-color-scheme: dark)').addEventListener('change', event => {
dispatch(setCurrentTheme(event.matches));
});
const [authCheck, setAuthCheck] = useState(false);
const [tab, setTab] = useState<SideNavTab>('Schema');
// const [visFullSize, setVisFullSize] = useState<boolean>(false);
......
import React, { useEffect, useState } from 'react';
import { useAppDispatch, useSchemaGraph, useSessionCache, useAuthorizationCache } from '@graphpolaris/shared/lib/data-access';
import React, { useEffect, useState, useCallback } from 'react';
import {
useAppDispatch,
useSchemaGraph,
useSessionCache,
useAuthorizationCache,
useCheckPermissionPolicy,
} from '@graphpolaris/shared/lib/data-access';
import { deleteSaveState, selectSaveState } from '@graphpolaris/shared/lib/data-access/store/sessionSlice';
import { SettingsForm } from './forms/settings';
import { LoadingSpinner } from '@graphpolaris/shared/lib/components/LoadingSpinner';
......@@ -21,7 +27,6 @@ export default function DatabaseSelector({}) {
const [dbSelectionMenuOpen, setDbSelectionMenuOpen] = useState<boolean>(false);
const [settingsMenuOpen, setSettingsMenuOpen] = useState<'add' | 'update' | undefined>(undefined);
const [selectedSaveState, setSelectedSaveState] = useState<SaveStateI | null>(null);
// const [addDbConnectionFormOpen, setAddDbConnectionFormOpen] = useState<boolean>(false);
useEffect(() => {
if (
......@@ -56,6 +61,29 @@ export default function DatabaseSelector({}) {
};
}, [connecting]);
const { canRead, canWrite } = useCheckPermissionPolicy();
const [readAllowed, setReadAllowed] = useState(false);
const [writeAllowed, setWriteAllowed] = useState(false);
const resource = 'database';
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]);
return (
<div className="menu-walkthrough">
<TooltipProvider delayDuration={1000}>
......@@ -85,11 +113,14 @@ export default function DatabaseSelector({}) {
>
<DropdownTrigger
onClick={() => {
setDbSelectionMenuOpen(!dbSelectionMenuOpen);
if (connecting || authCache.authorized === false || !!authCache.roomID || writeAllowed) {
console.debug('User blocked from editing query due to being a viewer');
setDbSelectionMenuOpen(!dbSelectionMenuOpen);
}
}}
className="w-[18rem]"
size="md"
disabled={connecting || authCache.authorized === false || !!authCache.roomID}
disabled={connecting || authCache.authorized === false || !!authCache.roomID || !writeAllowed}
title={
<div className="flex items-center">
{connecting && session.currentSaveState && session.currentSaveState in session.saveStates ? (
......
import React, { useEffect, useState } from 'react';
import {
DatabaseInfo,
DatabaseType,
SaveStateI,
databaseNameMapping,
databaseProtocolMapping,
nilUUID,
} from '@graphpolaris/shared/lib/data-access';
import { DatabaseType, SaveStateI, databaseNameMapping, databaseProtocolMapping, nilUUID } from '@graphpolaris/shared/lib/data-access';
import { Input } from '@graphpolaris/shared/lib/components/inputs';
import { useImmer } from 'use-immer';
import { initialState as qbInitialState } from '@graphpolaris/shared/lib/data-access/store/querybuilderSlice';
......
import React from 'react';
import { DatabaseInfo } from '@graphpolaris/shared/lib/data-access';
import { DatabaseType, SaveStateI, nilUUID } from '@graphpolaris/shared/lib/data-access/broker';
import { initialState as qbInitialState } from '@graphpolaris/shared/lib/data-access/store/querybuilderSlice';
......
......@@ -8,18 +8,24 @@
/* The comment above was added so the code coverage wouldn't count this file towards code coverage.
* We do not test components/renderfunctions/styling files.
* See testing plan for more details.*/
import React, { useState, useRef, useEffect } from 'react';
import { useAuthorizationCache, useAuth } from '@graphpolaris/shared/lib/data-access';
import React, { useState, useRef, useEffect, useCallback } from 'react';
import { useAuthorizationCache, useAuth, useCheckPermissionPolicy } from '@graphpolaris/shared/lib/data-access';
import DatabaseSelector from './DatabaseManagement/dbConnectionSelector';
import { DropdownItem } from '@graphpolaris/shared/lib/components/dropdowns';
import GpLogo from './gp-logo';
import { Popover, PopoverContent, PopoverTrigger } from '@graphpolaris/shared/lib/components/layout/Popover';
import { useDispatch } from 'react-redux';
import { Dialog, DialogContent, DialogTrigger } from '@graphpolaris/shared/lib/components/layout/Dialog';
import { UserManagementContent } from '@graphpolaris/shared/lib/components/userManagementContent/UserManagementContent';
import { addInfo } from '@graphpolaris/shared/lib/data-access/store/configSlice';
export const Navbar = () => {
const dropdownRef = useRef<HTMLDivElement>(null);
const auth = useAuth();
const authCache = useAuthorizationCache();
const [menuOpen, setMenuOpen] = useState(false);
const dispatch = useDispatch();
const buildInfo = import.meta.env.GRAPHPOLARIS_VERSION;
useEffect(() => {
const handleClickOutside = (event: MouseEvent) => {
......@@ -33,8 +39,37 @@ export const Navbar = () => {
};
}, [menuOpen]);
const buildInfo = import.meta.env.GRAPHPOLARIS_VERSION;
const { canRead, canWrite } = useCheckPermissionPolicy();
const [readAllowed, setReadAllowed] = useState(false);
const [writeAllowed, setWriteAllowed] = useState(false);
const resource = 'policy';
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]);
const handleConfirmUsers = (users: { name: string; email: string; type: string }[]) => {
//TODO !FIXME: when the user clicks on confirm, users state is ready to be sent to backend
};
const handleClickShareLink = () => {
//TODO !FIXME: add copy link to clipoard functionality
dispatch(addInfo('Link copied to clipboard'));
};
return (
<nav className="w-full px-4 h-12 flex flex-row items-center gap-2 md:gap-3 lg:gap-4">
<a href="https://graphpolaris.com/" target="_blank" className="shrink-0 text-dark">
......@@ -53,12 +88,12 @@ export const Navbar = () => {
</div>
</PopoverTrigger>
<PopoverContent className="w-56 z-30 bg-white rounded-sm border-[1px] outline-none">
<PopoverContent className="w-56 z-30 bg-light rounded-sm border-[1px] outline-none">
<div className="p-2 text-sm border-b">
<h2 className="font-bold">user: {authCache.username}</h2>
<h3 className="text-xs break-words">session: {authCache.sessionID}</h3>
<h3 className="text-xs break-words">license: Creator</h3>
</div>
{authCache.authorized ? (
<>
<DropdownItem
......@@ -75,7 +110,6 @@ export const Navbar = () => {
<DropdownItem value="Login" onClick={() => {}} />
</>
)}
{authCache?.roomID && (
<div className="p-2 border-b">
<h3 className="text-xs break-words">Share ID: {authCache.roomID}</h3>
......@@ -84,6 +118,20 @@ export const Navbar = () => {
<div className="p-2 border-t">
<h3 className="text-xs">Version: {buildInfo}</h3>
</div>
{writeAllowed && (
<>
<Dialog>
<DialogTrigger className="ml-2 text-sm hover:bg-secondary-200">Manage Viewers Permission</DialogTrigger>
<DialogContent>
<UserManagementContent
sessionId={authCache.sessionID ?? ''}
onConfirm={handleConfirmUsers}
onClickShareLink={handleClickShareLink}
/>
</DialogContent>
</Dialog>
</>
)}
</PopoverContent>
</Popover>
</div>
......
import React, { ReactNode } from 'react';
import { Tooltip, TooltipTrigger, TooltipContent, TooltipProvider } from '@graphpolaris/shared/lib/components/tooltip';
export type VisualizationTooltipProps = {
name: string;
......@@ -9,7 +8,7 @@ export type VisualizationTooltipProps = {
export const VisualizationTooltip: React.FC<VisualizationTooltipProps> = ({ name, colorHeader, children }) => {
return (
<div className="border-1 border-sec-200 bg-white w-[12rem] -mx-2 -my-2">
<div className="border-1 border-sec-200 bg-light w-[12rem] -mx-2 -my-2">
<div className="flex m-0 justify-start items-stretch border-b border-sec-200 relative">
<div className="left-0 top-0 h-auto w-1.5" style={{ backgroundColor: colorHeader }}></div>
<div className="px-2.5 py-1 truncate flex">
......
import React, { ReactElement, ReactPropTypes, useMemo } from 'react';
import React, { useMemo } from 'react';
import styles from './buttons.module.scss';
import { Icon, Sizes } from '../icon';
import { forwardRef } from 'react';
import { Icon } from '../icon';
import { Tooltip, TooltipContent, TooltipTrigger } from '../tooltip';
type ButtonProps = {
......
// BarPlot.stories.tsx
import React from 'react';
import { Meta } from '@storybook/react';
import AxisComponent, { AxisComponentProps } from '.';
import AxisComponent from '.';
import { scaleLinear } from 'd3';
export default {
......
import React from 'react';
import { Meta } from '@storybook/react';
import ColorLegendCat, { LegendProps } from '.';
import ColorLegendCat from '.';
export default {
title: 'Visual charts/Charts/ColorLegendCat',
......
import { axisBottom, scaleLinear, select } from 'd3';
import React, { useEffect, useRef } from 'react';
//import { tailwindColors, dataColors, divergenceColors, categoricalColors } from './../../../../../config/src/colors.js';
//import { tailwindColors, dataColors, divergenceColors, categoricalColors } from '@graphpolaris/config/colors.js';
export type ColorLegendSeqDivProps = {
colors: string[];
......@@ -15,9 +13,6 @@ export const ColorLegendSeqDiv = ({ colors, data, tickCount = 5, name }: ColorLe
useEffect(() => {
if (!svgRef.current) return;
console.log(colors);
//console.log(divergenceColors.blueRed);
const widthSVG: number = +svgRef.current.clientWidth;
const heightSVG: number = +svgRef.current.clientHeight;
......
export * from './barplot';
export * from './colorLegendCat';
export * from './colorLegendSeqDiv';
import React from 'react';
import { Meta } from '@storybook/react';
import Scatterplot, { ScatterplotProps, VisualRegionConfig, regionData, DataPoint } from '.';
import Scatterplot, { VisualRegionConfig, regionData, DataPoint } from '.';
import { scaleLinear } from 'd3';
const Component: Meta<typeof Scatterplot> = {
......
import React, { useEffect, useRef } from 'react';
import * as d3 from 'd3';
//import { VisualRegionConfig, regionData, DataPoint, DataPointXY } from './types';
import * as PIXI from 'pixi.js';
export interface ScatterplotProps {
......@@ -69,7 +68,6 @@ const Scatterplot: React.FC<ScatterplotProps> = ({ data, visualConfig, xScale, o
const yScale = d3.scaleLinear().domain([-1, 1]).range([visualConfig.heightMargin, 0]);
console.log(visualConfig);
// PIXI
const app = new PIXI.Application({
width: visualConfig.width,
......@@ -103,8 +101,6 @@ const Scatterplot: React.FC<ScatterplotProps> = ({ data, visualConfig, xScale, o
d.gfx = graphics;
});
console.log('DONE SIMULATION');
const svg = d3
.select(svgRef.current)
.attr('width', visualConfig.width)
......
import React, { useState, useEffect } from 'react';
import { Button } from '../buttons'; // Adjust the import path according to your project structure
import React, { useEffect } from 'react';
import { Button } from '../buttons';
import { Tooltip, TooltipContent, TooltipProvider, TooltipTrigger } from '../../components/tooltip';
import { setTheme, Theme } from '@graphpolaris/shared/lib/data-access/store/configSlice';
import { useAppDispatch, useConfig } from '@graphpolaris/shared/lib/data-access/store';
......@@ -23,14 +23,15 @@ const ColorMode = () => {
// Function to toggle the theme
const toggleTheme = () => {
const themes = [
Theme.system,
Theme.light,
Theme.dark,
]
];
const newTheme = themes[(themes.indexOf(config.theme) + 1) % themes.length];
dispatch(setTheme(newTheme));
};
const iconComponent = config.theme === Theme.dark ? 'icon-[ic--baseline-dark-mode]' : 'icon-[ic--baseline-light-mode]';
const iconComponent = config.theme === Theme.dark ? 'icon-[ic--baseline-dark-mode]' : config.theme === Theme.light ? 'icon-[ic--baseline-light-mode]' : 'icon-[ic--baseline-auto-mode]';
return (
<TooltipProvider delayDuration={0}>
......@@ -39,7 +40,7 @@ const ColorMode = () => {
<Button variant="ghost" size="sm" iconComponent={iconComponent} onClick={toggleTheme} />
</TooltipTrigger>
<TooltipContent>
<p>{`Switch to ${config.theme === Theme.dark ? 'light' : 'dark'}-mode`}</p>
<p>{`Currently on ${config.theme.split('-')[0]} theme`}</p>
</TooltipContent>
</Tooltip>
</TooltipProvider>
......
import React, { useState, useEffect, useRef, ReactNode } from 'react';
import React, { useState, useRef, ReactNode } from 'react';
import { Icon } from '../icon';
import { PopoverContent, PopoverTrigger, Popover, PopoverOptions } from '../layout/Popover';
import { space } from 'postcss/lib/list';
export const DropdownContainer = ({ children, ...props }: { children: React.ReactNode } & PopoverOptions) => {
return (
......
......@@ -3,7 +3,6 @@ import styles from './inputs.module.scss';
import { DropdownTrigger, DropdownContainer, DropdownItem, DropdownItemContainer } from '../dropdowns';
import Info from '../info';
import { Tooltip, TooltipContent, TooltipProvider, TooltipTrigger } from '../tooltip';
import { Popover } from '../layout/Popover';
import { Button } from '../buttons';
type SliderProps = {
......@@ -78,6 +77,11 @@ type BooleanProps = {
value: boolean;
tooltip?: string;
onChange?: (value: boolean) => void;
size?: 'xs' | 'sm' | 'md' | 'xl';
info?: string;
required?: boolean;
className?: string;
inline?: boolean;
};
type RadioProps = {
......@@ -106,7 +110,15 @@ type DropdownProps = {
onChange?: (value: number | string) => void;
};
export type InputProps = TextProps | SliderProps | CheckboxProps | DropdownProps | RadioProps | BooleanProps | NumberProps;
export type InputProps =
| TextProps
| SliderProps
| CheckboxProps
| DropdownProps
| RadioProps
| BooleanProps
| NumberProps
| ToggleSwitchProps;
export const Input = (props: InputProps) => {
switch (props.type) {
......@@ -124,6 +136,8 @@ export const Input = (props: InputProps) => {
return <BooleanInput {...(props as BooleanProps)} />;
case 'number':
return <NumberInput {...(props as NumberProps)} />;
case 'toggle':
return <ToggleSwitchInput {...(props as ToggleSwitchProps)} />;
default:
return null;
}
......@@ -407,23 +421,31 @@ export const CheckboxInput = ({ label, value, options, onChange, tooltip }: Chec
);
};
export const BooleanInput = ({ label, value, onChange, tooltip }: BooleanProps) => {
export const BooleanInput = ({ label, value, onChange, tooltip, info, size, required, className }: BooleanProps) => {
return (
<Tooltip>
<TooltipTrigger>
<label className={`label cursor-pointer w-fit gap-2 px-0 py-1`}>
<span className="text-sm">{label}</span>
<input
type="checkbox"
checked={value}
onChange={(event) => {
if (onChange) {
onChange(event.target.checked);
}
}}
className="checkbox checkbox-xs"
/>
</label>
<TooltipTrigger className={className + 'w-full flex justify-between'}>
{label && (
<label className="label p-0">
<span
className={`text-${size} text-left truncate font-medium text-secondary-700 ${required && "after:content-['*'] after:ml-0.5 after:text-danger-500"}`}
>
{label}
</span>
{info && <Info tooltip={info} />}
</label>
)}
<input
type="checkbox"
checked={value}
onChange={(event) => {
if (onChange) {
onChange(event.target.checked);
}
}}
className="checkbox checkbox-xs"
aria-label={`Toggle ${label}`}
/>
</TooltipTrigger>
{tooltip && <TooltipContent>{tooltip}</TooltipContent>}
</Tooltip>
......@@ -523,3 +545,42 @@ export const DropdownInput = ({
</Tooltip>
);
};
type ToggleSwitchProps = {
label: string;
type: 'toggle';
value: boolean;
classText?: string;
tooltip?: string;
onChange?: (value: boolean) => void;
};
export const ToggleSwitchInput = ({ label, classText, value, tooltip, onChange }: ToggleSwitchProps) => {
const [isSelected, setIsSelected] = useState(value);
const handleClick = () => {
const newValue = !isSelected;
setIsSelected(newValue);
if (onChange) {
onChange(newValue);
}
};
return (
<Tooltip>
<TooltipTrigger>
<div className="flex items-center space-x-2">
<div
className={`relative flex w-10 h-5 rounded-full cursor-pointer transition-all duration-500 ${isSelected ? 'bg-secondary-800' : 'bg-secondary-300'}`}
onClick={handleClick}
>
<div
className={`absolute top-0 left-0 w-5 h-5 rounded-full items-center bg-white transition-all duration-500 shadow-lg border-1 border-gray ${isSelected ? 'translate-x-full' : 'translate-x-0'} `}
></div>
</div>
<span className={classText}>{label}</span>
</div>
</TooltipTrigger>
{tooltip && <TooltipContent>{tooltip}</TooltipContent>}
</Tooltip>
);
};
import React from 'react';
import type { Meta, StoryObj } from '@storybook/react';
import { ToggleSwitchInput } from '.';
const Component: Meta<typeof ToggleSwitchInput> = {
title: 'Components/Inputs',
component: ToggleSwitchInput,
argTypes: { onChange: {} },
decorators: [(Story) => <div className="w-52 m-5">{Story()}</div>],
};
export default Component;
type Story = StoryObj<typeof Component>;
export const ToggleSwitchInputStory: Story = {
args: {
type: 'toggle',
label: 'Toggle Switch component',
value: false,
onChange: (value) => {},
},
};
import * as React from 'react';
import { useEffect, useRef } from 'react';
import {
useFloating,
useClick,
......
import React, { useEffect, useState } from 'react';
import React from 'react';
import { ControlContainer } from '..';
export type Panel = {
......
......@@ -3,7 +3,7 @@
import React, { useState } from 'react';
import type { Meta, StoryObj } from '@storybook/react';
import { Popover, PopoverTrigger, PopoverContent, PopoverHeading, PopoverDescription, PopoverClose } from './Popover'; // Adjust the path based on your component location
import { Popover, PopoverTrigger, PopoverContent, PopoverHeading, PopoverDescription, PopoverClose } from './Popover';
import { Icon } from '../icon';
const metaPopover: Meta<typeof Popover> = {
component: Popover,
......