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 (10)
Showing
with 162 additions and 51 deletions
......@@ -13,8 +13,9 @@ import { useAuthCache, useAuthentication } from '@graphpolaris/shared/lib/data-a
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 { showSharableExploration } from 'config';
import { Button, useActiveSaveStateAuthorization } from '@graphpolaris/shared';
import { useDispatch } from 'react-redux';
import { getEnvVariable, showManagePermissions, showSharableExploration } from 'config';
import { Button, Dialog, DialogContent, DialogTrigger, useActiveSaveStateAuthorization, useSessionCache } from '@graphpolaris/shared';
import { ManagementTrigger, ManagementViews } from '@graphpolaris/shared/lib/management';
const AuthURL = import.meta.env.GP_AUTH_URL;
......@@ -25,7 +26,7 @@ export const Navbar = () => {
const authCache = useAuthCache();
const authorization = useActiveSaveStateAuthorization();
const [menuOpen, setMenuOpen] = useState(false);
const buildInfo = import.meta.env.GRAPHPOLARIS_VERSION;
const buildInfo = getEnvVariable('GRAPHPOLARIS_VERSION');
const [managementOpen, setManagementOpen] = useState<boolean>(false);
const [current, setCurrent] = useState<ManagementViews>('overview');
......@@ -88,7 +89,7 @@ export const Navbar = () => {
<DropdownItem
value="Log out"
onClick={() => {
location.replace(`${AuthURL}/outpost.goauthentik.io/sign_out`);
location.replace(`${getEnvVariable('GP_AUTH_URL')}/outpost.goauthentik.io/sign_out`);
}}
/>
</>
......
......@@ -4,6 +4,7 @@ import { useLocation } from 'react-router-dom';
import { Button } from '@graphpolaris/shared/lib/components/buttons';
import { useCases } from './use-cases';
import { useAuthCache } from '@graphpolaris/shared/lib/data-access';
import { OnboardingTooltip } from './tooltip';
interface OnboardingState {
run?: boolean;
......@@ -54,7 +55,7 @@ export function Onboarding({}) {
return (
<div>
{showWalkthrough && (
<div className="bg-accent-light alert absolute bottom-5 left-5 w-fit cursor-pointer z-50">
<div className="bg-light alert absolute bottom-5 left-5 w-fit cursor-pointer z-50">
<Button onClick={startWalkThrough} label={'Start a Tour'} variant="ghost" />
<Button onClick={() => addWalkthroughCookie()} iconComponent="icon-[ic--baseline-close]" variant="ghost" rounded />
</div>
......@@ -68,6 +69,7 @@ export function Onboarding({}) {
showSkipButton={true}
hideCloseButton={true}
callback={handleJoyrideCallback}
tooltipComponent={OnboardingTooltip}
styles={{
options: {
primaryColor: '#FF7D00',
......
import React from 'react';
import { TooltipRenderProps } from 'react-joyride';
const OnboardingTooltip = (props: TooltipRenderProps) => {
const { backProps, closeProps, continuous, index, primaryProps, skipProps, step, tooltipProps } = props;
return (
<div className="bg-light p-4 rounded-lg shadow-lg" {...tooltipProps}>
<button className="absolute top-2 right-2 text-secondary-500 hover:text-secondary-700" {...closeProps}>
&times;
</button>
{step.title && <h4 className="text-lg font-semibold mb-2">{step.title}</h4>}
<div className="mb-4">{step.content}</div>
<div className="flex justify-between items-center">
<button className="text-sm text-gray-500 hover:text-gray-700" {...skipProps}>
{skipProps.title}
</button>
<div className="flex space-x-2">
{index > 0 && (
<button className="bg-light-200 text-light-700 px-3 py-1 rounded hover:bg-light-300" {...backProps}>
{backProps.title}
</button>
)}
{continuous && (
<button className="bg-primary text-light px-3 py-1 rounded hover:bg-primary-dark" {...primaryProps}>
{primaryProps.title}
</button>
)}
</div>
</div>
</div>
);
};
export { OnboardingTooltip };
......@@ -48,7 +48,7 @@ export const generalScript: GPStep[] = [
disableBeacon: true,
},
{
target: '.menu-walkthrough',
target: '.database-menu',
title: 'Menu',
content: 'In the menu you can manage databases, switch between them, and perform other actions.',
placement: 'bottom',
......
......@@ -5,11 +5,13 @@ import * as Sentry from '@sentry/react';
import { store } from '@graphpolaris/shared/lib/data-access/store';
import App from './app/App';
import { createRoot } from 'react-dom/client';
import { getEnvVariable } from 'config';
import './main.css';
import { ErrorBoundary } from '@graphpolaris/shared/lib/components/errorBoundary';
if (import.meta.env.SENTRY_ENABLED) {
if (getEnvVariable('SENTRY_ENABLED')) {
Sentry.init({
dsn: import.meta.env.SENTRY_URL,
dsn: getEnvVariable('SENTRY_URL'),
integrations: [Sentry.browserTracingIntegration(), Sentry.replayIntegration()],
tracesSampleRate: 1.0,
replaysSessionSampleRate: 0.1,
......@@ -25,10 +27,12 @@ if (domNode) {
root.render(
<Provider store={store}>
<Router>
<Routes>
<Route path="/" element={<App load={undefined} />}></Route>
<Route path="/fraud" element={<App load="5bdf3354-673f-4dec-b6a0-196e67cd211c" />}></Route>
</Routes>
<ErrorBoundary fallback={<div>Oops! Something went wrong. Please try again.</div>}>
<Routes>
<Route path="/" element={<App load={undefined} />}></Route>
<Route path="/fraud" element={<App load="5bdf3354-673f-4dec-b6a0-196e67cd211c" />}></Route>
</Routes>
</ErrorBoundary>
</Router>
</Provider>,
);
......
// Safely retrieve environment variable values with a default fallback
const getEnvVariable = (key: string, defaultValue: string = 'false'): string => {
return import.meta.env[key] ?? defaultValue;
const envFallbacks: Record<string, any> = {
SENTRY_ENABLED: false,
SENTRY_URL: '',
GRAPHPOLARIS_VERSION: 'prod',
GP_AUTH_URL: '',
BACKEND_WSS_URL: '',
BACKEND_URL: '',
BACKEND_USER: '',
WIP_TABLEVIS: true,
WIP_NODELINKVIS: true,
WIP_RAWJSONVIS: true,
WIP_PAOHVIS: true,
WIP_MATRIXVIS: true,
WIP_SEMANTICSUBSTRATESVIS: true,
WIP_MAPVIS: true,
WIP_INSIGHT_SHARING: true,
WIP_VIEWER_PERMISSIONS: true,
WIP_SHARABLE_EXPLORATION: true,
};
// Check if the environment is production
const isProduction = (): boolean => {
return getEnvVariable('GRAPHPOLARIS_VERSION', 'dev') === 'prod';
};
type EnvFallbackKey = keyof typeof envFallbacks;
// Check if the Manage Permissions feature is enabled
const showManagePermissions = (): boolean => {
return !isProduction() || (isProduction() && getEnvVariable('WIP_VIEWER_PERMISSIONS') === 'false');
// Safely retrieve environment variable values with a default fallback
const getEnvVariable = (key: EnvFallbackKey): any => {
const value = import.meta.env[key];
if (value === undefined) {
console.error(`Environment variable ${key} is missing, using fallback value.`);
return envFallbacks[key];
}
return value;
};
// Check if the Insight Sharing feature is enabled
const showInsightSharing = (): boolean => {
return !isProduction() || (isProduction() && getEnvVariable('WIP_INSIGHT_SHARING') === 'false');
// Check if the environment is production
const isProduction = (): boolean => {
return getEnvVariable('GRAPHPOLARIS_VERSION') === 'prod';
};
// Check if the Insight Sharing feature is enabled
const showSharableExploration = (): boolean => {
return !isProduction() || (isProduction() && getEnvVariable('WIP_SHARABLE_EXPLORATION') === 'false');
// Utility to check if a WIP feature is enabled
const isWIPFeatureEnabled = (featureKey: string): boolean => {
return getEnvVariable(`WIP_${featureKey.toUpperCase()}`) === 'false';
};
// Utility to check if a specific visualization is released based on environment variables
const isVisualizationReleased = (visualizationName: string): boolean => {
const visualizationFlag = getEnvVariable(`WIP_${visualizationName.toUpperCase()}`, 'false');
return !isProduction() || (isProduction() && visualizationFlag === 'false');
};
// Feature flags with checks for production and WIP feature flags
const showManagePermissions = (): boolean => !isProduction() || isWIPFeatureEnabled('viewer_permissions');
const showInsightSharing = (): boolean => !isProduction() || isWIPFeatureEnabled('insight_sharing');
const showSharableExploration = (): boolean => !isProduction() || isWIPFeatureEnabled('sharable_exploration');
const isVisualizationReleased = (visualizationName: string): boolean => !isProduction() || isWIPFeatureEnabled(visualizationName);
export { isProduction, showManagePermissions, showInsightSharing, showSharableExploration, isVisualizationReleased };
export { getEnvVariable, isProduction, showManagePermissions, showInsightSharing, showSharableExploration, isVisualizationReleased };
......@@ -13,6 +13,7 @@ const IconMap: Record<SchemaAttributeTypes, string> = {
duration: 'icon-[carbon--calendar]',
number: 'icon-[carbon--string-integer]',
location: 'icon-[carbon--location]',
array: 'icon-[ic--baseline-data-array]',
};
function getDataTypeIcon(data_type?: SchemaAttributeTypes): string {
......
......@@ -130,7 +130,7 @@ export function DropdownItem({ value, disabled, className, onClick, submenu, sel
onMouseLeave={() => setIsSubmenuOpen(false)}
>
{value}
{submenu != null ? <Icon component='icon-[ic--baseline-arrow-right] ms-2' size={14} /> : ''}
{submenu != null ? <Icon component="icon-[ic--baseline-arrow-right] ms-2" size={14} /> : ''}
{submenu && isSubmenuOpen && <DropdownSubmenuContainer ref={submenuRef}>{submenu}</DropdownSubmenuContainer>}
{children}
</li>
......@@ -143,7 +143,11 @@ type DropdownSubmenuContainerProps = {
export const DropdownSubmenuContainer = React.forwardRef<HTMLDivElement, DropdownSubmenuContainerProps>(({ children }, ref) => {
return (
<div ref={ref} className="absolute bg-light p-1 rounded max-h-60 overflow-auto focus:outline-none shadow-sm border left-[97%]" style={{transform: 'translate(0px, calc(50% - 19px))'}}>
<div
ref={ref}
className="absolute bg-light p-1 rounded max-h-60 overflow-auto focus:outline-none shadow-sm border left-[95%]"
style={{ transform: 'translate(0px, calc(50% - 19px))' }}
>
<ul className="text-sm text-secondary-700">{children}</ul>
</div>
);
......
......@@ -448,7 +448,7 @@ export const CheckboxInput = ({ label, value, options, onChange, tooltip }: Chec
export const BooleanInput = ({ label, value, onChange, tooltip, info, size, required, className }: BooleanProps) => {
return (
<Tooltip>
<TooltipTrigger className={className + 'w-full flex justify-between'}>
<TooltipTrigger className={className + ' w-full flex justify-between'}>
{label && (
<label className="label p-0">
<span
......
......@@ -5,11 +5,12 @@ export type Panel = {
title: string | React.ReactNode;
tooltips?: React.ReactNode;
children: React.ReactNode;
className?: string;
};
export function Panel(props: Panel) {
return (
<div className="flex flex-col border w-full h-full bg-light">
<div className={`flex flex-col border w-full h-full bg-light ${props.className || ''}`}>
<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">
<h1 className="text-xs font-semibold text-secondary-600 px-2 truncate">{props.title}</h1>
......
......@@ -4,6 +4,7 @@
* © Copyright Utrecht University (Department of Information and Computing Sciences)
*/
import { getEnvVariable } from 'config';
import { UseIsAuthorizedState } from '../store/authSlice';
import { ReceiveMessageI, SendMessageI, SendMessageWithSessionI } from './types';
......@@ -38,7 +39,7 @@ export class Broker {
/** Get the singleton instance of the Broker. */
public static instance(): Broker {
if (!this.singletonInstance) this.singletonInstance = new Broker(import.meta.env.BACKEND_WSS_URL);
if (!this.singletonInstance) this.singletonInstance = new Broker(getEnvVariable('BACKEND_WSS_URL'));
return this.singletonInstance;
}
......
import { getEnvVariable } from 'config';
import { useAppDispatch, useAuthCache } from '../store';
import { authenticated, changeRoom, UserAuthenticationHeader } from '../store/authSlice';
const domain = import.meta.env.BACKEND_URL;
const userURI = import.meta.env.BACKEND_USER;
const domain = getEnvVariable('BACKEND_URL');
const userURI = getEnvVariable('BACKEND_USER');
export const fetchSettings: RequestInit = {
method: 'GET',
......
......@@ -9,9 +9,10 @@ import { SelectionConfig } from '../vis/components/config/SelectionConfig';
import { SchemaSettings } from '../schema/panel/SchemaSettings';
import { QuerySettings } from '../querybuilder/panel/querysidepanel/QuerySettings';
import { useActiveVisualization } from '@graphpolaris/shared/lib/data-access';
import { getEnvVariable } from 'config';
export function InspectorPanel(props: { children?: React.ReactNode }) {
const buildInfo = import.meta.env.GRAPHPOLARIS_VERSION;
const buildInfo = getEnvVariable('GRAPHPOLARIS_VERSION');
const selection = useSelection();
const focus = useFocus();
const dispatch = useDispatch();
......
......@@ -41,7 +41,7 @@ export function ManagementTrigger({ managementOpen, setManagementOpen, current,
}, [connecting]);
return (
<div>
<div className="database-menu">
<ManagementDialog
open={managementOpen}
onClose={() => setManagementOpen(!managementOpen)}
......
......@@ -9,6 +9,7 @@ const mockDataArray = [
'mockLargeQueryResults',
'mockMobilityQueryResult',
'typesMockQueryResults',
'testUnMatchHeadersDataResults',
'smallFlightsQueryResults',
'smallVillainQueryResults',
'smallVillainDoubleArchQueryResults',
......
{
"edges": [
{ "from": "worker/1", "_id": "onderdeel_van/1100", "_key": "1100", "_rev": "_cYl_jTO--O", "to": "worker/3", "attributes": {} },
{ "from": "worker/3", "_id": "onderdeel_van/662", "_key": "662", "_rev": "_cYl_jR2--G", "to": "worker/1", "attributes": {} }
],
"nodes": [
{
"_id": "worker/1",
"_key": "1",
"_rev": "_cYl-Qmq-_H",
"attributes": {
"name": "John Doe",
"age": 30,
"height": 170.2
}
},
{
"_id": "worker/3",
"_key": "2",
"_rev": "_cYl-Qmq--5",
"attributes": {
"name": "Bob Johnson",
"age": 35 }
},
{
"_id": "worker/2",
"_key": "2",
"_rev": "_cYl-Qmq--5",
"attributes": {
"age": 38,
"height": 195.2
}
}
]
}
......@@ -76,6 +76,14 @@ export const ContextMenu = (props: {
}
function removeNode() {
if (!props.node) return;
const connectedLogicPills = graphologyGraph.neighbors(props.node.id);
connectedLogicPills.forEach((pill) => {
const attributes = graphologyGraph.getNodeAttributes(pill);
if (attributes.type === 'logic') {
graphologyGraph.dropNode(pill);
}
});
graphologyGraph.dropNode(props.node.id);
dispatch(setQuerybuilderGraphology(graphologyGraph));
props.onClose();
......
......@@ -19,11 +19,7 @@ import { EntityPill } from '@graphpolaris/shared/lib/components/pills';
import { Handle, Position, useUpdateNodeInternals } from 'reactflow';
import { NodeAttribute, SchemaReactflowEntityNode, toHandleId } from '../../../model';
import { PillAttributes } from '../../pillattributes/PillAttributes';
import { DropdownTrigger, DropdownContainer, DropdownItemContainer, DropdownItem } from '@graphpolaris/shared/lib/components/dropdowns';
import { PopoverContext } from '@graphpolaris/shared/lib/components/layout/Popover';
import { useDispatch } from 'react-redux';
import { isEqual } from 'lodash-es';
import { getDataTypeIcon } from '@graphpolaris/shared/lib/components/DataTypeIcon';
import { uniqBy } from 'lodash-es';
/**
* Component to render an entity flow element
......@@ -43,6 +39,7 @@ export const QueryEntityPill = React.memo((node: SchemaReactflowEntityNode) => {
[graph],
);
const uniqueAttributes = useMemo(() => uniqBy(data.attributes, (attr) => attr.handleData.attributeName), [data.attributes]);
const unionType = useQuerybuilderUnionTypes()[node.id];
return (
......@@ -75,7 +72,7 @@ export const QueryEntityPill = React.memo((node: SchemaReactflowEntityNode) => {
{data?.attributes && (
<PillAttributes
node={node}
attributes={data.attributes}
attributes={uniqueAttributes}
attributeEdges={attributeEdges.map((edge) => edge?.attributes)}
unionType={unionType}
/>
......
......@@ -29,7 +29,8 @@ export type SchemaAttributeTypes =
| 'datetime'
| 'duration'
| 'number'
| 'location';
| 'location'
| 'array';
export type DimensionType = 'categorical' | 'numerical' | 'temporal' | 'spatial';
......
......@@ -276,6 +276,7 @@ export const Schema = (props: Props) => {
return (
<Panel
title="Schema"
className="schema-panel"
tooltips={
<>
<Tooltip>
......@@ -351,7 +352,7 @@ export const Schema = (props: Props) => {
</>
}
>
<div className="schema-panel w-full h-full flex flex-col justify-between" ref={reactFlowRef}>
<div className="w-full h-full flex flex-col justify-between" ref={reactFlowRef}>
{schema.loading ? (
<div className="h-full flex flex-col items-center justify-center">
<Icon component="icon-[mingcute--loading-line]" size={56} className="w-15 h-15 animate-spin " />
......