diff --git a/astro.config.mjs b/astro.config.mjs index 0580c7b9..232fbfd9 100644 --- a/astro.config.mjs +++ b/astro.config.mjs @@ -508,6 +508,10 @@ export default defineConfig({ collapsed: true, autogenerate: { directory: '/snowflake/integrations' }, }, + { + label: 'Sample Apps', + slug: 'snowflake/sample-apps', + }, { label: 'Tutorials', collapsed: true, diff --git a/src/assets/images/snowflake/sample-apps/snowflake-polaris-trino-architecture.png b/src/assets/images/snowflake/sample-apps/snowflake-polaris-trino-architecture.png new file mode 100644 index 00000000..384f9a60 Binary files /dev/null and b/src/assets/images/snowflake/sample-apps/snowflake-polaris-trino-architecture.png differ diff --git a/src/assets/images/snowflake/sample-apps/snowflake-smart-factory-app-architecture.png b/src/assets/images/snowflake/sample-apps/snowflake-smart-factory-app-architecture.png new file mode 100644 index 00000000..703ee58f Binary files /dev/null and b/src/assets/images/snowflake/sample-apps/snowflake-smart-factory-app-architecture.png differ diff --git a/src/components/ApplicationsShowcase.astro b/src/components/ApplicationsShowcase.astro index a7d11d2a..28491897 100644 --- a/src/components/ApplicationsShowcase.astro +++ b/src/components/ApplicationsShowcase.astro @@ -2,39 +2,62 @@ import { ApplicationsShowcase } from './applications/ApplicationsShowcase'; import { getImage } from 'astro:assets'; +interface Props { docs?: 'aws' | 'snowflake' } +const { docs = 'aws' } = Astro.props as Props; + // Import data import applicationsData from '../data/developerhub/applications.json'; import services from '../data/developerhub/services.json'; import integrations from '../data/developerhub/integrations.json'; -const applications = applicationsData.applications; +const allApplications = applicationsData.applications; +const applications = allApplications.filter((app: any) => app.docs === docs); -const images = import.meta.glob<{ default: ImageMetadata }>( - '/src/assets/images/aws/sample-apps/*.{jpeg,jpg,png,gif}' +const awsImages = import.meta.glob<{ default: ImageMetadata }>( + '/src/assets/images/aws/sample-apps/*.{jpeg,jpg,png,gif,svg}' +); +const snowflakeImages = import.meta.glob<{ default: ImageMetadata }>( + '/src/assets/images/snowflake/sample-apps/*.{jpeg,jpg,png,gif,svg}' ); const applicationsUpdated = await Promise.all( - applications.map(async (application) => { + applications.map(async (application: any) => { const updatedApplication = { ...application }; - const imagePath = `/src/assets/images/aws/sample-apps/${application.teaser}`; - - if (images[imagePath]) { - const optimizedLeadImage = await getImage({ - src: images[imagePath](), - format: 'png', - width: 800, - quality: 90, - }); - updatedApplication.teaser = optimizedLeadImage.src; + if (docs === 'aws') { + const imagePath = `/src/assets/images/aws/sample-apps/${application.teaser}`; + if (awsImages[imagePath]) { + const optimizedLeadImage = await getImage({ + src: awsImages[imagePath](), + format: 'png', + width: 800, + quality: 90, + }); + updatedApplication.teaser = optimizedLeadImage.src; + } + } else if (docs === 'snowflake') { + const teaserName = String(application.teaser || '').split('/').pop(); + const imagePath = teaserName ? `/src/assets/images/snowflake/sample-apps/${teaserName}` : ''; + if (teaserName && snowflakeImages[imagePath]) { + const optimizedLeadImage = await getImage({ + src: snowflakeImages[imagePath](), + format: 'png', + width: 800, + quality: 90, + }); + updatedApplication.teaser = optimizedLeadImage.src; + } else if (teaserName) { + updatedApplication.teaser = `/images/snowflake/sample-apps/${teaserName}`; + } } return updatedApplication; }) ); ---- +--- diff --git a/src/components/applications/ApplicationsShowcase.tsx b/src/components/applications/ApplicationsShowcase.tsx index 58fcd1a4..dcbd941f 100644 --- a/src/components/applications/ApplicationsShowcase.tsx +++ b/src/components/applications/ApplicationsShowcase.tsx @@ -3,33 +3,40 @@ import React, { useState, useMemo } from 'react'; interface Application { name: string; description: string; - githubUrl: string; + githubUrl?: string; + docsUrl?: string; teaser: string; services: string[]; integrations: string[]; useCases: string[]; + // Snowflake-only + features?: string[]; } interface FilterState { services: string[]; useCases: string[]; integrations: string[]; + // Snowflake-only + features: string[]; } interface ApplicationsShowcaseProps { applications: Application[]; services: Record; integrations: Record; + docs?: 'aws' | 'snowflake'; } const ApplicationCard: React.FC<{ app: Application; services: Record; integrations: Record; -}> = ({ app, services, integrations }) => { + docs?: 'aws' | 'snowflake'; +}> = ({ app, services, integrations, docs }) => { return (

{app.name}

-
- {app.services.slice(0, 10).map((serviceCode) => ( -
- {services[serviceCode] -
- ))} - {app.services.length > 10 && ( -
+{app.services.length - 10}
- )} -
+ {docs === 'aws' && ( +
+ {app.services.slice(0, 10).map((serviceCode) => ( +
+ {services[serviceCode] +
+ ))} + {app.services.length > 10 && ( +
+{app.services.length - 10}
+ )} +
+ )} + {docs === 'snowflake' && (app.features?.length ?? 0) > 0 && ( +
+ {(app.features as string[]).slice(0, 10).map((feature) => ( + {feature} + ))} + {(app.features as string[]).length > 10 && ( +
+{(app.features as string[]).length - 10}
+ )} +
+ )}

{app.description}

@@ -73,11 +92,13 @@ export const ApplicationsShowcase: React.FC = ({ applications, services, integrations, + docs, }) => { const [filters, setFilters] = useState({ services: [], useCases: [], integrations: [], + features: [], }); const [searchTerm, setSearchTerm] = useState(''); @@ -89,6 +110,13 @@ export const ApplicationsShowcase: React.FC = ({ return Array.from(allServices).sort((a, b) => (services[a] || a).localeCompare(services[b] || b)); }, [applications, services]); + const uniqueFeatures = useMemo(() => { + const allFeatures = new Set( + applications.flatMap(app => (app.features ?? [])) + ); + return Array.from(allFeatures).sort(); + }, [applications]); + const uniqueUseCases = useMemo(() => { const allUseCases = new Set(applications.flatMap(app => app.useCases)); return Array.from(allUseCases).sort(); @@ -109,13 +137,19 @@ export const ApplicationsShowcase: React.FC = ({ app.name.toLowerCase().includes(searchLower) || app.description.toLowerCase().includes(searchLower) || app.useCases.some(useCase => useCase.toLowerCase().includes(searchLower)) || - app.services.some(service => (services[service] || service).toLowerCase().includes(searchLower)) || + (docs === 'aws' && app.services.some(service => (services[service] || service).toLowerCase().includes(searchLower))) || + (docs === 'snowflake' && (app.features ?? []).some(feature => feature.toLowerCase().includes(searchLower))) || app.integrations.some(integration => (integrations[integration] || integration).toLowerCase().includes(searchLower)); if (!matchesSearch) return false; } // Other filters - if (filters.services.length > 0 && !filters.services.some(service => app.services.includes(service))) return false; + if (docs === 'aws') { + if (filters.services.length > 0 && !filters.services.some(service => app.services.includes(service))) return false; + } else if (docs === 'snowflake') { + const appFeatures = app.features ?? []; + if (filters.features.length > 0 && !filters.features.some(feature => appFeatures.includes(feature))) return false; + } if (filters.useCases.length > 0 && !filters.useCases.some(useCase => app.useCases.includes(useCase))) return false; if (filters.integrations.length > 0 && !filters.integrations.some(integration => app.integrations.includes(integration))) return false; @@ -126,7 +160,7 @@ export const ApplicationsShowcase: React.FC = ({ return filtered.sort((a, b) => { return a.name.localeCompare(b.name); }); - }, [applications, filters, searchTerm, sortBy, services, integrations]); + }, [applications, filters, searchTerm, sortBy, services, integrations, docs]); const isSingleResult = filteredApplications.length === 1; const gridStyle = useMemo(() => ({ @@ -148,6 +182,7 @@ export const ApplicationsShowcase: React.FC = ({ services: [], useCases: [], integrations: [], + features: [], }); setSearchTerm(''); }; @@ -155,6 +190,7 @@ export const ApplicationsShowcase: React.FC = ({ const hasActiveFilters = filters.services.length > 0 || filters.useCases.length > 0 || filters.integrations.length > 0 || + filters.features.length > 0 || searchTerm.length > 0; return ( @@ -401,6 +437,22 @@ export const ApplicationsShowcase: React.FC = ({ font-size: 0.875rem; } + .feature-pills { + display: flex; + gap: 0.375rem; + flex-wrap: wrap; + margin: 0 0 0.75rem 0; + } + + .feature-pill { + padding: 0.25rem 0.5rem; + background: var(--sl-color-bg); + border: 1px solid var(--sl-color-gray-6); + border-radius: 0.25rem; + font-size: 0.75rem; + color: var(--sl-color-gray-3); + } + .card-footer { display: flex; justify-content: flex-start; @@ -557,21 +609,39 @@ export const ApplicationsShowcase: React.FC = ({ )}
- + {docs === 'aws' ? ( + + ) : ( + + )}