- Introduced custodian_hub_v3.mmd, custodian_hub_v4_final.mmd, and custodian_hub_v5_FINAL.mmd for Mermaid representation. - Created custodian_hub_FINAL.puml and custodian_hub_v3.puml for PlantUML representation. - Defined entities such as CustodianReconstruction, Identifier, TimeSpan, Agent, CustodianName, CustodianObservation, ReconstructionActivity, Appellation, ConfidenceMeasure, Custodian, LanguageCode, and SourceDocument. - Established relationships and associations between entities, including temporal extents, observations, and reconstruction activities. - Incorporated enumerations for various types, statuses, and classifications relevant to custodians and their activities.
889 lines
20 KiB
Markdown
889 lines
20 KiB
Markdown
# 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<void> {
|
|
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<void> {
|
|
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<RdfData | null> {
|
|
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<RdfData[]> {
|
|
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<void> {
|
|
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<void> {
|
|
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<Error | null>(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: '<http://example.org/s> <http://example.org/p> <http://example.org/o> .',
|
|
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 (
|
|
<div style={{ padding: '2rem' }}>
|
|
<h1>Error</h1>
|
|
<p>{error.message}</p>
|
|
</div>
|
|
);
|
|
}
|
|
|
|
if (!isReady) {
|
|
return (
|
|
<div style={{ padding: '2rem' }}>
|
|
<h1>Loading...</h1>
|
|
<p>Initializing database...</p>
|
|
</div>
|
|
);
|
|
}
|
|
|
|
return (
|
|
<div style={{ padding: '2rem' }}>
|
|
<h1>GLAM Heritage Custodian Ontology</h1>
|
|
<p>Welcome to the RDF visualization platform.</p>
|
|
|
|
<div style={{ marginTop: '2rem' }}>
|
|
<button
|
|
onClick={handleLoadData}
|
|
style={{
|
|
padding: '0.5rem 1rem',
|
|
fontSize: '1rem',
|
|
cursor: 'pointer',
|
|
}}
|
|
>
|
|
Load Data
|
|
</button>
|
|
</div>
|
|
|
|
<div style={{ marginTop: '2rem' }}>
|
|
<h2>Quick Stats</h2>
|
|
<ul>
|
|
<li>Database Status: {isReady ? '✅ Ready' : '❌ Not Ready'}</li>
|
|
<li>RDF Schemas: Available at /schemas/20251121/rdf/</li>
|
|
<li>UML Diagrams: Available at /schemas/20251121/uml/</li>
|
|
</ul>
|
|
</div>
|
|
</div>
|
|
);
|
|
};
|
|
EOF
|
|
```
|
|
|
|
### Update App.tsx
|
|
|
|
```bash
|
|
cat > src/App.tsx << 'EOF'
|
|
import { Home } from './pages/Home';
|
|
|
|
function App() {
|
|
return <Home />;
|
|
}
|
|
|
|
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
|