Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
90 changes: 81 additions & 9 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,12 +1,25 @@
# Vite React Template
# Living Page

A modern React template using Vite, TypeScript, Material-UI, and testing setup.
A collaborative web application where any user can add and improve content for everyone else. Built with Vite, React, TypeScript, Material-UI, and Firebase.

## Concept

The Living Page is a single, shared document composed of ordered blocks. Any authenticated user can:

- Add new blocks
- Edit existing blocks
- Delete blocks (soft delete)
- Reorder blocks

Every edit creates a new immutable version, enabling full version history tracking.

## Features

- ⚡️ Vite for super fast development
- 🎨 Material-UI with emotion styling
- 📝 TypeScript support
- 🔥 Firebase Authentication & Firestore
- 📦 Block-based content system with versioning
- ✅ Testing setup with Vitest and Testing Library
- 🔍 ESLint + Prettier configuration
- 📱 Responsive design ready
Expand All @@ -18,7 +31,13 @@ A modern React template using Vite, TypeScript, Material-UI, and testing setup.
```bash
pnpm i
```
3. Start the development server:
3. Set up Firebase:
- Create a Firebase project at https://console.firebase.google.com
- Enable Firestore Database
- Enable Google Authentication
- Copy your Firebase config and base64 encode it
- Create a `.env` file based on `.env.example` and add your encoded config
4. Start the development server:
```bash
make emulator
```
Expand All @@ -40,12 +59,65 @@ A modern React template using Vite, TypeScript, Material-UI, and testing setup.

```
src/
├── App.tsx # Root application component
├── components/ # Reusable components & their tests
├── test/ # Test configuration
├── types/ # TypeScript definitions
├── theme.ts # Theme configuration
└── main.tsx # Application entry point
├── App.tsx # Root application component
├── components/
│ ├── LivingPage.tsx # Main living page component
│ ├── BlockItem.tsx # Individual block with edit/delete/reorder
│ ├── AddBlockDialog.tsx # Dialog for adding new blocks
│ ├── VersionHistory.tsx # Version history viewer
│ └── Login.tsx # Authentication UI
├── context/
│ └── AuthContext.tsx # Firebase auth provider
├── hooks/
│ ├── useAuth.ts # Auth hook
│ └── useBlocks.ts # Block CRUD operations
├── lib/
│ └── firebase.ts # Firebase initialization
├── types/
│ └── block.ts # Block & version types
├── test/ # Test configuration
├── theme.ts # Theme configuration
└── main.tsx # Application entry point
```

## How It Works

### Block System

- Each block has an ordered position in the page
- Blocks contain multiple versions (immutable history)
- Only the current version is displayed
- Soft deletes keep data but hide blocks from view

### Versioning

- Every edit creates a new version with:
- Unique version ID
- Content snapshot
- Timestamp
- Author information
- All versions are preserved in Firestore
- Users can view complete version history

### Firestore Structure

```
blocks/
{blockId}/
order: number
isDeleted: boolean
currentVersionId: string
createdAt: timestamp
createdBy: userId
versions: [
{
id: string
content: string
createdAt: timestamp
createdBy: userId
createdByEmail: string
}
]
```

## Contributing
Expand Down
175 changes: 175 additions & 0 deletions USAGE.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,175 @@
# Living Page - Usage Guide

## Overview

The Living Page is a collaborative document where authenticated users can add, edit, delete, and reorder blocks of content. Every edit is versioned, creating an immutable history.

## Features

### Authentication

- Sign in with Google SSO
- All users must be authenticated to view or edit content
- User information (email, avatar) displayed in header
- Logout functionality available

### Block Management

#### Adding a Block

1. Click the blue floating action button (FAB) in the bottom-right corner
2. Enter your content in the dialog
3. Click "Add Block" or press Cmd+Enter (Mac) / Ctrl+Enter (Windows/Linux)

#### Editing a Block

1. Hover over a block to reveal action buttons
2. Click the Edit icon (pencil)
3. Modify the content in the text field
4. Click Save icon or press Cmd+Enter to save
5. Press Escape to cancel

#### Deleting a Block

1. Hover over a block to reveal action buttons
2. Click the Delete icon (trash can)
3. Confirm deletion in the dialog
4. Block is soft-deleted (hidden but preserved in database)

#### Reordering Blocks

