Client Portal System Implementation with Dynamic Collections and Enhanced UI
Summary
Implemented a comprehensive client portal system with dynamic collections for recommendations and projects, enhanced routing with client-specific URLs, and modern UI with CSS animations and responsive design.
Why Care
This client portal system provides a scalable foundation for delivering personalized content to different clients. The dynamic collection system allows for easy content management, while the enhanced UI with animations creates a professional, engaging user experience. The modular architecture makes it easy to add new clients and content types without code changes.
Implementation
Changes Made
Content Collections and Configuration
- File:
site/src/content.config.ts
- Added
clientRecommendationsCollection
with glob pattern for**/Recommendations/**/*.{md,mdx}
- Added
clientProjectsCollection
with glob pattern for**/Projects/**/*.{md,mdx}
- Both collections include slug generation and title processing via
processEntries
- Updated collections export to include new client collections
- Added path mappings for client content directories
Dynamic Routing System
- File:
site/src/pages/client/[client]/thread/[magazine].astro
- Created dynamic routing for client-specific magazine pages
- Supports both recommendations and projects collections
- Automatically discovers clients from folder structure
- Generates static paths for all client/magazine combinations
- Implements proper sorting by date with fallback handling
- File:
site/src/pages/client/[client]/read/[...slug].astro
- Created dedicated reader page for client content
- Supports both individual essay viewing and collection browsing
- Implements proper slug handling and navigation
- Includes scroll-to-content functionality for direct essay links
Enhanced Client Portal Layout
- File:
site/src/layouts/ClientPortalLayout.astro
- Removed reader section to reduce crowding
- Added animated client portal cards section
- Implemented staggered CSS animations for page load
- Enhanced responsive design with mobile optimizations
- Added proper icon styling with site accent colors
- Maintained references and tooling sections
UI Components and Styling
- File:
site/src/content/messages/clientPortalCards.json
- Created card configuration for Reader, Recommendations, and Projects
- Added custom SVG icons with site accent color styling
- Implemented proper routing paths for each card
- File:
site/src/components/basics/messages/IconHeaderMessageCardGrid.astro
- Enhanced grid component with responsive design
- Added proper card container styling
- Implemented center alignment for odd-numbered card sets
CSS Animations and Styling
- Added comprehensive animation system with keyframes:
fadeInSlideUp
for section containersfadeInContent
for content wrappersfadeInText
,fadeInSubtitle
,fadeInTitle
,fadeInDescription
for text elementsfadeInGrid
for card grid animations
- Implemented staggered timing (0.2s to 1.2s delays)
- Used cubic-bezier easing for smooth, professional animations
- Added responsive breakpoints for mobile optimization
Technical Details
Collection Schema Design
typescript
// Client collections follow consistent pattern
const clientRecommendationsCollection = defineCollection({
loader: glob({
pattern: "**/Recommendations/**/*.{md,mdx}",
base: resolveContentPath("client-content")
}),
schema: z.object({
aliases: z.union([
z.string().transform(str => [str]),
z.array(z.string()),
z.null(),
z.undefined()
]).transform(val => val ?? []).default([]),
}).passthrough().transform((data, context) => {
// Slug and title processing
const filename = String(context.path).split('/').pop()?.replace(/\.(md|mdx)$/, '') || '';
const displayTitle = data.title || filename.replace(/[-_]/g, ' ').replace(/\s+/g, ' ').trim();
return {
...data,
title: displayTitle,
slug: filename.toLowerCase().replace(/\s+/g, '-'),
};
})
});
Dynamic Path Generation
typescript
// Automatic client discovery from folder structure
const clients = [...new Set(allEntries.map(entry => {
const entryPath = String(entry.id);
const pathParts = entryPath.split('/');
return pathParts[0].toLowerCase();
}))];
// Generate paths for each client/magazine combination
for (const [magazineKey, { collection, urlPrefix }] of Object.entries(collectionMap)) {
for (const client of clients) {
const clientEntries = processedEntries.filter(entry => {
const entryPath = String(entry.id);
return entryPath.toLowerCase().includes(client.toLowerCase());
});
// Generate static paths...
}
}
Animation System
css
/* Staggered animation implementation */
.client-portal-cards-section {
animation: fadeInSlideUp 0.8s cubic-bezier(0.4, 0, 0.2, 1) forwards;
opacity: 0;
transform: translateY(30px);
}
.client-portal-cards-content {
animation: fadeInContent 1s cubic-bezier(0.4, 0, 0.2, 1) 0.2s forwards;
opacity: 0;
}
/* Keyframe definitions for smooth transitions */
@keyframes fadeInSlideUp {
from {
opacity: 0;
transform: translateY(30px);
}
to {
opacity: 1;
transform: translateY(0);
}
}
Integration Points
Content Structure Requirements
- Client content must be organized in
content/client-content/{ClientName}/
structure - Recommendations go in
{ClientName}/Recommendations/
folder - Projects go in
{ClientName}/Projects/
folder - Essays go in
{ClientName}/essays/
folder - Each client needs proper cased names (e.g., "Laerdal" not "laerdal")
URL Structure
- Client portal:
/client/{client}
- Reader:
/client/{client}/read
- Individual essays:
/client/{client}/read/{essay-slug}
- Recommendations:
/client/{client}/thread/recommendations
- Projects:
/client/{client}/thread/projects
Dependencies
- Requires
processEntries
utility for slug and title processing - Uses
toProperCase
utility for client name formatting - Integrates with existing
IconHeaderMessageCardGrid
component - Relies on
CollectionReaderLayout
for essay display
Documentation
Usage Examples
- Adding a new client: Create folder structure in
content/client-content/{ClientName}/
- Adding recommendations: Place markdown files in
{ClientName}/Recommendations/
- Adding projects: Place markdown files in
{ClientName}/Projects/
- Customizing cards: Edit
site/src/content/messages/clientPortalCards.json
File Structure
text
content/client-content/
├── Laerdal/
│ ├── Recommendations/
│ │ └── strategic-recommendations.md
│ ├── Projects/
│ │ └── active-projects.md
│ └── essays/
│ └── client-essays.md
└── OtherClient/
└── ...
Performance Considerations
- Static generation for all client routes ensures fast loading
- CSS animations use GPU acceleration via transform properties
- Responsive design prevents layout shifts on mobile devices
- Lazy loading implemented for images and content
Future Enhancements
- Support for additional content types (case studies, reports)
- Client-specific theming and branding
- Advanced filtering and search capabilities
- Integration with external content management systems