diff --git a/src/ui-app/components/landing/SlackBot.tsx b/src/ui-app/components/landing/SlackBot.tsx index e9228d4..d5b2c75 100644 --- a/src/ui-app/components/landing/SlackBot.tsx +++ b/src/ui-app/components/landing/SlackBot.tsx @@ -1,5 +1,3 @@ -import { IconArrowUpRight } from "@tabler/icons-react"; - export function SlackBot() { return (
@@ -14,16 +12,15 @@ export function SlackBot() { Your entire observability stack, accessible from Slack

- Ask anything about your infrastructure directly in Slack. Query - logs, metrics, traces, and alerts in plain English - the Parseable - bot responds with real data from your stack, right in the thread. + Query logs, metrics, traces, and alerts without leaving Slack, + right in the thread.

- - Set up the Slack bot{" "} - +
+ + {/* eslint-disable-next-line @next/next/no-img-element */} + Add to Slack + +
- {/* Right: mock Slack thread */} -
- {/* Slack header bar */} -
-
- - - -
- - # alerts-production - -
- {/* Messages */} -
- {/* Alert */} -
-
- - - - -
-
-
- - PagerDuty - - - 9:14 AM - -
-

- 🔴 P1 alert: Error - rate on{" "} - - api-gateway - {" "} - exceeded 5% for 3 min -

-
-
- {/* User question */} -
-
- - SK - -
-
-
- - Shivam - - - 9:15 AM - -
-

- @parseable show me the errors from api-gateway in the last - 10 minutes -

-
-
- {/* Bot response */} -
-
- Parseable -
-
-
- - Parseable - - - App - - - 9:15 AM - -
-
-

- Found{" "} - - 847 errors - {" "} - in the last 10 min - 94% from one route: -

-
-                      
-                        POST /v2/ingest → 503 Service Unavailable{"\n"}upstream:
-                        kafka-broker-3 (connection refused){"\n"}first seen:
-                        09:11:42 UTC
-                      
-                    
-

- Likely cause:{" "} - - kafka-broker-3 is unreachable - -

-
-
-
-
+ {/* Right: Slack thread preview */} +
+ {/* eslint-disable-next-line @next/next/no-img-element */} + Parseable Slack bot answering a data ingestion question in a thread
diff --git a/src/ui-app/components/landing/Tools.tsx b/src/ui-app/components/landing/Tools.tsx new file mode 100644 index 0000000..c78af06 --- /dev/null +++ b/src/ui-app/components/landing/Tools.tsx @@ -0,0 +1,175 @@ +import { useCallback, useState } from "react"; +import { IconCheck, IconCopy } from "@tabler/icons-react"; + +type CopyKey = string; + +function useCopy() { + const [copied, setCopied] = useState(null); + const copy = useCallback((text: string, key: CopyKey) => { + navigator.clipboard.writeText(text).catch(() => {}); + setCopied(key); + setTimeout(() => setCopied(null), 1500); + }, []); + return { copied, copy }; +} + +type ToolGroup = { group: string; tools: string[] }; + +const TOOL_GROUPS: ToolGroup[] = [ + { + group: "Datasets and events", + tools: [ + "list_datasets", + "get_dataset_info", + "get_dataset_schema", + "get_dataset_stats", + "sample_events", + ], + }, + { + group: "Queries", + tools: ["query_sql", "query_promql", "explain_query"], + }, + { + group: "Alerts", + tools: [ + "list_alerts", + "get_alert", + "list_alert_tags", + "enable_alert", + "disable_alert", + "evaluate_alert", + "create_alert", + ], + }, + { + group: "Alert targets", + tools: ["list_alert_targets", "get_alert_target", "create_alert_target"], + }, + { + group: "Access review", + tools: ["list_users", "get_user_roles", "list_roles", "get_role", "get_default_role"], + }, + { + group: "Cluster and retention", + tools: ["ping", "get_cluster_status", "get_cluster_metrics", "get_retention"], + }, +]; + +const TOOL_COUNT = TOOL_GROUPS.reduce((sum, g) => sum + g.tools.length, 0); + +const TOOLS_MARKDOWN = TOOL_GROUPS.map( + (g) => `## ${g.group}\n${g.tools.map((t) => `- ${t}`).join("\n")}`, +).join("\n\n"); + +function ToolListItem({ + index, + name, + copied, + onCopy, +}: { + index: number; + name: string; + copied: string | null; + onCopy: (text: string, key: string) => void; +}) { + const isCopied = copied === name; + return ( +
  • + +
  • + ); +} + +function ToolGroupCard({ group, tools }: ToolGroup) { + const { copied, copy } = useCopy(); + return ( +
    +

    + {group} +

    +
      + {tools.map((tool, i) => ( + + ))} +
    +
    + ); +} + +export function Tools() { + const { copied, copy } = useCopy(); + const isMarkdownCopied = copied === "tools-markdown"; + + return ( +
    +
    +
    +

    + The full Parseable toolkit, +
    + one call away +

    +

    + {TOOL_COUNT} tools across datasets, queries, alerts, and access + control. Your agent gets exactly the access you do. Nothing more, + nothing less. +

    + +
    + +
    + {TOOL_GROUPS.map((g) => ( + + ))} +
    +
    +
    + ); +} diff --git a/src/ui-app/pages/LandingPage.tsx b/src/ui-app/pages/LandingPage.tsx index 65267fa..ee8470f 100644 --- a/src/ui-app/pages/LandingPage.tsx +++ b/src/ui-app/pages/LandingPage.tsx @@ -5,7 +5,8 @@ import { Hero } from "../components/landing/Hero"; import { Nav } from "../components/landing/Nav"; import { Prompts } from "../components/landing/Prompts"; import { QuickSetup } from "../components/landing/QuickSetup"; -// import { SlackBot } from "../components/landing/SlackBot"; +import { SlackBot } from "../components/landing/SlackBot"; +import { Tools } from "../components/landing/Tools"; import { TwoWays } from "../components/landing/TwoWays"; export function LandingPage() { @@ -17,11 +18,10 @@ export function LandingPage() { - {/* Slack bot isn't published yet — re-enable by uncommenting the - import above and this line once it's live. */} - {/* */} + + diff --git a/src/ui-app/public/assets/slackbot-preview.png b/src/ui-app/public/assets/slackbot-preview.png new file mode 100644 index 0000000..9d03c31 Binary files /dev/null and b/src/ui-app/public/assets/slackbot-preview.png differ