From 90f741c94d4abefdee044b1ae39faca1adfd3dee Mon Sep 17 00:00:00 2001 From: Vladimir Klimontovich Date: Fri, 1 Dec 2023 15:03:30 -0500 Subject: [PATCH] feat: more internal links --- .../ConfigObjectEditor/EditorTitle.tsx | 2 +- .../ConnectionsDiagram/ConnectionsDiagram.tsx | 2 - .../EditorToolbar/EditorToolbar.tsx | 31 +++++++ .../components/ObjectTitle/ObjectTitle.tsx | 10 ++- .../PageLayout/WorkspacePageLayout.tsx | 2 +- .../pages/[workspaceId]/connections/index.tsx | 21 +---- .../pages/[workspaceId]/destinations.tsx | 40 ++++++++- webapps/console/pages/[workspaceId]/index.tsx | 84 ++++++++++++++----- .../console/pages/[workspaceId]/services.tsx | 5 +- .../console/pages/[workspaceId]/streams.tsx | 58 ++++++++++--- .../pages/[workspaceId]/syncs/index.tsx | 34 ++++---- webapps/console/styles/globals.css | 3 + 12 files changed, 218 insertions(+), 74 deletions(-) create mode 100644 webapps/console/components/EditorToolbar/EditorToolbar.tsx diff --git a/webapps/console/components/ConfigObjectEditor/EditorTitle.tsx b/webapps/console/components/ConfigObjectEditor/EditorTitle.tsx index 7c129e233..6848edf29 100644 --- a/webapps/console/components/ConfigObjectEditor/EditorTitle.tsx +++ b/webapps/console/components/ConfigObjectEditor/EditorTitle.tsx @@ -11,7 +11,7 @@ export type EditorTitleProps = { export const EditorTitle: React.FC = ({ title, subtitle, onBack }) => { return ( <> -
+

{title}

} type="link" size="small" onClick={onBack}> diff --git a/webapps/console/components/ConnectionsDiagram/ConnectionsDiagram.tsx b/webapps/console/components/ConnectionsDiagram/ConnectionsDiagram.tsx index 0064dbac7..3414f81fd 100644 --- a/webapps/console/components/ConnectionsDiagram/ConnectionsDiagram.tsx +++ b/webapps/console/components/ConnectionsDiagram/ConnectionsDiagram.tsx @@ -252,7 +252,6 @@ export const ConnectionsDiagram: React.FC = ({ key={s.id} ref={connectorsRef.current[idx] as any} className="cursor-pointer mb-4" - onClick={() => console.log("Clicked", s.id)} onMouseOver={() => { setMouseOverSrc(s.id); }} @@ -281,7 +280,6 @@ export const ConnectionsDiagram: React.FC = ({ key={s.id} ref={srcRefs.current[idx] as any} className="cursor-pointer mb-4" - onClick={() => console.log("Clicked", s.id)} onMouseOver={() => { setMouseOverSrc(s.id); }} diff --git a/webapps/console/components/EditorToolbar/EditorToolbar.tsx b/webapps/console/components/EditorToolbar/EditorToolbar.tsx new file mode 100644 index 000000000..ab1a25540 --- /dev/null +++ b/webapps/console/components/EditorToolbar/EditorToolbar.tsx @@ -0,0 +1,31 @@ +import React, { ReactNode } from "react"; +import classNames from "classnames"; +import Link from "next/link"; + +export type EditorToolbarProps = { + items: { + title: ReactNode; + icon: ReactNode; + href: string; + onClick?: () => void; + }[]; + className?: string; +}; + +export const EditorToolbar: React.FC = ({ items, className }) => { + return ( +
+ {items.map(({ href, icon, title, onClick }, index) => ( + +
{icon}
+ {title} + + ))} +
+ ); +}; diff --git a/webapps/console/components/ObjectTitle/ObjectTitle.tsx b/webapps/console/components/ObjectTitle/ObjectTitle.tsx index b68e035d7..6c8fd4dee 100644 --- a/webapps/console/components/ObjectTitle/ObjectTitle.tsx +++ b/webapps/console/components/ObjectTitle/ObjectTitle.tsx @@ -1,10 +1,13 @@ import React from "react"; +import Link from "next/link"; +import { FaExternalLinkAlt } from "react-icons/fa"; export const ObjectTitle: React.FC<{ size?: "small" | "default" | "large"; icon?: React.ReactNode; title: string | React.ReactNode; -}> = ({ icon, title, size = "default" }) => { + href?: string; +}> = ({ icon, title, size = "default", href }) => { const iconClassName = (() => { switch (size) { case "small": @@ -19,6 +22,11 @@ export const ObjectTitle: React.FC<{
{icon &&
{icon}
}
{title}
+ {href && ( + + + + )}
); }; diff --git a/webapps/console/components/PageLayout/WorkspacePageLayout.tsx b/webapps/console/components/PageLayout/WorkspacePageLayout.tsx index c6d3c4e48..ea7da4f7c 100644 --- a/webapps/console/components/PageLayout/WorkspacePageLayout.tsx +++ b/webapps/console/components/PageLayout/WorkspacePageLayout.tsx @@ -533,7 +533,7 @@ function PageHeader() { title: "Connectors", icon: , items: [ - { title: "Connected Services", path: "/services", icon: }, + { title: "Service Connections", path: "/services", icon: }, { title: "Syncs", path: "/syncs", icon: }, { title: "All Logs", path: "/syncs/tasks", icon: }, ], diff --git a/webapps/console/pages/[workspaceId]/connections/index.tsx b/webapps/console/pages/[workspaceId]/connections/index.tsx index e8e638ac1..d35c68213 100644 --- a/webapps/console/pages/[workspaceId]/connections/index.tsx +++ b/webapps/console/pages/[workspaceId]/connections/index.tsx @@ -19,7 +19,7 @@ import { TableProps } from "antd/es/table/InternalTable"; import { ColumnType, SortOrder } from "antd/es/table/interface"; import { Activity, Edit3, Inbox, XCircle } from "lucide-react"; import { PlusOutlined } from "@ant-design/icons"; -import { JitsuButton, WJitsuButton } from "../../../components/JitsuButton/JitsuButton"; +import { WJitsuButton } from "../../../components/JitsuButton/JitsuButton"; import { DestinationTitle } from "../destinations"; import { ButtonGroup, ButtonProps } from "../../../components/ButtonGroup/ButtonGroup"; import { StreamTitle } from "../streams"; @@ -152,14 +152,7 @@ function ConnectionsTable({ links, streams, destinations, functions, reloadCallb } return (
- - } - /> +
); }, @@ -179,17 +172,9 @@ function ConnectionsTable({ links, streams, destinations, functions, reloadCallb if (!destination) { return
Destination not found
; } - const coreDestinationType = getCoreDestinationType(destination.destinationType); return (
- - } - className="link" - size="small" - href={`/${workspace.id}/destinations?id=${link.toId}`} - /> +
); }, diff --git a/webapps/console/pages/[workspaceId]/destinations.tsx b/webapps/console/pages/[workspaceId]/destinations.tsx index 5ef48ec54..8f3626be6 100644 --- a/webapps/console/pages/[workspaceId]/destinations.tsx +++ b/webapps/console/pages/[workspaceId]/destinations.tsx @@ -38,6 +38,7 @@ import { useQueryStringState } from "../../lib/useQueryStringState"; import { CustomWidgetProps } from "../../components/ConfigObjectEditor/Editors"; import { Htmlizer } from "../../components/Htmlizer/Htmlizer"; import omit from "lodash/omit"; +import { EditorToolbar } from "../../components/EditorToolbar/EditorToolbar"; const log = getLog("destinations"); const Loader: React.FC<{}> = () => { @@ -198,13 +199,16 @@ function getEditorComponent(editor: string, editorProps?: any) { export const DestinationTitle: React.FC<{ destination?: DestinationConfig; size?: "small" | "default" | "large"; - title?: (d: DestinationConfig, t: DestinationType) => string | React.ReactNode; -}> = ({ destination, title = (d, t) => d.name, size = "default" }) => { + title?: (d: DestinationConfig, t: DestinationType) => React.ReactNode; + link?: boolean; +}> = ({ destination, title = (d, t) => d.name, size = "default", link }) => { + const w = useWorkspace(); const destinationType = coreDestinationsMap[destination?.destinationType ?? ""]; return ( ); @@ -668,6 +672,38 @@ const DestinationsList: React.FC<{ type?: string }> = ({ type }) => {
); }, + subtitle: (obj: DestinationConfig, isNew: boolean) => { + if (isNew) { + return undefined; + } + + return ( + , + href: `/${workspace.slugOrId}/sql?destinationId=${obj.id}`, + } + : undefined, + { + title: "Connected Sources", + icon: , + href: `/${workspace.slugOrId}/connections?destination=${obj.id}`, + }, + { + title: "Syncs", + icon: , + href: `/${workspace.slugOrId}/syncs?destination=${obj.id}`, + }, + ].filter(Boolean) as any + } + className="mb-4" + /> + ); + }, }; return ( <> diff --git a/webapps/console/pages/[workspaceId]/index.tsx b/webapps/console/pages/[workspaceId]/index.tsx index 1ff4fcbbe..31c2666bd 100644 --- a/webapps/console/pages/[workspaceId]/index.tsx +++ b/webapps/console/pages/[workspaceId]/index.tsx @@ -30,7 +30,7 @@ function HoverBorder({ children, forceHover }: { children: ReactNode; forceHover return (
setHover(true)} @@ -42,11 +42,23 @@ function HoverBorder({ children, forceHover }: { children: ReactNode; forceHover ); } +function ConditionalBadge({ icon, tooltip, children }: { icon?: ReactNode; tooltip?: ReactNode; children: ReactNode }) { + if (!icon) { + return <>{children}; + } + return ( + {icon} : icon}> + {children} + + ); +} + function Card({ title, configLink, icon, selected, + badge, actions, }: { title: string; @@ -54,30 +66,36 @@ function Card({ icon: ReactNode; actions?: { label: ReactNode; icon?: ReactNode; href: string }[]; selected?: boolean; + badge?: { + icon: ReactNode; + tooltip?: ReactNode; + }; }) { const card = ( - -
-
-
-
{icon}
-
- {title} + + +
+
+
+
{icon}
+
+ {title} +
+ {actions && actions.length > 0 && ( + , + }} + items={actions.map(a => ({ ...a, collapsed: true }))} + /> + )}
- {actions && actions.length > 0 && ( - , - }} - items={actions.map(a => ({ ...a, collapsed: true }))} - /> - )}
-
- + + ); return configLink ? {card} : card; } @@ -203,6 +221,19 @@ function WorkspaceOverview(props: { src={`/api/sources/logo?package=${encodeURIComponent(cfg.package)}&protocol=${cfg.protocol}`} /> } + badge={ + links?.find(l => l.fromId === id) + ? undefined + : { + icon: ( +
+ ! +
+ ), + tooltip: + "The source is not connected to any destination. Connect it to any destination to start seeing the data", + } + } title={name || id} configLink={`/${workspace.slug || workspace.id}/services?id=${id}`} actions={[ @@ -233,6 +264,19 @@ function WorkspaceOverview(props: { selected={forceSelect} icon={} title={name || id} + badge={ + links.find(l => l.fromId === id) + ? undefined + : { + icon: ( +
+ ! +
+ ), + tooltip: + "The source is not connected to any destination. Connect it to any destination to start seeing the data", + } + } configLink={`/${workspace.slug || workspace.id}/streams?id=${id}`} actions={[ { label: "Edit", href: `/streams?id=${id}`, icon: }, diff --git a/webapps/console/pages/[workspaceId]/services.tsx b/webapps/console/pages/[workspaceId]/services.tsx index 1a762a7e4..954554d9a 100644 --- a/webapps/console/pages/[workspaceId]/services.tsx +++ b/webapps/console/pages/[workspaceId]/services.tsx @@ -35,7 +35,9 @@ export const ServiceTitle: React.FC<{ service?: ServiceConfig; size?: "small" | "default" | "large"; title?: (d: ServiceConfig) => string | React.ReactNode; -}> = ({ service, title = d => d.name, size = "default" }) => { + link?: boolean; +}> = ({ service, title = d => d.name, size = "default", link }) => { + const workspace = useWorkspace(); return ( } size={size} + href={link && service ? `/${workspace.slugOrId}/services?id=${service?.id}` : undefined} title={service ? title(service) : "Unknown service"} /> ); diff --git a/webapps/console/pages/[workspaceId]/streams.tsx b/webapps/console/pages/[workspaceId]/streams.tsx index f6d062c11..8829a12cb 100644 --- a/webapps/console/pages/[workspaceId]/streams.tsx +++ b/webapps/console/pages/[workspaceId]/streams.tsx @@ -26,6 +26,7 @@ import { CustomWidgetProps } from "../../components/ConfigObjectEditor/Editors"; import { useLinksQuery } from "../../lib/queries"; import { toURL } from "../../lib/shared/url"; import JSON5 from "json5"; +import { EditorToolbar } from "../../components/EditorToolbar/EditorToolbar"; const Streams: React.FC = () => { return ( @@ -336,11 +337,13 @@ export const StreamTitle: React.FC<{ stream?: StreamConfig; size?: "small" | "default" | "large"; title?: (s: StreamConfig) => string | React.ReactNode; -}> = ({ stream, title = s => s.name, size = "default" }) => { + link?: boolean; +}> = ({ stream, title = s => s.name, size = "default", link }) => { return ( } size={size} + href={stream && link ? `/${stream.workspaceId}/streams?id=${stream?.id}` : undefined} title={stream ? title(stream) : "Unknown stream"} /> ); @@ -364,17 +367,48 @@ const StreamsList: React.FC<{}> = () => { const config: ConfigEditorProps = { subtitle: (obj, isNew) => !isNew && ( - { - router.replace(`/${workspace.slug || workspace.id}/streams?id=${obj.id}&implementationFor=${obj.id}`); - setImplementationDocumentationId(obj.id); - }} - className="flex items-center space-x-2 text-primary" - > - - Setup Instructions - + , + href: `/${workspace.slugOrId}/streams?id=${obj.id}&implementationFor=${obj.id}`, + onClick: () => { + setImplementationDocumentationId(obj.id); + }, + }, + { + title: "Live Events", + icon: , + href: toURL(`/${workspace.slugOrId}/data`, { + query: JSON5.stringify({ + activeView: "incoming", + viewState: { incoming: { actorId: obj.id } }, + }), + }), + }, + { + title: "Connected Destinations", + icon: , + href: `/${workspace.slugOrId}/connections?source=${obj.id}`, + }, + ]} + className="mb-4" + /> + //
+ // + // { + // router.replace(`/${workspace.slug || workspace.id}/streams?id=${obj.id}&implementationFor=${obj.id}`); + // setImplementationDocumentationId(obj.id); + // }} + // className="flex items-center space-x-2 border border-textLight px-2 py-1 rounded text-textLight text-xs" + // > + // + // Setup Instructions + // + //
), objectType: StreamConfig, icon: s => , diff --git a/webapps/console/pages/[workspaceId]/syncs/index.tsx b/webapps/console/pages/[workspaceId]/syncs/index.tsx index 0a6cf62b6..87f282dfc 100644 --- a/webapps/console/pages/[workspaceId]/syncs/index.tsx +++ b/webapps/console/pages/[workspaceId]/syncs/index.tsx @@ -162,29 +162,31 @@ function SyncsTable({ links, services, destinations, reloadCallback }: RemoteEnt }; const columns: ColumnType[] = [ { - title: "Sync", - width: "70%", - sortOrder: sorting.columns?.find(s => s.field === "Service")?.order, + title: "From", + width: "35%", + sortOrder: sorting.columns?.find(s => s.field === "From")?.order, sorter: (a, b) => { const serviceA = servicesById[a.fromId]; const serviceB = servicesById[b.fromId]; - return (serviceA.name || "").localeCompare(serviceB.name || ""); + return (serviceA?.name || "").localeCompare(serviceB?.name || ""); }, render: (text, link) => { const service = servicesById[link.fromId]; + return service ? : "Not Found"; + }, + }, + { + title: "To", + width: "35%", + sortOrder: sorting.columns?.find(s => s.field === "To")?.order, + sorter: (a, b) => { + const destinationA = destinations[a.toId]; + const destinationB = destinations[b.toId]; + return (destinationA?.name || "").localeCompare(destinationB?.name || ""); + }, + render: (text, link) => { const destination = destinationsById[link.toId]; - - return ( -
- -
- ); + return destination ? : "Not Found"; }, }, { diff --git a/webapps/console/styles/globals.css b/webapps/console/styles/globals.css index 04454e953..061e097f6 100644 --- a/webapps/console/styles/globals.css +++ b/webapps/console/styles/globals.css @@ -46,6 +46,9 @@ body { .debug-border { border: 1px solid red; } +.debug-border2 { + border: 1px solid green; +} /* See https://github.com/ant-design/ant-design/issues/13074