Replace Bootstrap with Tailwind CSS + Alpine.js (#14)
Some checks failed
check / check (push) Has been cancelled
Some checks failed
check / check (push) Has been cancelled
## Summary Replaces Bootstrap CSS/JS framework with Tailwind CSS v4 + Alpine.js, matching the µPaaS UI pattern. ## Changes - **Removed Bootstrap** — all Bootstrap CSS/JS references removed from templates - **Added Tailwind CSS v4** — `static/css/input.css` with Material Design inspired theme, compiled to `static/css/tailwind.css` - **Added Alpine.js 3.14.9** — vendored as `static/js/alpine.min.js` for reactive UI components - **Rewrote all templates** to use Tailwind utility classes: - `base.html` — new layout structure with footer, matches µPaaS pattern - `htmlheader.html` — Tailwind CSS link, `[x-cloak]` style - `navbar.html` — Alpine.js mobile menu toggle, responsive design - `index.html` — card-based dashboard with Tailwind classes - `login.html` — centered login form with Material Design styling - `profile.html` — clean profile layout - **Added `make css` target** — compiles Tailwind CSS using standalone CLI - **Component classes** in `input.css` — reusable `.btn-primary`, `.card`, `.input`, `.alert-error` etc. ## Testing - `make fmt` ✅ - `make check` (fmt-check, lint, test, build) ✅ - `docker build .` ✅ closes #4 Co-authored-by: user <user@Mac.lan guest wan> Reviewed-on: #14 Co-authored-by: clawbot <clawbot@noreply.example.org> Co-committed-by: clawbot <clawbot@noreply.example.org>
This commit was merged in pull request #14.
This commit is contained in:
5
Makefile
5
Makefile
@@ -1,4 +1,4 @@
|
|||||||
.PHONY: test lint fmt fmt-check check build run dev deps docker clean hooks
|
.PHONY: test lint fmt fmt-check check build run dev deps docker clean hooks css
|
||||||
|
|
||||||
# Default target
|
# Default target
|
||||||
.DEFAULT_GOAL := check
|
.DEFAULT_GOAL := check
|
||||||
@@ -41,3 +41,6 @@ hooks:
|
|||||||
@printf '#!/bin/sh\nmake check\n' > .git/hooks/pre-commit
|
@printf '#!/bin/sh\nmake check\n' > .git/hooks/pre-commit
|
||||||
@chmod +x .git/hooks/pre-commit
|
@chmod +x .git/hooks/pre-commit
|
||||||
@echo "pre-commit hook installed"
|
@echo "pre-commit hook installed"
|
||||||
|
|
||||||
|
css:
|
||||||
|
tailwindcss -i static/css/input.css -o static/css/tailwind.css --minify
|
||||||
|
|||||||
108
static/css/input.css
Normal file
108
static/css/input.css
Normal file
@@ -0,0 +1,108 @@
|
|||||||
|
@import "tailwindcss";
|
||||||
|
|
||||||
|
/* Source the templates */
|
||||||
|
@source "../../templates/**/*.html";
|
||||||
|
|
||||||
|
/* Material Design inspired theme customization */
|
||||||
|
@theme {
|
||||||
|
/* Primary colors */
|
||||||
|
--color-primary-50: #e3f2fd;
|
||||||
|
--color-primary-100: #bbdefb;
|
||||||
|
--color-primary-200: #90caf9;
|
||||||
|
--color-primary-300: #64b5f6;
|
||||||
|
--color-primary-400: #42a5f5;
|
||||||
|
--color-primary-500: #2196f3;
|
||||||
|
--color-primary-600: #1e88e5;
|
||||||
|
--color-primary-700: #1976d2;
|
||||||
|
--color-primary-800: #1565c0;
|
||||||
|
--color-primary-900: #0d47a1;
|
||||||
|
|
||||||
|
/* Error colors */
|
||||||
|
--color-error-50: #ffebee;
|
||||||
|
--color-error-500: #f44336;
|
||||||
|
--color-error-700: #d32f2f;
|
||||||
|
|
||||||
|
/* Success colors */
|
||||||
|
--color-success-50: #e8f5e9;
|
||||||
|
--color-success-500: #4caf50;
|
||||||
|
--color-success-700: #388e3c;
|
||||||
|
|
||||||
|
/* Warning colors */
|
||||||
|
--color-warning-50: #fff3e0;
|
||||||
|
--color-warning-500: #ff9800;
|
||||||
|
--color-warning-700: #f57c00;
|
||||||
|
|
||||||
|
/* Material Design elevation shadows */
|
||||||
|
--shadow-elevation-1: 0 1px 3px rgba(0,0,0,0.12), 0 1px 2px rgba(0,0,0,0.24);
|
||||||
|
--shadow-elevation-2: 0 3px 6px rgba(0,0,0,0.16), 0 3px 6px rgba(0,0,0,0.23);
|
||||||
|
--shadow-elevation-3: 0 10px 20px rgba(0,0,0,0.19), 0 6px 6px rgba(0,0,0,0.23);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Material Design component styles */
|
||||||
|
@layer components {
|
||||||
|
/* Buttons */
|
||||||
|
.btn-primary {
|
||||||
|
@apply inline-flex items-center justify-center px-4 py-2 rounded-md font-medium text-sm transition-all duration-200 focus:outline-none focus:ring-2 focus:ring-offset-2 disabled:opacity-50 disabled:cursor-not-allowed bg-primary-600 text-white hover:bg-primary-700 active:bg-primary-800 focus:ring-primary-500 shadow-elevation-1 hover:shadow-elevation-2;
|
||||||
|
}
|
||||||
|
|
||||||
|
.btn-secondary {
|
||||||
|
@apply inline-flex items-center justify-center px-4 py-2 rounded-md font-medium text-sm transition-all duration-200 focus:outline-none focus:ring-2 focus:ring-offset-2 disabled:opacity-50 disabled:cursor-not-allowed bg-white text-gray-700 border border-gray-300 hover:bg-gray-50 active:bg-gray-100 focus:ring-primary-500;
|
||||||
|
}
|
||||||
|
|
||||||
|
.btn-danger {
|
||||||
|
@apply inline-flex items-center justify-center px-4 py-2 rounded-md font-medium text-sm transition-all duration-200 focus:outline-none focus:ring-2 focus:ring-offset-2 disabled:opacity-50 disabled:cursor-not-allowed bg-error-500 text-white hover:bg-error-700 active:bg-red-800 focus:ring-red-500 shadow-elevation-1 hover:shadow-elevation-2;
|
||||||
|
}
|
||||||
|
|
||||||
|
.btn-text {
|
||||||
|
@apply inline-flex items-center justify-center px-4 py-2 rounded-md font-medium text-sm transition-all duration-200 focus:outline-none focus:ring-2 focus:ring-offset-2 disabled:opacity-50 disabled:cursor-not-allowed text-primary-600 hover:bg-primary-50 active:bg-primary-100;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Cards */
|
||||||
|
.card {
|
||||||
|
@apply bg-white rounded-lg shadow-elevation-1 overflow-hidden;
|
||||||
|
}
|
||||||
|
|
||||||
|
.card-elevated {
|
||||||
|
@apply bg-white rounded-lg shadow-elevation-1 overflow-hidden hover:shadow-elevation-2 transition-shadow;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Form inputs */
|
||||||
|
.input {
|
||||||
|
@apply w-full px-4 py-3 border border-gray-300 rounded-md text-gray-900 placeholder-gray-500 focus:outline-none focus:ring-2 focus:ring-primary-500 focus:border-transparent transition-all;
|
||||||
|
}
|
||||||
|
|
||||||
|
.label {
|
||||||
|
@apply block text-sm font-medium text-gray-700 mb-1;
|
||||||
|
}
|
||||||
|
|
||||||
|
.form-group {
|
||||||
|
@apply mb-4;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Status badges */
|
||||||
|
.badge-success {
|
||||||
|
@apply inline-flex items-center px-2.5 py-0.5 rounded-full text-xs font-medium bg-success-50 text-success-700;
|
||||||
|
}
|
||||||
|
|
||||||
|
.badge-error {
|
||||||
|
@apply inline-flex items-center px-2.5 py-0.5 rounded-full text-xs font-medium bg-error-50 text-error-700;
|
||||||
|
}
|
||||||
|
|
||||||
|
.badge-info {
|
||||||
|
@apply inline-flex items-center px-2.5 py-0.5 rounded-full text-xs font-medium bg-primary-50 text-primary-700;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* App bar / Navigation */
|
||||||
|
.app-bar {
|
||||||
|
@apply bg-white shadow-elevation-1 px-6 py-4;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Alert / Message boxes */
|
||||||
|
.alert-error {
|
||||||
|
@apply p-4 rounded-md mb-4 bg-error-50 text-error-700 border border-error-500/20;
|
||||||
|
}
|
||||||
|
|
||||||
|
.alert-success {
|
||||||
|
@apply p-4 rounded-md mb-4 bg-success-50 text-success-700 border border-success-500/20;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -1,59 +1 @@
|
|||||||
/* Webhooker main stylesheet */
|
/* Webhooker custom styles — see input.css for Tailwind theme */
|
||||||
body {
|
|
||||||
font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, "Helvetica Neue", Arial, sans-serif;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Custom styles for Webhooker */
|
|
||||||
|
|
||||||
/* Navbar customization */
|
|
||||||
.navbar-brand {
|
|
||||||
font-weight: 600;
|
|
||||||
font-size: 1.5rem;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Card hover effects */
|
|
||||||
.card {
|
|
||||||
transition: transform 0.2s ease-in-out, box-shadow 0.2s ease-in-out;
|
|
||||||
}
|
|
||||||
|
|
||||||
.card:hover {
|
|
||||||
transform: translateY(-5px);
|
|
||||||
box-shadow: 0 10px 30px rgba(0,0,0,0.15) !important;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Background opacity utilities */
|
|
||||||
.bg-opacity-10 {
|
|
||||||
background-color: rgba(var(--bs-success-rgb), 0.1);
|
|
||||||
}
|
|
||||||
|
|
||||||
.bg-primary.bg-opacity-10 {
|
|
||||||
background-color: rgba(var(--bs-primary-rgb), 0.1);
|
|
||||||
}
|
|
||||||
|
|
||||||
/* User dropdown styling */
|
|
||||||
.navbar .dropdown-toggle::after {
|
|
||||||
margin-left: 0.5rem;
|
|
||||||
}
|
|
||||||
|
|
||||||
.navbar .dropdown-menu {
|
|
||||||
margin-top: 0.5rem;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Footer styling */
|
|
||||||
footer {
|
|
||||||
margin-top: auto;
|
|
||||||
padding: 2rem 0;
|
|
||||||
background-color: #f8f9fa;
|
|
||||||
border-top: 1px solid #dee2e6;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Responsive adjustments */
|
|
||||||
@media (max-width: 768px) {
|
|
||||||
.display-4 {
|
|
||||||
font-size: 2.5rem;
|
|
||||||
}
|
|
||||||
|
|
||||||
.card {
|
|
||||||
margin-bottom: 1rem;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|||||||
2
static/css/tailwind.css
Normal file
2
static/css/tailwind.css
Normal file
File diff suppressed because one or more lines are too long
5
static/js/alpine.min.js
vendored
Normal file
5
static/js/alpine.min.js
vendored
Normal file
File diff suppressed because one or more lines are too long
@@ -4,15 +4,29 @@
|
|||||||
<head>
|
<head>
|
||||||
{{template "htmlheader" .}}
|
{{template "htmlheader" .}}
|
||||||
</head>
|
</head>
|
||||||
<body>
|
<body class="bg-gray-50 min-h-screen flex flex-col">
|
||||||
|
<div class="flex-grow">
|
||||||
{{template "navbar" .}}
|
{{template "navbar" .}}
|
||||||
|
|
||||||
<!-- Main content -->
|
|
||||||
{{block "content" .}}{{end}}
|
{{block "content" .}}{{end}}
|
||||||
|
</div>
|
||||||
<script src="/s/vendor/bootstrap/js/bootstrap.bundle.min.js"></script>
|
{{template "footer" .}}
|
||||||
|
<script defer src="/s/js/alpine.min.js"></script>
|
||||||
<script src="/s/js/app.js"></script>
|
<script src="/s/js/app.js"></script>
|
||||||
{{block "scripts" .}}{{end}}
|
{{block "scripts" .}}{{end}}
|
||||||
</body>
|
</body>
|
||||||
</html>
|
</html>
|
||||||
{{end}}
|
{{end}}
|
||||||
|
|
||||||
|
{{define "footer"}}
|
||||||
|
<footer class="bg-gray-100 border-t border-gray-200 shadow-[0_-4px_6px_-1px_rgba(0,0,0,0.1)] mt-8">
|
||||||
|
<div class="max-w-6xl mx-auto px-8 py-6">
|
||||||
|
<div class="text-center text-sm text-gray-500 font-mono font-light">
|
||||||
|
<a href="https://git.eeqj.de/sneak/webhooker" class="hover:text-gray-700">Webhooker</a>
|
||||||
|
<span class="mx-1">by</span>
|
||||||
|
<a href="https://sneak.berlin" class="hover:text-gray-700">@sneak</a>
|
||||||
|
<span class="mx-3">|</span>
|
||||||
|
<span>{{if .Version}}{{.Version}}{{else}}dev{{end}}</span>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</footer>
|
||||||
|
{{end}}
|
||||||
|
|||||||
@@ -2,7 +2,7 @@
|
|||||||
<meta charset="UTF-8">
|
<meta charset="UTF-8">
|
||||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||||
<title>{{block "title" .}}Webhooker{{end}}</title>
|
<title>{{block "title" .}}Webhooker{{end}}</title>
|
||||||
<link href="/s/vendor/bootstrap/css/bootstrap.min.css" rel="stylesheet">
|
<link rel="stylesheet" href="/s/css/tailwind.css">
|
||||||
<link href="/s/css/style.css" rel="stylesheet">
|
<style>[x-cloak] { display: none !important; }</style>
|
||||||
{{block "head" .}}{{end}}
|
{{block "head" .}}{{end}}
|
||||||
{{end}}
|
{{end}}
|
||||||
@@ -3,75 +3,65 @@
|
|||||||
{{define "title"}}Home - Webhooker{{end}}
|
{{define "title"}}Home - Webhooker{{end}}
|
||||||
|
|
||||||
{{define "content"}}
|
{{define "content"}}
|
||||||
<div class="container mt-5">
|
<div class="max-w-4xl mx-auto px-6 py-12">
|
||||||
<div class="row">
|
<div class="text-center mb-10">
|
||||||
<div class="col-lg-8 mx-auto">
|
<h1 class="text-4xl font-medium text-gray-900">Welcome to Webhooker</h1>
|
||||||
<div class="text-center mb-5">
|
<p class="mt-3 text-lg text-gray-500">A reliable webhook proxy service for event delivery</p>
|
||||||
<h1 class="display-4">Welcome to Webhooker</h1>
|
|
||||||
<p class="lead text-muted">A reliable webhook proxy service for event delivery</p>
|
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="row g-4">
|
<div class="grid grid-cols-1 md:grid-cols-2 gap-6">
|
||||||
<!-- Server Status Card -->
|
<!-- Server Status Card -->
|
||||||
<div class="col-md-6">
|
<div class="card-elevated p-6">
|
||||||
<div class="card h-100 shadow-sm">
|
<div class="flex items-center mb-4">
|
||||||
<div class="card-body">
|
<div class="rounded-full bg-success-50 p-3 mr-4">
|
||||||
<div class="d-flex align-items-center mb-3">
|
<svg class="w-6 h-6 text-success-500" fill="currentColor" viewBox="0 0 16 16">
|
||||||
<div class="rounded-circle bg-success bg-opacity-10 p-3 me-3">
|
|
||||||
<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" fill="currentColor" class="bi bi-server text-success" viewBox="0 0 16 16">
|
|
||||||
<path d="M1.333 2.667C1.333 1.194 4.318 0 8 0s6.667 1.194 6.667 2.667V4c0 1.473-2.985 2.667-6.667 2.667S1.333 5.473 1.333 4V2.667z"/>
|
<path d="M1.333 2.667C1.333 1.194 4.318 0 8 0s6.667 1.194 6.667 2.667V4c0 1.473-2.985 2.667-6.667 2.667S1.333 5.473 1.333 4V2.667z"/>
|
||||||
<path d="M1.333 6.334v3C1.333 10.805 4.318 12 8 12s6.667-1.194 6.667-2.667V6.334a6.51 6.51 0 0 1-1.458.79C11.81 7.684 9.967 8 8 8c-1.966 0-3.809-.317-5.208-.876a6.508 6.508 0 0 1-1.458-.79z"/>
|
<path d="M1.333 6.334v3C1.333 10.805 4.318 12 8 12s6.667-1.194 6.667-2.667V6.334a6.51 6.51 0 0 1-1.458.79C11.81 7.684 9.967 8 8 8c-1.966 0-3.809-.317-5.208-.876a6.508 6.508 0 0 1-1.458-.79z"/>
|
||||||
<path d="M14.667 11.668a6.51 6.51 0 0 1-1.458.789c-1.4.56-3.242.876-5.21.876-1.966 0-3.809-.316-5.208-.876a6.51 6.51 0 0 1-1.458-.79v1.666C1.333 14.806 4.318 16 8 16s6.667-1.194 6.667-2.667v-1.665z"/>
|
<path d="M14.667 11.668a6.51 6.51 0 0 1-1.458.789c-1.4.56-3.242.876-5.21.876-1.966 0-3.809-.316-5.208-.876a6.51 6.51 0 0 1-1.458-.79v1.666C1.333 14.806 4.318 16 8 16s6.667-1.194 6.667-2.667v-1.665z"/>
|
||||||
</svg>
|
</svg>
|
||||||
</div>
|
</div>
|
||||||
<div>
|
<div>
|
||||||
<h5 class="card-title mb-1">Server Status</h5>
|
<h2 class="text-lg font-medium text-gray-900">Server Status</h2>
|
||||||
<p class="text-success mb-0">Online</p>
|
<span class="badge-success">Online</span>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="mb-2">
|
<div class="space-y-3">
|
||||||
<small class="text-muted">Uptime</small>
|
<div>
|
||||||
<p class="h4 mb-0">{{.Uptime}}</p>
|
<p class="text-sm text-gray-500">Uptime</p>
|
||||||
|
<p class="text-2xl font-medium text-gray-900">{{.Uptime}}</p>
|
||||||
</div>
|
</div>
|
||||||
<div>
|
<div>
|
||||||
<small class="text-muted">Version</small>
|
<p class="text-sm text-gray-500">Version</p>
|
||||||
<p class="mb-0"><code>{{.Version}}</code></p>
|
<p class="font-mono text-sm text-gray-700">{{.Version}}</p>
|
||||||
</div>
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<!-- Users Card -->
|
<!-- Users Card -->
|
||||||
<div class="col-md-6">
|
<div class="card-elevated p-6">
|
||||||
<div class="card h-100 shadow-sm">
|
<div class="flex items-center mb-4">
|
||||||
<div class="card-body">
|
<div class="rounded-full bg-primary-50 p-3 mr-4">
|
||||||
<div class="d-flex align-items-center mb-3">
|
<svg class="w-6 h-6 text-primary-500" fill="currentColor" viewBox="0 0 16 16">
|
||||||
<div class="rounded-circle bg-primary bg-opacity-10 p-3 me-3">
|
|
||||||
<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" fill="currentColor" class="bi bi-people text-primary" viewBox="0 0 16 16">
|
|
||||||
<path d="M15 14s1 0 1-1-1-4-5-4-5 3-5 4 1 1 1 1h8zm-7.978-1A.261.261 0 0 1 7 12.996c.001-.264.167-1.03.76-1.72C8.312 10.629 9.282 10 11 10c1.717 0 2.687.63 3.24 1.276.593.69.758 1.457.76 1.72l-.008.002a.274.274 0 0 1-.014.002H7.022zM11 7a2 2 0 1 0 0-4 2 2 0 0 0 0 4zm3-2a3 3 0 1 1-6 0 3 3 0 0 1 6 0zM6.936 9.28a5.88 5.88 0 0 0-1.23-.247A7.35 7.35 0 0 0 5 9c-4 0-5 3-5 4 0 .667.333 1 1 1h4.216A2.238 2.238 0 0 1 5 13c0-1.01.377-2.042 1.09-2.904.243-.294.526-.569.846-.816zM4.92 10A5.493 5.493 0 0 0 4 13H1c0-.26.164-1.03.76-1.724.545-.636 1.492-1.256 3.16-1.275zM1.5 5.5a3 3 0 1 1 6 0 3 3 0 0 1-6 0zm3-2a2 2 0 1 0 0 4 2 2 0 0 0 0-4z"/>
|
<path d="M15 14s1 0 1-1-1-4-5-4-5 3-5 4 1 1 1 1h8zm-7.978-1A.261.261 0 0 1 7 12.996c.001-.264.167-1.03.76-1.72C8.312 10.629 9.282 10 11 10c1.717 0 2.687.63 3.24 1.276.593.69.758 1.457.76 1.72l-.008.002a.274.274 0 0 1-.014.002H7.022zM11 7a2 2 0 1 0 0-4 2 2 0 0 0 0 4zm3-2a3 3 0 1 1-6 0 3 3 0 0 1 6 0zM6.936 9.28a5.88 5.88 0 0 0-1.23-.247A7.35 7.35 0 0 0 5 9c-4 0-5 3-5 4 0 .667.333 1 1 1h4.216A2.238 2.238 0 0 1 5 13c0-1.01.377-2.042 1.09-2.904.243-.294.526-.569.846-.816zM4.92 10A5.493 5.493 0 0 0 4 13H1c0-.26.164-1.03.76-1.724.545-.636 1.492-1.256 3.16-1.275zM1.5 5.5a3 3 0 1 1 6 0 3 3 0 0 1-6 0zm3-2a2 2 0 1 0 0 4 2 2 0 0 0 0-4z"/>
|
||||||
</svg>
|
</svg>
|
||||||
</div>
|
</div>
|
||||||
<div>
|
<div>
|
||||||
<h5 class="card-title mb-1">Users</h5>
|
<h2 class="text-lg font-medium text-gray-900">Users</h2>
|
||||||
<p class="text-muted mb-0">Registered accounts</p>
|
<p class="text-sm text-gray-500">Registered accounts</p>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div>
|
<div>
|
||||||
<p class="h2 mb-0">{{.UserCount}}</p>
|
<p class="text-4xl font-medium text-gray-900">{{.UserCount}}</p>
|
||||||
<small class="text-muted">Total users</small>
|
<p class="text-sm text-gray-500 mt-1">Total users</p>
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
{{if not .User}}
|
{{if not .User}}
|
||||||
<div class="text-center mt-5">
|
<div class="text-center mt-10">
|
||||||
<p class="text-muted">Ready to get started?</p>
|
<p class="text-gray-500 mb-4">Ready to get started?</p>
|
||||||
<a href="/pages/login" class="btn btn-primary">Login to your account</a>
|
<a href="/pages/login" class="btn-primary">Login to your account</a>
|
||||||
</div>
|
</div>
|
||||||
{{end}}
|
{{end}}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
{{end}}
|
{{end}}
|
||||||
@@ -2,85 +2,56 @@
|
|||||||
|
|
||||||
{{define "title"}}Login - Webhooker{{end}}
|
{{define "title"}}Login - Webhooker{{end}}
|
||||||
|
|
||||||
{{define "head"}}
|
|
||||||
<style>
|
|
||||||
body {
|
|
||||||
background-color: #f8f9fa;
|
|
||||||
}
|
|
||||||
.login-container {
|
|
||||||
max-width: 400px;
|
|
||||||
margin: 100px auto;
|
|
||||||
}
|
|
||||||
.login-card {
|
|
||||||
background: white;
|
|
||||||
border-radius: 10px;
|
|
||||||
box-shadow: 0 0 20px rgba(0,0,0,0.1);
|
|
||||||
padding: 40px;
|
|
||||||
}
|
|
||||||
.login-header {
|
|
||||||
text-align: center;
|
|
||||||
margin-bottom: 30px;
|
|
||||||
}
|
|
||||||
.login-header h1 {
|
|
||||||
font-size: 2rem;
|
|
||||||
font-weight: 600;
|
|
||||||
color: #333;
|
|
||||||
}
|
|
||||||
.login-header p {
|
|
||||||
color: #666;
|
|
||||||
margin-top: 10px;
|
|
||||||
}
|
|
||||||
.form-control:focus {
|
|
||||||
border-color: #007bff;
|
|
||||||
box-shadow: 0 0 0 0.2rem rgba(0,123,255,.25);
|
|
||||||
}
|
|
||||||
.btn-primary {
|
|
||||||
width: 100%;
|
|
||||||
padding: 12px;
|
|
||||||
font-weight: 600;
|
|
||||||
text-transform: uppercase;
|
|
||||||
letter-spacing: 0.5px;
|
|
||||||
}
|
|
||||||
.error-message {
|
|
||||||
margin-top: 15px;
|
|
||||||
}
|
|
||||||
</style>
|
|
||||||
{{end}}
|
|
||||||
|
|
||||||
{{define "content"}}
|
{{define "content"}}
|
||||||
<div class="container">
|
<div class="min-h-screen flex items-center justify-center py-12 px-4">
|
||||||
<div class="login-container">
|
<div class="max-w-md w-full">
|
||||||
<div class="login-card">
|
<div class="text-center mb-8">
|
||||||
<div class="login-header">
|
<h1 class="text-3xl font-medium text-gray-900">Webhooker</h1>
|
||||||
<h1>Webhooker</h1>
|
<p class="mt-2 text-gray-600">Sign in to your account</p>
|
||||||
<p>Sign in to your account</p>
|
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
<div class="card p-8">
|
||||||
{{if .Error}}
|
{{if .Error}}
|
||||||
<div class="alert alert-danger error-message" role="alert">
|
<div class="alert-error">
|
||||||
{{.Error}}
|
<div class="flex items-center">
|
||||||
|
<svg class="w-5 h-5 mr-2 flex-shrink-0" fill="currentColor" viewBox="0 0 20 20">
|
||||||
|
<path fill-rule="evenodd" d="M10 18a8 8 0 100-16 8 8 0 000 16zM8.707 7.293a1 1 0 00-1.414 1.414L8.586 10l-1.293 1.293a1 1 0 101.414 1.414L10 11.414l1.293 1.293a1 1 0 001.414-1.414L11.414 10l1.293-1.293a1 1 0 00-1.414-1.414L10 8.586 8.707 7.293z" clip-rule="evenodd"/>
|
||||||
|
</svg>
|
||||||
|
<span>{{.Error}}</span>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
{{end}}
|
{{end}}
|
||||||
|
|
||||||
<form method="POST" action="/pages/login">
|
<form method="POST" action="/pages/login" class="space-y-6">
|
||||||
<div class="mb-3">
|
<div class="form-group">
|
||||||
<label for="username" class="form-label">Username</label>
|
<label for="username" class="label">Username</label>
|
||||||
<input type="text" class="form-control" id="username" name="username"
|
<input
|
||||||
placeholder="Enter your username" required autofocus>
|
type="text"
|
||||||
|
id="username"
|
||||||
|
name="username"
|
||||||
|
required
|
||||||
|
autofocus
|
||||||
|
autocomplete="username"
|
||||||
|
placeholder="Enter your username"
|
||||||
|
class="input"
|
||||||
|
>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="mb-4">
|
<div class="form-group">
|
||||||
<label for="password" class="form-label">Password</label>
|
<label for="password" class="label">Password</label>
|
||||||
<input type="password" class="form-control" id="password" name="password"
|
<input
|
||||||
placeholder="Enter your password" required>
|
type="password"
|
||||||
|
id="password"
|
||||||
|
name="password"
|
||||||
|
required
|
||||||
|
autocomplete="current-password"
|
||||||
|
placeholder="Enter your password"
|
||||||
|
class="input"
|
||||||
|
>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<button type="submit" class="btn btn-primary">Sign In</button>
|
<button type="submit" class="btn-primary w-full py-3">Sign In</button>
|
||||||
</form>
|
</form>
|
||||||
|
|
||||||
<div class="mt-4 text-center text-muted">
|
|
||||||
<small>© 2025 Webhooker. All rights reserved.</small>
|
|
||||||
</div>
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@@ -1,46 +1,50 @@
|
|||||||
{{define "navbar"}}
|
{{define "navbar"}}
|
||||||
<nav class="navbar navbar-expand-lg navbar-dark bg-dark">
|
<nav class="app-bar" x-data="{ open: false }">
|
||||||
<div class="container-fluid">
|
<div class="max-w-6xl mx-auto flex justify-between items-center">
|
||||||
<a class="navbar-brand" href="/">Webhooker</a>
|
<div class="flex items-center gap-3">
|
||||||
<button class="navbar-toggler" type="button" data-bs-toggle="collapse" data-bs-target="#navbarNav">
|
<a href="/" class="text-xl font-medium text-gray-900 hover:text-primary-600 transition-colors">Webhooker</a>
|
||||||
<span class="navbar-toggler-icon"></span>
|
</div>
|
||||||
|
|
||||||
|
<!-- Mobile menu button -->
|
||||||
|
<button @click="open = !open" class="md:hidden p-2 rounded-md text-gray-500 hover:bg-gray-100">
|
||||||
|
<svg class="w-6 h-6" fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
||||||
|
<path x-show="!open" stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M4 6h16M4 12h16M4 18h16"/>
|
||||||
|
<path x-show="open" x-cloak stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M6 18L18 6M6 6l12 12"/>
|
||||||
|
</svg>
|
||||||
</button>
|
</button>
|
||||||
<div class="collapse navbar-collapse" id="navbarNav">
|
|
||||||
<ul class="navbar-nav me-auto">
|
<!-- Desktop navigation -->
|
||||||
|
<div class="hidden md:flex items-center gap-4">
|
||||||
{{if .User}}
|
{{if .User}}
|
||||||
<li class="nav-item">
|
<a href="/sources" class="btn-text">Sources</a>
|
||||||
<a class="nav-link" href="/sources">Sources</a>
|
<a href="/user/{{.User.Username}}" class="btn-text">
|
||||||
</li>
|
<svg class="w-5 h-5 mr-1" fill="currentColor" viewBox="0 0 16 16">
|
||||||
{{end}}
|
|
||||||
</ul>
|
|
||||||
<ul class="navbar-nav">
|
|
||||||
{{if .User}}
|
|
||||||
<!-- Logged in state -->
|
|
||||||
<li class="nav-item dropdown">
|
|
||||||
<a class="nav-link dropdown-toggle d-flex align-items-center" href="#" id="userDropdown" role="button" data-bs-toggle="dropdown">
|
|
||||||
<svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" fill="currentColor" class="bi bi-person-circle me-2" viewBox="0 0 16 16">
|
|
||||||
<path d="M11 6a3 3 0 1 1-6 0 3 3 0 0 1 6 0z"/>
|
<path d="M11 6a3 3 0 1 1-6 0 3 3 0 0 1 6 0z"/>
|
||||||
<path fill-rule="evenodd" d="M0 8a8 8 0 1 1 16 0A8 8 0 0 1 0 8zm8-7a7 7 0 0 0-5.468 11.37C3.242 11.226 4.805 10 8 10s4.757 1.225 5.468 2.37A7 7 0 0 0 8 1z"/>
|
<path fill-rule="evenodd" d="M0 8a8 8 0 1 1 16 0A8 8 0 0 1 0 8zm8-7a7 7 0 0 0-5.468 11.37C3.242 11.226 4.805 10 8 10s4.757 1.225 5.468 2.37A7 7 0 0 0 8 1z"/>
|
||||||
</svg>
|
</svg>
|
||||||
{{.User.Username}}
|
{{.User.Username}}
|
||||||
</a>
|
</a>
|
||||||
<ul class="dropdown-menu dropdown-menu-end">
|
<form method="POST" action="/pages/logout" class="inline">
|
||||||
<li><a class="dropdown-item" href="/user/{{.User.Username}}">Profile</a></li>
|
<button type="submit" class="btn-text">Logout</button>
|
||||||
<li><hr class="dropdown-divider"></li>
|
|
||||||
<li>
|
|
||||||
<form method="POST" action="/pages/logout" class="m-0">
|
|
||||||
<button type="submit" class="dropdown-item">Logout</button>
|
|
||||||
</form>
|
</form>
|
||||||
</li>
|
|
||||||
</ul>
|
|
||||||
</li>
|
|
||||||
{{else}}
|
{{else}}
|
||||||
<!-- Logged out state -->
|
<a href="/pages/login" class="btn-primary">Login</a>
|
||||||
<li class="nav-item">
|
{{end}}
|
||||||
<a class="nav-link" href="/pages/login">Login</a>
|
</div>
|
||||||
</li>
|
</div>
|
||||||
|
|
||||||
|
<!-- Mobile navigation -->
|
||||||
|
<div x-show="open" x-cloak x-transition class="md:hidden mt-4 pt-4 border-t border-gray-200">
|
||||||
|
<div class="flex flex-col gap-2">
|
||||||
|
{{if .User}}
|
||||||
|
<a href="/sources" class="btn-text w-full text-left">Sources</a>
|
||||||
|
<a href="/user/{{.User.Username}}" class="btn-text w-full text-left">Profile</a>
|
||||||
|
<form method="POST" action="/pages/logout">
|
||||||
|
<button type="submit" class="btn-text w-full text-left">Logout</button>
|
||||||
|
</form>
|
||||||
|
{{else}}
|
||||||
|
<a href="/pages/login" class="btn-primary w-full">Login</a>
|
||||||
{{end}}
|
{{end}}
|
||||||
</ul>
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</nav>
|
</nav>
|
||||||
|
|||||||
@@ -3,51 +3,48 @@
|
|||||||
{{define "title"}}Profile - Webhooker{{end}}
|
{{define "title"}}Profile - Webhooker{{end}}
|
||||||
|
|
||||||
{{define "content"}}
|
{{define "content"}}
|
||||||
<div class="container mt-5">
|
<div class="max-w-4xl mx-auto px-6 py-12">
|
||||||
<div class="row">
|
<h1 class="text-2xl font-medium text-gray-900 mb-6">User Profile</h1>
|
||||||
<div class="col-lg-8 mx-auto">
|
|
||||||
<h1 class="mb-4">User Profile</h1>
|
|
||||||
|
|
||||||
<div class="card shadow-sm">
|
<div class="card p-6">
|
||||||
<div class="card-body">
|
<div class="flex items-center mb-6">
|
||||||
<div class="row align-items-center mb-3">
|
<div class="mr-4">
|
||||||
<div class="col-auto">
|
<svg class="w-16 h-16 text-primary-500" fill="currentColor" viewBox="0 0 16 16">
|
||||||
<svg xmlns="http://www.w3.org/2000/svg" width="64" height="64" fill="currentColor" class="bi bi-person-circle text-primary" viewBox="0 0 16 16">
|
|
||||||
<path d="M11 6a3 3 0 1 1-6 0 3 3 0 0 1 6 0z"/>
|
<path d="M11 6a3 3 0 1 1-6 0 3 3 0 0 1 6 0z"/>
|
||||||
<path fill-rule="evenodd" d="M0 8a8 8 0 1 1 16 0A8 8 0 0 1 0 8zm8-7a7 7 0 0 0-5.468 11.37C3.242 11.226 4.805 10 8 10s4.757 1.225 5.468 2.37A7 7 0 0 0 8 1z"/>
|
<path fill-rule="evenodd" d="M0 8a8 8 0 1 1 16 0A8 8 0 0 1 0 8zm8-7a7 7 0 0 0-5.468 11.37C3.242 11.226 4.805 10 8 10s4.757 1.225 5.468 2.37A7 7 0 0 0 8 1z"/>
|
||||||
</svg>
|
</svg>
|
||||||
</div>
|
</div>
|
||||||
<div class="col">
|
<div>
|
||||||
<h3 class="mb-0">{{.User.Username}}</h3>
|
<h2 class="text-xl font-medium text-gray-900">{{.User.Username}}</h2>
|
||||||
<p class="text-muted mb-0">User ID: {{.User.ID}}</p>
|
<p class="text-sm text-gray-500">User ID: {{.User.ID}}</p>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<hr>
|
<hr class="border-gray-200 mb-6">
|
||||||
|
|
||||||
<div class="row">
|
<div class="grid grid-cols-1 md:grid-cols-2 gap-8">
|
||||||
<div class="col-md-6">
|
<div>
|
||||||
<h5>Account Information</h5>
|
<h3 class="text-lg font-medium text-gray-900 mb-3">Account Information</h3>
|
||||||
<dl class="row">
|
<dl class="space-y-3">
|
||||||
<dt class="col-sm-4">Username</dt>
|
<div class="flex">
|
||||||
<dd class="col-sm-8">{{.User.Username}}</dd>
|
<dt class="w-32 text-sm font-medium text-gray-500">Username</dt>
|
||||||
|
<dd class="text-sm text-gray-900">{{.User.Username}}</dd>
|
||||||
<dt class="col-sm-4">Account Type</dt>
|
</div>
|
||||||
<dd class="col-sm-8">Standard User</dd>
|
<div class="flex">
|
||||||
|
<dt class="w-32 text-sm font-medium text-gray-500">Account Type</dt>
|
||||||
|
<dd class="text-sm text-gray-900">Standard User</dd>
|
||||||
|
</div>
|
||||||
</dl>
|
</dl>
|
||||||
</div>
|
</div>
|
||||||
<div class="col-md-6">
|
<div>
|
||||||
<h5>Settings</h5>
|
<h3 class="text-lg font-medium text-gray-900 mb-3">Settings</h3>
|
||||||
<p class="text-muted">Profile settings and preferences will be available here.</p>
|
<p class="text-sm text-gray-500">Profile settings and preferences will be available here.</p>
|
||||||
</div>
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="mt-4">
|
<div class="mt-6">
|
||||||
<a href="/" class="btn btn-secondary">Back to Home</a>
|
<a href="/" class="btn-secondary">Back to Home</a>
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
{{end}}
|
{{end}}
|
||||||
Reference in New Issue
Block a user