first commit
This commit is contained in:
@@ -0,0 +1,405 @@
|
||||
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 © 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>
|
||||
}
|
||||
Reference in New Issue
Block a user