Skip to content

Commit c60b462

Browse files
authored
Merge pull request #29 from Mazizova/new-catalog-detail-fields
Extend the resource detail card to Training/Events/Books/Community
2 parents fd0a449 + a7c8373 commit c60b462

9 files changed

Lines changed: 391 additions & 78 deletions

File tree

‎RESOURCES.md‎

Lines changed: 33 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -75,17 +75,19 @@ deployment: ["Self-Hosted"] # Self-Hosted, Kubernetes, Docker, Cloud-Native,
7575
pricing: ["Open Source"] # Open Source, Commercial, Free, Paid, Enterprise, Open Source & Open Core
7676
```
7777
78-
**Software & Tools / SaaS & Cloud Solutions / Services also get a structured detail page** (hero, actions row, sticky "Details" panel) with a few extra optional fields on top of the usual ones:
78+
**Every one of these seven sections gets the same structured detail page** (hero, actions row, sticky "Details" panel) — which fields the panel shows, in what order, and what the primary action button says is configured per section in [`data/resource_detail.yaml`](data/resource_detail.yaml), not hardcoded per template. A field with no value for a given resource just doesn't render — no placeholder row — so it's always safe to leave any of these unset.
79+
80+
Fields shared by every section:
7981
```yaml
80-
github: "owner/repo" # optional — shorthand, or a full https://github.com/... URL. Adds
81-
# a "View on GitHub" button and a Repository row. Only set it when
82-
# it's a genuinely separate URL from `link` — skip it if `link`
83-
# already points at the repo.
84-
member: true # optional — shows the "OurSQL Member" badge. Only set this when the
85-
# resource's org is an actual OurSQL Foundation member/sponsor/partner;
86-
# leave it unset otherwise (the row/badge is just omitted, no placeholder).
82+
member: true # optional (software/saas/services/community only) — shows the
83+
# "OurSQL Member" badge. Only set this when the resource's org is
84+
# an actual OurSQL Foundation member/sponsor/partner.
8785
last_reviewed: 2026-08-14 # optional — shows a "Last reviewed" row, credited to the editorial
8886
# team. Only set it once someone has actually reviewed the listing.
87+
github: "owner/repo" # optional (software/saas/services only) — shorthand, or a full
88+
# https://github.com/... URL. Adds a "View on GitHub" button and a
89+
# Repository row. Only set it when it's a genuinely separate URL
90+
# from `link` — skip it if `link` already points at the repo.
8991
```
9092
The full description (Markdown body, under the front matter) renders as the "About" section — plain-text `description` still serves as the short hero subtitle. "Suggest an update" always appears (top-right and in the Details panel) and links straight to editing the resource's own file on GitHub.
9193

@@ -111,7 +113,13 @@ training-formats: ["Self-Paced"] # Self-Paced, Instructor-Led, Workshop, Certifi
111113
levels: ["Beginner"] # Beginner, Intermediate, Advanced
112114
pricing: ["Free"] # Free, Paid, Free Trial Available
113115
languages: ["English"] # English, Spanish, Portuguese, German, Chinese, Other
114-
author: "Percona" # optional
116+
author: "Percona" # optional — shown as "Organization" on the detail page
117+
date: 2026-10-06 # optional — start of a specific dated delivery (a workshop
118+
# cohort, a bootcamp). Leave unset for self-paced / evergreen
119+
# courses — the "Date(s)" row only shows once BOTH `date` and
120+
# `date_end` are set, specifically so it never turns an ongoing
121+
# course into what looks like a one-day event.
122+
date_end: 2026-10-08 # optional — end of that same range
115123
```
116124
117125
**`content/resources/books/`** (leaf bundle with cover image) — subcategories: Books · Blogs · Newsletters · Reports & Research · Whitepapers · Case Studies · Podcasts & Videos · Documentation Portals
@@ -130,17 +138,29 @@ images:
130138
- cover.jpeg
131139
---
132140
```
141+
`images[0]` (the cover) replaces the usual square logo in the hero with a portrait book-cover shape — the only section where the hero media isn't square. `authors` and `year` show as their own Details-panel rows.
133142

134143
**`content/resources/events/`** — subcategories: Foundation Events · Community Calls · Webinars · Meetups · Conferences · Partner & Member Events · Past Events & Recordings — prefix the filename with the date
135144
```yaml
136145
date: 2026-09-11
137-
format: "in-person" # or "online" — drives the card's Online/In-person badge
138-
city: "Amsterdam" # optional, only for in-person events
146+
date_end: 2026-09-13 # optional — end date, for a multi-day conference
147+
format: "in-person" # or "online" — drives the card's Online/In-person badge
148+
city: "Amsterdam" # optional, only for in-person events — omit for online-only,
149+
# the Details panel's Location row is skipped when there's no
150+
# city and countries is just ["Global"]
139151
countries: ["Netherlands"] # a country name, or "Global" for an online/worldwide event
140152
event_year: ["2026"]
141153
event-formats: ["In-Person"] # In-Person, Online, Hybrid
142-
organizers: ["Community"] # OurSQL Foundation, Community, Member & Partner
143-
```
154+
organizers: ["Community"] # OurSQL Foundation, Community, Member & Partner — also the
155+
# detail page's "Organizer" row
156+
online_url: "https://..." # optional — shown as a Details-panel "Online" link
157+
registration_url: "https://..." # optional — becomes the primary "Register" button for an
158+
# upcoming event, and a "Registration" row either way
159+
recording_url: "https://..." # optional — becomes the primary "Watch recording" button and a
160+
# "Recording" row, but ONLY once the event's `date` has passed —
161+
# never set this for something that hasn't happened yet
162+
```
163+
Whether an event is upcoming or past is computed from `date` (compared to today), never a field you set by hand — it drives the "Past event" badge in the hero and which of the fields above are actually shown.
144164

