# GLAM Frontend Quick Start Guide ## 🚀 Get Started in 15 Minutes This guide will help you set up and start developing the GLAM frontend immediately. --- ## Prerequisites Check Before starting, ensure you have: ```bash # Check Node.js version (need 18+) node --version # Should show v18.x.x or higher # Check npm version npm --version # Should show 9.x.x or higher # Check Git git --version # Any recent version ``` If any are missing: - Install Node.js: https://nodejs.org/ (LTS version) - npm comes with Node.js - Install Git: https://git-scm.com/ --- ## Step 1: Project Initialization (5 minutes) ```bash # Navigate to the GLAM project cd /Users/kempersc/apps/glam # Create frontend directory mkdir -p frontend cd frontend # Initialize Vite project with React + TypeScript npm create vite@latest . -- --template react-ts # Install dependencies (this will take 2-3 minutes) npm install ``` ### Verify Installation ```bash # Start the dev server npm run dev # You should see: # ➜ Local: http://localhost:5173/ # ➜ press h to show help ``` Open http://localhost:5173/ in your browser. You should see the Vite + React welcome page. 🎉 **Stop the server** (Ctrl+C) before continuing. --- ## Step 2: Project Structure Setup (3 minutes) ```bash # Create the directory structure mkdir -p src/{components,lib,hooks,stores,types,utils,pages,styles} mkdir -p src/components/{layout,visualizations,forms,ui} mkdir -p src/lib/{rdf,storage,api,utils} mkdir -p src/components/visualizations/{GraphView,Timeline,Map,Hierarchy} mkdir -p tests/{unit,integration,e2e} mkdir -p public/assets # Verify structure tree -L 3 src/ # or use 'ls -R src/' if tree not installed ``` Expected output: ``` src/ ├── components/ │ ├── forms/ │ ├── layout/ │ ├── ui/ │ └── visualizations/ │ ├── GraphView/ │ ├── Hierarchy/ │ ├── Map/ │ └── Timeline/ ├── hooks/ ├── lib/ │ ├── api/ │ ├── rdf/ │ ├── storage/ │ └── utils/ ├── pages/ ├── stores/ ├── styles/ ├── types/ └── utils/ ``` --- ## Step 3: Install Core Dependencies (5 minutes) ```bash # Core dependencies npm install d3 @types/d3 n3 zustand @tanstack/react-query npm install react-router-dom axios npm install leaflet @types/leaflet npm install date-fns lodash @types/lodash # Dev dependencies npm install -D vitest @vitest/ui @testing-library/react npm install -D @testing-library/jest-dom @testing-library/user-event npm install -D @playwright/test npm install -D eslint @typescript-eslint/parser @typescript-eslint/eslint-plugin npm install -D prettier eslint-config-prettier eslint-plugin-prettier npm install -D @types/node # Install Playwright browsers (takes 1-2 minutes) npx playwright install ``` ### Verify Dependencies ```bash # Check package.json cat package.json | grep -A 20 '"dependencies"' ``` --- ## Step 4: Configuration Files (2 minutes) ### TypeScript Configuration ```bash cat > tsconfig.json << 'EOF' { "compilerOptions": { "target": "ES2022", "useDefineForClassFields": true, "lib": ["ES2022", "DOM", "DOM.Iterable"], "module": "ESNext", "skipLibCheck": true, "strict": true, /* Bundler mode */ "moduleResolution": "bundler", "allowImportingTsExtensions": true, "resolveJsonModule": true, "isolatedModules": true, "noEmit": true, "jsx": "react-jsx", /* Linting */ "noUnusedLocals": true, "noUnusedParameters": true, "noFallthroughCasesInSwitch": true, /* Path aliases */ "baseUrl": ".", "paths": { "@/*": ["./src/*"] } }, "include": ["src"], "references": [{ "path": "./tsconfig.node.json" }] } EOF ``` ### Vite Configuration ```bash cat > vite.config.ts << 'EOF' import { defineConfig } from 'vite' import react from '@vitejs/plugin-react' import path from 'path' export default defineConfig({ plugins: [react()], resolve: { alias: { '@': path.resolve(__dirname, './src'), }, }, server: { port: 5173, proxy: { '/api': { target: 'http://localhost:8000', changeOrigin: true, }, }, }, }) EOF ``` ### Vitest Configuration ```bash cat > vitest.config.ts << 'EOF' import { defineConfig } from 'vitest/config' import react from '@vitejs/plugin-react' import path from 'path' export default defineConfig({ plugins: [react()], test: { globals: true, environment: 'jsdom', setupFiles: './tests/setup.ts', coverage: { provider: 'v8', reporter: ['text', 'json', 'html'], exclude: [ 'node_modules/', 'tests/', '**/*.test.{ts,tsx}', '**/*.spec.{ts,tsx}', ], }, }, resolve: { alias: { '@': path.resolve(__dirname, './src'), }, }, }) EOF ``` ### ESLint Configuration ```bash cat > .eslintrc.json << 'EOF' { "extends": [ "eslint:recommended", "plugin:@typescript-eslint/recommended", "plugin:react/recommended", "plugin:react-hooks/recommended", "prettier" ], "parser": "@typescript-eslint/parser", "plugins": ["@typescript-eslint", "react", "react-hooks"], "rules": { "react/react-in-jsx-scope": "off", "@typescript-eslint/no-unused-vars": ["error", { "argsIgnorePattern": "^_" }] }, "settings": { "react": { "version": "detect" } } } EOF ``` ### Prettier Configuration ```bash cat > .prettierrc << 'EOF' { "semi": true, "trailingComma": "es5", "singleQuote": true, "printWidth": 100, "tabWidth": 2, "useTabs": false } EOF ``` --- ## Step 5: First Component - IndexedDB Manager Let's migrate the first utility from example_ld. ### Create Type Definitions ```bash cat > src/types/database.ts << 'EOF' export interface RdfData { id: string; data: string; format: RdfFormat; metadata: RdfMetadata; timestamp: Date; } export interface RdfMetadata { tripleCount: number; source: string; description?: string; } export type RdfFormat = | 'text/turtle' | 'application/n-triples' | 'application/ld+json' | 'application/rdf+xml'; export interface DatabaseConfig { name: string; version: number; stores: StoreConfig[]; } export interface StoreConfig { name: string; keyPath: string; indexes?: IndexConfig[]; } export interface IndexConfig { name: string; keyPath: string; unique: boolean; } EOF ``` ### Create IndexedDB Manager ```bash cat > src/lib/storage/indexed-db.ts << 'EOF' import type { RdfData, DatabaseConfig } from '@/types/database'; export class IndexedDBManager { private db: IDBDatabase | null = null; private readonly config: DatabaseConfig = { name: 'GLAM_RDF_DB', version: 1, stores: [ { name: 'rdf_data', keyPath: 'id', indexes: [ { name: 'timestamp', keyPath: 'timestamp', unique: false }, { name: 'format', keyPath: 'format', unique: false }, ], }, ], }; async initialize(): Promise { return new Promise((resolve, reject) => { const request = indexedDB.open(this.config.name, this.config.version); request.onerror = () => reject(request.error); request.onsuccess = () => { this.db = request.result; resolve(); }; request.onupgradeneeded = (event) => { const db = (event.target as IDBOpenDBRequest).result; this.config.stores.forEach((storeConfig) => { if (!db.objectStoreNames.contains(storeConfig.name)) { const store = db.createObjectStore(storeConfig.name, { keyPath: storeConfig.keyPath, }); storeConfig.indexes?.forEach((index) => { store.createIndex(index.name, index.keyPath, { unique: index.unique, }); }); } }); }; }); } async save(data: RdfData): Promise { if (!this.db) throw new Error('Database not initialized'); return new Promise((resolve, reject) => { const transaction = this.db!.transaction(['rdf_data'], 'readwrite'); const store = transaction.objectStore('rdf_data'); const request = store.put(data); request.onsuccess = () => resolve(); request.onerror = () => reject(request.error); }); } async get(id: string): Promise { if (!this.db) throw new Error('Database not initialized'); return new Promise((resolve, reject) => { const transaction = this.db!.transaction(['rdf_data'], 'readonly'); const store = transaction.objectStore('rdf_data'); const request = store.get(id); request.onsuccess = () => resolve(request.result || null); request.onerror = () => reject(request.error); }); } async getAll(): Promise { if (!this.db) throw new Error('Database not initialized'); return new Promise((resolve, reject) => { const transaction = this.db!.transaction(['rdf_data'], 'readonly'); const store = transaction.objectStore('rdf_data'); const request = store.getAll(); request.onsuccess = () => resolve(request.result); request.onerror = () => reject(request.error); }); } async delete(id: string): Promise { if (!this.db) throw new Error('Database not initialized'); return new Promise((resolve, reject) => { const transaction = this.db!.transaction(['rdf_data'], 'readwrite'); const store = transaction.objectStore('rdf_data'); const request = store.delete(id); request.onsuccess = () => resolve(); request.onerror = () => reject(request.error); }); } async clear(): Promise { if (!this.db) throw new Error('Database not initialized'); return new Promise((resolve, reject) => { const transaction = this.db!.transaction(['rdf_data'], 'readwrite'); const store = transaction.objectStore('rdf_data'); const request = store.clear(); request.onsuccess = () => resolve(); request.onerror = () => reject(request.error); }); } } // Singleton instance export const db = new IndexedDBManager(); EOF ``` ### Create React Hook ```bash cat > src/hooks/useDatabase.ts << 'EOF' import { useState, useEffect } from 'react'; import { db } from '@/lib/storage/indexed-db'; import type { RdfData } from '@/types/database'; export const useDatabase = () => { const [isReady, setIsReady] = useState(false); const [error, setError] = useState(null); useEffect(() => { db.initialize() .then(() => setIsReady(true)) .catch(setError); }, []); const saveData = async (data: RdfData) => { try { await db.save(data); } catch (err) { setError(err as Error); throw err; } }; const getData = async (id: string) => { try { return await db.get(id); } catch (err) { setError(err as Error); throw err; } }; const getAllData = async () => { try { return await db.getAll(); } catch (err) { setError(err as Error); throw err; } }; const deleteData = async (id: string) => { try { await db.delete(id); } catch (err) { setError(err as Error); throw err; } }; const clearAll = async () => { try { await db.clear(); } catch (err) { setError(err as Error); throw err; } }; return { isReady, error, saveData, getData, getAllData, deleteData, clearAll, }; }; EOF ``` ### Write Tests ```bash # Create test setup cat > tests/setup.ts << 'EOF' import '@testing-library/jest-dom'; import { expect, afterEach } from 'vitest'; import { cleanup } from '@testing-library/react'; afterEach(() => { cleanup(); }); EOF # Create test for IndexedDB manager cat > src/lib/storage/indexed-db.test.ts << 'EOF' import { describe, it, expect, beforeEach } from 'vitest'; import { IndexedDBManager } from './indexed-db'; import type { RdfData } from '@/types/database'; describe('IndexedDBManager', () => { let dbManager: IndexedDBManager; beforeEach(async () => { dbManager = new IndexedDBManager(); await dbManager.initialize(); await dbManager.clear(); }); it('should initialize database', async () => { expect(dbManager).toBeDefined(); }); it('should save and retrieve data', async () => { const testData: RdfData = { id: 'test-1', data: ' .', format: 'text/turtle', metadata: { tripleCount: 1, source: 'test', }, timestamp: new Date(), }; await dbManager.save(testData); const retrieved = await dbManager.get('test-1'); expect(retrieved).toBeDefined(); expect(retrieved?.id).toBe('test-1'); expect(retrieved?.format).toBe('text/turtle'); }); it('should delete data', async () => { const testData: RdfData = { id: 'test-2', data: 'test', format: 'text/turtle', metadata: { tripleCount: 0, source: 'test' }, timestamp: new Date(), }; await dbManager.save(testData); await dbManager.delete('test-2'); const retrieved = await dbManager.get('test-2'); expect(retrieved).toBeNull(); }); it('should get all data', async () => { const testData1: RdfData = { id: 'test-3', data: 'test1', format: 'text/turtle', metadata: { tripleCount: 0, source: 'test' }, timestamp: new Date(), }; const testData2: RdfData = { id: 'test-4', data: 'test2', format: 'application/n-triples', metadata: { tripleCount: 0, source: 'test' }, timestamp: new Date(), }; await dbManager.save(testData1); await dbManager.save(testData2); const all = await dbManager.getAll(); expect(all).toHaveLength(2); }); }); EOF ``` ### Run Tests ```bash # Run the test npm test # You should see output like: # ✓ src/lib/storage/indexed-db.test.ts (4 tests) # ✓ IndexedDBManager # ✓ should initialize database # ✓ should save and retrieve data # ✓ should delete data # ✓ should get all data ``` --- ## Step 6: Create First Page Component ```bash cat > src/pages/Home.tsx << 'EOF' import { useDatabase } from '@/hooks/useDatabase'; export const Home = () => { const { isReady, error, getAllData } = useDatabase(); const handleLoadData = async () => { try { const data = await getAllData(); console.log('Loaded data:', data); alert(`Loaded ${data.length} RDF datasets`); } catch (err) { console.error('Error loading data:', err); } }; if (error) { return (

