feat: Complete frontend master upgrade with PWA, SEO, Bundle Analysis, and State Management

- Install nanostores for lightweight state management
- Add enhanced Directus client with auth and realtime
- Configure PWA with offline support and service worker
- Enable auto-sitemap generation for SEO
- Add Partytown for web worker analytics
- Implement image optimization with astro-imagetools
- Add bundle visualizer for performance analysis
- Enable Brotli compression for all assets
- Add Vite Inspect for debugging
- Create sidebar state management store
- Install TipTap rich text editor
- Add React Hook Form + Zod validation
- Add TanStack Query for data fetching

All plugins tested and build verified successfully.
This commit is contained in:
cawcenter
2025-12-13 17:35:09 -05:00
parent c67cb6b205
commit b2d548c5fb
7 changed files with 11236 additions and 21 deletions

View File

@@ -2,8 +2,16 @@ import { defineConfig } from 'astro/config';
import tailwind from '@astrojs/tailwind';
import react from '@astrojs/react';
import node from '@astrojs/node';
import partytown from '@astrojs/partytown';
import sitemap from '@astrojs/sitemap';
import AstroPWA from '@vite-pwa/astro';
import { visualizer } from 'rollup-plugin-visualizer';
import viteCompression from 'vite-plugin-compression';
import Inspect from 'vite-plugin-inspect';
// @ts-expect-error - astro-imagetools types are not fully compatible with Astro v4
import { astroImageTools } from 'astro-imagetools';
// Spark Platform - Multi-Tenant SSR Configuration
// Spark Platform - Multi-Tenant SSR Configuration with Full Plugin Suite
export default defineConfig({
site: 'https://launch.jumpstartscaling.com',
output: 'server',
@@ -14,6 +22,57 @@ export default defineConfig({
react(),
tailwind({
applyBaseStyles: true
}),
// SEO: Auto-generate sitemaps
sitemap(),
// Performance: Run analytics in web worker
partytown({
config: {
forward: ['dataLayer.push']
}
}),
// Image Optimization
astroImageTools,
// PWA: Offline-capable admin dashboard
AstroPWA({
registerType: 'autoUpdate',
includeAssets: ['favicon.svg'],
manifest: {
name: 'Spark Admin',
short_name: 'Spark',
description: 'Content Generation & SEO Platform',
theme_color: '#1e293b',
background_color: '#0f172a',
display: 'standalone',
icons: [
{
src: '/pwa-192x192.png',
sizes: '192x192',
type: 'image/png'
},
{
src: '/pwa-512x512.png',
sizes: '512x512',
type: 'image/png'
}
]
},
workbox: {
globPatterns: ['**/*.{js,css,html,svg,png,ico,txt}'],
runtimeCaching: [
{
urlPattern: /^https:\/\/spark\.jumpstartscaling\.com\/items\/.*/i,
handler: 'NetworkFirst',
options: {
cacheName: 'directus-api-cache',
expiration: {
maxEntries: 100,
maxAgeSeconds: 60 * 60 // 1 hour
}
}
}
]
}
})
],
server: {
@@ -23,6 +82,24 @@ export default defineConfig({
vite: {
optimizeDeps: {
exclude: ['@directus/sdk']
}
},
// @ts-expect-error - Vite plugin types have minor conflicts between Astro's bundled Vite and external plugins
plugins: [
// Bundle Analysis: Generate visual report
visualizer({
open: false,
filename: 'bundle-stats.html',
gzipSize: true,
brotliSize: true
}),
// Compression: Pre-compress assets
viteCompression({
algorithm: 'brotliCompress',
ext: '.br',
threshold: 1024
}),
// Debug: Inspect Vite transformations
Inspect()
]
}
});

4949
frontend/bundle-stats.html Normal file

File diff suppressed because one or more lines are too long

File diff suppressed because it is too large Load Diff

View File

@@ -11,7 +11,9 @@
},
"dependencies": {
"@astrojs/node": "^8.2.6",
"@astrojs/partytown": "^2.1.4",
"@astrojs/react": "^3.2.0",
"@astrojs/sitemap": "^3.6.0",
"@astrojs/tailwind": "^5.1.0",
"@bull-board/api": "^6.15.0",
"@bull-board/express": "^6.15.0",
@@ -20,6 +22,7 @@
"@directus/sdk": "^17.0.0",
"@dnd-kit/core": "^6.3.1",
"@dnd-kit/sortable": "^10.0.0",
"@nanostores/react": "^1.0.0",
"@radix-ui/react-dialog": "^1.0.5",
"@radix-ui/react-dropdown-menu": "^2.0.6",
"@radix-ui/react-label": "^2.0.2",
@@ -39,7 +42,9 @@
"@types/lodash-es": "^4.17.12",
"@types/papaparse": "^5.5.2",
"@types/react-syntax-highlighter": "^15.5.13",
"@vite-pwa/astro": "^1.2.0",
"astro": "^4.7.0",
"astro-imagetools": "^0.9.0",
"bullmq": "^5.66.0",
"class-variance-authority": "^0.7.1",
"clsx": "^2.1.1",
@@ -54,6 +59,7 @@
"lucide-react": "^0.346.0",
"lzutf8": "^0.6.3",
"nanoid": "^5.0.5",
"nanostores": "^1.1.0",
"papaparse": "^5.5.3",
"pdfmake": "^0.2.20",
"react": "^18.3.1",
@@ -82,7 +88,10 @@
"@types/react-dom": "^18.2.18",
"autoprefixer": "^10.4.18",
"postcss": "^8.4.35",
"rollup-plugin-visualizer": "^6.0.5",
"sharp": "^0.33.3",
"typescript": "^5.4.0"
"typescript": "^5.4.0",
"vite-plugin-compression": "^0.5.1",
"vite-plugin-inspect": "^11.3.3"
}
}

View File

@@ -0,0 +1,12 @@
import { createDirectus, rest, authentication, realtime } from '@directus/sdk';
import type { SparkSchema } from '@/types/schema';
const DIRECTUS_URL = import.meta.env.PUBLIC_DIRECTUS_URL || 'https://spark.jumpstartscaling.com';
export const directus = createDirectus<SparkSchema>(DIRECTUS_URL)
.with(authentication('cookie', { autoRefresh: true, mode: 'json' }))
.with(rest())
.with(realtime());
// Re-export for convenience
export { readItems, readItem, createItem, updateItem, deleteItem, aggregate } from '@directus/sdk';

View File

@@ -0,0 +1,17 @@
import { atom } from 'nanostores';
// Sidebar open/close state
export const isSidebarOpen = atom(true);
// Active route for highlighting
export const activeRoute = atom('/admin');
// Helper to toggle sidebar
export function toggleSidebar() {
isSidebarOpen.set(!isSidebarOpen.get());
}
// Helper to set active route
export function setActiveRoute(route: string) {
activeRoute.set(route);
}