145165
**`content/resources/community/`** — subcategories: Forums · Slack, Discord & IRC · Mailing Lists · Reddit & Social · Blog Aggregators · Podcasts & Videos
146166
```yaml

‎assets/scss/_light-theme.scss‎

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -245,6 +245,12 @@
245245
.resource-detail-panel-subvalue { color: #94a3b8; }
246246
.resource-detail-panel-link { color: #0272a5; }
247247

248+
.status-chip {
249+
background: #f1f5f9;
250+
border-color: #dde3ed;
251+
color: #64748b;
252+
}
253+
248254
.resource-suggest-btn {
249255
color: #64748b;
250256
border-color: #dde3ed;

‎assets/scss/_resources.scss‎

Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1539,6 +1539,18 @@ body.sheet-open {
15391539
padding: 10px;
15401540
background: $white;
15411541
}
1542+
1543+
// Books & Publications: cover art stands in for the logo, so the media
1544+
// placeholder itself needs a portrait (book-cover) aspect ratio instead of
1545+
// the square used everywhere else.
1546+
&--portrait {
1547+
width: 90px;
1548+
height: 130px;
1549+
border-radius: 8px;
1550+
font-size: 1.0625rem;
1551+
1552+
&.resource-detail-logo--img { padding: 0; }
1553+
}
15421554
}
15431555

15441556
.resource-detail-titlerow {
@@ -1555,6 +1567,20 @@ body.sheet-open {
15551567
margin: 0;
15561568
}
15571569

1570+
// Events: computed "Past event" chip next to the title — never hand-authored.
1571+
.status-chip {
1572+
display: inline-flex;
1573+
align-items: center;
1574+
font-size: 0.75rem;
1575+
font-weight: 600;
1576+
color: $muted;
1577+
background: rgba($off-white, 0.06);
1578+
border: 1px solid $dark-border;
1579+
border-radius: 100px;
1580+
padding: 0.25rem 0.7rem;
1581+
white-space: nowrap;
1582+
}
1583+
15581584
.resource-detail-member-badge {
15591585
display: inline-flex;
15601586
align-items: center;
@@ -1685,11 +1711,16 @@ body.sheet-open {
16851711
}
16861712

16871713
.resource-detail-panel-label {
1714+
display: flex;
1715+
align-items: center;
1716+
gap: 0.35rem;
16881717
font-size: 0.6875rem;
16891718
font-weight: 700;
16901719
letter-spacing: 0.06em;
16911720
text-transform: uppercase;
16921721
color: $muted;
1722+
1723+
svg { flex-shrink: 0; }
16931724
}
16941725

16951726
.resource-detail-panel-value {

‎data/resource_detail.yaml‎

Lines changed: 94 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,94 @@
1+
# ── Resource detail-page config ─────────────────────────────────────────────
2+
# Drives layouts/partials/resources/detail-card.html: the hero media shape,
3+
# what the primary/secondary action buttons say and link to, and which rows
4+
# appear in the sticky "Details" panel — in order, per resources subsection.
5+
#
6+
# Every field is optional at the CONTENT level: a field whose param has no
7+
# value for a given resource just doesn't render (no placeholder row) — that
8+
# is the whole point of driving this from data instead of hardcoding a fixed
9+
# column list per category (a 5th category is a new entry here, not new
10+
# template code). `only_if: past` is the one row gated on something other
11+
# than "value present" (Events' Recording row — see detail-card.html).
12+
#
13+
# Field types the template understands:
14+
# category — always .Parent.Title
15+
# subcategory — .Params.subcategories, comma-joined
16+
# text — .Params.<param> as-is (comma-joined if it's a list)
17+
# domain_link — .Params.<param> is a URL; shown as its bare domain,
18+
# optionally prefixed with `prefix` (e.g. "Watch on ")
19+
# github_link — .Params.<param>, "owner/repo" shorthand or full URL
20+
# link — .Params.<param> is a URL; shown as this field's `text`
21+
# member_badge — only when .Params.member is exactly `true`
22+
# last_reviewed — .Params.<param>, a date; shown with "by OurSQL editorial team"
23+
# date_range — .Date (start) plus optional .Params.date_end
24+
25+
software: &standard_fields
26+
media: square
27+
primary_action: {label: "Visit website", param: link}
28+
secondary_action: {label: "View on GitHub", param: github, type: github}
29+
fields:
30+
- {key: category, label: Category, type: category}
31+
- {key: subcategory, label: Subcategory, type: subcategory}
32+
- {key: website, label: Website, type: domain_link, param: link}
33+
- {key: repository, label: Repository, type: github_link, param: github}
34+
- {key: member, label: "OurSQL Member", type: member_badge, param: member}
35+
- {key: last_reviewed, label: "Last reviewed", type: last_reviewed, param: last_reviewed}
36+
37+
saas: *standard_fields
38+
services: *standard_fields
39+
40+
community:
41+
media: square
42+
primary_action: {label: "Join the community", param: link}
43+
fields:
44+
- {key: category, label: Category, type: category}
45+
- {key: subcategory, label: Subcategory, type: subcategory}
46+
- {key: website, label: Website, type: domain_link, param: link}
47+
- {key: member, label: "OurSQL Member", type: member_badge, param: member}
48+
- {key: last_reviewed, label: "Last reviewed", type: last_reviewed, param: last_reviewed}
49+
50+
training:
51+
media: square
52+
primary_action: {label: "Visit website", param: link}
53+
fields:
54+
- {key: category, label: Category, type: category}
55+
- {key: subcategory, label: Subcategory, type: subcategory}
56+
- {key: organization, label: Organization, type: text, param: author}
57+
# require_end: the training section's own `date` front matter has long
58+
# meant "sort/reference date" (see content/resources/training/_index.md's
59+
# `sort: date`), not "this specific cohort runs on this day" — showing it
60+
# here unqualified would misrepresent every self-paced/evergreen course as
61+
# a one-day event. Only a genuinely dated delivery (start AND end set)
62+
# shows this row; see RESOURCES.md.
63+
- {key: dates, label: "Date(s)", type: date_range, icon: calendar, require_end: true}
64+
- {key: website, label: Website, type: domain_link, param: link}
65+
- {key: last_reviewed, label: "Last reviewed", type: last_reviewed, param: last_reviewed}
66+
67+
events:
68+
media: square
69+
# Primary action swaps by event state — handled directly in detail-card.html
70+
# (past → Watch recording / recording_url, future → Register / registration_url,
71+
# falling back to Visit website / link when neither applies) rather than as
72+
# config, since it's the one section where the action itself is conditional.
73+
secondary_action: {label: "Event website", param: link, type: link}
74+
fields:
75+
- {key: category, label: Category, type: category}
76+
- {key: subcategory, label: Subcategory, type: subcategory}
77+
- {key: dates, label: "Date(s)", type: date_range, icon: calendar}
78+
- {key: location, label: Location, type: location, icon: location}
79+
- {key: online, label: Online, type: domain_link, param: online_url}
80+
- {key: organizer, label: Organizer, type: text, param: organizers, list: true}
81+
- {key: registration, label: Registration, type: link, param: registration_url, text: Register}
82+
- {key: recording, label: Recording, type: domain_link, param: recording_url, prefix: "Watch on ", only_if: past}
83+
- {key: last_reviewed, label: "Last reviewed", type: last_reviewed, param: last_reviewed}
84+
85+
books:
86+
media: portrait
87+
primary_action: {label: "Find the book", param: link}
88+
fields:
89+
- {key: category, label: Category, type: category}
90+
- {key: subcategory, label: Subcategory, type: subcategory}
91+
- {key: authors, label: Authors, type: text, param: authors, list: true}
92+
- {key: year, label: Year, type: text, param: year}
93+
- {key: website, label: Website, type: domain_link, param: link}
94+
- {key: last_reviewed, label: "Last reviewed", type: last_reviewed, param: last_reviewed}

0 commit comments

Comments
 (0)