Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
43 changes: 0 additions & 43 deletions src/components/Learn/LearnComingSoon.tsx

This file was deleted.

117 changes: 117 additions & 0 deletions src/components/Learn/ToursLibrary.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,117 @@
import { Badge } from "@/components/ui/badge";
import { Button } from "@/components/ui/button";
import {
Card,
CardContent,
CardDescription,
CardHeader,
CardTitle,
} from "@/components/ui/card";
import { Icon } from "@/components/ui/icon";
import { BlockStack, InlineStack } from "@/components/ui/layout";
import { Heading, Paragraph, Text } from "@/components/ui/typography";
import { tracking } from "@/utils/tracking";

import {
type Tour,
TOUR_DIFFICULTY_BLURBS,
TOUR_DIFFICULTY_COLORS,
TOUR_DIFFICULTY_ICONS,
TOUR_DIFFICULTY_ORDER,
type TourDifficulty,
tours,
} from "./tours";

function TourCard({ tour }: { tour: Tour }) {
return (
<Card className="h-full py-4 gap-2 hover:border-primary/40 hover:shadow-md transition-all duration-200">
<CardHeader className="px-4 gap-2">
<CardTitle className="text-sm leading-snug">{tour.title}</CardTitle>
<CardDescription className="text-sm">
{tour.description}
</CardDescription>
</CardHeader>
<CardContent className="px-4 mt-auto">
<InlineStack gap="3" blockAlign="center" align="space-between">
<InlineStack gap="2" blockAlign="center">
<Badge size="sm" variant="secondary">
{tour.area}
</Badge>
<Text size="xs" tone="subdued">
{tour.duration}
</Text>
</InlineStack>
<Button
size="sm"
variant="ghost"
{...tracking("learning_hub.tours.start", { tour_id: tour.id })}
>
Start tour
<Icon name="Play" size="sm" aria-hidden="true" />
</Button>
</InlineStack>
</CardContent>
</Card>
);
}

function DifficultySection({
difficulty,
tours: difficultyTours,
}: {
difficulty: TourDifficulty;
tours: Tour[];
}) {
if (difficultyTours.length === 0) {
return null;
}

return (
<BlockStack gap="3">
<BlockStack gap="1">
<InlineStack gap="2" blockAlign="center">
<Icon
name={TOUR_DIFFICULTY_ICONS[difficulty]}
size="md"
className={TOUR_DIFFICULTY_COLORS[difficulty]}
aria-hidden="true"
/>
<Heading level={3}>{difficulty}</Heading>
<Text size="xs" tone="subdued">
{difficultyTours.length}{" "}
{difficultyTours.length === 1 ? "tour" : "tours"}
</Text>
</InlineStack>
<Paragraph size="sm" tone="subdued">
{TOUR_DIFFICULTY_BLURBS[difficulty]}
</Paragraph>
</BlockStack>
<div className="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-3 gap-4">
{difficultyTours.map((tour) => (
<TourCard key={tour.id} tour={tour} />
))}
</div>
</BlockStack>
);
}