1. Hover over a block to reveal action buttons
2. Click the up arrow to move block up
3. Click the down arrow to move block down
4. Changes are saved immediately

#### Viewing Version History

1. Hover over a block to reveal action buttons
2. Click the History icon (clock)
3. View all versions in chronological order
4. Current version is highlighted
5. Each version shows:
- Content
- Timestamp
- Author email
- Version number

## Keyboard Shortcuts

- **Cmd+Enter** (Mac) / **Ctrl+Enter** (Windows/Linux): Save edit or add block
- **Escape**: Cancel edit or close dialog

## UI Elements

### Header

- **Living Page** title
- User avatar and email (desktop only)
- Logout button

### Block Card

- Content display
- Action buttons (visible on hover on desktop, always visible on mobile):
- Edit
- Move up
- Move down
- Version history
- Delete
- Version info (if edited): "v{number} · Edited by {email}"

### Empty State

- Displays when no blocks exist
- "Add First Block" button to get started

### Floating Action Button (FAB)

- Fixed position in bottom-right corner
- Quick access to add new blocks
- Visible on all screen sizes

## Data Structure

### Block

- Ordered position in the page
- Soft delete flag
- Reference to current version
- Array of all versions

### Version

- Unique ID
- Content snapshot
- Creation timestamp
- Author information (ID and email)

## Best Practices

1. **Be Collaborative**: Remember that everyone can see and edit all content
2. **Write Clear Content**: Make blocks easy to understand
3. **Check Version History**: Before editing, review what others have contributed
4. **Respect Others' Work**: Build on existing content rather than replacing it
5. **Use Soft Delete**: Deleted blocks can be recovered by administrators

## Mobile Experience

- All features available on mobile devices
- Action buttons always visible (no hover required)
- Touch-optimized interface
- Responsive layout

## Technical Details

### Firebase Services Used

- **Authentication**: Google provider
- **Firestore**: Real-time database for blocks

### Security

- All operations require authentication
- Firestore security rules enforce permissions
- User identity tracked with every change

### Performance

- Real-time updates via Firestore listeners
- Optimistic UI updates
- Indexed queries for fast loading

## Troubleshooting

### Can't sign in

- Ensure Firebase Authentication is enabled
- Check that Google provider is configured
- Verify your email is authorized (if using restricted access)

### Blocks not loading

- Check browser console for errors
- Verify Firestore is enabled in Firebase project
- Ensure security rules are properly configured

### Changes not saving

- Check network connectivity
- Verify you're still authenticated
- Check browser console for Firestore errors

## Future Enhancements

Potential features for future development:

- Rich text editing
- Block templates
- Search functionality
- Comments on blocks
- User permissions and roles
- Block types (text, code, image, etc.)
- Export functionality
- Undo/redo
- Real-time collaboration indicators
19 changes: 19 additions & 0 deletions firestore.indexes.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
{
"indexes": [
{
"collectionGroup": "blocks",
"queryScope": "COLLECTION",
"fields": [
{
"fieldPath": "isDeleted",
"order": "ASCENDING"
},
{
"fieldPath": "order",
"order": "ASCENDING"
}
]
}
],
"fieldOverrides": []
}
15 changes: 15 additions & 0 deletions firestore.rules
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
rules_version = '2';
service cloud.firestore {
match /databases/{database}/documents {
match /blocks/{blockId} {
allow read: if request.auth != null;
allow create: if request.auth != null
&& request.resource.data.createdBy == request.auth.uid
&& request.resource.data.keys().hasAll(['order', 'isDeleted', 'currentVersionId', 'createdAt', 'createdBy', 'versions'])
&& request.resource.data.isDeleted == false;
allow update: if request.auth != null
&& (request.resource.data.diff(resource.data).affectedKeys().hasOnly(['currentVersionId', 'versions', 'isDeleted', 'order']));
allow delete: if false;
}
}
}
4 changes: 2 additions & 2 deletions src/App.tsx
Original file line number Diff line number Diff line change
@@ -1,13 +1,13 @@
import { CssBaseline, ThemeProvider } from '@mui/material'
import Profile from './components/Profile'
import { LivingPage } from './components/LivingPage'
import { theme } from './theme'

export function App() {
return (
<ThemeProvider theme={theme}>
<CssBaseline />
<div data-testid='app-root'>
<Profile />
<LivingPage />
</div>
</ThemeProvider>
)
Expand Down
Loading
Loading