GT-721-change-schema-regsitry-tab-position

PR-URL: https://github.com/hasura/graphql-engine-mono/pull/10335
GitOrigin-RevId: da2e2f53f85477b80e90dd8179ffde28108679f4
This commit is contained in:
Aaysha 2023-10-16 18:54:31 +05:30 committed by hasura-bot
parent 57b471ef03
commit f386414058
8 changed files with 245 additions and 224 deletions

View File

@ -3,6 +3,9 @@ import { Link, RouteComponentProps } from 'react-router';
import { isProConsole } from '../../../utils/proConsole'; import { isProConsole } from '../../../utils/proConsole';
import { useEELiteAccess } from '../../../features/EETrial'; import { useEELiteAccess } from '../../../features/EETrial';
import globals from '../../../Globals'; import globals from '../../../Globals';
import { IconTooltip } from '../../../new-components/Tooltip';
import { FaRegMap } from 'react-icons/fa';
import { sendTelemetryEvent } from '../../../telemetry';
type TopNavProps = { type TopNavProps = {
location: RouteComponentProps<unknown, unknown>['location']; location: RouteComponentProps<unknown, unknown>['location'];
@ -25,6 +28,12 @@ const TopNav: React.FC<TopNavProps> = ({ location }) => {
dataTestVal: 'rest-explorer-link', dataTestVal: 'rest-explorer-link',
title: 'REST', title: 'REST',
}, },
{
key: 'schema-registry',
link: '/api/schema-registry',
dataTestVal: 'schema-registry-link',
title: 'Schema Registry',
},
], ],
[ [
{ {
@ -68,13 +77,30 @@ const TopNav: React.FC<TopNavProps> = ({ location }) => {
: 'border-white hover:border-gray-100' : 'border-white hover:border-gray-100'
}`} }`}
key={section.key} key={section.key}
onClick={() => {
// Send Telemetry data for Schema Registry tab
if (section.key === 'schema-registry') {
sendTelemetryEvent({
type: 'CLICK_EVENT',
data: {
id: 'schema-registry-top-nav-tab',
},
});
}
}}
> >
<Link <Link
to={section.link} to={section.link}
data-test={section.dataTestVal} data-test={section.dataTestVal}
className="text-gray-600 font-semibold no-underline hover:text-gray-600 hover:no-underline focus:text-gray-600 focus:no-underline" className="flex text-gray-600 font-semibold no-underline hover:text-gray-600 hover:no-underline focus:text-gray-600 focus:no-underline"
> >
{section.title} {section.title}
{section.key === 'schema-registry' && (
<IconTooltip
icon={<FaRegMap />}
message="Detect breaking and dangerous changes, view schema change history. Keep your GraphQL services safe and reliable! 🚀"
/>
)}
</Link> </Link>
</div> </div>
)) ))

View File

