Files
2025-07-07 01:44:12 +02:00

406 lines
10 KiB
Plaintext

package components
import (
"fmt"
"tankstopp/internal/models"
)
templ BaseLayout(title string, user *models.User, username string) {
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8"/>
<meta name="viewport" content="width=device-width, initial-scale=1.0"/>
<title>{ title } - TankStopp</title>
<link href="https://cdn.jsdelivr.net/npm/@tabler/core@1.0.0-beta17/dist/css/tabler.min.css" rel="stylesheet"/>
<link href="https://cdn.jsdelivr.net/npm/@tabler/icons@latest/icons-sprite.svg" rel="stylesheet"/>
<link href="/static/style.css" rel="stylesheet"/>
</head>
<body>
<div class="page">
@Navbar(user, username)
<div class="page-wrapper">
{ children... }
@Footer()
</div>
</div>
<script src="https://cdn.jsdelivr.net/npm/@tabler/core@1.0.0-beta17/dist/js/tabler.min.js"></script>
</body>
</html>
}
templ Navbar(user *models.User, username string) {
<header class="navbar navbar-expand-md navbar-light d-print-none">
<div class="container-xl">
<button class="navbar-toggler" type="button" data-bs-toggle="collapse" data-bs-target="#navbar-menu">
<span class="navbar-toggler-icon"></span>
</button>
<h1 class="navbar-brand navbar-brand-autodark d-none-navbar-horizontal pe-0 pe-md-3">
<a href="/dashboard" class="text-decoration-none">
@Icon("fuel", 24)
TankStopp
</a>
</h1>
<div class="navbar-nav flex-row order-md-last">
if user != nil {
<a href="/add" class="btn btn-primary me-2">
@Icon("plus", 24)
Add Stop
</a>
@UserDropdown(user, username)
}
</div>
<div class="collapse navbar-collapse" id="navbar-menu">
<div class="d-flex flex-column flex-md-row flex-fill align-items-stretch align-items-md-center">
<ul class="navbar-nav">
@NavItem("/dashboard", "home", "Dashboard", false)
@NavItem("/vehicles", "car", "Vehicles", false)
@NavItem("/api/stats", "chart-bar", "API", false)
</ul>
</div>
</div>
</div>
</header>
}
templ NavItem(href, icon, title string, active bool) {
<li class={ "nav-item", templ.KV("active", active) }>
<a class="nav-link" href={ templ.SafeURL(href) }>
<span class="nav-link-icon d-md-none d-lg-inline-block">
@Icon(icon, 24)
</span>
<span class="nav-link-title">{ title }</span>
</a>
</li>
}
templ UserDropdown(user *models.User, username string) {
<div class="nav-item dropdown">
<a href="#" class="nav-link d-flex lh-1 text-reset p-0" data-bs-toggle="dropdown" aria-label="Open user menu">
<span class="avatar avatar-sm" style="background: var(--tblr-primary); text-transform: uppercase;">
if username != "" {
{ string(username[0]) }
} else {
{ "U" }
}
</span>
<div class="d-none d-xl-block ps-2">
<div>{ username }</div>
<div class="mt-1 small text-muted">
{ user.BaseCurrency }
</div>
</div>
</a>
<div class="dropdown-menu dropdown-menu-end dropdown-menu-arrow">
<div class="dropdown-item">
<div class="text-muted">Signed in as</div>
<strong>{ username }</strong>
</div>
<div class="dropdown-divider"></div>
<a href="/settings" class="dropdown-item">
@Icon("settings", 24)
Settings
</a>
<div class="dropdown-divider"></div>
<form method="POST" action="/logout" class="d-inline">
<button type="submit" class="dropdown-item text-danger">
@Icon("logout", 24)
Logout
</button>
</form>
</div>
</div>
}
templ Footer() {
<footer class="footer footer-transparent d-print-none">
<div class="container-xl">
<div class="row text-center align-items-center flex-row-reverse">
<div class="col-lg-auto ms-lg-auto">
<ul class="list-inline list-inline-dots mb-0">
<li class="list-inline-item">
<a href="https://github.com/tabler/tabler" class="link-secondary">Built with Tabler</a>
</li>
</ul>
</div>
<div class="col-12 col-lg-auto mt-3 mt-lg-0">
<ul class="list-inline list-inline-dots mb-0">
<li class="list-inline-item">
Copyright &copy; 2024 TankStopp - Fuel Tracking App
</li>
</ul>
</div>
</div>
</div>
</footer>
}
templ PageHeader(pretitle, title string) {
<div class="page-header d-print-none">
<div class="container-xl">
<div class="row g-2 align-items-center">
<div class="col">
if pretitle != "" {
<div class="page-pretitle">{ pretitle }</div>
}
<h2 class="page-title">{ title }</h2>
</div>
</div>
</div>
</div>
}
templ Alert(alertType, message string) {
<div class={ "alert", "alert-" + alertType, "alert-dismissible" } role="alert">
<div class="d-flex">
<div>
switch alertType {
case "success":
@Icon("check", 24)
case "danger":
@Icon("alert-circle", 24)
case "warning":
@Icon("alert-triangle", 24)
case "info":
@Icon("info-circle", 24)
}
</div>
<div>{ message }</div>
</div>
<a class="btn-close" data-bs-dismiss="alert" aria-label="close"></a>
</div>
}
templ Card(title string, icon string) {
<div class="card">
if title != "" {
<div class="card-header">
<h3 class="card-title">
if icon != "" {
@Icon(icon, 24)
}
{ title }
</h3>
</div>
}
<div class="card-body">
{ children... }
</div>
</div>
}
templ EmptyState(icon, title, subtitle, actionText, actionHref string) {
<div class="empty">
<div class="empty-img">
@Icon(icon, 128)
</div>
<p class="empty-title">{ title }</p>
<p class="empty-subtitle text-muted">{ subtitle }</p>
if actionText != "" && actionHref != "" {
<div class="empty-action">
<a href={ templ.SafeURL(actionHref) } class="btn btn-primary">
@Icon("plus", 24)
{ actionText }
</a>
</div>
}
</div>
}
templ LoadingSpinner(size string) {
<div class={ "spinner-border", "spinner-border-" + size } role="status">
<span class="visually-hidden">Loading...</span>
</div>
}
templ Badge(text, variant string) {
<span class={ "badge", "bg-" + variant }>{ text }</span>
}
templ ProgressBar(percentage int, variant string) {
<div class="progress">
<div class={ "progress-bar", "bg-" + variant } role="progressbar" style={ fmt.Sprintf("width: %d%%", percentage) }>
{ fmt.Sprintf("%d%%", percentage) }
</div>
</div>
}
templ Modal(id, title string) {
<div class="modal fade" id={ id } tabindex="-1" aria-labelledby={ id + "Label" } aria-hidden="true">
<div class="modal-dialog">
<div class="modal-content">
<div class="modal-header">
<h5 class="modal-title" id={ id + "Label" }>{ title }</h5>
<button type="button" class="btn-close" data-bs-dismiss="modal" aria-label="Close"></button>
</div>
<div class="modal-body">
{ children... }
</div>
<div class="modal-footer">
{ children... }
</div>
</div>
</div>
</div>
}
templ Tooltip(text string) {
<span data-bs-toggle="tooltip" data-bs-placement="top" title={ text }>
{ children... }
</span>
}
templ Breadcrumb(items []BreadcrumbItem) {
<nav aria-label="breadcrumb">
<ol class="breadcrumb">
for i, item := range items {
if i == len(items)-1 {
<li class="breadcrumb-item active" aria-current="page">{ item.Title }</li>
} else {
<li class="breadcrumb-item">
<a href={ templ.SafeURL(item.Href) }>{ item.Title }</a>
</li>
}
}
</ol>
</nav>
}
type BreadcrumbItem struct {
Title string
Href string
}
templ Tabs(activeTab string, tabs []TabItem) {
<ul class="nav nav-tabs" role="tablist">
for _, tab := range tabs {
<li class="nav-item" role="presentation">
<a
class={ "nav-link", templ.KV("active", tab.ID == activeTab) }
href={ templ.SafeURL(tab.Href) }
role="tab"
>
if tab.Icon != "" {
@Icon(tab.Icon, 24)
}
{ tab.Title }
</a>
</li>
}
</ul>
}
type TabItem struct {
ID string
Title string
Href string
Icon string
}
templ StatCard(title, value, subtitle, icon, variant string) {
<div class="card">
<div class="card-body">
<div class="d-flex align-items-center">
<div class="flex-fill">
<div class="subheader">{ title }</div>
<div class="h2 mb-0">{ value }</div>
if subtitle != "" {
<div class="text-muted">{ subtitle }</div>
}
</div>
<div class="ms-auto">
<div class={ "text-" + variant }>
@Icon(icon, 32)
</div>
</div>
</div>
</div>
</div>
}
templ ActionButton(href, text, icon, variant string) {
<a href={ templ.SafeURL(href) } class={ "btn", "btn-" + variant }>
if icon != "" {
@Icon(icon, 24)
}
{ text }
</a>
}
templ TableResponsive() {
<div class="table-responsive">
<table class="table table-vcenter table-mobile-md card-table">
{ children... }
</table>
</div>
}
templ Pagination(currentPage, totalPages int, baseURL string) {
if totalPages > 1 {
<nav aria-label="Page navigation">
<ul class="pagination">
if currentPage > 1 {
<li class="page-item">
<a class="page-link" href={ templ.SafeURL(fmt.Sprintf("%s?page=%d", baseURL, currentPage-1)) }>Previous</a>
</li>
}
for i := 1; i <= totalPages; i++ {
<li class={ "page-item", templ.KV("active", i == currentPage) }>
<a class="page-link" href={ templ.SafeURL(fmt.Sprintf("%s?page=%d", baseURL, i)) }>{ fmt.Sprintf("%d", i) }</a>
</li>
}
if currentPage < totalPages {
<li class="page-item">
<a class="page-link" href={ templ.SafeURL(fmt.Sprintf("%s?page=%d", baseURL, currentPage+1)) }>Next</a>
</li>
}
</ul>
</nav>
}
}
templ ConfirmDialog(id, title, message, confirmText, cancelText string) {
<div class="modal fade" id={ id } tabindex="-1" aria-labelledby={ id + "Label" } aria-hidden="true">
<div class="modal-dialog">
<div class="modal-content">
<div class="modal-header">
<h5 class="modal-title" id={ id + "Label" }>{ title }</h5>
<button type="button" class="btn-close" data-bs-dismiss="modal" aria-label="Close"></button>
</div>
<div class="modal-body">
<p>{ message }</p>
</div>
<div class="modal-footer">
<button type="button" class="btn btn-secondary" data-bs-dismiss="modal">{ cancelText }</button>
<button type="button" class="btn btn-danger" onclick="confirmAction(this)" data-action={ id }>{ confirmText }</button>
</div>
</div>
</div>
</div>
}
templ ListGroup() {
<div class="list-group">
{ children... }
</div>
}
templ ListGroupItem(active bool) {
<div class={ "list-group-item", templ.KV("active", active) }>
{ children... }
</div>
}
templ ButtonToolbar() {
<div class="btn-toolbar" role="toolbar">
{ children... }
</div>
}
templ StatusIndicator(status, text string) {
<span class="status-indicator">
<span class={ "status", "status-" + status }></span>
{ text }
</span>
}