Error

{error.message}

); } if (!isReady) { return (

Loading...

Initializing database...

); } return (

GLAM Heritage Custodian Ontology

Welcome to the RDF visualization platform.

Quick Stats

  • Database Status: {isReady ? '✅ Ready' : '❌ Not Ready'}
  • RDF Schemas: Available at /schemas/20251121/rdf/
  • UML Diagrams: Available at /schemas/20251121/uml/
); }; EOF ``` ### Update App.tsx ```bash cat > src/App.tsx << 'EOF' import { Home } from './pages/Home'; function App() { return ; } export default App; EOF ``` --- ## Step 7: Test Your Application ```bash # Start the development server npm run dev ``` Visit http://localhost:5173/ and you should see: - "GLAM Heritage Custodian Ontology" heading - "Database Status: ✅ Ready" - A "Load Data" button Click the button - it should show an alert with "Loaded 0 RDF datasets" (since we haven't added any data yet). --- ## Step 8: Update package.json Scripts ```bash # Add these scripts to package.json (merge with existing) cat >> package.json << 'EOF' Add these to your "scripts" section: "scripts": { "dev": "vite", "build": "tsc && vite build", "preview": "vite preview", "test": "vitest", "test:ui": "vitest --ui", "test:coverage": "vitest --coverage", "test:e2e": "playwright test", "lint": "eslint src --ext ts,tsx --report-unused-disable-directives --max-warnings 0", "lint:fix": "eslint src --ext ts,tsx --fix", "format": "prettier --write \"src/**/*.{ts,tsx,json,css,md}\"", "type-check": "tsc --noEmit" } EOF ``` --- ## ✅ Success! You're Ready to Develop You now have: - ✅ A working Vite + React + TypeScript project - ✅ Complete directory structure - ✅ All core dependencies installed - ✅ TypeScript, ESLint, Prettier configured - ✅ First utility (IndexedDB manager) migrated from example_ld - ✅ First React hook (useDatabase) - ✅ First page component (Home) - ✅ Working tests with Vitest --- ## Next Steps ### Option 1: Continue with Phase 1 (Recommended) Follow the master checklist at `docs/plan/frontend/05-master-checklist.md`: - Day 6-7: Complete RDF Parser migration - Day 8-9: Utilities migration - Day 10: API client migration ### Option 2: Jump to Visualizations If you want to see something visual quickly: - Skip to Phase 3 (Week 5) in the master checklist - Start building the D3 force-directed graph - Load actual RDF data from `/schemas/20251121/rdf/` ### Option 3: Explore Example LD Study the original implementation: ```bash cd /Users/kempersc/apps/example_ld cat js/db.js # Compare with your implementation cat js/rdfParser.js # Next migration target ``` --- ## Useful Commands Reference ```bash # Development npm run dev # Start dev server npm run build # Production build npm run preview # Preview production build # Testing npm test # Run tests in watch mode npm run test:ui # Visual test interface npm run test:coverage # Coverage report # Code Quality npm run lint # Check for errors npm run lint:fix # Auto-fix errors npm run format # Format code npm run type-check # TypeScript check # Debugging npm run dev -- --debug # Debug mode npm run dev -- --host # Expose to network ``` --- ## Troubleshooting ### Problem: "Cannot find module '@/...'" **Solution**: Make sure `tsconfig.json` has the path alias configured and restart your IDE. ### Problem: IndexedDB tests fail **Solution**: Tests run in jsdom environment which has limited IndexedDB support. Consider using `fake-indexeddb` package for testing. ### Problem: Port 5173 already in use **Solution**: ```bash # Kill the process using the port lsof -ti:5173 | xargs kill -9 # Or use a different port npm run dev -- --port 3000 ``` ### Problem: Type errors with D3 **Solution**: Make sure you installed both `d3` and `@types/d3`: ```bash npm install d3 @types/d3 ``` --- ## Getting Help - **Documentation**: `docs/plan/frontend/` directory - **Master Checklist**: `docs/plan/frontend/05-master-checklist.md` - **Visual Roadmap**: `docs/plan/frontend/06-visual-roadmap.md` - **Example LD Reference**: `/Users/kempersc/apps/example_ld` --- ## Pro Tips 1. **Use TypeScript Strict Mode**: It catches bugs early 2. **Write Tests First**: TDD leads to better design 3. **Keep Components Small**: Easy to test and maintain 4. **Use React Query**: Simplifies server state management 5. **Profile Performance**: Use React DevTools Profiler --- **Congratulations!** 🎉 You're now ready to build the GLAM frontend. **Estimated Time to Full Implementation**: 16 weeks following the master checklist. **Next milestone**: Complete Phase 1 (Core Libraries) in 2 weeks. --- **Last Updated**: 2025-11-22 **Version**: 1.0 **Guide Completion Time**: ~15 minutes