Skip to content

Commit 1618541

Browse files
committed
refactor: Use centralized config for home page products
- Read product data from repos-config.json - Group products by category with proper ordering - Add Third Party section for isThirdParty products - Show product icons for all sections
1 parent e0c53fd commit 1618541

1 file changed

Lines changed: 124 additions & 198 deletions

File tree

src/pages/index.js

Lines changed: 124 additions & 198 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,11 @@ import useBaseUrl from '@docusaurus/useBaseUrl';
88

99
import styles from './index.module.css';
1010

11+
// Import centralized product configuration
12+
import reposConfig from '../../repos-config.json';
13+
14+
const { categories, products } = reposConfig;
15+
1116
/**
1217
* Build an absolute URL using the site config base URL.
1318
* @param {string} siteUrl
@@ -26,6 +31,72 @@ function buildAbsoluteUrl(siteUrl, baseUrl, pathname) {
2631
return `${origin}${baseUrlWithSlash}${normalizedPath}`;
2732
}
2833

34+
/**
35+
* Get products by category ID, sorted alphabetically by label.
36+
* @param {string} categoryId
37+
* @param {Object} options
38+
* @param {string} options.showFirst - Product ID to show first
39+
* @param {boolean} options.excludeThirdParty - Exclude third-party products
40+
* @returns {Array}
41+
*/
42+
function getProductsByCategory(categoryId, options = {}) {
43+
const { showFirst, excludeThirdParty = false } = options;
44+
45+
let filtered = products.filter((p) => {
46+
if (p.category !== categoryId) return false;
47+
if (excludeThirdParty && p.isThirdParty) return false;
48+
return true;
49+
});
50+
51+
// Sort alphabetically
52+
filtered.sort((a, b) => a.label.localeCompare(b.label));
53+
54+
// Move showFirst product to the beginning
55+
if (showFirst) {
56+
const firstIndex = filtered.findIndex((p) => p.id === showFirst);
57+
if (firstIndex > 0) {
58+
const [first] = filtered.splice(firstIndex, 1);
59+
filtered.unshift(first);
60+
}
61+
}
62+
63+
return filtered.map((p) => ({
64+
id: p.id,
65+
title: p.label,
66+
description: p.description || '',
67+
link: `/docs/${p.id}/`,
68+
}));
69+
}
70+
71+
/**
72+
* Get third-party products (isThirdParty: true).
73+
* @returns {Array}
74+
*/
75+
function getThirdPartyProducts() {
76+
return products
77+
.filter((p) => p.isThirdParty === true)
78+
.sort((a, b) => a.label.localeCompare(b.label))
79+
.map((p) => ({
80+
id: p.id,
81+
title: p.label,
82+
description: p.description || '',
83+
link: `/docs/${p.id}/`,
84+
}));
85+
}
86+
87+
/**
88+
* Get sorted categories for display.
89+
* @returns {Array}
90+
*/
91+
function getSortedCategories() {
92+
return Object.entries(categories)
93+
.map(([id, config]) => ({
94+
id,
95+
...config,
96+
}))
97+
.sort((a, b) => (a.position || 99) - (b.position || 99));
98+
}
99+
29100
/**
30101
* Render the homepage hero header.
31102
* @returns {JSX.Element}
@@ -44,187 +115,25 @@ function HomepageHeader() {
44115
);
45116
}
46117

47-
// Main GravityKit Products
48-
const mainProducts = [
49-
{
50-
title: 'GravityView',
51-
description: 'Display Gravity Forms entries in beautiful, customizable layouts.',
52-
link: '/docs/gravityview/'
53-
},
54-
{
55-
title: 'GravityCalendar',
56-
description: 'Transform entries into interactive calendars with FullCalendar.',
57-
link: '/docs/gravitycalendar/'
58-
},
59-
{
60-
title: 'GravityCharts',
61-
description: 'Visualize form data with powerful charts and graphs.',
62-
link: '/docs/gravitycharts/'
63-
},
64-
{
65-
title: 'GravityImport',
66-
description: 'Import data into Gravity Forms from CSV, Excel, and more.',
67-
link: '/docs/gravityimport/'
68-
},
69-
{
70-
title: 'GravityExport',
71-
description: 'Export form entries in multiple formats.',
72-
link: '/docs/gravityexport/'
73-
},
74-
{
75-
title: 'GravityMath',
76-
description: 'Advanced mathematical calculations for your forms.',
77-
link: '/docs/gravitymath/'
78-
},
79-
{
80-
title: 'GravityEdit',
81-
description: 'Edit Gravity Forms entries inline to save time and streamline your workflow.',
82-
link: '/docs/gravityedit/'
83-
},
84-
{
85-
title: 'GravityActions',
86-
description: 'Update multiple entries at once, send bulk emails, and automate workflows.',
87-
link: '/docs/gravityactions/'
88-
},
89-
{
90-
title: 'GravityRevisions',
91-
description: 'Track, compare, and restore changes made to forms and entries.',
92-
link: '/docs/gravityrevisions/'
93-
},
94-
{
95-
title: 'GravityMigrate',
96-
description: 'Migrate all Gravity Forms data including forms, entries, Views, and feeds.',
97-
link: '/docs/gravitymigrate/'
98-
},
99-
{
100-
title: 'GravityBoard',
101-
description: 'Manage projects with collaborative Kanban-style project boards.',
102-
link: '/docs/gravityboard/'
103-
}
104-
];
105-
106-
// GravityView Layouts
107-
const gravityviewLayouts = [
108-
{
109-
title: 'DataTables',
110-
description: 'Enhance Views with sortable, searchable DataTables.',
111-
link: '/docs/gravityview-datatables/',
112-
slug: 'gravityview-datatables'
113-
},
114-
{
115-
title: 'DIY Layout',
116-
description: 'Build custom layouts with complete control over HTML and CSS.',
117-
link: '/docs/gravityview-diy-layout/',
118-
slug: 'gravityview-diy-layout'
119-
},
120-
{
121-
title: 'Maps',
122-
description: 'Display entries on interactive Google Maps.',
123-
link: '/docs/gravityview-maps/',
124-
slug: 'gravityview-maps'
125-
}
126-
];
127-
128-
// GravityView Extensions
129-
const gravityviewExtensions = [
130-
{
131-
title: 'Advanced Filtering',
132-
description: 'Add powerful filtering capabilities to your Views.',
133-
link: '/docs/gravityview-advanced-filtering/',
134-
slug: 'gravityview-advanced-filtering'
135-
},
136-
{
137-
title: 'A-Z Filters',
138-
description: 'Filter entries alphabetically with letter-based navigation.',
139-
link: '/docs/gravityview-az-filters/',
140-
slug: 'gravityview-az-filters'
141-
},
142-
{
143-
title: 'Dashboard Views',
144-
description: 'Display Views in the WordPress admin dashboard.',
145-
link: '/docs/gravityview-dashboard-views/',
146-
slug: 'gravityview-dashboard-views'
147-
},
148-
{
149-
title: 'Featured Entries',
150-
description: 'Highlight and pin important entries to the top of Views.',
151-
link: '/docs/gravityview-featured-entries/',
152-
slug: 'gravityview-featured-entries'
153-
},
154-
{
155-
title: 'Magic Links',
156-
description: 'Share unique links for accessing entries without logging in.',
157-
link: '/docs/gravityview-magic-links/',
158-
slug: 'gravityview-magic-links'
159-
},
160-
{
161-
title: 'Multiple Forms',
162-
description: 'Combine entries from multiple forms into a single View.',
163-
link: '/docs/gravityview-multiple-forms/',
164-
slug: 'gravityview-multiple-forms'
165-
},
166-
{
167-
title: 'Ratings & Reviews',
168-
description: 'Add star ratings and reviews to your entries.',
169-
link: '/docs/gravityview-ratings-reviews/',
170-
slug: 'gravityview-ratings-reviews'
171-
},
172-
{
173-
title: 'Social Sharing & SEO',
174-
description: 'Enable social sharing and optimize entries for search engines.',
175-
link: '/docs/gravityview-social-sharing-seo/',
176-
slug: 'gravityview-social-sharing-seo'
177-
}
178-
];
179-
180-
// Free Gravity Forms Add-ons
181-
const gravityFormsAddons = [
182-
{
183-
title: 'Zero Spam',
184-
description: 'Block spam submissions without CAPTCHAs or honeypots.',
185-
link: '/docs/gravity-forms-zero-spam/',
186-
slug: 'gravity-forms-zero-spam'
187-
},
188-
{
189-
title: 'Dynamic Lookup',
190-
description: 'Dynamically populate fields from other forms or entries.',
191-
link: '/docs/gravity-forms-dynamic-lookup/',
192-
slug: 'gravity-forms-dynamic-lookup'
193-
},
194-
{
195-
title: 'Entry Tags',
196-
description: 'Organize entries with customizable tags.',
197-
link: '/docs/gravity-forms-entry-tags/',
198-
slug: 'gravity-forms-entry-tags'
199-
},
200-
{
201-
title: 'Event Field',
202-
description: 'Add event scheduling fields with date, time, and recurrence.',
203-
link: '/docs/gravity-forms-event-field/',
204-
slug: 'gravity-forms-event-field'
205-
},
206-
{
207-
title: 'Elementor Widget',
208-
description: 'Embed Gravity Forms in Elementor with a native widget.',
209-
link: '/docs/gravity-forms-elementor-widget/',
210-
slug: 'gravity-forms-elementor-widget'
211-
}
212-
];
213-
214118
/**
215119
* Render a product card.
216-
* @param {{product: {title: string, description: string, link: string, slug?: string}, showImage?: boolean}} props
120+
* @param {{product: {id: string, title: string, description: string, link: string}, showImage?: boolean}} props
217121
* @returns {JSX.Element}
218122
*/
219123
function ProductCard({ product, showImage = true }) {
220-
const imageName = product.slug || product.title.toLowerCase();
124+
const imagePath = useBaseUrl(`/img/${product.id}.svg`);
125+
221126
return (
222127
<div className={clsx('col col--4 margin-bottom--lg')}>
223128
<div className="card">
224129
<div className="card__header">
225130
{showImage && (
226131
<Link to={product.link}>
227-
<img src={useBaseUrl(`/img/${imageName}.svg`)} alt={product.title} />
132+
<img
133+
src={imagePath}
134+
alt={product.title}
135+
onError={(e) => { e.target.style.display = 'none'; }}
136+
/>
228137
</Link>
229138
)}
230139
<Link to={product.link}>
@@ -248,10 +157,14 @@ function ProductCard({ product, showImage = true }) {
248157

249158
/**
250159
* Render a section of product cards.
251-
* @param {{title: string, description: string, products: Array<{title: string, description: string, link: string, slug?: string}>, showImages?: boolean}} props
252-
* @returns {JSX.Element}
160+
* @param {{title: string, description: string, products: Array, showImages?: boolean}} props
161+
* @returns {JSX.Element|null}
253162
*/
254163
function ProductSection({ title, description, products, showImages = true }) {
164+
if (!products || products.length === 0) {
165+
return null;
166+
}
167+
255168
return (
256169
<section className={styles.products}>
257170
<div className="container">
@@ -260,8 +173,8 @@ function ProductSection({ title, description, products, showImages = true }) {
260173
{description && <p className="hero__subtitle">{description}</p>}
261174
</div>
262175
<div className="row">
263-
{products.map((product, idx) => (
264-
<ProductCard key={idx} product={product} showImage={showImages} />
176+
{products.map((product) => (
177+
<ProductCard key={product.id} product={product} showImage={showImages} />
265178
))}
266179
</div>
267180
</div>
@@ -275,7 +188,8 @@ function ProductSection({ title, description, products, showImages = true }) {
275188
*/
276189
export default function Home() {
277190
const {siteConfig} = useDocusaurusContext();
278-
const totalProducts = mainProducts.length + gravityviewLayouts.length + gravityviewExtensions.length + gravityFormsAddons.length;
191+
const sortedCategories = getSortedCategories();
192+
const thirdPartyProducts = getThirdPartyProducts();
279193
const llmsHref = buildAbsoluteUrl(siteConfig.url, siteConfig.baseUrl, 'llms.txt');
280194

281195
return (
@@ -287,38 +201,50 @@ export default function Home() {
287201
</Head>
288202
<HomepageHeader />
289203
<main>
290-
<ProductSection
291-
title="Main Products"
292-
description="Core GravityKit products with comprehensive functionality"
293-
products={mainProducts}
294-
showImages={true}
295-
/>
204+
{sortedCategories.map((category) => {
205+
// Special handling for gravitykit category: include GravityView first
206+
const options = category.id === 'gravitykit'
207+
? { showFirst: 'gravityview' }
208+
: { excludeThirdParty: true };
296209

297-
<ProductSection
298-
title="GravityView Layouts"
299-
description="Alternative ways to display your View data"
300-
products={gravityviewLayouts}
301-
showImages={false}
302-
/>
210+
// For gravitykit, also include gravityview at the end
211+
let categoryProducts;
212+
if (category.id === 'gravitykit') {
213+
const gravityview = products.find((p) => p.id === 'gravityview');
214+
const gravityKitProducts = getProductsByCategory('gravitykit');
215+
categoryProducts = gravityview
216+
? [...gravityKitProducts, { id: gravityview.id, title: gravityview.label, description: gravityview.description, link: `/docs/${gravityview.id}/` }]
217+
: gravityKitProducts;
218+
} else if (category.id === 'gravityview') {
219+
// Skip gravityview category since it's included in gravitykit
220+
return null;
221+
} else {
222+
categoryProducts = getProductsByCategory(category.id, options);
223+
}
303224

304-
<ProductSection
305-
title="GravityView Extensions"
306-
description="Add features and functionality to GravityView"
307-
products={gravityviewExtensions}
308-
showImages={false}
309-
/>
225+
return (
226+
<ProductSection
227+
key={category.id}
228+
title={category.label}
229+
description={category.description}
230+
products={categoryProducts}
231+
showImages={true}
232+
/>
233+
);
234+
})}
310235

236+
{/* Third Party Section */}
311237
<ProductSection
312-
title="Gravity Forms Add-ons"
313-
description="Free plugins that enhance Gravity Forms"
314-
products={gravityFormsAddons}
315-
showImages={false}
238+
title="Third Party"
239+
description="Documentation for third-party plugins."
240+
products={thirdPartyProducts}
241+
showImages={true}
316242
/>
317243

318244
<div className="container">
319245
<div className="text--center margin-top--lg margin-bottom--lg">
320246
<p>
321-
<strong>{totalProducts} products</strong> with comprehensive hook documentation
247+
<strong>{products.length} products</strong> with comprehensive hook documentation
322248
</p>
323249
<p>
324250
Looking for user documentation? Visit the{' '}

0 commit comments

Comments
 (0)