@ -15,7 +15,6 @@ import {
import { useEELiteAccess } from '../../../features/EETrial'; import { useEELiteAccess } from '../../../features/EETrial';
import { getQueryResponseCachingRoute } from '../../../utils/routeUtils'; import { getQueryResponseCachingRoute } from '../../../utils/routeUtils';
import { isCloudConsole } from '../../../utils/cloudConsole';
import { isOpenTelemetrySupported } from '../../../utils/proConsole'; import { isOpenTelemetrySupported } from '../../../utils/proConsole';
export interface Metadata { export interface Metadata {
@ -33,8 +32,7 @@ type SectionDataKey =
| 'security' | 'security'
| 'monitoring' | 'monitoring'
| 'performance' | 'performance'
| 'about' | 'about';
| 'graphql';
const Sidebar: React.FC<SidebarProps> = ({ location, metadata }) => { const Sidebar: React.FC<SidebarProps> = ({ location, metadata }) => {
const eeLiteAccess = useEELiteAccess(globals); const eeLiteAccess = useEELiteAccess(globals);
@ -47,25 +45,6 @@ const Sidebar: React.FC<SidebarProps> = ({ location, metadata }) => {
label: 'Metadata', label: 'Metadata',
items: [], items: [],
}; };
if (
isCloudConsole(globals) &&
(globals.userRole === 'admin' || globals.userRole === 'owner')
) {
sectionsData.graphql = {
key: 'graphql',
label: 'GraphQL',
items: [
{
key: 'schema-registry',
label: 'Schema Registry (Beta)',
route: '/settings/schema-registry',
dataTestVal: 'metadata-schema-registry-link',
},
],
};
}
sectionsData.metadata.items.push({ sectionsData.metadata.items.push({
key: 'actions', key: 'actions',
label: 'Metadata Actions', label: 'Metadata Actions',

View File

@ -6,23 +6,19 @@ import { SCHEMA_REGISTRY_FEATURE_NAME } from '../constants';
import { FaBell } from 'react-icons/fa'; import { FaBell } from 'react-icons/fa';
import { IconTooltip } from '../../../new-components/Tooltip'; import { IconTooltip } from '../../../new-components/Tooltip';
import { AlertsDialog } from './AlertsDialog'; import { AlertsDialog } from './AlertsDialog';
import { Badge } from '../../../new-components/Badge'; import { Analytics, InitializeTelemetry } from '../../Analytics';
import { SCHEMA_REGISTRY_REF_URL } from '../constants';
import { Analytics } from '../../Analytics';
import { useGetV2Info } from '../hooks/useGetV2Info'; import { useGetV2Info } from '../hooks/useGetV2Info';
import { telemetryUserEventsTracker } from '../../../telemetry';
const SchemaRegistryHeader: React.VFC = () => { const SchemaRegistryHeader: React.VFC = () => {
const [isAlertModalOpen, setIsAlertModalOpen] = useState(false); const [isAlertModalOpen, setIsAlertModalOpen] = useState(false);
return ( return (
<div className="flex flex-col w-full"> <div className="flex flex-col w-full pl-12 mb-2">
<div className="flex mb-xs mt-md w-full"> <div className="flex mb-xs mt-md w-full">
<h1 className="inline-block text-xl font-semibold mr-2 text-slate-900"> <h1 className="inline-block text-xl font-semibold mr-2 text-slate-900">
GraphQL Schema Registry GraphQL Schema Registry
</h1> </h1>
<Badge className="mx-2" color="blue">
BETA
</Badge>
<Analytics name="data-schema-registry-alerts-btn"> <Analytics name="data-schema-registry-alerts-btn">
<div <div
className="flex text-lg mt-2 mx-2 cursor-pointer" className="flex text-lg mt-2 mx-2 cursor-pointer"
@ -36,14 +32,6 @@ const SchemaRegistryHeader: React.VFC = () => {
</div> </div>
</Analytics> </Analytics>
</div> </div>
<a
className="text-muted w-auto mb-xs text-md"
href={SCHEMA_REGISTRY_REF_URL}
target="_blank"
rel="noreferrer noopener"
>
What is Schema Registry?
</a>
<span className="text-muted text-md mb-2 italic"> <span className="text-muted text-md mb-2 italic">
GraphQL Schema Registry changes will only be retained for 14 days. GraphQL Schema Registry changes will only be retained for 14 days.
</span> </span>
@ -96,8 +84,9 @@ export const SchemaRegistryContainer: React.VFC<
); );
return ( return (
<div className="p-4 flex flex-col w-full"> <div className="flex flex-col w-[80%] pl-10 ml-10 justify-center">
<SchemaRegistryHeader /> <SchemaRegistryHeader />
<InitializeTelemetry tracker={telemetryUserEventsTracker} skip={false} />
<SchemaRegistryBody <SchemaRegistryBody
hasFeatureAccess={hasFeatureAccess} hasFeatureAccess={hasFeatureAccess}
schemaId={schemaId} schemaId={schemaId}

View File

@ -4,42 +4,18 @@ import { useGetSchema } from '../hooks/useGetSchema';
import { Tabs } from '../../../new-components/Tabs'; import { Tabs } from '../../../new-components/Tabs';
import { IconTooltip } from '../../../new-components/Tooltip'; import { IconTooltip } from '../../../new-components/Tooltip';
import { SchemaRow } from './SchemaRow'; import { SchemaRow } from './SchemaRow';
import { ChangeSummary } from './ChangeSummary';
import { import {
findIfSubStringExists, findIfSubStringExists,
schemaTransformFn, schemaTransformFn,
getPublishTime, getPublishTime,
} from '../utils'; } from '../utils';
import { Link } from 'react-router';
import { RoleBasedSchema, Schema } from '../types'; import { RoleBasedSchema, Schema } from '../types';
import { import { FaSearch, FaShareAlt } from 'react-icons/fa';
FaHome,
FaAngleRight,
FaFileImport,
FaSearch,
FaShareAlt,
} from 'react-icons/fa';
import { Input } from '../../../new-components/Form'; import { Input } from '../../../new-components/Form';
import AceEditor from 'react-ace'; import AceEditor from 'react-ace';
import { SearchableSelect } from '../../../components/Common'; import { SearchableSelect } from '../../../components/Common';
import { Analytics } from '../../Analytics'; import { Analytics } from '../../Analytics';
export const Breadcrumbs = () => (
<div className="flex items-center space-x-xs mb-4">
<Link
to="/settings/schema-registry"
className="cursor-pointer flex items-center text-muted hover:text-gray-900"
>
<FaHome className="mr-1.5" />
<span className="text-sm">Schema</span>
</Link>
<FaAngleRight className="text-muted" />
<div className="cursor-pointer flex items-center text-yellow-500">
<FaFileImport className="mr-1.5" />
<span className="text-sm">Roles</span>
</div>
</div>
);
interface SchemaChangeDetailsProps { interface SchemaChangeDetailsProps {
schemaId: string; schemaId: string;
} }
@ -113,16 +89,17 @@ const SchemasDetails: React.VFC<{
/> />
<div className="px-4 mb-2"> <div className="px-4 mb-2">
<div className="flex mt-4"> <div className="flex mt-4">
<div className="flex-col "> <div className="flex items-center ">
<div className="flex items-center"> <p className="font-bold text-gray-500 py-2">Published: </p>
<p className="font-bold text-gray-500 py-2">Published</p>
<div className="flex items-center ml-2 font-semibold text-gray-600">
<span>{getPublishTime(schema.created_at)}</span>
<IconTooltip message="The time at which this GraphQL schema was generated" /> <IconTooltip message="The time at which this GraphQL schema was generated" />
</div> </div>
<span>{getPublishTime(schema.created_at)}</span>
</div> </div>
<div className="flex items-center justify-around ml-auto w-1/2"> <div className="flex items-center justify-around ml-auto w-1/2">
<div className="flex-col"> <div className="flex-col">
<div className="flex items-center"> <div className="flex items-center mt-4">
<p className="font-bold text-gray-500 py-2">Hash</p> <p className="font-bold text-gray-500 py-2">Hash</p>
<IconTooltip message="Hash of the GraphQL Schema SDL. Hash for two identical schema is identical." /> <IconTooltip message="Hash of the GraphQL Schema SDL. Hash for two identical schema is identical." />
</div> </div>
@ -263,65 +240,64 @@ export const ChangesView: React.VFC<{
changesList.filter(c => c.criticality.level === 'NON_BREAKING'); changesList.filter(c => c.criticality.level === 'NON_BREAKING');
const showBreakingChanges = const showBreakingChanges =
!selectedChangeLevel || selectedChangeLevel === 'breaking'; (!selectedChangeLevel || selectedChangeLevel === 'breaking') &&
!!breakingChanges?.length;
const showDangerousChanges = const showDangerousChanges =
!selectedChangeLevel || selectedChangeLevel === 'dangerous'; (!selectedChangeLevel || selectedChangeLevel === 'dangerous') &&
!!dangerousChanges?.length;
const showSafeChanges = const showSafeChanges =
!selectedChangeLevel || selectedChangeLevel === 'safe'; (!selectedChangeLevel || selectedChangeLevel === 'safe') &&
!!safeChanges?.length;
const onFilterChange = (op: Option) => { const onFilterChange = (op: Option) => {
setSelectedChangeLevel(op.value); setSelectedChangeLevel(op.value);
}; };
return ( return (
<div className="flex-col"> <div className="flex-col">
<div className="flex-col"> <div className="flex w-full mt-4 mb-4 justify-between items-center">
<div className="font-semibold text-lg mb-8 mt-8 text-gray-500">
Change Summary
</div>
<div className="mr-8">
<ChangeSummary changes={changes} />
</div>
</div>
<div className="flex w-full border-b border-gray-300 my-8" />
<div className="flex w-full mb-4 justify-between items-center">
<div className="flex font-semibold text-lg text-gray-500">Changes</div> <div className="flex font-semibold text-lg text-gray-500">Changes</div>
<div className="flex block w-[50%]"> <div className="flex w-[50%]">
<div className="flex w-full"> <div className="flex w-full">
<div className="px-2 w-1/2"> <div className="px-2 w-1/2">
<SearchableSelect <Analytics name="schema-registry-schema-change-details-filter">
options={[ <SearchableSelect
{ options={[
value: 'breaking', {
label: 'Breaking', value: 'breaking',
}, label: 'Breaking',
{ },
value: 'dangerous', {
label: 'Dangerous', value: 'dangerous',
}, label: 'Dangerous',
{ },
value: 'safe', {
label: 'Safe', value: 'safe',
}, label: 'Safe',
]} },
onChange={op => { ]}
onFilterChange(op as Option); onChange={op => {
}} onFilterChange(op as Option);
filterOption="prefix" }}
placeholder="Filter" filterOption="prefix"
isClearable={true} placeholder="Filter"
/> isClearable={true}
</div>
<div className="pr-2 w-1/2">
<label>
<Input
type="text"
placeholder="Search"
name="search"
icon={<FaSearch />}
iconPosition="start"
onChange={handleSearch}
/> />
</label> </Analytics>
</div>
<div className="pr-2 w-1/2">
<Analytics name="schema-registry-schema-change-details-search">
<label>
<Input
type="text"
placeholder="Search"
name="search"
icon={<FaSearch />}
iconPosition="start"
onChange={handleSearch}
/>
</label>
</Analytics>
</div> </div>
</div> </div>
</div> </div>
@ -329,31 +305,27 @@ export const ChangesView: React.VFC<{
{showBreakingChanges && ( {showBreakingChanges && (
<div> <div>
<div className="font-semibold text-md text-gray-500 mt-4"> <div className="flex items-center font-semibold text-md text-gray-500 mt-4">
Breaking Breaking
</div> <IconTooltip message="Breaking changes due the schema change" />
<div className="text-sm text-gray-600 pb-2">
Content for What is breaking on schema registry
</div> </div>
<ChangesTable changes={breakingChanges} level="breaking" /> <ChangesTable changes={breakingChanges} level="breaking" />
</div> </div>
)} )}
{showDangerousChanges && ( {showDangerousChanges && dangerousChanges && (
<div> <div>
<div className="font-semibold text-md text-gray-500 mt-4"> <div className="flex items-center font-semibold text-md text-gray-500 mt-4">
Dangerous Dangerous
</div> <IconTooltip message="Dangerous changes due the schema change" />
<div className="text-sm text-gray-600 pb-2">
Content for What is dangerous on schema registry
</div> </div>
<ChangesTable changes={dangerousChanges} level="dangerous" /> <ChangesTable changes={dangerousChanges} level="dangerous" />
</div> </div>
)} )}
{showSafeChanges && ( {showSafeChanges && safeChanges && (
<div> <div>
<div className="font-semibold text-md text-gray-500 mt-4">Safe</div> <div className="flex items-center font-semibold text-md text-gray-500 mt-4">
<div className="text-sm text-gray-600 pb-2"> Safe
Content for What is safe on schema registry <IconTooltip message="Safe changes due the schema change" />
</div> </div>
<ChangesTable changes={safeChanges} level="safe" /> <ChangesTable changes={safeChanges} level="safe" />
</div> </div>
@ -385,7 +357,7 @@ const ChangesTable: React.FC<ChangesTableProps> = ({ changes, level }) => {
</tr> </tr>
</thead> </thead>
<tbody> <tbody>
{changes && changes.length ? ( {changes &&
changes.map((change, index) => ( changes.map((change, index) => (
<tr <tr
className={`${textColor[level]} border border-neutral-200 p-2`} className={`${textColor[level]} border border-neutral-200 p-2`}
@ -393,12 +365,7 @@ const ChangesTable: React.FC<ChangesTableProps> = ({ changes, level }) => {
> >
<td className="pl-2">{change.message}</td> <td className="pl-2">{change.message}</td>
</tr> </tr>
)) ))}
) : (
<tr>
<td className="pl-2">{`No ${level} changes`}</td>
</tr>
)}
</tbody> </tbody>
</table> </table>
</div> </div>

View File

@ -19,7 +19,7 @@ import AceEditor from 'react-ace';
export const Breadcrumbs = () => ( export const Breadcrumbs = () => (
<div className="flex items-center space-x-xs mb-4"> <div className="flex items-center space-x-xs mb-4">
<Link <Link
to="/settings/schema-registry" to="/api/schema-registry"
className="cursor-pointer flex items-center text-muted hover:text-gray-900" className="cursor-pointer flex items-center text-muted hover:text-gray-900"
> >
<FaHome className="mr-1.5" /> <FaHome className="mr-1.5" />

View File

@ -78,7 +78,7 @@ export const SchemaRegistryHome: React.FC<SchemaRegistryHomeProps> = props => {
if (schemaRoleID === EMPTY_UUID_STRING) { if (schemaRoleID === EMPTY_UUID_STRING) {
if (latestAdminRoleID) { if (latestAdminRoleID) {
setSelectedRoleID(latestAdminRoleID); setSelectedRoleID(latestAdminRoleID);
dispatch(_push(`/settings/schema-registry/${latestAdminRoleID}`)); dispatch(_push(`/api/schema-registry/${latestAdminRoleID}`));
} }
} else { } else {
setSelectedRoleID(schemaRoleID); setSelectedRoleID(schemaRoleID);
@ -88,7 +88,7 @@ export const SchemaRegistryHome: React.FC<SchemaRegistryHomeProps> = props => {
useEffect(() => { useEffect(() => {
if (latestAdminRoleID) { if (latestAdminRoleID) {
setSelectedRoleID(latestAdminRoleID); setSelectedRoleID(latestAdminRoleID);
dispatch(_push(`/settings/schema-registry/${latestAdminRoleID}`)); dispatch(_push(`/api/schema-registry/${latestAdminRoleID}`));
} }
setSearchParam(pageNo); setSearchParam(pageNo);
}, [latestAdminRoleID, pageNo]); }, [latestAdminRoleID, pageNo]);
@ -118,8 +118,8 @@ export const SchemaRegistryHome: React.FC<SchemaRegistryHomeProps> = props => {
fetchSchemasResponse.response fetchSchemasResponse.response
); );
return ( return (
<div className="flex w-full"> <div className="flex w-[80%] justify-center">
<div className="w-1/4"> <div className="w-[20%]">
<SchemaChangeList <SchemaChangeList
schemas={schemaList} schemas={schemaList}
urlSchemaCard={URLSchemaCard} urlSchemaCard={URLSchemaCard}
@ -172,27 +172,33 @@ export const SchemaChangeList: React.VFC<{
return ( return (
<div className="overflow-x-auto rounded-md border-neutral-200 bg-white border mr-sm"> <div className="overflow-x-auto rounded-md border-neutral-200 bg-white border mr-sm">
<div className="w-full flex bg-gray-100 px-4 py-2"> <div className="w-full flex bg-gray-100 px-4 py-2">
<div className="flex text-base w-[69%] justify-start"> <div className="flex text-base w-[50%] justify-start">
<span className="text-sm font-bold">PUBLISHED SCHEMA</span> <span className="text-sm font-bold">PUBLISHED SCHEMA</span>
</div> </div>
</div> </div>
<div className="flex flex-col w-full"> <div className="flex flex-col w-full">
{schemas.length ? ( {schemas.length ? (
<div className="mb-md"> <div className="mb-md">
{schemaList.map((schema, index) => ( <div
<SchemaCard style={{ maxHeight: '80vh' }}
cardKey={index} className={`flex flex-col w-full max-h-400px overflow-y-scroll`}
openSchemaCardIndex={openSchemaCardIndex} >
selectedRoleID={selectedRoleID} {schemaList.map((schema, index) => (
createdAt={schema.created_at} <SchemaCard
roles={schema.roles} cardKey={index}
entryHash={schema.entry_hash} openSchemaCardIndex={openSchemaCardIndex}
tags={schema.tags} selectedRoleID={selectedRoleID}
dispatch={dispatch} createdAt={schema.created_at}
handleClick={() => setIsOpenSchemaCardIndex(index)} roles={schema.roles}
/> entryHash={schema.entry_hash}
))} tags={schema.tags}
<div className="flex w-full justify-center items-center"> dispatch={dispatch}
handleClick={() => setIsOpenSchemaCardIndex(index)}
/>
))}
</div>
<div className="flex w-full justify-center items-center mt-2">
<button <button
className="btn btn-primary items-center max-w-full justify-center inline-flex text-sm font-sans font-semibold border rounded shadow-sm focus-visible:outline-none h-btn px-sm mx-1 disabled:border-gray-300 disabled:opacity-60 disabled:cursor-not-allowed" className="btn btn-primary items-center max-w-full justify-center inline-flex text-sm font-sans font-semibold border rounded shadow-sm focus-visible:outline-none h-btn px-sm mx-1 disabled:border-gray-300 disabled:opacity-60 disabled:cursor-not-allowed"
disabled={pageNumber === 0} disabled={pageNumber === 0}
@ -281,7 +287,8 @@ const SchemaCard: React.VFC<{
handleClick, handleClick,
} = props; } = props;
const [isTagModalOpen, setIsTagModalOpen] = React.useState(false); const [isTagModalOpen, setIsTagModalOpen] = React.useState(false);
const [isRolesMenuOpen, setIsRolesMenuOpen] = useState(false); const [isCurrentCardOpen, setIsCurrentCardOpen] = useState(false);
const [tagsList, setTagsList] = React.useState<SchemaRegistryTag[]>(tags); const [tagsList, setTagsList] = React.useState<SchemaRegistryTag[]>(tags);
const onRemoveTag = (id: string) => { const onRemoveTag = (id: string) => {
const filteredTags = tagsList.filter( const filteredTags = tagsList.filter(
@ -289,92 +296,128 @@ const SchemaCard: React.VFC<{
); );
setTagsList(filteredTags); setTagsList(filteredTags);
}; };
const adminIndex = roles.findIndex(role => role.role === 'admin');
// If "admin" is not at the 0 index, move it there
if (adminIndex !== 0 && adminIndex !== -1) {
const adminRole = roles.splice(adminIndex, 1)[0];
roles.unshift(adminRole);
}
useEffect(() => { useEffect(() => {
setTagsList(tags); setTagsList(tags);
}, [tags]); }, [tags]);
React.useEffect(() => { React.useEffect(() => {
setIsRolesMenuOpen(cardKey === openSchemaCardIndex); setIsCurrentCardOpen(cardKey === openSchemaCardIndex);
}, [cardKey, openSchemaCardIndex]); }, [cardKey, openSchemaCardIndex]);
const handleRoleClick = (roleBasedChange: Role) => { const handleRoleClick = (roleBasedChange: Role) => {
dispatch(_push(`/settings/schema-registry/${roleBasedChange.id}`)); console.log('click');
dispatch(_push(`/api/schema-registry/${roleBasedChange.id}`));
}; };
const defaultShowAllRoles = isCurrentCardOpen || roles.length <= 3;
const RolesList = defaultShowAllRoles ? roles : roles.slice(0, 3);
return ( return (
<div <div
className="w-full flex flex-col px-4 bg-white rounded" className={`w-full flex flex-col px-4 bg-white rounded mb-1`}
onClick={handleClick} onClick={() => {
if (!roles.some(role => role.id === selectedRoleID)) {
dispatch(_push(`/api/schema-registry/${roles[0].id}`));
}
handleClick();
}}
> >
<div className="flex flex-col mt-2 justify-between h-full"> <div
<div className="flex text-gray-600 text-sm justify-start"> className={`mt-2 ${
isCurrentCardOpen ? 'bg-gray-100' : ' bg-white'
} hover:bg-gray-100 `}
>
<div className="flex mt-1 mb-2 text-gray-600 text-sm items-center justify-start h-full px-2">
<span>{getPublishTime(createdAt)}</span> <span>{getPublishTime(createdAt)}</span>
<span>
<Analytics name="schema-registry-add-tag-btn">
{isTagModalOpen && (
<AddSchemaRegistryTagDialog
tagsList={tagsList}
setTagsList={setTagsList}
entryHash={entryHash}
onClose={() => {
setIsTagModalOpen(false);
}}
/>
)}
<div className="flex items-center">
<div className="flex flex-nowrap items-center justify-start ">
{tagsList &&
tagsList.map(schemaRegistryTag => (
<div className="mx-1 ">
<SchemaTag
schemaRegistryTag={schemaRegistryTag}
onRemove={onRemoveTag}
/>
</div>
))}
<div className="ml-1" onClick={() => setIsTagModalOpen(true)}>
<span>
<IconTooltip
message="Add a Tag"
icon={<FaPlusCircle />}
/>
</span>
</div>
</div>
</div>
</Analytics>
</span>
</div> </div>
<div <div
className={`flex font-semibold text-md justify-end hover:text-gray-600 ${ className={`flex flex-col justify-start w-full pt-auto ${
isRolesMenuOpen ? 'text-gray-600' : 'text-gray-400' isCurrentCardOpen ? 'bg-gray-100' : ''
} cursor-pointer`} } rounded `}
> >
{roles.length} {roles.length === 1 ? 'Role' : 'Roles'} <div className="text-sm text-gray-500 font-bold mb-1 px-2">
<span className="mr-1">{<FaChevronDown />}</span> {roles.length} {roles.length === 1 ? 'ROLE' : 'ROLES'}
</div>
</div>
{isRolesMenuOpen && (
<div className="w-full pt-auto">
<div className="text-sm text-gray-500 font-bold mb-3">
{roles.length === 1 ? 'ROLE' : 'ROLES'}
</div> </div>
{roles.map((roleBasedChange, index) => ( <div>
<div {RolesList.map((roleBasedChange, index) => (
className={`flex w-full p-2 ${ <div
roleBasedChange.id === selectedRoleID ? 'bg-gray-100' : '' className={`flex w-full px-2 py-1 ${
} rounded hover:bg-gray-200`} isCurrentCardOpen && roleBasedChange.id === selectedRoleID
onClick={() => { ? 'bg-gray-200'
handleRoleClick(roleBasedChange); : ''
}} } rounded hover:bg-gray-200`}
key={index} onClick={() => {
> handleRoleClick(roleBasedChange);
<div className="flex items-center justify-between w-full rounded"> }}
<div className="text-base rounded cursor-pointer"> key={index}
<p className="text-sm text-teal-800 font-bold bg-gray-200 px-1 rounded"> >
{CapitalizeFirstLetter(roleBasedChange.role)} <div className="flex items-center justify-between w-full rounded">
</p> <div className="text-base rounded cursor-pointer">
<p className="text-sm text-teal-800 font-bold bg-gray-200 px-1 rounded">
{CapitalizeFirstLetter(roleBasedChange.role)}
</p>
</div>
<FaChevronRight />
</div> </div>
<FaChevronRight />
</div> </div>
</div> ))}
))} {!defaultShowAllRoles && (
</div> <Analytics name="schema-registry-see-more-roles-btn">
)} <div
{isTagModalOpen && ( className="flex justify-center items-center cursor-pointer text-base hover:bg-gray-200 pt-1"
<AddSchemaRegistryTagDialog //here there is no need for onClick as clicking anywhere on a schemaCard sets the schemaCard as open and defaultShowAllRoles will be set to true
tagsList={tagsList} >
setTagsList={setTagsList} <span className="text-gray font-semibold">
entryHash={entryHash} See More Roles
onClose={() => { </span>
setIsTagModalOpen(false); <FaChevronDown />
}} </div>
/> </Analytics>
)} )}
<div className="flex flex-nowrap items-center justify-start mt-2">
{tagsList &&
tagsList.map(schemaRegistryTag => (
<div className="mr-2 ">
<SchemaTag
schemaRegistryTag={schemaRegistryTag}
onRemove={onRemoveTag}
/>
</div>
))}
<Analytics name="schema-registry-add-tag-btn">
<div
className="mt-[7px] ml-[-6px] pb-2"
onClick={() => setIsTagModalOpen(true)}
>
<span>
<IconTooltip message="Add a Tag" icon={<FaPlusCircle />} />
</span>
</div> </div>
</Analytics> </div>
</div> </div>
<div className="flex w-full border-b border-gray-300 my-2"></div> <div className="flex w-full border-b border-gray-300 my-1"></div>
</div> </div>
); );
}; };

View File

@ -3,6 +3,7 @@ import React from 'react';
import { Link, RouteComponentProps } from 'react-router'; import { Link, RouteComponentProps } from 'react-router';
import { FaCheckCircle, FaTimesCircle } from 'react-icons/fa'; import { FaCheckCircle, FaTimesCircle } from 'react-icons/fa';
import { LocationDescriptor } from 'history'; import { LocationDescriptor } from 'history';
import { sendTelemetryEvent } from '../../telemetry';
export type NavigationSidebarItem = { export type NavigationSidebarItem = {
key: string; key: string;
@ -50,6 +51,17 @@ export const NavigationSidebar = ({
? 'text-amber-500' ? 'text-amber-500'
: 'text-muted' : 'text-muted'
)} )}
onClick={() => {
// Check if section.key is "schema-registry" and trigger telemetry event
if (item.key === 'schema-registry') {
sendTelemetryEvent({
type: 'CLICK_EVENT',
data: {
id: 'schema-registry-settings-btn',
},
});
}
}}
> >
{item.label} {item.label}
</div> </div>

View File

@ -346,6 +346,11 @@ const routes = store => {
<Route path="details/:name" component={RestEndpointDetailsPage} /> <Route path="details/:name" component={RestEndpointDetailsPage} />
<Route path="edit/:name" component={CreateRestView} /> <Route path="edit/:name" component={CreateRestView} />
</Route> </Route>
<Route path="schema-registry" component={SchemaRegistryContainer} />
<Route
path="schema-registry/:id"
component={SchemaRegistryContainer}
/>
<Route path="allow-list"> <Route path="allow-list">
<IndexRedirect to="detail" /> <IndexRedirect to="detail" />
<Route <Route