mirror of
https://github.com/hasura/graphql-engine.git
synced 2024-12-14 17:02:49 +03:00
console: fix bugs for login and upgrade header style
[PLAT-437]: https://hasurahq.atlassian.net/browse/PLAT-437?atlOrigin=eyJpIjoiNWRkNTljNzYxNjVmNDY3MDlhMDU5Y2ZhYzA5YTRkZjUiLCJwIjoiZ2l0aHViLWNvbS1KU1cifQ PR-URL: https://github.com/hasura/graphql-engine-mono/pull/7947 GitOrigin-RevId: 9ac807cdb038818d45b0678ac0d98157ee292fab
This commit is contained in:
parent
565176c155
commit
96856f5de3
@ -6,6 +6,7 @@ import { DocsContainer, DocsPage } from '@storybook/addon-docs';
|
||||
import { initialize, mswDecorator } from 'msw-storybook-addon';
|
||||
import { DARK_MODE_EVENT_NAME } from 'storybook-dark-mode';
|
||||
import theme from './theme';
|
||||
import 'react-loading-skeleton/dist/skeleton.css';
|
||||
import '../src/lib/theme/tailwind.css';
|
||||
import { store } from '../src/lib/store';
|
||||
import '../src/lib/components/Common/Common.module.scss';
|
||||
|
@ -23,6 +23,7 @@ export { prefetchOnboardingData } from '../lib/features/CloudOnboarding/Onboardi
|
||||
export { default as PageNotFound } from '../lib/components/Error/PageNotFound';
|
||||
export * from '../lib/new-components/Button/';
|
||||
export * from '../lib/new-components/Tooltip/';
|
||||
export * from '../lib/new-components/Badge/';
|
||||
export { CONSOLE_ADMIN_SECRET } from '../lib/components/AppState';
|
||||
export { default as dataHeaders } from '../lib/components/Services/Data/Common/Headers';
|
||||
export { handleMigrationErrors } from '../lib/components/Services/Data/TableModify/ModifyActions';
|
||||
|
@ -18,7 +18,7 @@ import hasuraEELogo from './black-logo-ee.svg';
|
||||
|
||||
const validationSchema = z.object({
|
||||
password: z.string().min(1, { message: 'Please add password' }),
|
||||
savePassword: z.enum(['checked']).array(),
|
||||
savePassword: z.enum(['checked']).array().optional(),
|
||||
});
|
||||
|
||||
const Login: React.FC<ConnectInjectedProps> = ({ dispatch, children }) => {
|
||||
@ -107,7 +107,7 @@ const Login: React.FC<ConnectInjectedProps> = ({ dispatch, children }) => {
|
||||
options={[
|
||||
{
|
||||
value: 'checked',
|
||||
label: 'Remember in this browser',
|
||||
label: 'Remember on the browser',
|
||||
},
|
||||
]}
|
||||
/>
|
||||
|
@ -3,8 +3,14 @@ import { Link } from 'react-router';
|
||||
import { getPathRoot } from '@/components/Common/utils/urlUtils';
|
||||
import { Tooltip } from '@/new-components/Tooltip';
|
||||
import { IconType } from 'react-icons';
|
||||
import styles from './Main.module.scss';
|
||||
import { isBlockActive } from './Main.utils';
|
||||
import clsx from 'clsx';
|
||||
|
||||
export const linkStyle =
|
||||
'flex items-stretch gap-2 text-white font-bold rounded py-2 px-3 hover:!text-white active:text-primary focus:text-primary bg-transparent hover:bg-slate-900 !no-underline cursor-pointer';
|
||||
export const activeLinkStyle =
|
||||
'text-primary hover:!text-primary focus:!text-primary visited:!text-primary bg-slate-900';
|
||||
export const itemContainerStyle = 'h-full flex items-center ml-xs';
|
||||
|
||||
interface NavItemProps {
|
||||
title: string;
|
||||
@ -33,22 +39,22 @@ const HeaderNavItem: React.VFC<NavItemProps> = ({
|
||||
pathname,
|
||||
});
|
||||
|
||||
const className = isCurrentBlockActive ? styles.navSideBarActive : '';
|
||||
|
||||
return (
|
||||
<li>
|
||||
<Tooltip side="bottom" tooltipContentChildren={tooltipText}>
|
||||
<Tooltip
|
||||
className="h-full"
|
||||
side="bottom"
|
||||
tooltipContentChildren={tooltipText}
|
||||
>
|
||||
<Link
|
||||
className={className}
|
||||
className={clsx(linkStyle, isCurrentBlockActive && activeLinkStyle)}
|
||||
to={appPrefix + path}
|
||||
data-test={`${title.toLowerCase()}-tab-link`}
|
||||
>
|
||||
<div className={styles.iconCenter} data-test={block}>
|
||||
{React.createElement(icon, {
|
||||
'aria-hidden': true,
|
||||
})}
|
||||
</div>
|
||||
<p className="uppercase">{title}</p>
|
||||
<span className="text-sm self-baseline" data-test={block}>
|
||||
{icon}
|
||||
</span>
|
||||
<span className="uppercase text-left">{title}</span>
|
||||
</Link>
|
||||
</Tooltip>
|
||||
</li>
|
||||
|
@ -1,11 +1,14 @@
|
||||
import React from 'react';
|
||||
import clsx from 'clsx';
|
||||
|
||||
import {
|
||||
FaCloud,
|
||||
FaCog,
|
||||
FaCogs,
|
||||
FaDatabase,
|
||||
FaInfoCircle,
|
||||
FaExclamationCircle,
|
||||
FaExclamationTriangle,
|
||||
FaFlask,
|
||||
FaPlug,
|
||||
} from 'react-icons/fa';
|
||||
@ -25,7 +28,6 @@ import {
|
||||
getDataSourceBaseRoute,
|
||||
} from '../Common/utils/routesUtils';
|
||||
import { getPathRoot } from '../Common/utils/urlUtils';
|
||||
import WarningSymbol from '../Common/WarningSymbol/WarningSymbol';
|
||||
import _push from '../Services/Data/push';
|
||||
import {
|
||||
emitProClickedEvent,
|
||||
@ -49,7 +51,13 @@ import {
|
||||
setLoveConsentState,
|
||||
setProClickState,
|
||||
} from './utils';
|
||||
import HeaderNavItem from './HeaderNavItem';
|
||||
import HeaderNavItem, {
|
||||
linkStyle,
|
||||
itemContainerStyle,
|
||||
activeLinkStyle,
|
||||
} from './HeaderNavItem';
|
||||
import { Tooltip } from './../../new-components/Tooltip';
|
||||
import { Badge } from './../../new-components/Badge';
|
||||
|
||||
export const updateRequestHeaders = props => {
|
||||
const { requestHeaders, dispatch } = props;
|
||||
@ -295,19 +303,19 @@ class Main extends React.Component {
|
||||
return mainContent;
|
||||
};
|
||||
|
||||
const getMetadataStatusIcon = () => {
|
||||
const getMetadataStatusIcon = pathname => {
|
||||
if (metadata.inconsistentObjects.length === 0) {
|
||||
return <FaCog className={styles.question} />;
|
||||
return <FaCog />;
|
||||
}
|
||||
|
||||
return (
|
||||
<div className={styles.question}>
|
||||
<div className="relative">
|
||||
<FaCog />
|
||||
<div className={styles.overlappingExclamation}>
|
||||
<div className={styles.iconWhiteBackground} />
|
||||
<div>
|
||||
<FaExclamationCircle />
|
||||
</div>
|
||||
<div className="absolute -top-2 left-2 ">
|
||||
<FaExclamationCircle
|
||||
className="bg-white rounded-full"
|
||||
color="#d9534f"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
@ -318,22 +326,24 @@ class Main extends React.Component {
|
||||
|
||||
if (!globals.isAdminSecretSet) {
|
||||
adminSecretHtml = (
|
||||
<div className={styles.secureSection}>
|
||||
<Tooltip
|
||||
side="bottom"
|
||||
tooltipContentChildren={`This graphql endpoint is public and you should add an ${globals.adminSecretLabel}`}
|
||||
>
|
||||
<div className={itemContainerStyle}>
|
||||
<a
|
||||
className={clsx(linkStyle)}
|
||||
href="https://hasura.io/docs/latest/deployment/securing-graphql-endpoint/"
|
||||
target="_blank"
|
||||
rel="noopener noreferrer"
|
||||
>
|
||||
<WarningSymbol
|
||||
tooltipText={tooltips.secureEndpoint}
|
||||
tooltipPlacement={'left'}
|
||||
customStyle={styles.secureSectionSymbol}
|
||||
/>
|
||||
<span className={styles.secureSectionText}>
|
||||
<Badge color="yellow">
|
||||
<FaExclamationTriangle />
|
||||
Secure your endpoint
|
||||
</span>
|
||||
</Badge>
|
||||
</a>
|
||||
</div>
|
||||
</Tooltip>
|
||||
);
|
||||
}
|
||||
return adminSecretHtml;
|
||||
@ -349,24 +359,22 @@ class Main extends React.Component {
|
||||
metadata={metadata.metadataObject}
|
||||
/>
|
||||
<div className={styles.flexRow}>
|
||||
<div className={styles.sidebar}>
|
||||
<div className={styles.header_logo_wrapper}>
|
||||
<div className={styles.logoParent}>
|
||||
<div className={styles.logo}>
|
||||
<div className="font-sans bg-slate-700 text-slate-100 flex h-16">
|
||||
<div className="flex gap-1 flex-grow">
|
||||
<div className="px-5 py-2 flex items-center gap-3">
|
||||
<Link to="/">
|
||||
<img className="img img-responsive" src={logo} />
|
||||
<img className="w-24" src={logo} />
|
||||
</Link>
|
||||
<Link to="/">
|
||||
<div className="text-white text-xs max-w-[128px]">
|
||||
{serverVersion}
|
||||
</div>
|
||||
</Link>
|
||||
</div>
|
||||
<Link to="/">
|
||||
<div className={styles.project_version}>{serverVersion}</div>
|
||||
</Link>
|
||||
</div>
|
||||
</div>
|
||||
<div className={styles.header_items}>
|
||||
<ul className={styles.sidebarItems}>
|
||||
<ul className="flex gap-2">
|
||||
<HeaderNavItem
|
||||
title="API"
|
||||
icon={FaFlask}
|
||||
icon={<FaFlask aria-hidden="true" />}
|
||||
tooltipText={tooltips.apiExplorer}
|
||||
path="/api/api-explorer"
|
||||
appPrefix={appPrefix}
|
||||
@ -375,7 +383,7 @@ class Main extends React.Component {
|
||||
/>
|
||||
<HeaderNavItem
|
||||
title="Data"
|
||||
icon={FaDatabase}
|
||||
icon={<FaDatabase aria-hidden="true" />}
|
||||
tooltipText={tooltips.data}
|
||||
path={getDataPath()}
|
||||
appPrefix={appPrefix}
|
||||
@ -383,7 +391,7 @@ class Main extends React.Component {
|
||||
/>
|
||||
<HeaderNavItem
|
||||
title="Actions"
|
||||
icon={FaCogs}
|
||||
icon={<FaCogs aria-hidden="true" />}
|
||||
tooltipText={tooltips.actions}
|
||||
path="/actions/manage/actions"
|
||||
appPrefix={appPrefix}
|
||||
@ -391,7 +399,7 @@ class Main extends React.Component {
|
||||
/>
|
||||
<HeaderNavItem
|
||||
title="Remote Schemas"
|
||||
icon={FaPlug}
|
||||
icon={<FaPlug aria-hidden="true" />}
|
||||
tooltipText={tooltips.remoteSchema}
|
||||
path="/remote-schemas/manage/schemas"
|
||||
appPrefix={appPrefix}
|
||||
@ -399,7 +407,7 @@ class Main extends React.Component {
|
||||
/>
|
||||
<HeaderNavItem
|
||||
title="Events"
|
||||
icon={FaCloud}
|
||||
icon={<FaCloud aria-hidden="true" />}
|
||||
tooltipText={tooltips.events}
|
||||
path="/events/data/manage"
|
||||
appPrefix={appPrefix}
|
||||
@ -409,30 +417,42 @@ class Main extends React.Component {
|
||||
</div>
|
||||
<div
|
||||
id="dropdown_wrapper"
|
||||
className={`${styles.clusterInfoWrapper} ${
|
||||
className={clsx(
|
||||
'flex gap-2 justify-end items-stretch relative mr-4',
|
||||
this.state.isDropdownOpen ? 'open' : ''
|
||||
}`}
|
||||
)}
|
||||
>
|
||||
{getAdminSecretSection()}
|
||||
<div className={itemContainerStyle}>
|
||||
<Link
|
||||
className={clsx(
|
||||
linkStyle,
|
||||
currentActiveBlock === 'settings' && activeLinkStyle
|
||||
)}
|
||||
to="/settings"
|
||||
>
|
||||
<span className="text-sm self-baseline">
|
||||
{getMetadataStatusIcon()}
|
||||
</span>
|
||||
<span className="uppercase text-left">Settings</span>
|
||||
</Link>
|
||||
</div>
|
||||
{/* Compensate legacy styles directly with style attribute */}
|
||||
<div className={styles.proWrapper} style={{ padding: '0' }}>
|
||||
<div className={itemContainerStyle}>
|
||||
<div
|
||||
className={`${styles.headerRightNavbarBtn} ${styles.proWrapper}`}
|
||||
className={clsx(linkStyle, isPopUpOpen && activeLinkStyle)}
|
||||
onClick={this.onProIconClick}
|
||||
>
|
||||
<span
|
||||
className={`
|
||||
${isProClicked ? styles.proNameClicked : styles.proName}
|
||||
${isPopUpOpen ? styles.navActive : ''}`}
|
||||
>
|
||||
CLOUD
|
||||
<span className="text-sm self-baseline">
|
||||
<FaInfoCircle />
|
||||
</span>
|
||||
<span className="uppercase text-left">CLOUD</span>
|
||||
</div>
|
||||
</div>
|
||||
{isPopUpOpen && <ProPopup toggleOpen={this.toggleProPopup} />}
|
||||
</div>
|
||||
<Link to="/settings">
|
||||
<div className={styles.headerRightNavbarBtn}>
|
||||
{getMetadataStatusIcon()}
|
||||
{getSettingsSelectedMarker(pathname)}
|
||||
</div>
|
||||
</Link>
|
||||
|
||||
<Help isSelected={currentActiveBlock === 'support'} />
|
||||
<NotificationSection
|
||||
isDropDownOpen={this.state.isDropdownOpen}
|
||||
|
@ -1005,7 +1005,7 @@
|
||||
}
|
||||
|
||||
.help_dropdown_menu {
|
||||
top: 43px;
|
||||
top: 49px;
|
||||
right: 53px;
|
||||
background-color: transparent;
|
||||
box-shadow: none;
|
||||
@ -1051,7 +1051,7 @@
|
||||
}
|
||||
|
||||
.dropdown_menu {
|
||||
top: 43px;
|
||||
top: 49px;
|
||||
right: 0px;
|
||||
background-color: transparent;
|
||||
box-shadow: none;
|
||||
@ -1315,6 +1315,7 @@
|
||||
}
|
||||
|
||||
.closeNotificationIcon {
|
||||
color: #a8a8a8;
|
||||
user-select: none;
|
||||
font-size: 16px;
|
||||
top: 52px;
|
||||
|
@ -30,6 +30,12 @@ import { HASURA_COLLABORATOR_TOKEN } from '../../constants';
|
||||
import { StyledText } from '../UIKit/atoms/Typography/Typography';
|
||||
import { LS_KEYS } from '../../utils/localStorage';
|
||||
import { ConsoleState, NotificationsState } from '../../telemetry/state';
|
||||
import {
|
||||
linkStyle,
|
||||
activeLinkStyle,
|
||||
itemContainerStyle,
|
||||
} from './HeaderNavItem';
|
||||
import clsx from 'clsx';
|
||||
|
||||
const getDateString = (date: NotificationDate) => {
|
||||
if (!date) {
|
||||
@ -721,22 +727,28 @@ const HasuraNotifications: React.FC<
|
||||
|
||||
return (
|
||||
<>
|
||||
<div className={itemContainerStyle}>
|
||||
<div
|
||||
className={`${styles.shareSection} ${styles.headerRightNavbarBtn} ${
|
||||
isDropDownOpen ? styles.opened : ''
|
||||
} dropdown-toggle`}
|
||||
className={clsx(
|
||||
'dropdown-toggle',
|
||||
linkStyle,
|
||||
isDropDownOpen ? styles.opened + ' ' + activeLinkStyle : ''
|
||||
)}
|
||||
aria-expanded="false"
|
||||
onClick={onClickNotificationButton}
|
||||
ref={wrapperRef}
|
||||
>
|
||||
<span className="text-sm">
|
||||
<FaBell className={styles.bellIcon} />
|
||||
<ToReadBadge
|
||||
numberNotifications={numberNotifications}
|
||||
show={showBadge || !!fixedVersion}
|
||||
/>
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
<Box
|
||||
className={`dropdown-menu ${styles.consoleNotificationPanel}`}
|
||||
className={`dropdown-menu absolute ${styles.consoleNotificationPanel}`}
|
||||
ref={dropDownRef}
|
||||
>
|
||||
<Flex
|
||||
|
@ -1,20 +1,26 @@
|
||||
import clsx from 'clsx';
|
||||
import React from 'react';
|
||||
import { FaQuestionCircle } from 'react-icons/fa';
|
||||
import { Link } from 'react-router';
|
||||
|
||||
import styles from '../Main.module.scss';
|
||||
import {
|
||||
activeLinkStyle,
|
||||
itemContainerStyle,
|
||||
linkStyle,
|
||||
} from '../HeaderNavItem';
|
||||
|
||||
export const Help = ({ isSelected }: { isSelected: boolean }) => {
|
||||
return (
|
||||
<Link to="/support/forums/">
|
||||
<div className={styles.headerRightNavbarBtn}>
|
||||
HELP
|
||||
{isSelected ? (
|
||||
<span
|
||||
className={styles.selected}
|
||||
style={{ width: '90%', marginLeft: '5%' }}
|
||||
/>
|
||||
) : null}
|
||||
</div>
|
||||
<div className={itemContainerStyle}>
|
||||
<Link
|
||||
id="help"
|
||||
className={clsx(linkStyle, isSelected && activeLinkStyle)}
|
||||
to="/support/forums/"
|
||||
>
|
||||
<span className="text-sm">
|
||||
<FaQuestionCircle />
|
||||
</span>
|
||||
<span className="uppercase">HELP</span>
|
||||
</Link>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
@ -14,7 +14,7 @@ export function LinkButton(props: LinkButtonProps) {
|
||||
<a href={url} target="_blank" rel="noopener noreferrer">
|
||||
<Button
|
||||
mode="default"
|
||||
className="mr-sm bg-none bg-transparent border-red-500"
|
||||
className="mr-sm bg-none !bg-transparent border-red-500"
|
||||
icon={icon}
|
||||
iconPosition={iconPosition}
|
||||
>
|
||||
|
@ -32,7 +32,7 @@ const ClearButton: React.VFC<{ fieldName: string; className: string }> = ({
|
||||
return (
|
||||
<Button
|
||||
className={clsx(
|
||||
'border-0 bg-transparent bg-none shadow-none active:opacity-75 pointer-events-auto',
|
||||
'border-0 !bg-transparent bg-none shadow-none active:opacity-75 pointer-events-auto',
|
||||
className
|
||||
)}
|
||||
onClick={() => setValue(fieldName, '')}
|
||||
|
@ -1,9 +1,15 @@
|
||||
import styles from './Main.module.scss';
|
||||
import { Link } from 'react-router';
|
||||
import React from 'react';
|
||||
import { Link } from 'react-router';
|
||||
import clsx from 'clsx';
|
||||
|
||||
import { Tooltip } from '@hasura/console-oss';
|
||||
|
||||
export const linkStyle =
|
||||
'flex items-stretch gap-2 text-white font-bold rounded py-2 px-3 hover:!text-white active:text-primary focus:text-primary bg-transparent hover:bg-slate-900 !no-underline cursor-pointer';
|
||||
export const activeLinkStyle =
|
||||
'text-primary hover:!text-primary focus:!text-primary visited:!text-primary bg-slate-900';
|
||||
export const itemContainerStyle = 'h-full flex items-center ml-xs';
|
||||
|
||||
const HeaderNavItem = ({
|
||||
title,
|
||||
icon,
|
||||
@ -14,10 +20,8 @@ const HeaderNavItem = ({
|
||||
currentActiveBlock,
|
||||
isDefault = false,
|
||||
}) => {
|
||||
const className =
|
||||
currentActiveBlock === itemPath || (isDefault && currentActiveBlock === '')
|
||||
? styles.navSideBarActive
|
||||
: '';
|
||||
const isActive =
|
||||
currentActiveBlock === itemPath || (isDefault && currentActiveBlock === '');
|
||||
|
||||
return (
|
||||
<li>
|
||||
@ -25,16 +29,17 @@ const HeaderNavItem = ({
|
||||
id={tooltipText}
|
||||
side="bottom"
|
||||
tooltipContentChildren={tooltipText}
|
||||
className="h-full"
|
||||
>
|
||||
<Link
|
||||
className={className}
|
||||
className={clsx(linkStyle, isActive && activeLinkStyle)}
|
||||
data-test={`${title.toLowerCase()}-tab-link`}
|
||||
to={appPrefix + linkPath}
|
||||
>
|
||||
<div className={styles.iconCenter} data-test={itemPath}>
|
||||
<span className="text-sm self-baseline" data-test={itemPath}>
|
||||
{icon}
|
||||
</div>
|
||||
<p className="uppercase">{title}</p>
|
||||
</span>
|
||||
<span className="uppercase text-left">{title}</span>
|
||||
</Link>
|
||||
</Tooltip>
|
||||
</li>
|
||||
|
@ -12,17 +12,24 @@ import {
|
||||
FaCloud,
|
||||
FaPlug,
|
||||
FaChartLine,
|
||||
FaQuestionCircle,
|
||||
FaServer,
|
||||
} from 'react-icons/fa';
|
||||
import clsx from 'clsx';
|
||||
|
||||
import HeaderNavItem from './HeaderNavItem';
|
||||
import HeaderNavItem, {
|
||||
linkStyle,
|
||||
activeLinkStyle,
|
||||
itemContainerStyle,
|
||||
} from './HeaderNavItem';
|
||||
|
||||
import Dropdown from 'react-bootstrap/lib/Dropdown';
|
||||
import MenuItem from 'react-bootstrap/lib/MenuItem';
|
||||
import * as DropdownMenu from '@radix-ui/react-dropdown-menu';
|
||||
|
||||
import globals from '../../Globals';
|
||||
import 'react-toggle/style.css';
|
||||
import { Spinner } from '@hasura/console-oss';
|
||||
import {
|
||||
Badge,
|
||||
NotificationSection,
|
||||
Onboarding,
|
||||
CloudOnboarding,
|
||||
@ -363,29 +370,19 @@ class Main extends React.Component {
|
||||
return mainContent;
|
||||
};
|
||||
|
||||
const getMetadataSelectedMarker = () => {
|
||||
let metadataSelectedMarker = null;
|
||||
|
||||
if (currentActiveBlock === 'settings') {
|
||||
metadataSelectedMarker = <span className={styles.selected} />;
|
||||
}
|
||||
|
||||
return metadataSelectedMarker;
|
||||
};
|
||||
|
||||
const getMetadataIcon = () => {
|
||||
if (metadata.inconsistentObjects.length === 0) {
|
||||
return <FaCog className={styles.question} />;
|
||||
return <FaCog />;
|
||||
}
|
||||
|
||||
return (
|
||||
<div className={styles.question}>
|
||||
<div className="relative">
|
||||
<FaCog />
|
||||
<div className={styles.overlappingExclamation}>
|
||||
<div className={styles.iconWhiteBackground} />
|
||||
<div>
|
||||
<FaExclamationCircle />
|
||||
</div>
|
||||
<div className="absolute -top-2 left-2 ">
|
||||
<FaExclamationCircle
|
||||
className="bg-white rounded-full"
|
||||
color="#d9534f"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
@ -396,20 +393,24 @@ class Main extends React.Component {
|
||||
|
||||
if (!globals.isAdminSecretSet) {
|
||||
adminSecretHtml = (
|
||||
<div className={styles.secureSection}>
|
||||
<Tooltip
|
||||
side="left"
|
||||
side="bottom"
|
||||
tooltipContentChildren={`This graphql endpoint is public and you should add an ${globals.adminSecretLabel}`}
|
||||
>
|
||||
<div className={itemContainerStyle}>
|
||||
<a
|
||||
className={clsx(linkStyle)}
|
||||
href="https://hasura.io/docs/latest/deployment/securing-graphql-endpoint/"
|
||||
target="_blank"
|
||||
rel="noopener noreferrer"
|
||||
>
|
||||
<FaExclamationTriangle className={styles.padd_small_right} />
|
||||
<Badge color="yellow">
|
||||
<FaExclamationTriangle />
|
||||
Secure your endpoint
|
||||
</Badge>
|
||||
</a>
|
||||
</Tooltip>
|
||||
</div>
|
||||
</Tooltip>
|
||||
);
|
||||
}
|
||||
return adminSecretHtml;
|
||||
@ -458,45 +459,79 @@ class Main extends React.Component {
|
||||
const renderLogout = () => {
|
||||
// userRole is only being set for team console
|
||||
return extendedGlobals.consoleType !== 'cloud' ? (
|
||||
<Dropdown
|
||||
id="dropdown-custom-menu"
|
||||
className={`${styles.dropDownContainer} ${styles.flexCenterContainer}`}
|
||||
<div className={itemContainerStyle}>
|
||||
<DropdownMenu.Root>
|
||||
{/* Compensate legacy styles directly with style attribute */}
|
||||
<DropdownMenu.Trigger
|
||||
noCaret
|
||||
className={clsx(
|
||||
linkStyle,
|
||||
'data-state-open:bg-slate-900 data-state-closed:!text-white data-state-open:!text-primary'
|
||||
)}
|
||||
style={{
|
||||
border: 'none',
|
||||
}}
|
||||
>
|
||||
<span className="text-sm self-baseline">
|
||||
<FaUser aria-hidden="true" />
|
||||
</span>
|
||||
</DropdownMenu.Trigger>
|
||||
<DropdownMenu.Portal>
|
||||
<DropdownMenu.Content
|
||||
className="max-w-md -translate-x-6 translate-y-2 data-state-open:animate-dropdownMenuContentOpen data-state-closed:animate-dropdownMenuContentClose"
|
||||
sideOffset={8}
|
||||
>
|
||||
<DropdownMenu.Item
|
||||
className="shadow-lg bg-white hover:bg-slate-100 p-4 rounded overflow-hidden cursor-pointer"
|
||||
onSelect={this.logoutCollaboratorWorkFlow.bind(this)}
|
||||
>
|
||||
<Dropdown.Toggle noCaret className={styles.userBtn}>
|
||||
<FaUser aria-hidden="true" className="text-white" />
|
||||
</Dropdown.Toggle>
|
||||
<Dropdown.Menu className={styles.dropdownUl}>
|
||||
<MenuItem onClick={this.logoutCollaboratorWorkFlow.bind(this)}>
|
||||
<img
|
||||
className={styles.navBarIcon}
|
||||
src={logoutIcon}
|
||||
alt={'logout'}
|
||||
/>{' '}
|
||||
Logout
|
||||
</MenuItem>
|
||||
</Dropdown.Menu>
|
||||
</Dropdown>
|
||||
</DropdownMenu.Item>
|
||||
</DropdownMenu.Content>
|
||||
</DropdownMenu.Portal>
|
||||
</DropdownMenu.Root>
|
||||
</div>
|
||||
) : null;
|
||||
};
|
||||
|
||||
const renderProjectInfo = () => {
|
||||
const detailsPath = `${window.location.protocol}//${window.location.host}/project/${globals.hasuraCloudProjectId}/details`;
|
||||
return window.__env.consoleType === 'cloud' ? (
|
||||
<span className={styles.projectInfo}>
|
||||
<img src={projectImg} />
|
||||
<a href={detailsPath} target="_blank" rel="noopener noreferrer">
|
||||
{globals.projectName}
|
||||
<div className={clsx(itemContainerStyle, 'ml-0')}>
|
||||
<a
|
||||
className={linkStyle}
|
||||
href={detailsPath}
|
||||
target="_blank"
|
||||
rel="noopener noreferrer"
|
||||
>
|
||||
<Badge>
|
||||
<FaServer />
|
||||
|
||||
<span>{globals.projectName}</span>
|
||||
</Badge>
|
||||
</a>
|
||||
</span>
|
||||
</div>
|
||||
) : null;
|
||||
};
|
||||
|
||||
const renderMetadataIcon = () => (
|
||||
<Link to={getConsolidatedPath('/settings', '/settings', accessState)}>
|
||||
<div className={styles.headerRightNavbarBtn}>
|
||||
{getMetadataIcon()}
|
||||
{getMetadataSelectedMarker()}
|
||||
</div>
|
||||
<div className={itemContainerStyle}>
|
||||
<Link
|
||||
className={clsx(
|
||||
linkStyle,
|
||||
currentActiveBlock === 'settings' && activeLinkStyle
|
||||
)}
|
||||
to={getConsolidatedPath('/settings', '/settings', accessState)}
|
||||
>
|
||||
<span className="text-sm self-baseline">{getMetadataIcon()}</span>
|
||||
<span className="uppercase text-left">Settings</span>
|
||||
</Link>
|
||||
</div>
|
||||
);
|
||||
|
||||
const getLogoSrc = () => {
|
||||
@ -510,25 +545,23 @@ class Main extends React.Component {
|
||||
return (
|
||||
<div className={styles.container}>
|
||||
<div className={styles.flexRow}>
|
||||
<div className={styles.sidebar}>
|
||||
<div className={styles.header_logo_wrapper}>
|
||||
<div className={styles.logoParent}>
|
||||
<div className={styles.logo}>
|
||||
<div className="font-sans bg-slate-700 text-slate-100 flex h-16">
|
||||
<div className="flex gap-1 flex-grow">
|
||||
<div className="px-5 py-2 flex items-center gap-3">
|
||||
<Link
|
||||
to={accessState.hasGraphQLAccess ? '/' : '/access-denied'}
|
||||
>
|
||||
<img className="img img-responsive" src={getLogoSrc()} />
|
||||
<img className="w-24" src={getLogoSrc()} />
|
||||
</Link>
|
||||
</div>
|
||||
<Link
|
||||
to={accessState.hasGraphQLAccess ? '/' : '/access-denied'}
|
||||
>
|
||||
<div className={styles.project_version}>{serverVersion}</div>
|
||||
<div className="text-white text-xs max-w-[128px]">
|
||||
{serverVersion}
|
||||
</div>
|
||||
</Link>
|
||||
</div>
|
||||
</div>
|
||||
<div className={styles.header_items}>
|
||||
<ul className={styles.sidebarItems}>
|
||||
<ul className="flex gap-2">
|
||||
<HeaderNavItem
|
||||
title="API"
|
||||
icon={<FaFlask aria-hidden="true" />}
|
||||
@ -600,21 +633,28 @@ class Main extends React.Component {
|
||||
</div>
|
||||
<div
|
||||
id="dropdown_wrapper"
|
||||
className={`${styles.clusterInfoWrapper} ${
|
||||
className={clsx(
|
||||
'flex gap-2 justify-end items-stretch relative mr-4',
|
||||
this.state.isDropdownOpen ? 'open' : ''
|
||||
}`}
|
||||
)}
|
||||
>
|
||||
{getAdminSecretSection()}
|
||||
{renderProjectInfo()}
|
||||
{renderMetadataIcon()}
|
||||
<div className={itemContainerStyle}>
|
||||
<a
|
||||
id="help"
|
||||
className={clsx(linkStyle)}
|
||||
href={'https://hasura.io/help'}
|
||||
target="_blank"
|
||||
rel="noopener noreferrer"
|
||||
>
|
||||
<div className={styles.headerRightNavbarBtn}>HELP</div>
|
||||
<span className="text-sm self-baseline">
|
||||
<FaQuestionCircle />
|
||||
</span>
|
||||
<span className="uppercase text-left">HELP</span>
|
||||
</a>
|
||||
</div>
|
||||
{serverVersion &&
|
||||
accessState &&
|
||||
'hasDataAccess' in accessState &&
|
||||
|
@ -238,6 +238,7 @@
|
||||
}
|
||||
|
||||
.clusterInfoWrapper {
|
||||
align-items: stretch;
|
||||
width: auto;
|
||||
margin-left: auto;
|
||||
display: flex;
|
||||
@ -1000,6 +1001,7 @@
|
||||
.headerRightNavbarBtn {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
height: 100%;
|
||||
padding: 15px;
|
||||
position: relative;
|
||||
color: #fff;
|
||||
|
@ -1,4 +1,31 @@
|
||||
const colors = require('tailwindcss/colors');
|
||||
const plugin = require('tailwindcss/plugin');
|
||||
|
||||
function dataStateVariant(state, { addVariant, e }) {
|
||||
addVariant(`data-state-${state}`, ({ modifySelectors, separator }) => {
|
||||
modifySelectors(({ className }) => {
|
||||
return `.${e(
|
||||
`data-state-${state}${separator}${className}`
|
||||
)}[data-state='${state}']`;
|
||||
});
|
||||
});
|
||||
|
||||
addVariant(`group-data-state-${state}`, ({ modifySelectors, separator }) => {
|
||||
modifySelectors(({ className }) => {
|
||||
return `.group[data-state='${state}'] .${e(
|
||||
`group-data-state-${state}${separator}${className}`
|
||||
)}`;
|
||||
});
|
||||
});
|
||||
|
||||
addVariant(`peer-data-state-${state}`, ({ modifySelectors, separator }) => {
|
||||
modifySelectors(({ className }) => {
|
||||
return `.peer[data-state='${state}'] ~ .${e(
|
||||
`peer-data-state-${state}${separator}${className}`
|
||||
)}`;
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
module.exports = {
|
||||
safelist: {
|
||||
@ -155,6 +182,22 @@ module.exports = {
|
||||
transform: `translateY(-100%)`,
|
||||
},
|
||||
},
|
||||
dropdownMenuContentOpen: {
|
||||
from: {
|
||||
opacity: '0',
|
||||
},
|
||||
to: {
|
||||
opacity: '1',
|
||||
},
|
||||
},
|
||||
dropdownMenuContentClose: {
|
||||
from: {
|
||||
opacity: '1',
|
||||
},
|
||||
to: {
|
||||
opacity: '0',
|
||||
},
|
||||
},
|
||||
},
|
||||
animation: {
|
||||
collapsibleContentOpen: 'collapsibleContentOpen 300ms ease-out',
|
||||
@ -168,6 +211,8 @@ module.exports = {
|
||||
'slideLeftAndFade 400ms cubic-bezier(0.16, 1, 0.3, 1)',
|
||||
notificationOpen: 'notificationOpen 300ms ease-in-out',
|
||||
notificationClose: 'notificationClose 300ms ease-in-out',
|
||||
dropdownMenuContentOpen: 'dropdownMenuContentOpen 100ms ease-in',
|
||||
dropdownMenuContentClose: 'dropdownMenuContentClose 100ms ease-out',
|
||||
},
|
||||
},
|
||||
},
|
||||
@ -175,5 +220,13 @@ module.exports = {
|
||||
require('@tailwindcss/typography'),
|
||||
require('@tailwindcss/forms'),
|
||||
require('tailwindcss-radix')(),
|
||||
plugin(helpers => {
|
||||
// variants that help styling Radix-UI components
|
||||
dataStateVariant('open', helpers);
|
||||
dataStateVariant('closed', helpers);
|
||||
dataStateVariant('on', helpers);
|
||||
dataStateVariant('checked', helpers);
|
||||
dataStateVariant('unchecked', helpers);
|
||||
}),
|
||||
],
|
||||
};
|
||||
|
Loading…
Reference in New Issue
Block a user