export function ToursLibrary() {
const buckets = new Map<TourDifficulty, Tour[]>();
for (const tour of tours) {
const list = buckets.get(tour.difficulty) ?? [];
list.push(tour);
buckets.set(tour.difficulty, list);
}

return (
<BlockStack gap="8">
{TOUR_DIFFICULTY_ORDER.map((difficulty) => (
<DifficultySection
key={difficulty}
difficulty={difficulty}
tours={buckets.get(difficulty) ?? []}
/>
))}
</BlockStack>
);
}
90 changes: 90 additions & 0 deletions src/components/Learn/tours.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,90 @@
[
{
"id": "first-pipeline",
"title": "Build your first pipeline",
"description": "Walk through dragging components from the library, wiring tasks together, and submitting your very first run.",
"difficulty": "Beginner",
"duration": "4 min",
"area": "Editor"
},
{
"id": "navigating-editor",
"title": "Find your way around the editor",
"description": "Get oriented with the canvas, side panels, properties view, and the menu bar so you know where every feature lives.",
"difficulty": "Beginner",
"duration": "3 min",
"area": "Editor"
},
{
"id": "component-library-basics",
"title": "Browse the component library",
"description": "Learn how to search, filter, favorite, and inspect components before dropping them on the canvas.",
"difficulty": "Beginner",
"duration": "3 min",
"area": "Library"
},
{
"id": "submitting-and-monitoring",
"title": "Submit a run and watch it execute",
"description": "Submit a pipeline, locate it in All Runs, and step through task logs as the run progresses.",
"difficulty": "Beginner",
"duration": "4 min",
"area": "Runs"
},
{
"id": "using-secrets",
"title": "Use secrets without leaking credentials",
"description": "Create a secret, reference it from a task via the lightning-bolt menu, and confirm it never lands in the pipeline YAML.",
"difficulty": "Intermediate",
"duration": "3 min",
"area": "Security"
},
{
"id": "create-component",
"title": "Create a custom component in the browser",
"description": "Author a Python or YAML component using the in-app editor, no local development environment required.",
"difficulty": "Intermediate",
"duration": "5 min",
"area": "Library"
},
{
"id": "subgraphs",
"title": "Group tasks into reusable subgraphs",
"description": "Select related tasks, bundle them into a subgraph, and use the Pipeline Tree window to navigate nested levels.",
"difficulty": "Intermediate",
"duration": "4 min",
"area": "Editor"
},
{
"id": "templatized-run-names",
"title": "Templatize run names for experiment tracking",
"description": "Enable the beta feature and craft a name template so every submitted run gets a unique, scannable label.",
"difficulty": "Intermediate",
"duration": "3 min",
"area": "Runs"
},
{
"id": "caching",
"title": "Save hours with task-level caching",
"description": "Understand how cache keys are derived, when caches hit, and how to disable caching on a single task when you must.",
"difficulty": "Advanced",
"duration": "5 min",
"area": "Performance"
},
{
"id": "github-libraries",
"title": "Register a GitHub repo as a component source",
"description": "Point Tangle at a shared GitHub repository to pull in an entire library of team components at once.",
"difficulty": "Advanced",
"duration": "4 min",
"area": "Library"
},
{
"id": "multinode-tasks",
"title": "Configure a multi-node task",
"description": "Set the multi-node annotation, reference per-node system data, and submit a distributed run across multiple workers.",
"difficulty": "Advanced",
"duration": "6 min",
"area": "Compute"
}
]
40 changes: 40 additions & 0 deletions src/components/Learn/tours.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
import type { IconName } from "@/components/ui/icon";

import toursData from "./tours.json";

export type TourDifficulty = "Beginner" | "Intermediate" | "Advanced";

export interface Tour {
id: string;
title: string;
description: string;
difficulty: TourDifficulty;
duration: string;
area: string;
}

export const tours = toursData as Tour[];

export const TOUR_DIFFICULTY_ORDER: TourDifficulty[] = [
"Beginner",
"Intermediate",
"Advanced",
];

export const TOUR_DIFFICULTY_ICONS: Record<TourDifficulty, IconName> = {
Beginner: "Sprout",
Intermediate: "Mountain",
Advanced: "Trophy",
};

export const TOUR_DIFFICULTY_COLORS: Record<TourDifficulty, string> = {
Beginner: "text-emerald-500",
Intermediate: "text-amber-500",
Advanced: "text-violet-500",
};

export const TOUR_DIFFICULTY_BLURBS: Record<TourDifficulty, string> = {
Beginner: "Start here. No prior knowledge required.",
Intermediate: "Layer in workflows that build on the basics.",
Advanced: "Complex features and edge cases worth knowing.",
};
10 changes: 3 additions & 7 deletions src/routes/Dashboard/Learn/LearnToursView.tsx
Original file line number Diff line number Diff line change
@@ -1,21 +1,17 @@
import { LearnComingSoon } from "@/components/Learn/LearnComingSoon";
import { LearnPageHeader } from "@/components/Learn/LearnPageHeader";
import { ToursLibrary } from "@/components/Learn/ToursLibrary";
import { BlockStack } from "@/components/ui/layout";

export function LearnToursView() {
return (
<BlockStack gap="8">
<LearnPageHeader
title="Guided Tours"
description="Interactive step-by-step walkthroughs of features across the app."
description="Interactive step-by-step walkthroughs of features across Tangle."
icon="Compass"
backTo="/learn"
/>
<LearnComingSoon
title="Tours are on the way"
description="Launch interactive tours that highlight UI elements and walk you through real workflows."
icon="Compass"
/>
<ToursLibrary />
</BlockStack>
);
}
Loading