330 lines
9.6 KiB
Markdown
330 lines
9.6 KiB
Markdown
# TankStopp Templ Optimization
|
|
|
|
## Overview
|
|
|
|
This document describes the optimization of TankStopp's template system using `a-h/templ` - a compile-time template system for Go that generates type-safe HTML templates.
|
|
|
|
## Migration Summary
|
|
|
|
The application has been migrated from traditional HTML templates to `a-h/templ` templates, providing:
|
|
|
|
- **Type Safety**: Templates are compiled to Go code with full type checking
|
|
- **Performance**: Templates are compiled at build time, eliminating runtime parsing
|
|
- **Component Reusability**: Modular components that can be composed together
|
|
- **Developer Experience**: IDE support, auto-completion, and compile-time error checking
|
|
|
|
## Project Structure
|
|
|
|
The new template organization follows a clean architecture:
|
|
|
|
```
|
|
tankstopp/internal/views/
|
|
├── components/
|
|
│ ├── layout.templ # Base layout, navbar, footer, cards, etc.
|
|
│ ├── forms.templ # Form components, inputs, buttons
|
|
│ └── icons.templ # Icon components with SVG definitions
|
|
└── pages/
|
|
├── auth.templ # Authentication pages (login, register)
|
|
├── dashboard.templ # Dashboard page with statistics
|
|
├── fuelstops.templ # Add/edit fuel stop pages
|
|
├── vehicles.templ # Vehicle management pages
|
|
└── settings.templ # Settings page
|
|
```
|
|
|
|
## Component Architecture
|
|
|
|
### Layout Components (`components/layout.templ`)
|
|
|
|
#### Base Layout
|
|
```go
|
|
templ BaseLayout(title string, user *models.User, username string) {
|
|
// Full HTML document structure with navbar, footer, and content area
|
|
}
|
|
```
|
|
|
|
#### Navigation Components
|
|
- `Navbar()` - Main navigation bar
|
|
- `NavItem()` - Individual navigation items
|
|
- `UserDropdown()` - User account dropdown
|
|
- `Footer()` - Application footer
|
|
|
|
#### UI Components
|
|
- `Card()` - Reusable card component
|
|
- `Alert()` - Alert messages (success, error, warning, info)
|
|
- `EmptyState()` - Empty state placeholder
|
|
- `PageHeader()` - Page header with title and subtitle
|
|
- `Badge()` - Status badges
|
|
- `ProgressBar()` - Progress indicators
|
|
- `Modal()` - Modal dialogs
|
|
- `Tabs()` - Tab navigation
|
|
- `Pagination()` - Pagination controls
|
|
|
|
### Form Components (`components/forms.templ`)
|
|
|
|
#### Input Components
|
|
- `Input()` - Basic text inputs
|
|
- `NumberInput()` - Number inputs with validation
|
|
- `DateInput()` - Date picker inputs
|
|
- `TextArea()` - Multi-line text areas
|
|
- `Select()` - Dropdown selects
|
|
- `PasswordInput()` - Password inputs with visibility toggle
|
|
|
|
#### Specialized Selects
|
|
- `CurrencySelect()` - Currency dropdown
|
|
- `VehicleSelect()` - Vehicle dropdown
|
|
- `FuelTypeSelect()` - Fuel type dropdown
|
|
|
|
#### Form Layout
|
|
- `Form()` - Form wrapper
|
|
- `FormGroup()` - Input group with label and hints
|
|
- `FormRow()` - Form row wrapper
|
|
- `FormCol()` - Form column wrapper
|
|
- `FormButtons()` - Form action buttons
|
|
- `InputGroup()` - Input with prefix/suffix
|
|
|
|
### Icon Components (`components/icons.templ`)
|
|
|
|
Comprehensive icon system with 40+ icons:
|
|
- `Icon(name, size)` - Basic icon component
|
|
- `IconWithClass(name, size, class)` - Icon with custom classes
|
|
|
|
Available icons include: fuel, plus, home, car, settings, location, edit, trash, save, user, lock, etc.
|
|
|
|
## Page Templates
|
|
|
|
### Authentication Pages (`pages/auth.templ`)
|
|
- `LoginPage()` - User login form
|
|
- `RegisterPage()` - User registration form
|
|
- `AuthLayout()` - Shared layout for auth pages
|
|
|
|
### Dashboard (`pages/dashboard.templ`)
|
|
- `DashboardPage()` - Main dashboard with statistics and fuel stops table
|
|
- `FuelStopsTable()` - Reusable fuel stops table component
|
|
- `DashboardScript()` - JavaScript for dashboard functionality
|
|
|
|
### Fuel Stops (`pages/fuelstops.templ`)
|
|
- `AddFuelStopPage()` - Add new fuel stop form
|
|
- `EditFuelStopPage()` - Edit existing fuel stop form
|
|
- `AddFuelStopScript()` - JavaScript for form functionality including:
|
|
- Auto-calculation of total costs
|
|
- Current date/time defaults
|
|
- Nearby gas station finder using Overpass API
|
|
- Form validation
|
|
|
|
### Vehicles (`pages/vehicles.templ`)
|
|
- `VehiclesPage()` - Vehicle management dashboard
|
|
- `VehicleCard()` - Individual vehicle card component
|
|
- `AddVehiclePage()` - Add new vehicle form
|
|
- `EditVehiclePage()` - Edit vehicle form
|
|
- `VehicleBrandSelect()` - Vehicle brand dropdown
|
|
- Helper functions for vehicle statistics
|
|
|
|
### Settings (`pages/settings.templ`)
|
|
- `SettingsPage()` - Comprehensive settings page with:
|
|
- Profile settings
|
|
- Application preferences
|
|
- Security settings
|
|
- Data management (import/export)
|
|
- Account management
|
|
- `SettingsScript()` - JavaScript for settings functionality
|
|
|
|
## JavaScript Integration
|
|
|
|
The templ templates include embedded JavaScript using the `script` template type:
|
|
|
|
```go
|
|
script DashboardScript() {
|
|
function applyFilters() {
|
|
// JavaScript functionality
|
|
}
|
|
}
|
|
```
|
|
|
|
This approach provides:
|
|
- Type-safe JavaScript embedding
|
|
- Scoped functionality per page
|
|
- Compile-time validation of JavaScript references
|
|
|
|
## Usage Examples
|
|
|
|
### Basic Page Structure
|
|
```go
|
|
templ MyPage(user *models.User, data MyData) {
|
|
@components.BaseLayout("My Page", user, user.Username) {
|
|
@components.PageHeader("Subtitle", "My Page Title")
|
|
|
|
<div class="page-body">
|
|
<div class="container-xl">
|
|
@components.Card("Card Title", "icon-name") {
|
|
// Card content
|
|
}
|
|
</div>
|
|
</div>
|
|
}
|
|
}
|
|
```
|
|
|
|
### Form with Validation
|
|
```go
|
|
templ MyForm(data FormData) {
|
|
@components.Card("Form Title", "form-icon") {
|
|
@components.Form("post", "/submit") {
|
|
@components.FormGroup("Field Label", "Help text") {
|
|
@components.Input("field_name", "text", "Placeholder", data.Value, true)
|
|
}
|
|
|
|
@components.FormButtons("/cancel", "Save", "save")
|
|
}
|
|
}
|
|
}
|
|
```
|
|
|
|
### Data Table
|
|
```go
|
|
templ DataTable(items []Item) {
|
|
@components.TableResponsive() {
|
|
<thead>
|
|
<tr>
|
|
<th>Column 1</th>
|
|
<th>Column 2</th>
|
|
<th>Actions</th>
|
|
</tr>
|
|
</thead>
|
|
<tbody>
|
|
for _, item := range items {
|
|
<tr>
|
|
<td>{ item.Field1 }</td>
|
|
<td>{ item.Field2 }</td>
|
|
<td>
|
|
@components.ButtonGroup() {
|
|
@components.EditButton("/edit/" + item.ID)
|
|
@components.DeleteButton("/delete/" + item.ID, item.Name)
|
|
}
|
|
</td>
|
|
</tr>
|
|
}
|
|
</tbody>
|
|
}
|
|
}
|
|
```
|
|
|
|
## Build Integration
|
|
|
|
To generate the Go code from templ files:
|
|
|
|
```bash
|
|
# Install templ CLI
|
|
go install github.com/a-h/templ/cmd/templ@latest
|
|
|
|
# Generate templates
|
|
templ generate
|
|
|
|
# Or use the shorthand
|
|
templ fmt . # Format templates
|
|
templ generate . # Generate Go code
|
|
```
|
|
|
|
## Handler Integration
|
|
|
|
In your HTTP handlers, use the generated template functions:
|
|
|
|
```go
|
|
func DashboardHandler(w http.ResponseWriter, r *http.Request) {
|
|
// Get data...
|
|
|
|
// Render template
|
|
component := pages.DashboardPage(user, username, stops, vehicles, totalStops, totalCost, avgConsumption, lastFillUp)
|
|
component.Render(r.Context(), w)
|
|
}
|
|
```
|
|
|
|
## Benefits
|
|
|
|
### Performance
|
|
- Templates are compiled to Go code at build time
|
|
- No runtime template parsing
|
|
- Minimal memory allocation
|
|
- Type-safe rendering
|
|
|
|
### Developer Experience
|
|
- Full IDE support with auto-completion
|
|
- Compile-time error checking
|
|
- Refactoring support
|
|
- Hot reload during development
|
|
|
|
### Maintainability
|
|
- Component-based architecture
|
|
- Clear separation of concerns
|
|
- Reusable UI components
|
|
- Consistent styling and behavior
|
|
|
|
### Security
|
|
- Automatic HTML escaping
|
|
- XSS protection by default
|
|
- Type-safe data binding
|
|
- Compile-time validation
|
|
|
|
## Migration from HTML Templates
|
|
|
|
### Before (HTML Templates)
|
|
```html
|
|
<!DOCTYPE html>
|
|
<html>
|
|
<head>
|
|
<title>{{.Title}} - TankStopp</title>
|
|
<!-- CSS links -->
|
|
</head>
|
|
<body>
|
|
<!-- Navbar HTML -->
|
|
<div class="page-body">
|
|
{{range .Items}}
|
|
<div class="card">
|
|
<div class="card-body">
|
|
<h4>{{.Name}}</h4>
|
|
<p>{{.Description}}</p>
|
|
</div>
|
|
</div>
|
|
{{end}}
|
|
</div>
|
|
<!-- Footer HTML -->
|
|
</body>
|
|
</html>
|
|
```
|
|
|
|
### After (Templ Templates)
|
|
```go
|
|
templ MyPage(title string, items []Item) {
|
|
@components.BaseLayout(title, user, username) {
|
|
<div class="page-body">
|
|
<div class="container-xl">
|
|
for _, item := range items {
|
|
@components.Card(item.Name, "icon") {
|
|
<p>{ item.Description }</p>
|
|
}
|
|
}
|
|
</div>
|
|
</div>
|
|
}
|
|
}
|
|
```
|
|
|
|
## Best Practices
|
|
|
|
1. **Component Composition**: Build complex UIs by composing smaller components
|
|
2. **Type Safety**: Leverage Go's type system for template data
|
|
3. **Reusability**: Create reusable components for common UI patterns
|
|
4. **Performance**: Use templ's compile-time optimization
|
|
5. **Maintainability**: Keep templates focused and well-organized
|
|
6. **Security**: Rely on templ's automatic escaping and validation
|
|
|
|
## Future Enhancements
|
|
|
|
- **Internationalization**: Add multi-language support
|
|
- **Theming**: Dynamic theme switching
|
|
- **Progressive Enhancement**: Enhanced JavaScript functionality
|
|
- **Accessibility**: ARIA labels and keyboard navigation
|
|
- **Performance Monitoring**: Template rendering metrics
|
|
|
|
## Conclusion
|
|
|
|
The migration to `a-h/templ` provides a modern, type-safe, and performant template system that improves developer experience while maintaining excellent runtime performance. The component-based architecture makes the application more maintainable and provides a solid foundation for future enhancements. |