build-valuecurve/apps/homepage/src/routes/notebooks/+page.svelte

163 lines
6.1 KiB
Svelte

<script lang="ts">
import Navbar from '$lib/components/Navbar.svelte';
import Footer from '$lib/components/Footer.svelte';
interface Notebook {
title: string;
description: string;
category: string;
colabUrl: string;
icon: string;
}
const notebooks: Notebook[] = [
{
title: 'Exploratory Data Analysis',
description: 'Exploratory data analysis using the Gapminder dataset. Learn data wrangling, visualization, and insights extraction.',
category: 'EDA',
colabUrl: 'https://colab.research.google.com/github/valuecurve/notebooks/blob/main/eda-gapminder.ipynb',
icon: '🌍'
},
{
title: 'Statistical Tests Practice',
description: 'Hands-on practice with t-tests, ANOVA, chi-square, and correlation analysis using real datasets.',
category: 'STATISTICS',
colabUrl: 'https://colab.research.google.com/github/valuecurve/notebooks/blob/main/statistical-tests.ipynb',
icon: '📊'
},
{
title: 'Linear Regression Deep Dive',
description: 'From simple to multiple regression. Understand assumptions, diagnostics, and interpretation.',
category: 'ML',
colabUrl: '#',
icon: '📈'
},
{
title: 'Classification Models',
description: 'Logistic regression, decision trees, and random forests. Compare model performance and interpret results.',
category: 'ML',
colabUrl: '#',
icon: '🎯'
}
];
let searchQuery = $state('');
let selectedCategory = $state('all');
const categories = ['all', 'EDA', 'STATISTICS', 'ML'];
const filteredNotebooks = $derived(
notebooks.filter(nb => {
const matchesSearch = nb.title.toLowerCase().includes(searchQuery.toLowerCase()) ||
nb.description.toLowerCase().includes(searchQuery.toLowerCase());
const matchesCategory = selectedCategory === 'all' || nb.category === selectedCategory;
return matchesSearch && matchesCategory;
})
);
function getCategoryColor(cat: string): string {
switch (cat.toUpperCase()) {
case 'EDA':
return 'bg-teal-100 text-teal-700';
case 'STATISTICS':
return 'bg-blue-100 text-blue-700';
case 'ML':
return 'bg-orange-100 text-orange-700';
default:
return 'bg-gray-100 text-gray-700';
}
}
</script>
<svelte:head>
<title>Notebooks | Build with AI</title>
<meta name="description" content="Interactive Jupyter notebooks for hands-on learning. Open directly in Google Colab." />
</svelte:head>
<div class="min-h-screen bg-gradient-to-b from-slate-50 to-white">
<Navbar />
<main class="pt-24 pb-16 px-6">
<div class="max-w-6xl mx-auto">
<!-- Header -->
<div class="text-center mb-12">
<h1 class="text-4xl font-bold text-gray-900 mb-4">Notebooks</h1>
<p class="text-gray-600 max-w-2xl mx-auto">
Interactive Jupyter notebooks for hands-on learning. Open directly in Google Colab - no setup required.
</p>
</div>
<!-- Search & Filters -->
<div class="flex flex-col sm:flex-row gap-4 mb-8">
<div class="flex-1">
<input
type="text"
placeholder="Search notebooks..."
bind:value={searchQuery}
class="w-full px-4 py-2.5 border border-gray-200 rounded-lg focus:ring-2 focus:ring-primary-500 focus:border-primary-500 transition-all"
/>
</div>
<select
bind:value={selectedCategory}
class="px-4 py-2.5 border border-gray-200 rounded-lg focus:ring-2 focus:ring-primary-500 focus:border-primary-500 bg-white"
>
<option value="all">All Categories</option>
{#each categories.filter(c => c !== 'all') as category}
<option value={category}>{category}</option>
{/each}
</select>
</div>
<!-- Notebooks Grid -->
<div class="grid md:grid-cols-2 lg:grid-cols-3 gap-6">
{#each filteredNotebooks as notebook}
{@const isAvailable = notebook.colabUrl !== '#'}
<div class="group block p-6 bg-white rounded-2xl border border-gray-100 {isAvailable ? 'card-hover' : 'opacity-75'}">
<div class="flex items-start justify-between mb-4">
<div class="w-12 h-12 rounded-xl {isAvailable ? 'bg-primary-50' : 'bg-gray-100'} flex items-center justify-center {isAvailable ? 'transition-transform group-hover:scale-110' : ''}">
<span class="text-2xl {isAvailable ? '' : 'grayscale'}">{notebook.icon}</span>
</div>
<span class="text-xs font-medium px-2 py-1 rounded-full {getCategoryColor(notebook.category)}">
{notebook.category}
</span>
</div>
<h3 class="text-lg font-semibold {isAvailable ? 'text-gray-900 group-hover:text-primary-600' : 'text-gray-400'} mb-2 transition-colors">
{notebook.title}
</h3>
<p class="{isAvailable ? 'text-gray-600' : 'text-gray-400'} text-sm leading-relaxed mb-4">
{notebook.description}
</p>
{#if isAvailable}
<a
href={notebook.colabUrl}
target="_blank"
rel="noopener noreferrer"
class="inline-flex items-center gap-2 px-4 py-2 bg-yellow-400 hover:bg-yellow-500 text-gray-900 rounded-lg text-sm font-medium transition-colors"
>
<svg class="w-4 h-4" viewBox="0 0 24 24" fill="currentColor">
<path d="M12 0C5.372 0 0 5.372 0 12s5.372 12 12 12 12-5.372 12-12S18.628 0 12 0zm0 2.4c5.302 0 9.6 4.298 9.6 9.6s-4.298 9.6-9.6 9.6S2.4 17.302 2.4 12 6.698 2.4 12 2.4z"/>
</svg>
Open in Colab
</a>
{:else}
<span class="inline-flex items-center gap-2 px-4 py-2 bg-gray-100 text-gray-400 rounded-lg text-sm font-medium">
Coming Soon
</span>
{/if}
</div>
{/each}
</div>
{#if filteredNotebooks.length === 0}
<div class="text-center py-12">
<p class="text-gray-500">No notebooks found matching your criteria.</p>
</div>
{/if}
</div>
</main>
<Footer />
</div>