From 097eed65c1a0ba837a7a7add7768ed4cd317d525 Mon Sep 17 00:00:00 2001 From: James Borlase Date: Wed, 29 Jul 2026 10:09:46 +0200 Subject: [PATCH 1/6] Replace tutorial card grid with compact table view Tutorials now render as a compact table with columns for use case, topic, technology, and progress. Rows collapse into labeled stacked entries on narrow viewports. Co-Authored-By: Claude Fable 5 --- .../tutorials/TutorialList/index.tsx | 6 +- src/components/tutorials/TutorialTable.tsx | 159 ++++++++++++++++++ 2 files changed, 162 insertions(+), 3 deletions(-) create mode 100644 src/components/tutorials/TutorialTable.tsx diff --git a/src/components/tutorials/TutorialList/index.tsx b/src/components/tutorials/TutorialList/index.tsx index df49664d0..aefd9a5aa 100644 --- a/src/components/tutorials/TutorialList/index.tsx +++ b/src/components/tutorials/TutorialList/index.tsx @@ -2,7 +2,7 @@ import React, { FC, useState, useEffect } from 'react' import Head from '@docusaurus/Head' import { Meta, Tutorial } from '../models' import { getSteps } from '../utils' -import TutorialGrid from '../TutorialGrid' +import TutorialTable from '../TutorialTable' import TutorialSearch from '../TutorialSearch' import { Accordion, AccordionContent, AccordionItem, AccordionTrigger } from '@site/src/components/ui/accordion' import { @@ -185,8 +185,8 @@ const TutorialList: FC = () => { {/* Main content - Center aligned with page */}
- {/* Tutorial Grid */} - + {/* Tutorial list */} +
diff --git a/src/components/tutorials/TutorialTable.tsx b/src/components/tutorials/TutorialTable.tsx new file mode 100644 index 000000000..e979dd826 --- /dev/null +++ b/src/components/tutorials/TutorialTable.tsx @@ -0,0 +1,159 @@ +import React from 'react' +import Link from '@docusaurus/Link' +import { cn } from '@site/src/lib/utils' +import { Tutorial } from './models' +import { getSteps } from './utils' + +// Function to get tutorial progress from localStorage +function getTutorialProgress( + tutorialId: string, + totalSteps: number +): { completed: number; percentage: number } { + if (typeof window === 'undefined') return { completed: 0, percentage: 0 } + + const storedVisited = localStorage.getItem(`tutorial-progress-${tutorialId}`) + const visitedSteps = storedVisited + ? new Set(JSON.parse(storedVisited)) + : new Set() + const completed = visitedSteps.size + const percentage = + totalSteps > 0 ? Math.round((completed / totalSteps) * 100) : 0 + return { completed, percentage } +} + +function getFirstStepPath(tutorialId: string): string | null { + const steps = getSteps(tutorialId) + if (steps.length === 0) { + return null + } + return steps[0].path +} + +const gridColumns = + 'lg:grid lg:grid-cols-[minmax(0,2.6fr)_minmax(0,1fr)_minmax(0,1fr)_minmax(0,1fr)_4.5rem] lg:gap-4 lg:items-center' + +const TutorialRow: React.FC<{ tutorial: Tutorial }> = ({ tutorial }) => { + const firstStep = getFirstStepPath(tutorial.meta.id) + const progress = getTutorialProgress(tutorial.meta.id, tutorial.steps.length) + + const rowContent = ( +
+ {/* Tutorial title and description */} +
+
+ {firstStep + ? tutorial.meta.title + : `${tutorial.meta.title} (No steps found)`} +
+

+ {tutorial.meta.description} +

+
+ + {/* Use case */} +
+ + Use case: + + {tutorial.meta.useCase} +
+ + {/* Topic */} +
+ Topic: + {tutorial.meta.label} +
+ + {/* Technologies */} +
+ + Technology: + + {tutorial.meta.technologies.length > 0 ? ( + tutorial.meta.technologies.join(', ') + ) : ( + + )} +
+ + {/* Progress */} +
+ + Progress: + + + {progress.completed}/{tutorial.steps.length} + +
+
+
+
+
+ ) + + return firstStep ? ( + + {rowContent} + + ) : ( +
{rowContent}
+ ) +} + +interface TutorialTableProps { + tutorials: Tutorial[] + className?: string +} + +export const TutorialTable: React.FC = ({ + tutorials, + className, +}) => { + if (tutorials.length === 0) { + return ( +
+

+ No tutorials found matching your criteria. +

+
+ ) + } + + return ( +
+ {/* Column headers - desktop only */} +
+
Tutorial
+
Use case
+
Topic
+
Technology
+
Progress
+
+ +
+ {tutorials.map((tutorial) => ( + + ))} +
+
+ ) +} + +export default TutorialTable From bfc34d1bc529313f97f8a633d89abb1d5c7c0cb1 Mon Sep 17 00:00:00 2001 From: James Borlase Date: Wed, 29 Jul 2026 15:13:53 +0200 Subject: [PATCH 2/6] Increase font size and spacing in tutorial table Address PM feedback that the compact table felt cramped: bump titles and cell text up a size, add row padding, and widen column gaps. Co-Authored-By: Claude Fable 5 --- src/components/tutorials/TutorialTable.tsx | 24 +++++++++++----------- 1 file changed, 12 insertions(+), 12 deletions(-) diff --git a/src/components/tutorials/TutorialTable.tsx b/src/components/tutorials/TutorialTable.tsx index e979dd826..948380cdd 100644 --- a/src/components/tutorials/TutorialTable.tsx +++ b/src/components/tutorials/TutorialTable.tsx @@ -30,7 +30,7 @@ function getFirstStepPath(tutorialId: string): string | null { } const gridColumns = - 'lg:grid lg:grid-cols-[minmax(0,2.6fr)_minmax(0,1fr)_minmax(0,1fr)_minmax(0,1fr)_4.5rem] lg:gap-4 lg:items-center' + 'lg:grid lg:grid-cols-[minmax(0,2.6fr)_minmax(0,1fr)_minmax(0,1fr)_minmax(0,1fr)_5rem] lg:gap-5 lg:items-center' const TutorialRow: React.FC<{ tutorial: Tutorial }> = ({ tutorial }) => { const firstStep = getFirstStepPath(tutorial.meta.id) @@ -39,25 +39,25 @@ const TutorialRow: React.FC<{ tutorial: Tutorial }> = ({ tutorial }) => { const rowContent = (
{/* Tutorial title and description */} -
-
+
+
{firstStep ? tutorial.meta.title : `${tutorial.meta.title} (No steps found)`}
-

+

{tutorial.meta.description}

{/* Use case */} -
+
Use case: @@ -65,13 +65,13 @@ const TutorialRow: React.FC<{ tutorial: Tutorial }> = ({ tutorial }) => {
{/* Topic */} -
+
Topic: {tutorial.meta.label}
{/* Technologies */} -
+
Technology: @@ -84,13 +84,13 @@ const TutorialRow: React.FC<{ tutorial: Tutorial }> = ({ tutorial }) => { {/* Progress */}
- + Progress: - + {progress.completed}/{tutorial.steps.length} -
+
= ({ {/* Column headers - desktop only */}
From aa89ba745affc69562d0977ccdc063f03986acc8 Mon Sep 17 00:00:00 2001 From: James Borlase Date: Wed, 29 Jul 2026 16:18:27 +0200 Subject: [PATCH 3/6] Align tutorial table next to filter sidebar The main content area centered the table in the remaining width, which opened a large gutter between the filters and the table on wide screens. Left-align it instead. Co-Authored-By: Claude Fable 5 --- src/components/tutorials/TutorialList/index.tsx | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/components/tutorials/TutorialList/index.tsx b/src/components/tutorials/TutorialList/index.tsx index aefd9a5aa..489488d80 100644 --- a/src/components/tutorials/TutorialList/index.tsx +++ b/src/components/tutorials/TutorialList/index.tsx @@ -182,8 +182,8 @@ const TutorialList: FC = () => {
)} - {/* Main content - Center aligned with page */} -
+ {/* Main content - Aligned next to the filter sidebar */} +
{/* Tutorial list */} From 35b86d0eb561c8aaa06c122530e644433c784dcd Mon Sep 17 00:00:00 2001 From: James Borlase Date: Wed, 29 Jul 2026 17:26:47 +0200 Subject: [PATCH 4/6] Drop topic column and stack progress count above bar Give the tutorial title and description more room by removing the topic column, and use the two-line row height to place the step count above the progress bar. Co-Authored-By: Claude Fable 5 --- src/components/tutorials/TutorialTable.tsx | 11 ++--------- 1 file changed, 2 insertions(+), 9 deletions(-) diff --git a/src/components/tutorials/TutorialTable.tsx b/src/components/tutorials/TutorialTable.tsx index 948380cdd..ae62606b5 100644 --- a/src/components/tutorials/TutorialTable.tsx +++ b/src/components/tutorials/TutorialTable.tsx @@ -30,7 +30,7 @@ function getFirstStepPath(tutorialId: string): string | null { } const gridColumns = - 'lg:grid lg:grid-cols-[minmax(0,2.6fr)_minmax(0,1fr)_minmax(0,1fr)_minmax(0,1fr)_5rem] lg:gap-5 lg:items-center' + 'lg:grid lg:grid-cols-[minmax(0,3.2fr)_minmax(0,1fr)_minmax(0,1fr)_5rem] lg:gap-5 lg:items-center' const TutorialRow: React.FC<{ tutorial: Tutorial }> = ({ tutorial }) => { const firstStep = getFirstStepPath(tutorial.meta.id) @@ -64,12 +64,6 @@ const TutorialRow: React.FC<{ tutorial: Tutorial }> = ({ tutorial }) => { {tutorial.meta.useCase}
- {/* Topic */} -
- Topic: - {tutorial.meta.label} -
- {/* Technologies */}
@@ -83,7 +77,7 @@ const TutorialRow: React.FC<{ tutorial: Tutorial }> = ({ tutorial }) => {
{/* Progress */} -
+
Progress: @@ -142,7 +136,6 @@ export const TutorialTable: React.FC = ({ >
Tutorial
Use case
-
Topic
Technology
Progress
From c7e00e0704b64ec13b6a304b3dcaae29c9d5e10d Mon Sep 17 00:00:00 2001 From: James Borlase Date: Wed, 29 Jul 2026 17:51:02 +0200 Subject: [PATCH 5/6] Remove doubled gap between filter sidebar and table The sidebar already pads its right edge, so the main content only needs right padding; keeping px-6 made the gutter twice the normal size. Co-Authored-By: Claude Fable 5 --- src/components/tutorials/TutorialList/index.tsx | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/components/tutorials/TutorialList/index.tsx b/src/components/tutorials/TutorialList/index.tsx index 489488d80..f23e9631f 100644 --- a/src/components/tutorials/TutorialList/index.tsx +++ b/src/components/tutorials/TutorialList/index.tsx @@ -182,8 +182,8 @@ const TutorialList: FC = () => {
)} - {/* Main content - Aligned next to the filter sidebar */} -
+ {/* Main content - Aligned next to the filter sidebar, which already pads its right edge */} +
{/* Tutorial list */} From 002c95e8f8dbed0ec960baed6c72a453ca64499c Mon Sep 17 00:00:00 2001 From: James Borlase Date: Thu, 30 Jul 2026 13:01:32 +0200 Subject: [PATCH 6/6] Align table headers with top of filter sidebar Add matching top padding to the main content area so the column headers sit level with the search box. Co-Authored-By: Claude Fable 5 --- src/components/tutorials/TutorialList/index.tsx | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/src/components/tutorials/TutorialList/index.tsx b/src/components/tutorials/TutorialList/index.tsx index f23e9631f..c64994820 100644 --- a/src/components/tutorials/TutorialList/index.tsx +++ b/src/components/tutorials/TutorialList/index.tsx @@ -182,8 +182,9 @@ const TutorialList: FC = () => {
)} - {/* Main content - Aligned next to the filter sidebar, which already pads its right edge */} -
+ {/* Main content - Aligned next to the filter sidebar, which already pads its right edge. + Top padding matches the sidebar's so the column headers line up with the search box. */} +
{/* Tutorial list */}