416 lines
8.1 KiB
Markdown
416 lines
8.1 KiB
Markdown
# Frontend Development Guide
|
|
|
|
## Overview
|
|
|
|
The Hotel Pi frontend is a fullscreen kiosk application built with **Vite** and **Svelte**. It provides a premium, responsive interface for browsing restaurants, attractions, and launching media apps.
|
|
|
|
## Architecture
|
|
|
|
```
|
|
src/
|
|
├── App.svelte # Root component (routing & state)
|
|
├── main.js # Entry point
|
|
├── components/ # Reusable UI components
|
|
│ ├── IdleScreen.svelte # Welcome/idle display
|
|
│ ├── HomeScreen.svelte # Main menu
|
|
│ ├── RestaurantsPage.svelte # Restaurant list
|
|
│ ├── AttractionsPage.svelte # Attractions list
|
|
│ └── Clock.svelte # Time display
|
|
├── lib/
|
|
│ ├── store.js # Svelte state management
|
|
│ ├── api.js # Directus CMS integration
|
|
│ ├── websocket.js # WebSocket client
|
|
│ └── qrcode.js # QR code generation
|
|
└── routes/ # Page components (if using SvelteKit)
|
|
|
|
index.html # HTML entry point
|
|
vite.config.js # Build configuration
|
|
tsconfig.json # TypeScript config
|
|
package.json # Dependencies
|
|
```
|
|
|
|
## Development
|
|
|
|
### Setup
|
|
|
|
```bash
|
|
cd frontend
|
|
npm install
|
|
npm run dev
|
|
```
|
|
|
|
Frontend will run on http://localhost:5173 with hot reload enabled.
|
|
|
|
### Commands
|
|
|
|
```bash
|
|
npm run dev # Start dev server
|
|
npm run build # Build for production
|
|
npm run preview # Preview production build
|
|
npm run format # Format code with Prettier
|
|
npm run lint # Check code style
|
|
```
|
|
|
|
## Core Components
|
|
|
|
### App.svelte
|
|
|
|
Main application component handling:
|
|
- Screen navigation (idle → home → restaurants/attractions)
|
|
- Input handling (keyboard and WebSocket)
|
|
- State management
|
|
- API data fetching
|
|
- Idle timeout logic
|
|
|
|
**Props:**
|
|
- None (root component)
|
|
|
|
**State:**
|
|
- `currentScreen` - Active screen
|
|
- `selectedIndex` - Currently selected menu item
|
|
- `restaurants`, `attractions` - CMS content
|
|
- `wsConnected` - Control service connection status
|
|
|
|
### IdleScreen.svelte
|
|
|
|
Fullscreen ambient display shown when idle.
|
|
|
|
**Features:**
|
|
- Animated gradient background
|
|
- Current time display (Clock component)
|
|
- Welcome message
|
|
- Floating ambient elements
|
|
- Auto-advance on any input
|
|
|
|
**Props:**
|
|
```javascript
|
|
export let welcomeName = 'Guest';
|
|
```
|
|
|
|
### HomeScreen.svelte
|
|
|
|
Main menu with three options:
|
|
1. Watch Plex
|
|
2. Restaurants
|
|
3. Things to Do
|
|
|
|
**Features:**
|
|
- Grid-based menu layout
|
|
- Navigation with arrow keys
|
|
- Visual feedback for selected item
|
|
- Smooth transitions
|
|
|
|
### RestaurantsPage.svelte
|
|
|
|
Carousel of restaurants with details.
|
|
|
|
**Features:**
|
|
- Full-screen restaurant display
|
|
- Image gallery
|
|
- QR code for website
|
|
- Description and cuisine type
|
|
- Navigation controls
|
|
|
|
**Data:**
|
|
Pulled from Directus `restaurants` collection:
|
|
- name
|
|
- description
|
|
- cuisine_type
|
|
- website_url
|
|
- image
|
|
|
|
### AttractionsPage.svelte
|
|
|
|
Similar to RestaurantsPage but for attractions.
|
|
|
|
**Features:**
|
|
- Attraction showcase
|
|
- Category badges
|
|
- Distance information
|
|
- Operating hours
|
|
- Website QR code
|
|
|
|
**Data:**
|
|
From Directus `attractions` collection:
|
|
- name
|
|
- description
|
|
- category
|
|
- distance_km
|
|
- website_url
|
|
- hours
|
|
- image
|
|
|
|
### Clock.svelte
|
|
|
|
Real-time clock display.
|
|
|
|
**Features:**
|
|
- Updates every second
|
|
- 12-hour format with AM/PM
|
|
- Large, readable typeface
|
|
- Text shadow for visibility
|
|
|
|
## Store (State Management)
|
|
|
|
Located in `src/lib/store.js`:
|
|
|
|
```javascript
|
|
export const currentScreen; // Svelte store
|
|
export const selectedIndex; // Current selection
|
|
export const restaurants; // From CMS
|
|
export const attractions; // From CMS
|
|
export const wsConnected; // WebSocket status
|
|
|
|
export function pushScreen(screen); // Navigate to screen
|
|
export function popScreen(); // Go back
|
|
export function resetNavigation(); // Reset to idle
|
|
```
|
|
|
|
### Usage
|
|
|
|
```svelte
|
|
<script>
|
|
import { currentScreen, selectedIndex, pushScreen } from '$lib/store.js';
|
|
</script>
|
|
|
|
{#if $currentScreen === 'home'}
|
|
<HomeScreen />
|
|
{/if}
|
|
|
|
<button on:click={() => pushScreen('restaurants')}>View Restaurants</button>
|
|
```
|
|
|
|
## API Integration
|
|
|
|
Located in `src/lib/api.js`:
|
|
|
|
```javascript
|
|
export async function fetchRestaurants() // Get all restaurants
|
|
export async function fetchAttractions() // Get all attractions
|
|
export function getImageUrl(filename) // Construct image URL
|
|
```
|
|
|
|
### Example
|
|
|
|
```javascript
|
|
import { fetchRestaurants } from '$lib/api.js';
|
|
|
|
const restaurants = await fetchRestaurants();
|
|
// Returns:
|
|
// [
|
|
// {
|
|
// id: 'uuid',
|
|
// name: 'La Bella Vita',
|
|
// description: '...',
|
|
// image: { ... }
|
|
// }
|
|
// ]
|
|
```
|
|
|
|
## WebSocket Communication
|
|
|
|
Located in `src/lib/websocket.js`:
|
|
|
|
```javascript
|
|
const ws = new WebSocketManager(url);
|
|
|
|
ws.on('connected', () => {});
|
|
ws.on('disconnected', () => {});
|
|
ws.on('input', (data) => {});
|
|
ws.connect();
|
|
ws.send('launch-plex');
|
|
```
|
|
|
|
## Styling
|
|
|
|
### Design System
|
|
|
|
- **Colors:**
|
|
- Primary: `#667eea` (purple-blue)
|
|
- Accent Red: `#e94560`
|
|
- Accent Cyan: `#00d4ff`
|
|
- Dark BG: `#1a1a2e`
|
|
- Card BG: `#0f3460`
|
|
|
|
- **Typography:**
|
|
- Font: Inter (system fonts fallback)
|
|
- Sizes: 0.875rem to 4rem
|
|
- Weights: 300 (light), 400 (regular), 600 (semibold), 700 (bold)
|
|
|
|
- **Spacing:**
|
|
- Base unit: 1rem
|
|
- Gaps: 0.5rem to 3rem
|
|
|
|
### CSS Features
|
|
|
|
- CSS Grid for layouts
|
|
- Flexbox for components
|
|
- CSS animations (preferred over JS)
|
|
- Media queries for responsive design
|
|
- CSS variables for theming
|
|
|
|
### Animations
|
|
|
|
Key animations used:
|
|
|
|
```css
|
|
@keyframes fade-in
|
|
@keyframes slide-down
|
|
@keyframes float
|
|
@keyframes bounce
|
|
@keyframes gradient-shift
|
|
```
|
|
|
|
Keep animations smooth and under 600ms for best UX.
|
|
|
|
## Input Handling
|
|
|
|
### Keyboard Input
|
|
|
|
```javascript
|
|
'arrowup', 'arrowdown', 'arrowleft', 'arrowright' - Navigate
|
|
'enter' - Select
|
|
'escape' - Go back
|
|
any key - Wake from idle (if on idle screen)
|
|
```
|
|
|
|
### WebSocket Input
|
|
|
|
Control service sends `input` events:
|
|
|
|
```json
|
|
{
|
|
"type": "input",
|
|
"payload": {
|
|
"type": "up" // "up", "down", "left", "right", "select", "back"
|
|
}
|
|
}
|
|
```
|
|
|
|
## Performance Optimization
|
|
|
|
1. **Lazy Loading:** Images load on-demand
|
|
2. **CSS Animations:** Prefer over JavaScript transitions
|
|
3. **Minimal Dependencies:** Only `qrcode` library
|
|
4. **Vite Optimization:**
|
|
- Tree-shaking
|
|
- Minification in production
|
|
- Code splitting
|
|
|
|
## Building for Production
|
|
|
|
```bash
|
|
npm run build
|
|
# Creates dist/ with optimized bundle
|
|
```
|
|
|
|
### Docker Production Build
|
|
|
|
```dockerfile
|
|
FROM node:20-alpine
|
|
WORKDIR /app
|
|
COPY . .
|
|
RUN npm ci && npm run build
|
|
CMD ["npm", "run", "preview"]
|
|
```
|
|
|
|
## Troubleshooting
|
|
|
|
### Hot Reload Not Working
|
|
|
|
- Clear `.svelte-kit/` and `dist/`
|
|
- Restart dev server
|
|
- Check Vite config
|
|
|
|
### Components Not Updating
|
|
|
|
- Verify store subscriptions use `$` prefix
|
|
- Check for reactive declarations (`:`)
|
|
- Ensure state updates are triggering changes
|
|
|
|
### CSS Not Applied
|
|
|
|
- Check CSS is within `<style>` block
|
|
- Verify selector specificity
|
|
- Use `:global()` for global styles carefully
|
|
|
|
### API Connection Failed
|
|
|
|
- Verify `VITE_API_URL` in `.env`
|
|
- Check Directus is running
|
|
- Review CORS settings
|
|
|
|
### WebSocket Disconnects
|
|
|
|
- Check `VITE_WS_URL` is correct
|
|
- Verify control service is running
|
|
- Review firewall rules
|
|
- Check browser console for connection errors
|
|
|
|
## Code Quality
|
|
|
|
### Formatting
|
|
|
|
```bash
|
|
npm run format # Fix formatting with Prettier
|
|
```
|
|
|
|
### Linting
|
|
|
|
```bash
|
|
npm run lint # Check code style
|
|
```
|
|
|
|
### Type Checking
|
|
|
|
TypeScript configured but optional. Add type annotations for better IDE support:
|
|
|
|
```svelte
|
|
<script>
|
|
let count: number = 0;
|
|
let items: Array<{id: string; name: string}> = [];
|
|
</script>
|
|
```
|
|
|
|
## Component Template
|
|
|
|
```svelte
|
|
<script>
|
|
import { onMount } from 'svelte';
|
|
|
|
let isLoading = false;
|
|
|
|
onMount(async () => {
|
|
// Initialization
|
|
});
|
|
</script>
|
|
|
|
<div class="component">
|
|
{#if isLoading}
|
|
<p>Loading...</p>
|
|
{:else}
|
|
<p>Content</p>
|
|
{/if}
|
|
</div>
|
|
|
|
<style>
|
|
.component {
|
|
/* Styles */
|
|
}
|
|
</style>
|
|
```
|
|
|
|
## Browser Support
|
|
|
|
- Chrome/Chromium 90+
|
|
- Firefox 88+
|
|
- Safari 14+
|
|
|
|
Tested primarily on Chromium for Raspberry Pi.
|
|
|
|
## Resources
|
|
|
|
- [Svelte Docs](https://svelte.dev)
|
|
- [Vite Docs](https://vitejs.dev)
|
|
- [Inter Font](https://fonts.google.com/specimen/Inter)
|