mirror of
https://github.com/NousResearch/hermes-agent.git
synced 2026-05-04 02:21:47 +00:00
docs(website): add User Stories and Use Cases collage page (#18282)
Adds a new top-of-sidebar docs page at /docs/user-stories that is a masonry-style collage of 99 real user stories sourced from X/Twitter, GitHub issues/PRs, Reddit, Hacker News, YouTube, blogs (Medium, Substack, dev.to), podcasts, LinkedIn, GitHub Gists, and Product Hunt. Every tile links to the original post/issue/video/gist where someone described a specific use case: personal assistants, dev workflows, trading bots, research briefs, family WhatsApp agents, Kubernetes deployments, legal-domain self-hosted setups, and more. - docs/user-stories.mdx: MDX entry mounting the collage component - src/components/UserStoriesCollage: React component with category + source filters, CSS-columns masonry layout, per-category accent colors - src/data/userStories.json: source-of-truth dataset (force-added; the root .gitignore's unanchored 'data/' rule would otherwise swallow it, same reason skills.json is explicitly listed in website/.gitignore) - sidebars.ts: link added at the top of the docs sidebar
This commit is contained in:
parent
dfe512c58d
commit
a2a32688ca
5 changed files with 1664 additions and 0 deletions
310
website/src/components/UserStoriesCollage/index.tsx
Normal file
310
website/src/components/UserStoriesCollage/index.tsx
Normal file
|
|
@ -0,0 +1,310 @@
|
|||
import React, { useMemo, useState } from 'react';
|
||||
import stories from '@site/src/data/userStories.json';
|
||||
import styles from './styles.module.css';
|
||||
|
||||
interface Story {
|
||||
id: string;
|
||||
source: string;
|
||||
author: string;
|
||||
url: string;
|
||||
date: string;
|
||||
category: string;
|
||||
headline: string;
|
||||
quote: string;
|
||||
size: 'sm' | 'md' | 'lg';
|
||||
}
|
||||
|
||||
const allStories = stories as Story[];
|
||||
|
||||
// Category → pretty label + accent colors (solid + soft fill + gradient top-strip)
|
||||
const CATEGORIES: Record<
|
||||
string,
|
||||
{ label: string; solid: string; soft: string; strip: string }
|
||||
> = {
|
||||
'dev-workflow': {
|
||||
label: 'Dev Workflow',
|
||||
solid: '#60a5fa',
|
||||
soft: 'rgba(96, 165, 250, 0.14)',
|
||||
strip: 'linear-gradient(90deg, #3b82f6, #60a5fa, #a78bfa)',
|
||||
},
|
||||
'personal-assistant': {
|
||||
label: 'Personal Assistant',
|
||||
solid: '#34d399',
|
||||
soft: 'rgba(52, 211, 153, 0.14)',
|
||||
strip: 'linear-gradient(90deg, #10b981, #34d399, #a7f3d0)',
|
||||
},
|
||||
'content-creation': {
|
||||
label: 'Content Creation',
|
||||
solid: '#f472b6',
|
||||
soft: 'rgba(244, 114, 182, 0.14)',
|
||||
strip: 'linear-gradient(90deg, #ec4899, #f472b6, #fda4af)',
|
||||
},
|
||||
'business-ops': {
|
||||
label: 'Business Ops',
|
||||
solid: '#fb923c',
|
||||
soft: 'rgba(251, 146, 60, 0.14)',
|
||||
strip: 'linear-gradient(90deg, #f97316, #fb923c, #fcd34d)',
|
||||
},
|
||||
trading: {
|
||||
label: 'Trading & Markets',
|
||||
solid: '#facc15',
|
||||
soft: 'rgba(250, 204, 21, 0.16)',
|
||||
strip: 'linear-gradient(90deg, #eab308, #facc15, #fde047)',
|
||||
},
|
||||
research: {
|
||||
label: 'Research',
|
||||
solid: '#a78bfa',
|
||||
soft: 'rgba(167, 139, 250, 0.14)',
|
||||
strip: 'linear-gradient(90deg, #8b5cf6, #a78bfa, #c4b5fd)',
|
||||
},
|
||||
creative: {
|
||||
label: 'Creative',
|
||||
solid: '#f87171',
|
||||
soft: 'rgba(248, 113, 113, 0.14)',
|
||||
strip: 'linear-gradient(90deg, #ef4444, #f87171, #fca5a5)',
|
||||
},
|
||||
marketing: {
|
||||
label: 'Marketing',
|
||||
solid: '#e879f9',
|
||||
soft: 'rgba(232, 121, 249, 0.14)',
|
||||
strip: 'linear-gradient(90deg, #d946ef, #e879f9, #f0abfc)',
|
||||
},
|
||||
integrations: {
|
||||
label: 'Integrations',
|
||||
solid: '#38bdf8',
|
||||
soft: 'rgba(56, 189, 248, 0.14)',
|
||||
strip: 'linear-gradient(90deg, #0ea5e9, #38bdf8, #7dd3fc)',
|
||||
},
|
||||
enterprise: {
|
||||
label: 'Enterprise',
|
||||
solid: '#94a3b8',
|
||||
soft: 'rgba(148, 163, 184, 0.16)',
|
||||
strip: 'linear-gradient(90deg, #64748b, #94a3b8, #cbd5e1)',
|
||||
},
|
||||
messaging: {
|
||||
label: 'Messaging',
|
||||
solid: '#22d3ee',
|
||||
soft: 'rgba(34, 211, 238, 0.14)',
|
||||
strip: 'linear-gradient(90deg, #06b6d4, #22d3ee, #67e8f9)',
|
||||
},
|
||||
privacy: {
|
||||
label: 'Privacy & Self-Hosted',
|
||||
solid: '#4ade80',
|
||||
soft: 'rgba(74, 222, 128, 0.14)',
|
||||
strip: 'linear-gradient(90deg, #16a34a, #4ade80, #86efac)',
|
||||
},
|
||||
'cost-optimization': {
|
||||
label: 'Cost Optimization',
|
||||
solid: '#fbbf24',
|
||||
soft: 'rgba(251, 191, 36, 0.16)',
|
||||
strip: 'linear-gradient(90deg, #f59e0b, #fbbf24, #fde68a)',
|
||||
},
|
||||
meta: {
|
||||
label: 'Meta & Ecosystem',
|
||||
solid: '#c084fc',
|
||||
soft: 'rgba(192, 132, 252, 0.14)',
|
||||
strip: 'linear-gradient(90deg, #a855f7, #c084fc, #d8b4fe)',
|
||||
},
|
||||
general: {
|
||||
label: 'General',
|
||||
solid: '#9ca3af',
|
||||
soft: 'rgba(156, 163, 175, 0.16)',
|
||||
strip: 'linear-gradient(90deg, #6b7280, #9ca3af, #d1d5db)',
|
||||
},
|
||||
};
|
||||
|
||||
// Source → compact label shown in the badge row
|
||||
const SOURCE_LABELS: Record<string, string> = {
|
||||
x: 'X · Twitter',
|
||||
hn: 'Hacker News',
|
||||
reddit: 'Reddit',
|
||||
github: 'GitHub',
|
||||
youtube: 'YouTube',
|
||||
blog: 'Blog',
|
||||
podcast: 'Podcast',
|
||||
linkedin: 'LinkedIn',
|
||||
gist: 'GitHub Gist',
|
||||
producthunt: 'Product Hunt',
|
||||
};
|
||||
|
||||
function sourceColor(source: string): string {
|
||||
switch (source) {
|
||||
case 'x': return '#1d9bf0';
|
||||
case 'hn': return '#ff6600';
|
||||
case 'reddit': return '#ff4500';
|
||||
case 'github': return '#8b949e';
|
||||
case 'youtube': return '#ff0033';
|
||||
case 'blog': return '#a78bfa';
|
||||
case 'podcast': return '#8b5cf6';
|
||||
case 'linkedin': return '#0a66c2';
|
||||
case 'gist': return '#8b949e';
|
||||
case 'producthunt': return '#da552f';
|
||||
default: return '#64748b';
|
||||
}
|
||||
}
|
||||
|
||||
export default function UserStoriesCollage(): JSX.Element {
|
||||
const [activeCategory, setActiveCategory] = useState<string>('all');
|
||||
const [activeSource, setActiveSource] = useState<string>('all');
|
||||
|
||||
const categoryCounts = useMemo(() => {
|
||||
const counts: Record<string, number> = {};
|
||||
for (const s of allStories) counts[s.category] = (counts[s.category] ?? 0) + 1;
|
||||
return counts;
|
||||
}, []);
|
||||
|
||||
const sourceCounts = useMemo(() => {
|
||||
const counts: Record<string, number> = {};
|
||||
for (const s of allStories) counts[s.source] = (counts[s.source] ?? 0) + 1;
|
||||
return counts;
|
||||
}, []);
|
||||
|
||||
const visible = useMemo(() => {
|
||||
return allStories.filter((s) => {
|
||||
if (activeCategory !== 'all' && s.category !== activeCategory) return false;
|
||||
if (activeSource !== 'all' && s.source !== activeSource) return false;
|
||||
return true;
|
||||
});
|
||||
}, [activeCategory, activeSource]);
|
||||
|
||||
return (
|
||||
<div className={styles.wrap}>
|
||||
<div className={styles.hero}>
|
||||
<h1>User Stories & Use Cases</h1>
|
||||
<p>
|
||||
What the Hermes Agent community is actually building. Every tile
|
||||
below links to a real post, issue, video, or gist where someone
|
||||
describes how they use Hermes — scraped from X, GitHub, Reddit,
|
||||
Hacker News, YouTube, blogs, and podcasts.
|
||||
</p>
|
||||
<div className={styles.meta}>
|
||||
<span><strong>{allStories.length}</strong> stories</span>
|
||||
<span><strong>{Object.keys(categoryCounts).length}</strong> categories</span>
|
||||
<span><strong>{Object.keys(sourceCounts).length}</strong> sources</span>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{/* Category filters */}
|
||||
<div className={styles.filters}>
|
||||
<button
|
||||
type="button"
|
||||
className={`${styles.filterBtn} ${activeCategory === 'all' ? styles.filterActive : ''}`}
|
||||
onClick={() => setActiveCategory('all')}
|
||||
>
|
||||
All<span className={styles.filterCount}>{allStories.length}</span>
|
||||
</button>
|
||||
{Object.entries(CATEGORIES)
|
||||
.filter(([key]) => categoryCounts[key])
|
||||
.sort((a, b) => (categoryCounts[b[0]] ?? 0) - (categoryCounts[a[0]] ?? 0))
|
||||
.map(([key, meta]) => (
|
||||
<button
|
||||
key={key}
|
||||
type="button"
|
||||
className={`${styles.filterBtn} ${activeCategory === key ? styles.filterActive : ''}`}
|
||||
onClick={() => setActiveCategory(key)}
|
||||
style={
|
||||
activeCategory === key
|
||||
? { background: meta.solid, borderColor: meta.solid, color: '#0f172a' }
|
||||
: undefined
|
||||
}
|
||||
>
|
||||
{meta.label}
|
||||
<span className={styles.filterCount}>{categoryCounts[key]}</span>
|
||||
</button>
|
||||
))}
|
||||
</div>
|
||||
|
||||
{/* Source filters — smaller, secondary row */}
|
||||
<div className={styles.filters} style={{ marginTop: '-0.75rem' }}>
|
||||
<button
|
||||
type="button"
|
||||
className={`${styles.filterBtn} ${activeSource === 'all' ? styles.filterActive : ''}`}
|
||||
onClick={() => setActiveSource('all')}
|
||||
style={{ fontSize: '0.72rem' }}
|
||||
>
|
||||
All sources
|
||||
</button>
|
||||
{Object.entries(SOURCE_LABELS)
|
||||
.filter(([key]) => sourceCounts[key])
|
||||
.map(([key, label]) => (
|
||||
<button
|
||||
key={key}
|
||||
type="button"
|
||||
className={`${styles.filterBtn} ${activeSource === key ? styles.filterActive : ''}`}
|
||||
onClick={() => setActiveSource(key)}
|
||||
style={{
|
||||
fontSize: '0.72rem',
|
||||
...(activeSource === key
|
||||
? { background: sourceColor(key), borderColor: sourceColor(key), color: '#fff' }
|
||||
: {}),
|
||||
}}
|
||||
>
|
||||
{label}
|
||||
<span className={styles.filterCount}>{sourceCounts[key]}</span>
|
||||
</button>
|
||||
))}
|
||||
</div>
|
||||
|
||||
{/* Collage grid */}
|
||||
{visible.length === 0 ? (
|
||||
<div className={styles.empty}>No stories match that filter.</div>
|
||||
) : (
|
||||
<div className={styles.grid}>
|
||||
{visible.map((s) => {
|
||||
const cat = CATEGORIES[s.category] ?? CATEGORIES.general;
|
||||
const sizeClass =
|
||||
s.size === 'lg' ? styles.tileLg : s.size === 'sm' ? styles.tileSm : styles.tileMd;
|
||||
const srcColor = sourceColor(s.source);
|
||||
return (
|
||||
<a
|
||||
key={s.id}
|
||||
className={`${styles.tile} ${sizeClass}`}
|
||||
href={s.url}
|
||||
target="_blank"
|
||||
rel="noopener noreferrer"
|
||||
style={
|
||||
{
|
||||
'--tile-accent': cat.strip,
|
||||
'--tile-accent-solid': cat.solid,
|
||||
'--tile-accent-soft': cat.soft,
|
||||
} as React.CSSProperties
|
||||
}
|
||||
>
|
||||
<div className={styles.badgeRow}>
|
||||
<span className={styles.sourceBadge}>
|
||||
<span className={styles.sourceIcon} style={{ background: srcColor }} />
|
||||
{SOURCE_LABELS[s.source] ?? s.source}
|
||||
</span>
|
||||
<span className={styles.catTag}>{cat.label}</span>
|
||||
</div>
|
||||
<h3 className={styles.headline}>{s.headline}</h3>
|
||||
<p className={styles.quote}>“{s.quote}”</p>
|
||||
<span className={styles.author}>
|
||||
{s.author}
|
||||
{s.date ? <> · {s.date}</> : null}
|
||||
</span>
|
||||
<span className={styles.external} aria-hidden="true">↗</span>
|
||||
</a>
|
||||
);
|
||||
})}
|
||||
</div>
|
||||
)}
|
||||
|
||||
<div className={styles.footer}>
|
||||
Built something with Hermes?{' '}
|
||||
<a
|
||||
href="https://github.com/NousResearch/hermes-agent/edit/main/website/src/data/userStories.json"
|
||||
target="_blank"
|
||||
rel="noopener noreferrer"
|
||||
>
|
||||
Add your story to this page
|
||||
</a>{' '}
|
||||
by editing <code>userStories.json</code>, or post it in the{' '}
|
||||
<a href="https://discord.gg/NousResearch" target="_blank" rel="noopener noreferrer">
|
||||
Nous Research Discord
|
||||
</a>{' '}
|
||||
and we'll pick it up.
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
252
website/src/components/UserStoriesCollage/styles.module.css
Normal file
252
website/src/components/UserStoriesCollage/styles.module.css
Normal file
|
|
@ -0,0 +1,252 @@
|
|||
/* User Stories collage — masonry grid with category-driven accents. */
|
||||
|
||||
.wrap {
|
||||
max-width: 1280px;
|
||||
margin: 0 auto;
|
||||
padding: 0 0 4rem;
|
||||
}
|
||||
|
||||
.hero {
|
||||
padding: 2.5rem 0 2rem;
|
||||
text-align: center;
|
||||
}
|
||||
.hero h1 {
|
||||
font-size: clamp(2rem, 4vw, 3.25rem);
|
||||
margin-bottom: 0.75rem;
|
||||
background: linear-gradient(120deg, #a78bfa 0%, #60a5fa 50%, #34d399 100%);
|
||||
-webkit-background-clip: text;
|
||||
background-clip: text;
|
||||
-webkit-text-fill-color: transparent;
|
||||
}
|
||||
.hero p {
|
||||
max-width: 680px;
|
||||
margin: 0 auto;
|
||||
color: var(--ifm-color-emphasis-700);
|
||||
font-size: 1.05rem;
|
||||
line-height: 1.6;
|
||||
}
|
||||
|
||||
.meta {
|
||||
display: flex;
|
||||
gap: 1.5rem;
|
||||
justify-content: center;
|
||||
margin-top: 1.25rem;
|
||||
flex-wrap: wrap;
|
||||
font-size: 0.85rem;
|
||||
color: var(--ifm-color-emphasis-600);
|
||||
}
|
||||
.meta strong {
|
||||
color: var(--ifm-color-emphasis-900);
|
||||
font-weight: 600;
|
||||
}
|
||||
|
||||
/* Filter bar */
|
||||
.filters {
|
||||
display: flex;
|
||||
gap: 0.4rem;
|
||||
flex-wrap: wrap;
|
||||
justify-content: center;
|
||||
margin: 1.75rem 0 2rem;
|
||||
padding: 0 1rem;
|
||||
}
|
||||
.filterBtn {
|
||||
padding: 0.35rem 0.85rem;
|
||||
border-radius: 999px;
|
||||
border: 1px solid var(--ifm-color-emphasis-300);
|
||||
background: transparent;
|
||||
color: var(--ifm-color-emphasis-800);
|
||||
font-size: 0.8rem;
|
||||
font-weight: 500;
|
||||
cursor: pointer;
|
||||
transition: all 0.18s ease;
|
||||
white-space: nowrap;
|
||||
}
|
||||
.filterBtn:hover {
|
||||
border-color: var(--ifm-color-emphasis-500);
|
||||
color: var(--ifm-color-emphasis-1000);
|
||||
transform: translateY(-1px);
|
||||
}
|
||||
.filterActive {
|
||||
background: var(--ifm-color-emphasis-900);
|
||||
color: var(--ifm-background-color);
|
||||
border-color: var(--ifm-color-emphasis-900);
|
||||
}
|
||||
[data-theme='dark'] .filterActive {
|
||||
background: #e2e8f0;
|
||||
color: #0f172a;
|
||||
border-color: #e2e8f0;
|
||||
}
|
||||
.filterCount {
|
||||
margin-left: 0.35rem;
|
||||
opacity: 0.5;
|
||||
font-variant-numeric: tabular-nums;
|
||||
}
|
||||
|
||||
/* Masonry — use CSS columns for a true collage feel */
|
||||
.grid {
|
||||
column-count: 4;
|
||||
column-gap: 1rem;
|
||||
padding: 0 1rem;
|
||||
}
|
||||
@media (max-width: 1200px) { .grid { column-count: 3; } }
|
||||
@media (max-width: 850px) { .grid { column-count: 2; } }
|
||||
@media (max-width: 560px) { .grid { column-count: 1; } }
|
||||
|
||||
/* Tile */
|
||||
.tile {
|
||||
break-inside: avoid;
|
||||
margin-bottom: 1rem;
|
||||
position: relative;
|
||||
display: block;
|
||||
padding: 1.1rem 1.2rem 1.15rem;
|
||||
border-radius: 14px;
|
||||
border: 1px solid var(--ifm-color-emphasis-200);
|
||||
background: var(--ifm-card-background-color, var(--ifm-background-surface-color));
|
||||
color: inherit !important;
|
||||
text-decoration: none !important;
|
||||
overflow: hidden;
|
||||
transition: transform 0.22s ease, box-shadow 0.22s ease, border-color 0.22s ease;
|
||||
}
|
||||
.tile::before {
|
||||
/* Color accent strip */
|
||||
content: '';
|
||||
position: absolute;
|
||||
top: 0; left: 0; right: 0;
|
||||
height: 3px;
|
||||
background: var(--tile-accent, linear-gradient(90deg, #a78bfa, #60a5fa));
|
||||
opacity: 0.9;
|
||||
}
|
||||
.tile::after {
|
||||
/* Subtle hover glow */
|
||||
content: '';
|
||||
position: absolute;
|
||||
inset: -1px;
|
||||
border-radius: 14px;
|
||||
box-shadow: 0 0 0 0 transparent;
|
||||
pointer-events: none;
|
||||
transition: box-shadow 0.22s ease;
|
||||
}
|
||||
.tile:hover {
|
||||
transform: translateY(-3px);
|
||||
border-color: var(--tile-accent-solid, var(--ifm-color-primary));
|
||||
box-shadow: 0 8px 24px -8px rgba(0, 0, 0, 0.25);
|
||||
}
|
||||
[data-theme='dark'] .tile:hover {
|
||||
box-shadow: 0 10px 30px -12px rgba(120, 120, 200, 0.45);
|
||||
}
|
||||
|
||||
/* Size variants — big tiles get more visual weight */
|
||||
.tileSm { min-height: 130px; }
|
||||
.tileMd { min-height: 180px; }
|
||||
.tileLg {
|
||||
min-height: 240px;
|
||||
padding: 1.35rem 1.45rem 1.45rem;
|
||||
}
|
||||
.tileLg .headline {
|
||||
font-size: 1.3rem;
|
||||
}
|
||||
|
||||
/* Tile body */
|
||||
.badgeRow {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
align-items: center;
|
||||
gap: 0.5rem;
|
||||
margin-bottom: 0.75rem;
|
||||
font-size: 0.7rem;
|
||||
letter-spacing: 0.06em;
|
||||
text-transform: uppercase;
|
||||
color: var(--ifm-color-emphasis-600);
|
||||
}
|
||||
.sourceBadge {
|
||||
display: inline-flex;
|
||||
align-items: center;
|
||||
gap: 0.35rem;
|
||||
font-weight: 600;
|
||||
}
|
||||
.sourceIcon {
|
||||
display: inline-block;
|
||||
width: 14px;
|
||||
height: 14px;
|
||||
border-radius: 3px;
|
||||
background: var(--tile-accent-solid, #a78bfa);
|
||||
flex-shrink: 0;
|
||||
}
|
||||
.catTag {
|
||||
display: inline-block;
|
||||
padding: 0.15rem 0.55rem;
|
||||
border-radius: 999px;
|
||||
background: var(--tile-accent-soft, rgba(167, 139, 250, 0.12));
|
||||
color: var(--tile-accent-solid, #a78bfa);
|
||||
font-weight: 600;
|
||||
letter-spacing: 0.04em;
|
||||
}
|
||||
|
||||
.headline {
|
||||
font-size: 1.02rem;
|
||||
font-weight: 700;
|
||||
line-height: 1.3;
|
||||
margin: 0 0 0.5rem;
|
||||
color: var(--ifm-color-emphasis-1000);
|
||||
}
|
||||
|
||||
.quote {
|
||||
font-size: 0.875rem;
|
||||
line-height: 1.55;
|
||||
color: var(--ifm-color-emphasis-800);
|
||||
margin: 0;
|
||||
display: -webkit-box;
|
||||
-webkit-line-clamp: 6;
|
||||
-webkit-box-orient: vertical;
|
||||
overflow: hidden;
|
||||
}
|
||||
.tileLg .quote { -webkit-line-clamp: 8; }
|
||||
.tileSm .quote { -webkit-line-clamp: 4; }
|
||||
|
||||
.author {
|
||||
display: block;
|
||||
margin-top: 0.7rem;
|
||||
font-size: 0.78rem;
|
||||
color: var(--ifm-color-emphasis-600);
|
||||
font-weight: 500;
|
||||
}
|
||||
|
||||
.external {
|
||||
position: absolute;
|
||||
top: 0.9rem;
|
||||
right: 0.9rem;
|
||||
opacity: 0;
|
||||
font-size: 0.85rem;
|
||||
color: var(--tile-accent-solid, var(--ifm-color-primary));
|
||||
transition: opacity 0.2s ease, transform 0.2s ease;
|
||||
}
|
||||
.tile:hover .external {
|
||||
opacity: 1;
|
||||
transform: translate(2px, -2px);
|
||||
}
|
||||
|
||||
/* Footer */
|
||||
.footer {
|
||||
margin: 3rem auto 0;
|
||||
padding: 1.5rem;
|
||||
text-align: center;
|
||||
max-width: 720px;
|
||||
border-radius: 14px;
|
||||
background: var(--ifm-color-emphasis-100);
|
||||
font-size: 0.95rem;
|
||||
color: var(--ifm-color-emphasis-800);
|
||||
line-height: 1.6;
|
||||
}
|
||||
.footer a {
|
||||
color: var(--ifm-color-primary);
|
||||
text-decoration: none;
|
||||
font-weight: 600;
|
||||
}
|
||||
.footer a:hover { text-decoration: underline; }
|
||||
|
||||
.empty {
|
||||
padding: 3rem 1rem;
|
||||
text-align: center;
|
||||
color: var(--ifm-color-emphasis-600);
|
||||
font-size: 0.95rem;
|
||||
}
|
||||
Loading…
Add table
Add a link
Reference in a new issue