aggiornato per uso di taiwind
This commit is contained in:
parent
275d3df3f1
commit
3fc01cc4f7
|
|
@ -35,3 +35,7 @@ ui-kit/node_modules/
|
|||
*.sqlite3
|
||||
*.db
|
||||
/data/emails/
|
||||
|
||||
# Root JS deps
|
||||
node_modules/
|
||||
flowbite-ui/node_modules/
|
||||
|
|
|
|||
30
Makefile
30
Makefile
|
|
@ -1,20 +1,22 @@
|
|||
.PHONY: dev ui-build ui-dev css-build css-dev test db-reset fmt
|
||||
.PHONY: tw-build tw-watch htmx-copy flowbite-copy assets server test db-reset fmt
|
||||
|
||||
dev:
|
||||
tw-build:
|
||||
npm --prefix flowbite-ui run tw:build
|
||||
|
||||
tw-watch:
|
||||
npm --prefix flowbite-ui run tw:watch
|
||||
|
||||
htmx-copy:
|
||||
mkdir -p web/static/vendor && cp flowbite-ui/node_modules/htmx.org/dist/htmx.min.js web/static/vendor/htmx.min.js
|
||||
|
||||
flowbite-copy:
|
||||
mkdir -p web/static/vendor && cp flowbite-ui/node_modules/flowbite/dist/flowbite.min.js web/static/vendor/flowbite.js
|
||||
|
||||
assets: htmx-copy flowbite-copy tw-build
|
||||
|
||||
server:
|
||||
go run ./cmd/server
|
||||
|
||||
ui-build:
|
||||
cd ui-kit && npm i && npm run build && npm run css:build
|
||||
|
||||
ui-dev:
|
||||
cd ui-kit && npm i && npm run dev
|
||||
|
||||
css-build:
|
||||
cd ui-kit && npm i && npm run css:build
|
||||
|
||||
css-dev:
|
||||
cd ui-kit && npm i && npm run css:dev
|
||||
|
||||
test:
|
||||
go test ./...
|
||||
|
||||
|
|
|
|||
74
README.md
74
README.md
|
|
@ -1,14 +1,34 @@
|
|||
# GoFiber MVC Boilerplate
|
||||
|
||||
Boilerplate GoFiber MVC + HTMX + Svelte Custom Elements + GORM, con auth server-rendered, area private/admin e mail sink in sviluppo.
|
||||
Boilerplate GoFiber MVC + HTMX + Flowbite + GORM, con auth server-rendered, area private/admin e mail sink in sviluppo.
|
||||
|
||||
## Setup Assets + Server
|
||||
|
||||
Terminale 1:
|
||||
|
||||
```bash
|
||||
npm i --prefix flowbite-ui
|
||||
make assets
|
||||
make tw-watch
|
||||
```
|
||||
|
||||
Terminale 2:
|
||||
|
||||
```bash
|
||||
make server
|
||||
```
|
||||
|
||||
`make assets` esegue:
|
||||
- copia di `flowbite-ui/node_modules/flowbite/dist/flowbite.min.js` in `web/static/vendor/flowbite.js`
|
||||
- build Tailwind in `web/static/css/app.css`
|
||||
|
||||
## Quickstart SQLite
|
||||
|
||||
```bash
|
||||
cp .env.example .env
|
||||
make css-build
|
||||
make ui-build
|
||||
make dev
|
||||
npm i --prefix flowbite-ui
|
||||
make assets
|
||||
make server
|
||||
```
|
||||
|
||||
Default SQLite path: `./data/app.sqlite3`.
|
||||
|
|
@ -37,32 +57,12 @@ DB_PG_DSN=postgres://trustcontact:trustcontact@localhost:5432/trustcontact?sslmo
|
|||
|
||||
`DB_POSTGRES_DSN` è comunque supportato.
|
||||
|
||||
## Tailwind + UI Kit
|
||||
|
||||
Tailwind (template server-rendered) compila in `web/static/css/app.css`.
|
||||
|
||||
UI kit (Svelte custom elements) compila in `web/static/ui`.
|
||||
|
||||
Comandi:
|
||||
|
||||
```bash
|
||||
make css-build # build tailwind
|
||||
make css-dev # watch tailwind
|
||||
make ui-build # build ui-kit + css tailwind
|
||||
make ui-dev # vite dev server ui-kit
|
||||
```
|
||||
|
||||
Layout include:
|
||||
|
||||
- `/static/css/app.css?v={{.BuildHash}}`
|
||||
- `/static/ui/ui.css?v={{.BuildHash}}`
|
||||
- `/static/ui/ui.esm.js?v={{.BuildHash}}`
|
||||
|
||||
## Template Directories
|
||||
|
||||
- Public: `web/templates/public`
|
||||
- Private: `web/templates/private`
|
||||
- Admin: `web/templates/admin`
|
||||
- Components Flowbite: `web/templates/components`
|
||||
|
||||
## Email in Develop
|
||||
|
||||
|
|
@ -70,11 +70,25 @@ In `develop`, le email vengono salvate in `./data/emails`.
|
|||
|
||||
## Make Targets
|
||||
|
||||
- `make dev` -> `go run ./cmd/server`
|
||||
- `make ui-build` -> install + build ui-kit + build css tailwind
|
||||
- `make ui-dev` -> watch UI con Vite
|
||||
- `make css-build` -> build Tailwind CSS
|
||||
- `make css-dev` -> watch Tailwind CSS
|
||||
- `make tw-build` -> build Tailwind CSS
|
||||
- `make tw-watch` -> watch Tailwind CSS
|
||||
- `make flowbite-copy` -> copia `flowbite-ui/node_modules/flowbite/dist/flowbite.min.js` in `web/static/vendor/flowbite.js`
|
||||
- `make assets` -> `flowbite-copy` + `tw-build`
|
||||
- `make server` -> `go run ./cmd/server`
|
||||
- `make test` -> `go test ./...`
|
||||
- `make db-reset` -> reset DB sqlite locale (`./data/app.db` / `./data/app.sqlite3`)
|
||||
- `make fmt` -> `gofmt` su `cmd/` e `internal/`
|
||||
# Third-Party Notices
|
||||
|
||||
This project uses third-party software distributed under the MIT License.
|
||||
|
||||
## Flowbite
|
||||
- Package: `flowbite`
|
||||
- License: MIT
|
||||
- Upstream: https://github.com/themesberg/flowbite
|
||||
- Full text: `licenses/FLOWBITE-MIT.txt`
|
||||
|
||||
## Tailwind CSS
|
||||
- Packages: `tailwindcss`, `@tailwindcss/cli`
|
||||
- License: MIT
|
||||
- Upstream: https://github.com/tailwindlabs/tailwindcss
|
||||
|
|
|
|||
|
|
@ -0,0 +1 @@
|
|||
@import "../../flowbite-ui/node_modules/tailwindcss/index.css";
|
||||
|
|
@ -0,0 +1,71 @@
|
|||
TASK: Integra Flowbite (UI + JS behavior) e aggiungi Makefile per Tailwind CLI (build/watch). Uso più terminali: non aggiungere tool per runner multi-process.
|
||||
|
||||
1) Dipendenze Node
|
||||
- Se non esiste package.json in root, crearlo.
|
||||
- Installare:
|
||||
- npm install -D @tailwindcss/cli tailwindcss
|
||||
- npm install flowbite
|
||||
|
||||
2) Tailwind config
|
||||
- Creare tailwind.config.js con:
|
||||
content: [
|
||||
"./web/templates/**/*.{html,gohtml}",
|
||||
"./web/static/**/*.js",
|
||||
"./node_modules/flowbite/**/*.js"
|
||||
],
|
||||
theme: { extend: {} },
|
||||
plugins: [ require("flowbite/plugin") ]
|
||||
|
||||
3) CSS input
|
||||
- Creare ./assets/tailwind/input.css con:
|
||||
@import "tailwindcss";
|
||||
|
||||
4) Scripts npm
|
||||
- In package.json aggiungere:
|
||||
"scripts": {
|
||||
"tw:build": "npx @tailwindcss/cli -i ./assets/tailwind/input.css -o ./web/static/css/app.css --minify",
|
||||
"tw:watch": "npx @tailwindcss/cli -i ./assets/tailwind/input.css -o ./web/static/css/app.css --watch"
|
||||
}
|
||||
|
||||
5) Copia Flowbite JS
|
||||
- Creare directory web/static/vendor se manca
|
||||
- Copiare:
|
||||
node_modules/flowbite/dist/flowbite.min.js -> web/static/vendor/flowbite.js
|
||||
|
||||
6) Layout
|
||||
- Aggiornare /web/templates/layout.html per includere:
|
||||
<link rel="stylesheet" href="/static/css/app.css?v={{.BuildHash}}">
|
||||
<script src="/static/vendor/htmx.min.js"></script>
|
||||
<script src="/static/vendor/flowbite.js"></script>
|
||||
- Rimuovere dal layout riferimenti attivi al vecchio UI kit Svelte (ma non cancellare /ui-kit dal repo)
|
||||
|
||||
7) Makefile
|
||||
- Creare/aggiornare Makefile con target:
|
||||
- tw-build: npm run tw:build
|
||||
- tw-watch: npm run tw:watch
|
||||
- flowbite-copy: mkdir -p web/static/vendor && cp node_modules/flowbite/dist/flowbite.min.js web/static/vendor/flowbite.js
|
||||
- assets: flowbite-copy tw-build
|
||||
- server: go run ./cmd/server
|
||||
- Non creare target che avvia più processi insieme.
|
||||
|
||||
8) Partials Flowbite
|
||||
- Creare /web/templates/components:
|
||||
- navbar.html
|
||||
- modal.html
|
||||
- dropdown.html
|
||||
- tabs.html
|
||||
- collapse.html
|
||||
Con markup Flowbite + data-attributes standard, senza JS custom.
|
||||
|
||||
9) Licenze
|
||||
- Creare /licenses/FLOWBITE-MIT.txt e THIRD_PARTY_NOTICES.md (Flowbite MIT, Tailwind MIT).
|
||||
|
||||
10) README
|
||||
- Aggiornare README con istruzioni:
|
||||
Terminale 1: npm i && make assets && make tw-watch
|
||||
Terminale 2: make server
|
||||
|
||||
Criteri:
|
||||
- make assets genera CSS e copia flowbite.js
|
||||
- modals/dropdowns Flowbite funzionano
|
||||
- progetto compilabile e avviabile.
|
||||
|
|
@ -0,0 +1,141 @@
|
|||
TASK: Convertire tutti i template HTML esistenti per usare componenti Flowbite (markup + behavior JS) mantenendo logica MVC e HTMX.
|
||||
|
||||
Non modificare controller, services, repo.
|
||||
Modificare solo template e layout.
|
||||
|
||||
-------------------------------------
|
||||
1) LAYOUT GLOBALE
|
||||
-------------------------------------
|
||||
|
||||
Aggiornare /web/templates/layout.html:
|
||||
|
||||
- Layout container moderno Tailwind
|
||||
- Navbar Flowbite responsive con:
|
||||
- Logo/AppName a sinistra
|
||||
- Link:
|
||||
- Public: Login / Signup
|
||||
- Private: Dashboard / Users
|
||||
- Admin: Admin (solo se role=admin)
|
||||
- Dropdown utente con logout
|
||||
- Include:
|
||||
<link rel="stylesheet" href="/static/css/app.css?v={{.BuildHash}}">
|
||||
<script src="/static/vendor/htmx.min.js"></script>
|
||||
<script src="/static/vendor/flowbite.js"></script>
|
||||
|
||||
Struttura:
|
||||
- Navbar top
|
||||
- Container max-w-7xl mx-auto p-6
|
||||
- Footer minimale
|
||||
|
||||
-------------------------------------
|
||||
2) PUBLIC TEMPLATES
|
||||
-------------------------------------
|
||||
|
||||
Convertire:
|
||||
|
||||
/web/templates/public/login.html
|
||||
/web/templates/public/signup.html
|
||||
/web/templates/public/forgot_password.html
|
||||
/web/templates/public/reset_password.html
|
||||
|
||||
Usare:
|
||||
- Card Flowbite per form
|
||||
- Input Flowbite style
|
||||
- Button primary
|
||||
- Alert Flowbite per flash messages
|
||||
- Layout centrato verticalmente (flex items-center justify-center min-h-screen)
|
||||
|
||||
-------------------------------------
|
||||
3) PRIVATE USERS
|
||||
-------------------------------------
|
||||
|
||||
/web/templates/private/users/index.html
|
||||
|
||||
- Header sezione con:
|
||||
- Titolo
|
||||
- Pulsante "Nuovo Utente" (modal Flowbite)
|
||||
- Search input Flowbite
|
||||
- Table Flowbite styled (striped, hover)
|
||||
- Pagination button Flowbite
|
||||
- Modal Flowbite per dettaglio utente
|
||||
|
||||
Assicurarsi che:
|
||||
- hx-get
|
||||
- hx-target
|
||||
- hx-swap
|
||||
rimangano funzionanti
|
||||
|
||||
-------------------------------------
|
||||
4) ADMIN
|
||||
-------------------------------------
|
||||
|
||||
/web/templates/admin/dashboard.html
|
||||
/web/templates/admin/users.html
|
||||
/web/templates/admin/audit_logs.html
|
||||
|
||||
Usare:
|
||||
- Card summary (stats)
|
||||
- Table Flowbite
|
||||
- Badge per ruoli (admin = red, user = blue)
|
||||
- Tabs Flowbite se presenti più sezioni
|
||||
|
||||
-------------------------------------
|
||||
5) FLASH MESSAGES
|
||||
-------------------------------------
|
||||
|
||||
Creare partial:
|
||||
- /web/templates/components/flash.html
|
||||
|
||||
Usare Flowbite alert component:
|
||||
- success -> green
|
||||
- error -> red
|
||||
- warning -> yellow
|
||||
|
||||
Includere in layout sopra {{embed}}
|
||||
|
||||
-------------------------------------
|
||||
6) MODAL STANDARD
|
||||
-------------------------------------
|
||||
|
||||
Creare partial riusabile:
|
||||
/web/templates/components/modal.html
|
||||
|
||||
Markup Flowbite standard con:
|
||||
- id dinamico
|
||||
- header con titolo
|
||||
- body slot
|
||||
- footer slot opzionale
|
||||
- data-modal-toggle attributes
|
||||
|
||||
Usare nelle pagine private.
|
||||
|
||||
-------------------------------------
|
||||
7) ACCESSIBILITÀ
|
||||
-------------------------------------
|
||||
|
||||
- Usare aria attributes corretti come in documentazione Flowbite
|
||||
- Non rompere keyboard interaction
|
||||
- Mantenere form method e csrf se presente
|
||||
|
||||
-------------------------------------
|
||||
8) RESPONSIVE DESIGN
|
||||
-------------------------------------
|
||||
|
||||
- Navbar collapse mobile
|
||||
- Table scrollable mobile
|
||||
- Forms full width mobile
|
||||
|
||||
-------------------------------------
|
||||
9) CRITERI DI ACCETTAZIONE
|
||||
-------------------------------------
|
||||
|
||||
- Tutte le pagine hanno layout moderno Flowbite
|
||||
- Modals funzionano
|
||||
- Dropdown funzionano
|
||||
- Navbar responsive
|
||||
- HTMX partial update continua a funzionare
|
||||
- Nessuna modifica backend richiesta
|
||||
- Nessun errore JS in console
|
||||
|
||||
Scrivere codice pulito, leggibile, con commenti minimi.
|
||||
Non eliminare logica Go template esistente.
|
||||
|
|
@ -0,0 +1,5 @@
|
|||
@config "./tailwind.config.js";
|
||||
@source "../web/templates/**/*.{html,gohtml}";
|
||||
@source "../web/static/**/*.js";
|
||||
@source "./node_modules/flowbite/**/*.js";
|
||||
@import "tailwindcss";
|
||||
File diff suppressed because it is too large
Load Diff
|
|
@ -0,0 +1,17 @@
|
|||
{
|
||||
"name": "trustcontact-flowbite",
|
||||
"private": true,
|
||||
"version": "1.0.0",
|
||||
"scripts": {
|
||||
"tw:build": "npx @tailwindcss/cli -i ./input.css -o ../web/static/css/app.css --minify",
|
||||
"tw:watch": "npx @tailwindcss/cli -i ./input.css -o ../web/static/css/app.css --watch"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@tailwindcss/cli": "^4.1.13",
|
||||
"tailwindcss": "^4.1.13"
|
||||
},
|
||||
"dependencies": {
|
||||
"flowbite": "^3.1.2",
|
||||
"htmx.org": "^2.0.6"
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,9 @@
|
|||
module.exports = {
|
||||
content: [
|
||||
"../web/templates/**/*.{html,gohtml}",
|
||||
"../web/static/**/*.js",
|
||||
"./node_modules/flowbite/**/*.js"
|
||||
],
|
||||
theme: { extend: {} },
|
||||
plugins: [require("flowbite/plugin")]
|
||||
};
|
||||
|
|
@ -0,0 +1,21 @@
|
|||
MIT License
|
||||
|
||||
Copyright (c) 2020 Themesberg
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
in the Software without restriction, including without limitation the rights
|
||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
copies of the Software, and to permit persons to whom the Software is
|
||||
furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in all
|
||||
copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
SOFTWARE.
|
||||
File diff suppressed because it is too large
Load Diff
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
|
|
@ -0,0 +1,32 @@
|
|||
{{define "content"}}
|
||||
<section class="space-y-6">
|
||||
<h1 class="text-3xl font-bold text-gray-900">Audit Logs</h1>
|
||||
|
||||
<div class="mb-4 border-b border-gray-200">
|
||||
<ul class="-mb-px flex flex-wrap text-center text-sm font-medium" id="audit-tab" data-tabs-toggle="#audit-tab-content" role="tablist">
|
||||
<li class="me-2" role="presentation">
|
||||
<button class="inline-block rounded-t-lg border-b-2 p-4" id="activity-tab" data-tabs-target="#activity" type="button" role="tab" aria-controls="activity" aria-selected="true">Activity</button>
|
||||
</li>
|
||||
<li class="me-2" role="presentation">
|
||||
<button class="inline-block rounded-t-lg border-b-2 p-4" id="security-tab" data-tabs-target="#security" type="button" role="tab" aria-controls="security" aria-selected="false">Security</button>
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
|
||||
<div id="audit-tab-content">
|
||||
<div class="hidden rounded-lg border border-gray-200 bg-white p-4 shadow-sm" id="activity" role="tabpanel" aria-labelledby="activity-tab">
|
||||
<div class="relative overflow-x-auto">
|
||||
<table class="w-full text-left text-sm text-gray-500">
|
||||
<thead class="bg-gray-50 text-xs uppercase text-gray-700">
|
||||
<tr><th class="px-6 py-3">Timestamp</th><th class="px-6 py-3">Actor</th><th class="px-6 py-3">Action</th></tr>
|
||||
</thead>
|
||||
<tbody><tr class="border-b bg-white"><td class="px-6 py-4">-</td><td class="px-6 py-4">-</td><td class="px-6 py-4">-</td></tr></tbody>
|
||||
</table>
|
||||
</div>
|
||||
</div>
|
||||
<div class="hidden rounded-lg border border-gray-200 bg-white p-4 shadow-sm" id="security" role="tabpanel" aria-labelledby="security-tab">
|
||||
<p class="text-sm text-gray-600">Security logs placeholder.</p>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
{{end}}
|
||||
|
|
@ -1,9 +1,29 @@
|
|||
{{define "content"}}
|
||||
<div class="space-y-3">
|
||||
<h1 class="text-2xl font-semibold">Admin Dashboard</h1>
|
||||
<p class="muted">Area amministrazione.</p>
|
||||
<div class="row">
|
||||
<a href="/admin/users" class="rounded-lg border border-slate-300 px-4 py-2 hover:bg-slate-50">Gestione utenti</a>
|
||||
</div>
|
||||
<section class="space-y-6">
|
||||
<div>
|
||||
<h1 class="text-3xl font-bold text-gray-900">Admin Dashboard</h1>
|
||||
<p class="text-gray-600">Area amministrazione.</p>
|
||||
</div>
|
||||
|
||||
<div class="grid gap-4 sm:grid-cols-2 lg:grid-cols-3">
|
||||
<article class="rounded-lg border border-gray-200 bg-white p-6 shadow-sm">
|
||||
<p class="text-sm font-medium text-gray-500">Utenti</p>
|
||||
<p class="mt-2 text-3xl font-semibold text-gray-900">{{if .PageData}}{{.PageData.Total}}{{else}}-{{end}}</p>
|
||||
</article>
|
||||
<article class="rounded-lg border border-gray-200 bg-white p-6 shadow-sm">
|
||||
<p class="text-sm font-medium text-gray-500">Ruolo corrente</p>
|
||||
<p class="mt-2">
|
||||
{{if and .CurrentUser (eq .CurrentUser.Role "admin")}}
|
||||
<span class="rounded-sm bg-red-100 px-2.5 py-0.5 text-xs font-medium text-red-800">admin</span>
|
||||
{{else}}
|
||||
<span class="rounded-sm bg-blue-100 px-2.5 py-0.5 text-xs font-medium text-blue-800">user</span>
|
||||
{{end}}
|
||||
</p>
|
||||
</article>
|
||||
<article class="rounded-lg border border-gray-200 bg-white p-6 shadow-sm">
|
||||
<p class="text-sm font-medium text-gray-500">Navigazione</p>
|
||||
<a href="/admin/users" class="mt-2 inline-flex rounded-lg bg-blue-700 px-4 py-2 text-sm font-medium text-white hover:bg-blue-800">Gestione utenti</a>
|
||||
</article>
|
||||
</div>
|
||||
</section>
|
||||
{{end}}
|
||||
|
|
|
|||
|
|
@ -0,0 +1,8 @@
|
|||
{{define "content"}}
|
||||
<section class="space-y-4">
|
||||
<h1 class="text-3xl font-bold text-gray-900">Admin Users</h1>
|
||||
<div class="rounded-lg border border-gray-200 bg-white p-4 shadow-sm">
|
||||
<p class="text-gray-600">Template pagina utenti admin in stile Flowbite.</p>
|
||||
</div>
|
||||
</section>
|
||||
{{end}}
|
||||
|
|
@ -1,13 +1,36 @@
|
|||
{{define "users_modal"}}
|
||||
<div style="padding:16px;">
|
||||
<h3 style="margin-top:0;">Dettaglio utente #{{.User.ID}}</h3>
|
||||
<p><strong>Name:</strong> {{if .User.Name}}{{.User.Name}}{{else}}-{{end}}</p>
|
||||
<p><strong>Email:</strong> {{.User.Email}}</p>
|
||||
<p><strong>Role:</strong> {{.User.Role}}</p>
|
||||
<p><strong>Verified:</strong> {{if .User.EmailVerified}}yes{{else}}no{{end}}</p>
|
||||
<p><strong>Created:</strong> {{.User.CreatedAt}}</p>
|
||||
<div class="row">
|
||||
<button type="button" onclick="document.getElementById('userModal').removeAttribute('open')">Chiudi</button>
|
||||
<div class="grid gap-3 text-sm text-gray-700 sm:grid-cols-2">
|
||||
<div>
|
||||
<span class="font-semibold text-gray-900">ID:</span>
|
||||
<span>{{.User.ID}}</span>
|
||||
</div>
|
||||
<div>
|
||||
<span class="font-semibold text-gray-900">Role:</span>
|
||||
{{if eq .User.Role "admin"}}
|
||||
<span class="rounded-sm bg-red-100 px-2.5 py-0.5 text-xs font-medium text-red-800">admin</span>
|
||||
{{else}}
|
||||
<span class="rounded-sm bg-blue-100 px-2.5 py-0.5 text-xs font-medium text-blue-800">user</span>
|
||||
{{end}}
|
||||
</div>
|
||||
<div class="sm:col-span-2">
|
||||
<span class="font-semibold text-gray-900">Name:</span>
|
||||
<span>{{if .User.Name}}{{.User.Name}}{{else}}-{{end}}</span>
|
||||
</div>
|
||||
<div class="sm:col-span-2">
|
||||
<span class="font-semibold text-gray-900">Email:</span>
|
||||
<span>{{.User.Email}}</span>
|
||||
</div>
|
||||
<div>
|
||||
<span class="font-semibold text-gray-900">Verified:</span>
|
||||
<span>{{if .User.EmailVerified}}yes{{else}}no{{end}}</span>
|
||||
</div>
|
||||
<div>
|
||||
<span class="font-semibold text-gray-900">Created:</span>
|
||||
<span>{{.User.CreatedAt}}</span>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="mt-5">
|
||||
<button type="button" class="rounded-lg bg-blue-700 px-5 py-2.5 text-sm font-medium text-white hover:bg-blue-800" data-modal-hide="userModal">Chiudi</button>
|
||||
</div>
|
||||
{{end}}
|
||||
|
|
|
|||
|
|
@ -1,47 +1,53 @@
|
|||
{{define "users_table"}}
|
||||
{{ $p := .PageData }}
|
||||
<table style="width:100%;border-collapse:collapse;margin-top:16px;">
|
||||
<thead>
|
||||
<div class="relative overflow-x-auto">
|
||||
<table class="w-full text-left text-sm text-gray-500 rtl:text-right">
|
||||
<thead class="bg-gray-50 text-xs uppercase text-gray-700">
|
||||
<tr>
|
||||
<th style="text-align:left;border-bottom:1px solid #e5e7eb;padding:8px;">
|
||||
<a href="#" hx-get="/admin/users/table?q={{$p.Q}}&sort=id&dir={{if and (eq $p.Sort "id") (eq $p.Dir "asc")}}desc{{else}}asc{{end}}&page=1&pageSize={{$p.PageSize}}" hx-target="#usersTableContainer" hx-swap="innerHTML">ID</a>
|
||||
<th scope="col" class="px-6 py-3">
|
||||
<button type="button" class="inline-flex items-center gap-1 hover:text-blue-700" hx-get="/admin/users/table?q={{$p.Q}}&sort=id&dir={{if and (eq $p.Sort "id") (eq $p.Dir "asc")}}desc{{else}}asc{{end}}&page=1&pageSize={{$p.PageSize}}" hx-target="#usersTableContainer" hx-swap="innerHTML">ID</button>
|
||||
</th>
|
||||
<th style="text-align:left;border-bottom:1px solid #e5e7eb;padding:8px;">
|
||||
<a href="#" hx-get="/admin/users/table?q={{$p.Q}}&sort=name&dir={{if and (eq $p.Sort "name") (eq $p.Dir "asc")}}desc{{else}}asc{{end}}&page=1&pageSize={{$p.PageSize}}" hx-target="#usersTableContainer" hx-swap="innerHTML">Name</a>
|
||||
<th scope="col" class="px-6 py-3">
|
||||
<button type="button" class="inline-flex items-center gap-1 hover:text-blue-700" hx-get="/admin/users/table?q={{$p.Q}}&sort=name&dir={{if and (eq $p.Sort "name") (eq $p.Dir "asc")}}desc{{else}}asc{{end}}&page=1&pageSize={{$p.PageSize}}" hx-target="#usersTableContainer" hx-swap="innerHTML">Name</button>
|
||||
</th>
|
||||
<th style="text-align:left;border-bottom:1px solid #e5e7eb;padding:8px;">
|
||||
<a href="#" hx-get="/admin/users/table?q={{$p.Q}}&sort=email&dir={{if and (eq $p.Sort "email") (eq $p.Dir "asc")}}desc{{else}}asc{{end}}&page=1&pageSize={{$p.PageSize}}" hx-target="#usersTableContainer" hx-swap="innerHTML">Email</a>
|
||||
<th scope="col" class="px-6 py-3">
|
||||
<button type="button" class="inline-flex items-center gap-1 hover:text-blue-700" hx-get="/admin/users/table?q={{$p.Q}}&sort=email&dir={{if and (eq $p.Sort "email") (eq $p.Dir "asc")}}desc{{else}}asc{{end}}&page=1&pageSize={{$p.PageSize}}" hx-target="#usersTableContainer" hx-swap="innerHTML">Email</button>
|
||||
</th>
|
||||
<th style="text-align:left;border-bottom:1px solid #e5e7eb;padding:8px;">Role</th>
|
||||
<th style="text-align:left;border-bottom:1px solid #e5e7eb;padding:8px;">Azioni</th>
|
||||
<th scope="col" class="px-6 py-3">Role</th>
|
||||
<th scope="col" class="px-6 py-3">Azioni</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
{{range $u := $p.Users}}
|
||||
<tr>
|
||||
<td style="border-bottom:1px solid #f1f5f9;padding:8px;">{{$u.ID}}</td>
|
||||
<td style="border-bottom:1px solid #f1f5f9;padding:8px;">{{if $u.Name}}{{$u.Name}}{{else}}-{{end}}</td>
|
||||
<td style="border-bottom:1px solid #f1f5f9;padding:8px;">{{$u.Email}}</td>
|
||||
<td style="border-bottom:1px solid #f1f5f9;padding:8px;">{{$u.Role}}</td>
|
||||
<td style="border-bottom:1px solid #f1f5f9;padding:8px;">
|
||||
<button
|
||||
hx-get="/admin/users/{{$u.ID}}/modal"
|
||||
hx-target="#userModalContent"
|
||||
hx-swap="innerHTML"
|
||||
>Apri</button>
|
||||
<tr class="border-b bg-white hover:bg-gray-50">
|
||||
<td class="px-6 py-4">{{$u.ID}}</td>
|
||||
<td class="px-6 py-4 font-medium text-gray-900">{{if $u.Name}}{{$u.Name}}{{else}}-{{end}}</td>
|
||||
<td class="px-6 py-4">{{$u.Email}}</td>
|
||||
<td class="px-6 py-4">
|
||||
{{if eq $u.Role "admin"}}
|
||||
<span class="rounded-sm bg-red-100 px-2.5 py-0.5 text-xs font-medium text-red-800">admin</span>
|
||||
{{else}}
|
||||
<span class="rounded-sm bg-blue-100 px-2.5 py-0.5 text-xs font-medium text-blue-800">user</span>
|
||||
{{end}}
|
||||
</td>
|
||||
<td class="px-6 py-4">
|
||||
<button type="button" class="rounded-lg bg-blue-700 px-3 py-2 text-xs font-medium text-white hover:bg-blue-800" data-modal-target="userModal" data-modal-toggle="userModal" hx-get="/admin/users/{{$u.ID}}/modal" hx-target="#userModalContent" hx-swap="innerHTML">Apri</button>
|
||||
</td>
|
||||
</tr>
|
||||
{{else}}
|
||||
<tr><td colspan="5" style="padding:12px;">Nessun utente trovato.</td></tr>
|
||||
<tr class="bg-white">
|
||||
<td colspan="5" class="px-6 py-4 text-center text-gray-500">Nessun utente trovato.</td>
|
||||
</tr>
|
||||
{{end}}
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
|
||||
<div class="row" style="margin-top:12px;align-items:center;justify-content:space-between;">
|
||||
<div class="muted">Totale: {{$p.Total}} utenti. Pagina {{$p.Page}}{{if gt $p.TotalPages 0}} / {{$p.TotalPages}}{{end}}</div>
|
||||
<div class="row">
|
||||
<button {{if not $p.HasPrev}}disabled{{end}} hx-get="/admin/users/table?q={{$p.Q}}&sort={{$p.Sort}}&dir={{$p.Dir}}&page={{$p.PrevPage}}&pageSize={{$p.PageSize}}" hx-target="#usersTableContainer" hx-swap="innerHTML">Prev</button>
|
||||
<button {{if not $p.HasNext}}disabled{{end}} hx-get="/admin/users/table?q={{$p.Q}}&sort={{$p.Sort}}&dir={{$p.Dir}}&page={{$p.NextPage}}&pageSize={{$p.PageSize}}" hx-target="#usersTableContainer" hx-swap="innerHTML">Next</button>
|
||||
<div class="mt-4 flex flex-wrap items-center justify-between gap-3">
|
||||
<div class="text-sm text-gray-600">Totale: {{$p.Total}} utenti. Pagina {{$p.Page}}{{if gt $p.TotalPages 0}} / {{$p.TotalPages}}{{end}}</div>
|
||||
<div class="inline-flex gap-2">
|
||||
<button type="button" class="rounded-lg border border-gray-300 bg-white px-4 py-2 text-sm font-medium text-gray-900 hover:bg-gray-100 disabled:cursor-not-allowed disabled:opacity-50" {{if not $p.HasPrev}}disabled{{end}} hx-get="/admin/users/table?q={{$p.Q}}&sort={{$p.Sort}}&dir={{$p.Dir}}&page={{$p.PrevPage}}&pageSize={{$p.PageSize}}" hx-target="#usersTableContainer" hx-swap="innerHTML">Prev</button>
|
||||
<button type="button" class="rounded-lg border border-gray-300 bg-white px-4 py-2 text-sm font-medium text-gray-900 hover:bg-gray-100 disabled:cursor-not-allowed disabled:opacity-50" {{if not $p.HasNext}}disabled{{end}} hx-get="/admin/users/table?q={{$p.Q}}&sort={{$p.Sort}}&dir={{$p.Dir}}&page={{$p.NextPage}}&pageSize={{$p.PageSize}}" hx-target="#usersTableContainer" hx-swap="innerHTML">Next</button>
|
||||
</div>
|
||||
</div>
|
||||
{{end}}
|
||||
|
|
|
|||
|
|
@ -1,36 +1,74 @@
|
|||
{{define "content"}}
|
||||
<h1>Users</h1>
|
||||
<p class="muted">Ricerca, ordinamento e paging server-side via HTMX.</p>
|
||||
<section class="space-y-6">
|
||||
<div class="flex flex-wrap items-center justify-between gap-3">
|
||||
<div>
|
||||
<h1 class="text-3xl font-bold text-gray-900">Users</h1>
|
||||
<p class="text-gray-600">Ricerca, ordinamento e paging server-side via HTMX.</p>
|
||||
</div>
|
||||
<button type="button" data-modal-target="newUserModal" data-modal-toggle="newUserModal" class="inline-flex items-center rounded-lg bg-blue-700 px-5 py-2.5 text-sm font-medium text-white hover:bg-blue-800 focus:outline-none focus:ring-4 focus:ring-blue-300">
|
||||
Nuovo Utente
|
||||
</button>
|
||||
</div>
|
||||
|
||||
<form id="usersFilters" class="row" hx-get="/admin/users/table" hx-target="#usersTableContainer" hx-swap="innerHTML">
|
||||
<input type="text" name="q" placeholder="Cerca nome o email" value="{{.PageData.Q}}">
|
||||
<input type="number" name="pageSize" min="1" max="100" value="{{.PageData.PageSize}}" style="max-width:120px;">
|
||||
<form id="usersFilters" class="grid gap-3 rounded-lg border border-gray-200 bg-white p-4 shadow-sm md:grid-cols-4" hx-get="/admin/users/table" hx-target="#usersTableContainer" hx-swap="innerHTML">
|
||||
<div class="md:col-span-2">
|
||||
<label for="users-q" class="mb-2 block text-sm font-medium text-gray-900">Search</label>
|
||||
<input id="users-q" type="text" name="q" placeholder="Cerca nome o email" value="{{.PageData.Q}}" class="block w-full rounded-lg border border-gray-300 bg-gray-50 p-2.5 text-sm text-gray-900 focus:border-blue-500 focus:ring-blue-500">
|
||||
</div>
|
||||
<div>
|
||||
<label for="users-size" class="mb-2 block text-sm font-medium text-gray-900">Page size</label>
|
||||
<input id="users-size" type="number" name="pageSize" min="1" max="100" value="{{.PageData.PageSize}}" class="block w-full rounded-lg border border-gray-300 bg-gray-50 p-2.5 text-sm text-gray-900 focus:border-blue-500 focus:ring-blue-500">
|
||||
</div>
|
||||
<div class="flex items-end">
|
||||
<button type="submit" class="w-full rounded-lg bg-blue-700 px-5 py-2.5 text-sm font-medium text-white hover:bg-blue-800 focus:outline-none focus:ring-4 focus:ring-blue-300">Cerca</button>
|
||||
</div>
|
||||
<input type="hidden" name="sort" value="{{.PageData.Sort}}">
|
||||
<input type="hidden" name="dir" value="{{.PageData.Dir}}">
|
||||
<input type="hidden" name="page" value="1">
|
||||
<button type="submit">Cerca</button>
|
||||
</form>
|
||||
|
||||
<div id="usersTableContainer" hx-get="/admin/users/table?q={{.PageData.Q}}&sort={{.PageData.Sort}}&dir={{.PageData.Dir}}&page={{.PageData.Page}}&pageSize={{.PageData.PageSize}}" hx-trigger="load" hx-swap="innerHTML">
|
||||
<div id="usersTableContainer" class="rounded-lg border border-gray-200 bg-white p-2 shadow-sm md:p-4" hx-get="/admin/users/table?q={{.PageData.Q}}&sort={{.PageData.Sort}}&dir={{.PageData.Dir}}&page={{.PageData.Page}}&pageSize={{.PageData.PageSize}}" hx-trigger="load" hx-swap="innerHTML">
|
||||
{{template "users_table" .}}
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<ui-modal id="userModal" title="Dettaglio utente">
|
||||
<div
|
||||
id="userModalContent"
|
||||
hx-on:htmx:after-swap="document.getElementById('userModal').setAttribute('open','')"
|
||||
></div>
|
||||
</ui-modal>
|
||||
<div id="userModal" tabindex="-1" aria-hidden="true" class="fixed left-0 right-0 top-0 z-50 hidden h-[calc(100%-1rem)] max-h-full w-full items-center justify-center overflow-y-auto overflow-x-hidden p-4 md:inset-0">
|
||||
<div class="relative max-h-full w-full max-w-2xl">
|
||||
<div class="relative rounded-lg bg-white shadow-sm">
|
||||
<div class="flex items-start justify-between rounded-t border-b p-4 md:p-5">
|
||||
<h3 class="text-xl font-semibold text-gray-900">Dettaglio utente</h3>
|
||||
<button type="button" class="ms-auto inline-flex h-8 w-8 items-center justify-center rounded-lg bg-transparent text-sm text-gray-400 hover:bg-gray-200 hover:text-gray-900" data-modal-hide="userModal" aria-label="Close modal">
|
||||
<span class="sr-only">Close modal</span>
|
||||
<svg class="h-3 w-3" aria-hidden="true" xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 14 14">
|
||||
<path stroke="currentColor" stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="m1 1 6 6m0 0 6 6M7 7l6-6M7 7l-6 6" />
|
||||
</svg>
|
||||
</button>
|
||||
</div>
|
||||
<div id="userModalContent" class="space-y-4 p-4 md:p-5"></div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<script>
|
||||
(function () {
|
||||
var modal = document.getElementById('userModal');
|
||||
var content = document.getElementById('userModalContent');
|
||||
if (!modal || !content || modal.dataset.closeBound === '1') return;
|
||||
modal.dataset.closeBound = '1';
|
||||
modal.addEventListener('ui:close', function () {
|
||||
content.innerHTML = '';
|
||||
});
|
||||
})();
|
||||
</script>
|
||||
<div id="newUserModal" tabindex="-1" aria-hidden="true" class="fixed left-0 right-0 top-0 z-50 hidden h-[calc(100%-1rem)] max-h-full w-full items-center justify-center overflow-y-auto overflow-x-hidden p-4 md:inset-0">
|
||||
<div class="relative max-h-full w-full max-w-xl">
|
||||
<div class="relative rounded-lg bg-white shadow-sm">
|
||||
<div class="flex items-start justify-between rounded-t border-b p-4 md:p-5">
|
||||
<h3 class="text-xl font-semibold text-gray-900">Nuovo utente</h3>
|
||||
<button type="button" class="ms-auto inline-flex h-8 w-8 items-center justify-center rounded-lg bg-transparent text-sm text-gray-400 hover:bg-gray-200 hover:text-gray-900" data-modal-hide="newUserModal" aria-label="Close modal">
|
||||
<span class="sr-only">Close modal</span>
|
||||
<svg class="h-3 w-3" aria-hidden="true" xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 14 14">
|
||||
<path stroke="currentColor" stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="m1 1 6 6m0 0 6 6M7 7l6-6M7 7l-6 6" />
|
||||
</svg>
|
||||
</button>
|
||||
</div>
|
||||
<div class="space-y-4 p-4 md:p-5">
|
||||
<p class="text-sm text-gray-600">Placeholder UI Flowbite. La creazione utente può essere collegata a una route backend quando disponibile.</p>
|
||||
<div>
|
||||
<label for="new-user-email" class="mb-2 block text-sm font-medium text-gray-900">Email</label>
|
||||
<input id="new-user-email" type="email" class="block w-full rounded-lg border border-gray-300 bg-gray-50 p-2.5 text-sm text-gray-900" placeholder="name@company.com" disabled>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
{{end}}
|
||||
|
|
|
|||
|
|
@ -0,0 +1,13 @@
|
|||
{{define "flowbite_collapse"}}
|
||||
<button data-collapse-toggle="collapseExample" type="button" class="flex w-full items-center justify-between rounded-lg bg-gray-100 px-5 py-2.5 text-left text-sm font-medium text-gray-500 hover:bg-gray-200" aria-expanded="false" aria-controls="collapseExample">
|
||||
<span>Toggle collapse</span>
|
||||
<svg data-accordion-icon class="h-3 w-3 shrink-0" xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 10 6">
|
||||
<path stroke="currentColor" stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M9 5 5 1 1 5"/>
|
||||
</svg>
|
||||
</button>
|
||||
<div id="collapseExample" class="hidden">
|
||||
<div class="rounded-b-lg border border-gray-200 p-5">
|
||||
<p class="text-sm text-gray-500">Collapsed content.</p>
|
||||
</div>
|
||||
</div>
|
||||
{{end}}
|
||||
|
|
@ -0,0 +1,16 @@
|
|||
{{define "flowbite_dropdown"}}
|
||||
<button id="dropdownDefaultButton" data-dropdown-toggle="dropdown" class="inline-flex items-center rounded-lg bg-blue-700 px-5 py-2.5 text-center text-sm font-medium text-white hover:bg-blue-800" type="button">
|
||||
Dropdown
|
||||
<svg class="ms-3 h-2.5 w-2.5" aria-hidden="true" xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 10 6">
|
||||
<path stroke="currentColor" stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="m1 1 4 4 4-4"/>
|
||||
</svg>
|
||||
</button>
|
||||
|
||||
<div id="dropdown" class="z-10 hidden w-44 divide-y divide-gray-100 rounded-lg bg-white shadow-sm">
|
||||
<ul class="py-2 text-sm text-gray-700" aria-labelledby="dropdownDefaultButton">
|
||||
<li><a href="#" class="block px-4 py-2 hover:bg-gray-100">Dashboard</a></li>
|
||||
<li><a href="#" class="block px-4 py-2 hover:bg-gray-100">Settings</a></li>
|
||||
<li><a href="#" class="block px-4 py-2 hover:bg-gray-100">Sign out</a></li>
|
||||
</ul>
|
||||
</div>
|
||||
{{end}}
|
||||
|
|
@ -0,0 +1,17 @@
|
|||
{{define "flowbite_flash"}}
|
||||
{{if .FlashSuccess}}
|
||||
<div class="mb-4 flex items-center rounded-lg border border-green-200 bg-green-50 p-4 text-green-800" role="alert">
|
||||
<span class="text-sm font-medium">{{.FlashSuccess}}</span>
|
||||
</div>
|
||||
{{end}}
|
||||
{{if .FlashError}}
|
||||
<div class="mb-4 flex items-center rounded-lg border border-red-200 bg-red-50 p-4 text-red-800" role="alert">
|
||||
<span class="text-sm font-medium">{{.FlashError}}</span>
|
||||
</div>
|
||||
{{end}}
|
||||
{{if .FlashWarning}}
|
||||
<div class="mb-4 flex items-center rounded-lg border border-yellow-200 bg-yellow-50 p-4 text-yellow-800" role="alert">
|
||||
<span class="text-sm font-medium">{{.FlashWarning}}</span>
|
||||
</div>
|
||||
{{end}}
|
||||
{{end}}
|
||||
|
|
@ -0,0 +1,25 @@
|
|||
{{define "flowbite_modal"}}
|
||||
<div id="{{.ModalID}}" tabindex="-1" aria-hidden="true" class="fixed left-0 right-0 top-0 z-50 hidden h-[calc(100%-1rem)] max-h-full w-full items-center justify-center overflow-y-auto overflow-x-hidden p-4 md:inset-0">
|
||||
<div class="relative max-h-full w-full max-w-2xl">
|
||||
<div class="relative rounded-lg bg-white shadow-sm">
|
||||
<div class="flex items-start justify-between rounded-t border-b p-4 md:p-5">
|
||||
<h3 class="text-xl font-semibold text-gray-900">{{.ModalTitle}}</h3>
|
||||
<button type="button" class="ms-auto inline-flex h-8 w-8 items-center justify-center rounded-lg bg-transparent text-sm text-gray-400 hover:bg-gray-200 hover:text-gray-900" data-modal-hide="{{.ModalID}}" aria-label="Close modal">
|
||||
<span class="sr-only">Close modal</span>
|
||||
<svg class="h-3 w-3" aria-hidden="true" xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 14 14">
|
||||
<path stroke="currentColor" stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="m1 1 6 6m0 0 6 6M7 7l6-6M7 7l-6 6"/>
|
||||
</svg>
|
||||
</button>
|
||||
</div>
|
||||
<div class="space-y-4 p-4 md:p-5">
|
||||
{{template .ModalBodyTemplate .}}
|
||||
</div>
|
||||
{{if .ModalFooterTemplate}}
|
||||
<div class="flex items-center rounded-b border-t border-gray-200 p-4 md:p-5">
|
||||
{{template .ModalFooterTemplate .}}
|
||||
</div>
|
||||
{{end}}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
{{end}}
|
||||
|
|
@ -0,0 +1,21 @@
|
|||
{{define "flowbite_navbar"}}
|
||||
<nav class="border-gray-200 bg-white">
|
||||
<div class="mx-auto flex max-w-screen-xl flex-wrap items-center justify-between p-4">
|
||||
<a href="#" class="flex items-center space-x-3 rtl:space-x-reverse">
|
||||
<span class="self-center whitespace-nowrap text-2xl font-semibold">Trustcontact</span>
|
||||
</a>
|
||||
<button data-collapse-toggle="navbar-default" type="button" class="inline-flex h-10 w-10 items-center justify-center rounded-lg p-2 text-sm text-gray-500 hover:bg-gray-100 focus:outline-none focus:ring-2 focus:ring-gray-200 md:hidden" aria-controls="navbar-default" aria-expanded="false">
|
||||
<span class="sr-only">Open main menu</span>
|
||||
<svg class="h-5 w-5" aria-hidden="true" xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 17 14">
|
||||
<path stroke="currentColor" stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M1 1h15M1 7h15M1 13h15"/>
|
||||
</svg>
|
||||
</button>
|
||||
<div class="hidden w-full md:block md:w-auto" id="navbar-default">
|
||||
<ul class="mt-4 flex flex-col rounded-lg border border-gray-100 bg-gray-50 p-4 font-medium md:mt-0 md:flex-row md:space-x-8 md:border-0 md:bg-white md:p-0 rtl:space-x-reverse">
|
||||
<li><a href="#" class="block rounded-sm bg-blue-700 px-3 py-2 text-white md:bg-transparent md:p-0 md:text-blue-700" aria-current="page">Home</a></li>
|
||||
<li><a href="#" class="block rounded-sm px-3 py-2 text-gray-900 hover:bg-gray-100 md:p-0 md:hover:bg-transparent md:hover:text-blue-700">About</a></li>
|
||||
</ul>
|
||||
</div>
|
||||
</div>
|
||||
</nav>
|
||||
{{end}}
|
||||
|
|
@ -0,0 +1,20 @@
|
|||
{{define "flowbite_tabs"}}
|
||||
<div class="mb-4 border-b border-gray-200">
|
||||
<ul class="-mb-px flex flex-wrap text-center text-sm font-medium" id="default-tab" data-tabs-toggle="#default-tab-content" role="tablist">
|
||||
<li class="me-2" role="presentation">
|
||||
<button class="inline-block rounded-t-lg border-b-2 p-4" id="profile-tab" data-tabs-target="#profile" type="button" role="tab" aria-controls="profile" aria-selected="false">Profile</button>
|
||||
</li>
|
||||
<li class="me-2" role="presentation">
|
||||
<button class="inline-block rounded-t-lg border-b-2 p-4" id="dashboard-tab" data-tabs-target="#dashboard" type="button" role="tab" aria-controls="dashboard" aria-selected="false">Dashboard</button>
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
<div id="default-tab-content">
|
||||
<div class="hidden rounded-lg bg-gray-50 p-4" id="profile" role="tabpanel" aria-labelledby="profile-tab">
|
||||
<p class="text-sm text-gray-500">Profile tab content.</p>
|
||||
</div>
|
||||
<div class="hidden rounded-lg bg-gray-50 p-4" id="dashboard" role="tabpanel" aria-labelledby="dashboard-tab">
|
||||
<p class="text-sm text-gray-500">Dashboard tab content.</p>
|
||||
</div>
|
||||
</div>
|
||||
{{end}}
|
||||
|
|
@ -5,38 +5,85 @@
|
|||
<meta name="viewport" content="width=device-width, initial-scale=1">
|
||||
<title>{{.Title}}</title>
|
||||
<link rel="stylesheet" href="/static/css/app.css?v={{.BuildHash}}">
|
||||
<link rel="stylesheet" href="/static/ui/ui.css?v={{.BuildHash}}">
|
||||
<script src="https://unpkg.com/htmx.org@1.9.12"></script>
|
||||
<script type="module" src="/static/ui/ui.esm.js?v={{.BuildHash}}"></script>
|
||||
<script src="/static/vendor/htmx.min.js"></script>
|
||||
<script src="/static/vendor/flowbite.js"></script>
|
||||
</head>
|
||||
<body>
|
||||
<nav class="relative flex items-center justify-between border-b border-gray-300 bg-white px-6 py-4 transition-all md:px-16 lg:px-24 xl:px-32">
|
||||
<a href="/" class="text-lg font-semibold text-slate-800">Trustcontact</a>
|
||||
<body class="bg-gray-50 text-gray-900 antialiased">
|
||||
<nav class="border-b border-gray-200 bg-white">
|
||||
<div class="mx-auto flex max-w-7xl flex-wrap items-center justify-between p-4">
|
||||
<a href="/" class="flex items-center space-x-3 rtl:space-x-reverse">
|
||||
<span class="self-center whitespace-nowrap text-xl font-semibold">Trustcontact</span>
|
||||
</a>
|
||||
|
||||
<div class="hidden items-center gap-8 sm:flex">
|
||||
{{if and .CurrentUser (eq .CurrentUser.Role "admin")}}
|
||||
<a href="/admin" class="text-slate-700 hover:text-slate-900 {{if eq .NavSection "admin"}}font-semibold{{end}}">Admin</a>
|
||||
<button data-collapse-toggle="navbar-main" type="button" class="inline-flex h-10 w-10 items-center justify-center rounded-lg p-2 text-sm text-gray-500 hover:bg-gray-100 focus:outline-none focus:ring-2 focus:ring-gray-200 md:hidden" aria-controls="navbar-main" aria-expanded="false">
|
||||
<span class="sr-only">Apri menu principale</span>
|
||||
<svg class="h-5 w-5" aria-hidden="true" xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 17 14">
|
||||
<path stroke="currentColor" stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M1 1h15M1 7h15M1 13h15" />
|
||||
</svg>
|
||||
</button>
|
||||
|
||||
<div class="hidden w-full items-center justify-between md:order-1 md:flex md:w-auto" id="navbar-main">
|
||||
<ul class="mt-4 flex flex-col gap-2 rounded-lg border border-gray-100 bg-gray-50 p-4 text-sm font-medium md:mt-0 md:flex-row md:items-center md:gap-1 md:border-0 md:bg-transparent md:p-0">
|
||||
{{if .CurrentUser}}
|
||||
<li>
|
||||
<a href="/welcome" class="block rounded-lg px-3 py-2 {{if eq .NavSection "private"}}bg-blue-100 text-blue-700{{else}}text-gray-700 hover:bg-gray-100{{end}}">Dashboard</a>
|
||||
</li>
|
||||
<li>
|
||||
<a href="/admin/users" class="block rounded-lg px-3 py-2 {{if eq .NavSection "users"}}bg-blue-100 text-blue-700{{else}}text-gray-700 hover:bg-gray-100{{end}}">Users</a>
|
||||
</li>
|
||||
{{if eq .CurrentUser.Role "admin"}}
|
||||
<li>
|
||||
<a href="/admin" class="block rounded-lg px-3 py-2 {{if eq .NavSection "admin"}}bg-blue-100 text-blue-700{{else}}text-gray-700 hover:bg-gray-100{{end}}">Admin</a>
|
||||
</li>
|
||||
{{end}}
|
||||
{{else}}
|
||||
<li>
|
||||
<a href="/login" class="block rounded-lg px-3 py-2 {{if eq .NavSection "login"}}bg-blue-100 text-blue-700{{else}}text-gray-700 hover:bg-gray-100{{end}}">Login</a>
|
||||
</li>
|
||||
<li>
|
||||
<a href="/signup" class="block rounded-lg px-3 py-2 {{if eq .NavSection "signup"}}bg-blue-100 text-blue-700{{else}}text-gray-700 hover:bg-gray-100{{end}}">Signup</a>
|
||||
</li>
|
||||
{{end}}
|
||||
</ul>
|
||||
|
||||
{{if .CurrentUser}}
|
||||
<form action="/logout" method="post">
|
||||
<button type="submit" class="cursor-pointer rounded-full bg-indigo-500 px-8 py-2 text-white transition hover:bg-indigo-600">
|
||||
Logout
|
||||
<div class="relative mt-4 md:mt-0 md:ms-4">
|
||||
<button type="button" class="flex items-center rounded-full bg-gray-800 text-sm focus:ring-4 focus:ring-gray-300 md:me-0" id="user-menu-button" aria-expanded="false" data-dropdown-toggle="user-dropdown" data-dropdown-placement="bottom">
|
||||
<span class="sr-only">Apri menu utente</span>
|
||||
<span class="inline-flex h-9 w-9 items-center justify-center rounded-full bg-blue-600 font-semibold text-white">
|
||||
{{if .CurrentUser.Name}}{{printf "%.1s" .CurrentUser.Name}}{{else}}{{printf "%.1s" .CurrentUser.Email}}{{end}}
|
||||
</span>
|
||||
</button>
|
||||
<div class="z-50 my-4 hidden w-56 list-none divide-y divide-gray-100 rounded-lg bg-white text-base shadow-sm" id="user-dropdown">
|
||||
<div class="px-4 py-3">
|
||||
<span class="block truncate text-sm text-gray-900">{{if .CurrentUser.Name}}{{.CurrentUser.Name}}{{else}}Utente{{end}}</span>
|
||||
<span class="block truncate text-sm text-gray-500">{{.CurrentUser.Email}}</span>
|
||||
</div>
|
||||
<ul class="py-2" aria-labelledby="user-menu-button">
|
||||
<li><a href="/welcome" class="block px-4 py-2 text-sm text-gray-700 hover:bg-gray-100">Dashboard</a></li>
|
||||
<li><a href="/admin/users" class="block px-4 py-2 text-sm text-gray-700 hover:bg-gray-100">Users</a></li>
|
||||
</ul>
|
||||
<div class="py-2">
|
||||
<form action="/logout" method="post" class="px-2">
|
||||
<button type="submit" class="block w-full rounded-lg px-2 py-2 text-left text-sm text-red-700 hover:bg-red-50">Logout</button>
|
||||
</form>
|
||||
{{else}}
|
||||
<a href="/login" class="cursor-pointer rounded-full bg-indigo-500 px-8 py-2 text-white transition hover:bg-indigo-600">
|
||||
Login
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
{{end}}
|
||||
</div>
|
||||
</div>
|
||||
</nav>
|
||||
|
||||
<div class="mx-auto my-5 max-w-5xl px-4">
|
||||
<main class="mx-auto max-w-7xl p-6">
|
||||
{{template "_flash.html" .}}
|
||||
<div class="rounded-xl bg-white p-5 shadow-sm">
|
||||
{{template "content" .}}
|
||||
</main>
|
||||
|
||||
<footer class="border-t border-gray-200 bg-white">
|
||||
<div class="mx-auto max-w-7xl px-6 py-4 text-sm text-gray-500">
|
||||
Trustcontact
|
||||
</div>
|
||||
</div>
|
||||
</footer>
|
||||
</body>
|
||||
</html>
|
||||
|
|
|
|||
|
|
@ -0,0 +1,49 @@
|
|||
{{define "content"}}
|
||||
<section class="space-y-6">
|
||||
<div class="flex items-center justify-between">
|
||||
<h1 class="text-3xl font-bold text-gray-900">Users</h1>
|
||||
<button type="button" class="rounded-lg bg-blue-700 px-5 py-2.5 text-sm font-medium text-white hover:bg-blue-800" data-modal-target="privateUserModal" data-modal-toggle="privateUserModal">Nuovo Utente</button>
|
||||
</div>
|
||||
|
||||
<div class="rounded-lg border border-gray-200 bg-white p-4 shadow-sm">
|
||||
<label for="private-users-search" class="mb-2 block text-sm font-medium text-gray-900">Search</label>
|
||||
<input id="private-users-search" type="text" class="block w-full rounded-lg border border-gray-300 bg-gray-50 p-2.5 text-sm text-gray-900" placeholder="Cerca utenti">
|
||||
</div>
|
||||
|
||||
<div class="relative overflow-x-auto rounded-lg border border-gray-200 bg-white shadow-sm">
|
||||
<table class="w-full text-left text-sm text-gray-500">
|
||||
<thead class="bg-gray-50 text-xs uppercase text-gray-700">
|
||||
<tr>
|
||||
<th class="px-6 py-3">Name</th>
|
||||
<th class="px-6 py-3">Email</th>
|
||||
<th class="px-6 py-3">Role</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
<tr class="border-b bg-white hover:bg-gray-50">
|
||||
<td class="px-6 py-4">-</td>
|
||||
<td class="px-6 py-4">-</td>
|
||||
<td class="px-6 py-4"><span class="rounded-sm bg-blue-100 px-2.5 py-0.5 text-xs font-medium text-blue-800">user</span></td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<div id="privateUserModal" tabindex="-1" aria-hidden="true" class="fixed left-0 right-0 top-0 z-50 hidden h-[calc(100%-1rem)] max-h-full w-full items-center justify-center overflow-y-auto overflow-x-hidden p-4 md:inset-0">
|
||||
<div class="relative max-h-full w-full max-w-xl">
|
||||
<div class="relative rounded-lg bg-white shadow-sm">
|
||||
<div class="flex items-start justify-between rounded-t border-b p-4 md:p-5">
|
||||
<h3 class="text-xl font-semibold text-gray-900">Nuovo utente</h3>
|
||||
<button type="button" class="ms-auto inline-flex h-8 w-8 items-center justify-center rounded-lg bg-transparent text-sm text-gray-400 hover:bg-gray-200 hover:text-gray-900" data-modal-hide="privateUserModal" aria-label="Close modal">
|
||||
<span class="sr-only">Close modal</span>
|
||||
<svg class="h-3 w-3" aria-hidden="true" xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 14 14"><path stroke="currentColor" stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="m1 1 6 6m0 0 6 6M7 7l6-6M7 7l-6 6" /></svg>
|
||||
</button>
|
||||
</div>
|
||||
<div class="p-4 md:p-5">
|
||||
<p class="text-sm text-gray-600">Template Flowbite pronto per integrazione backend.</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
{{end}}
|
||||
|
|
@ -1,16 +1,20 @@
|
|||
{{define "content"}}
|
||||
<div class="space-y-5">
|
||||
<div>
|
||||
<h1 class="text-2xl font-semibold">Welcome</h1>
|
||||
<section class="grid gap-6 md:grid-cols-3">
|
||||
<article class="rounded-lg border border-gray-200 bg-white p-6 shadow-sm md:col-span-2">
|
||||
<h1 class="mb-2 text-2xl font-bold text-gray-900">Dashboard</h1>
|
||||
{{if .CurrentUser}}
|
||||
<p class="muted">Bentornato {{if .CurrentUser.Name}}{{.CurrentUser.Name}}{{else}}{{.CurrentUser.Email}}{{end}}.</p>
|
||||
<p class="text-gray-600">Bentornato {{if .CurrentUser.Name}}{{.CurrentUser.Name}}{{else}}{{.CurrentUser.Email}}{{end}}.</p>
|
||||
{{else}}
|
||||
<p class="muted">Benvenuto.</p>
|
||||
<p class="text-gray-600">Benvenuto.</p>
|
||||
{{end}}
|
||||
</div>
|
||||
</article>
|
||||
|
||||
{{if and .CurrentUser (ne .CurrentUser.Role "admin")}}
|
||||
<p class="muted">Non hai privilegi admin.</p>
|
||||
{{end}}
|
||||
<article class="rounded-lg border border-gray-200 bg-white p-6 shadow-sm">
|
||||
<h2 class="mb-3 text-lg font-semibold text-gray-900">Quick Links</h2>
|
||||
<div class="space-y-2">
|
||||
<a href="/welcome" class="block rounded-lg px-3 py-2 text-sm text-gray-700 hover:bg-gray-100">Dashboard</a>
|
||||
<a href="/admin/users" class="block rounded-lg px-3 py-2 text-sm text-gray-700 hover:bg-gray-100">Users</a>
|
||||
</div>
|
||||
</article>
|
||||
</section>
|
||||
{{end}}
|
||||
|
|
|
|||
|
|
@ -1,6 +1,26 @@
|
|||
{{define "_flash.html"}}
|
||||
{{if .FlashSuccess}}
|
||||
<div style="background:#dcfce7;color:#166534;padding:12px;border-radius:8px;margin:0 0 12px;">{{.FlashSuccess}}</div>
|
||||
<div class="mb-4 flex items-center rounded-lg border border-green-200 bg-green-50 p-4 text-green-800" role="alert">
|
||||
<svg class="me-3 inline h-4 w-4 shrink-0" aria-hidden="true" xmlns="http://www.w3.org/2000/svg" fill="currentColor" viewBox="0 0 20 20">
|
||||
<path d="M10 0a10 10 0 1 0 10 10A10 10 0 0 0 10 0Zm3.707 8.707-4 4a1 1 0 0 1-1.414 0l-2-2 1.414-1.414L9 10.586l3.293-3.293Z"/>
|
||||
</svg>
|
||||
<span class="text-sm font-medium">{{.FlashSuccess}}</span>
|
||||
</div>
|
||||
{{end}}
|
||||
{{if .FlashError}}
|
||||
<div style="background:#fee2e2;color:#991b1b;padding:12px;border-radius:8px;margin:0 0 12px;">{{.FlashError}}</div>
|
||||
<div class="mb-4 flex items-center rounded-lg border border-red-200 bg-red-50 p-4 text-red-800" role="alert">
|
||||
<svg class="me-3 inline h-4 w-4 shrink-0" aria-hidden="true" xmlns="http://www.w3.org/2000/svg" fill="currentColor" viewBox="0 0 20 20">
|
||||
<path d="M10 0a10 10 0 1 0 10 10A10 10 0 0 0 10 0Zm1 14H9v-2h2Zm0-4H9V5h2Z"/>
|
||||
</svg>
|
||||
<span class="text-sm font-medium">{{.FlashError}}</span>
|
||||
</div>
|
||||
{{end}}
|
||||
{{if .FlashWarning}}
|
||||
<div class="mb-4 flex items-center rounded-lg border border-yellow-200 bg-yellow-50 p-4 text-yellow-800" role="alert">
|
||||
<svg class="me-3 inline h-4 w-4 shrink-0" aria-hidden="true" xmlns="http://www.w3.org/2000/svg" fill="currentColor" viewBox="0 0 20 20">
|
||||
<path d="M8.257 3.099c.765-1.36 2.722-1.36 3.486 0l6.518 11.596c.75 1.334-.213 2.99-1.742 2.99H3.48c-1.53 0-2.492-1.656-1.743-2.99L8.257 3.1ZM11 13H9v2h2v-2Zm0-6H9v5h2V7Z"/>
|
||||
</svg>
|
||||
<span class="text-sm font-medium">{{.FlashWarning}}</span>
|
||||
</div>
|
||||
{{end}}
|
||||
{{end}}
|
||||
|
|
|
|||
|
|
@ -1,25 +1,19 @@
|
|||
{{define "content"}}
|
||||
<div class="mx-auto w-full max-w-96 rounded-xl border border-gray-200 px-6 py-8">
|
||||
<div class="mx-auto mb-6 flex h-16 w-16 items-center justify-center rounded-full bg-amber-100">
|
||||
<svg class="h-8 w-8 text-amber-500" fill="none" stroke="currentColor" viewBox="0 0 24 24" xmlns="http://www.w3.org/2000/svg">
|
||||
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M12 11c0 1.657-1.343 3-3 3S6 12.657 6 11s1.343-3 3-3 3 1.343 3 3zm0 0V9a4 4 0 118 0v2m-8 0h8m-8 0H4m16 0v8a2 2 0 01-2 2H6a2 2 0 01-2-2v-8"></path>
|
||||
</svg>
|
||||
<div class="flex min-h-screen items-center justify-center">
|
||||
<div class="w-full max-w-md rounded-lg border border-gray-200 bg-white p-6 shadow-sm md:p-8">
|
||||
<h1 class="mb-1 text-2xl font-bold text-gray-900">Forgot Password</h1>
|
||||
<p class="mb-6 text-sm text-gray-500">Inserisci la tua email per ricevere il link di reset.</p>
|
||||
|
||||
<form action="/forgot-password" method="post" class="space-y-5">
|
||||
<div>
|
||||
<label for="forgot-email" class="mb-2 block text-sm font-medium text-gray-900">Email</label>
|
||||
<input id="forgot-email" type="email" name="email" value="{{.Email}}" class="block w-full rounded-lg border border-gray-300 bg-gray-50 p-2.5 text-sm text-gray-900 focus:border-blue-500 focus:ring-blue-500" required />
|
||||
</div>
|
||||
|
||||
<h3 class="mb-3 text-center text-xl font-bold text-gray-800">Forgot Password</h3>
|
||||
<p class="mb-6 text-center text-sm text-gray-500">Inserisci la tua email. Se l'account esiste e risulta verificato, invieremo un link di reset.</p>
|
||||
<button type="submit" class="w-full rounded-lg bg-blue-700 px-5 py-2.5 text-center text-sm font-medium text-white hover:bg-blue-800 focus:outline-none focus:ring-4 focus:ring-blue-300">Invia link reset</button>
|
||||
|
||||
<form action="/forgot-password" method="post">
|
||||
<div class="mb-6">
|
||||
<label class="mb-1 block text-sm font-medium text-gray-700">Email</label>
|
||||
<input type="email" name="email" value="{{.Email}}" class="w-full rounded-lg border border-gray-300 px-3 py-2 transition outline-none focus:border-amber-500 focus:ring-2 focus:ring-amber-500" required />
|
||||
</div>
|
||||
|
||||
<button type="submit" class="w-full rounded-lg bg-amber-500 px-4 py-2 font-medium text-white transition duration-300 hover:bg-amber-600">Invia link reset</button>
|
||||
|
||||
<div class="mt-4 text-center">
|
||||
<a href="/login" class="text-sm text-slate-600 hover:text-slate-800">Torna al login</a>
|
||||
</div>
|
||||
<p class="text-center text-sm text-gray-600"><a href="/login" class="text-blue-700 hover:underline">Torna al login</a></p>
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
{{end}}
|
||||
|
|
|
|||
|
|
@ -1,10 +1,10 @@
|
|||
{{define "content"}}
|
||||
<div class="space-y-3">
|
||||
<h1 class="text-2xl font-semibold">Trustcontact</h1>
|
||||
<p class="muted">Accedi o registrati per continuare.</p>
|
||||
<div class="row">
|
||||
<a class="rounded-lg border border-slate-300 px-4 py-2 hover:bg-slate-50" href="/login">Accedi</a>
|
||||
<a class="rounded-lg border border-slate-300 px-4 py-2 hover:bg-slate-50" href="/signup">Registrati</a>
|
||||
</div>
|
||||
<section class="rounded-lg border border-gray-200 bg-white p-8 shadow-sm">
|
||||
<h1 class="mb-2 text-3xl font-bold text-gray-900">Trustcontact</h1>
|
||||
<p class="mb-6 text-gray-600">Accedi o registrati per continuare.</p>
|
||||
<div class="flex flex-wrap gap-3">
|
||||
<a class="rounded-lg bg-blue-700 px-5 py-2.5 text-sm font-medium text-white hover:bg-blue-800" href="/login">Accedi</a>
|
||||
<a class="rounded-lg border border-gray-300 bg-white px-5 py-2.5 text-sm font-medium text-gray-900 hover:bg-gray-100" href="/signup">Registrati</a>
|
||||
</div>
|
||||
</section>
|
||||
{{end}}
|
||||
|
|
|
|||
|
|
@ -1,32 +1,27 @@
|
|||
{{define "content"}}
|
||||
<div class="mx-auto w-full max-w-96 rounded-xl border border-gray-200 px-6 py-8">
|
||||
<div class="mx-auto mb-6 flex h-16 w-16 items-center justify-center rounded-full bg-blue-100">
|
||||
<svg class="h-8 w-8 text-blue-500" fill="none" stroke="currentColor" viewBox="0 0 24 24" xmlns="http://www.w3.org/2000/svg">
|
||||
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M12 15v2m-6 4h12a2 2 0 002-2v-6a2 2 0 00-2-2H6a2 2 0 00-2 2v6a2 2 0 002 2zm10-10V7a4 4 0 00-8 0v4h8z"></path>
|
||||
</svg>
|
||||
<div class="flex min-h-screen items-center justify-center">
|
||||
<div class="w-full max-w-md rounded-lg border border-gray-200 bg-white p-6 shadow-sm md:p-8">
|
||||
<h1 class="mb-1 text-2xl font-bold text-gray-900">Login</h1>
|
||||
<p class="mb-6 text-sm text-gray-500">Accedi al tuo account.</p>
|
||||
|
||||
<form action="/login" method="post" class="space-y-5">
|
||||
<div>
|
||||
<label for="email" class="mb-2 block text-sm font-medium text-gray-900">Email</label>
|
||||
<input id="email" type="text" name="email" value="{{.Email}}" class="block w-full rounded-lg border border-gray-300 bg-gray-50 p-2.5 text-sm text-gray-900 focus:border-blue-500 focus:ring-blue-500" required />
|
||||
</div>
|
||||
|
||||
<h3 class="mb-6 text-center text-xl font-bold text-gray-800">Quick Login</h3>
|
||||
|
||||
<form action="/login" method="post">
|
||||
<div class="mb-4">
|
||||
<label class="mb-1 block text-sm font-medium text-gray-700">Email or Patient ID</label>
|
||||
<input type="text" name="email" value="{{.Email}}" class="w-full rounded-lg border border-gray-300 px-3 py-2 transition outline-none focus:border-blue-500 focus:ring-2 focus:ring-blue-500" required />
|
||||
<div>
|
||||
<label for="password" class="mb-2 block text-sm font-medium text-gray-900">Password</label>
|
||||
<input id="password" type="password" name="password" class="block w-full rounded-lg border border-gray-300 bg-gray-50 p-2.5 text-sm text-gray-900 focus:border-blue-500 focus:ring-blue-500" required />
|
||||
</div>
|
||||
|
||||
<div class="mb-6">
|
||||
<label class="mb-1 block text-sm font-medium text-gray-700">Password</label>
|
||||
<input type="password" name="password" class="w-full rounded-lg border border-gray-300 px-3 py-2 transition outline-none focus:border-blue-500 focus:ring-2 focus:ring-blue-500" required />
|
||||
</div>
|
||||
<button type="submit" class="w-full rounded-lg bg-blue-700 px-5 py-2.5 text-center text-sm font-medium text-white hover:bg-blue-800 focus:outline-none focus:ring-4 focus:ring-blue-300">Sign in</button>
|
||||
|
||||
<button type="submit" class="w-full rounded-lg bg-blue-500 px-4 py-2 font-medium text-white transition duration-300 hover:bg-blue-600">Sign In</button>
|
||||
|
||||
<div class="mt-4 text-center">
|
||||
<a href="/forgot-password" class="text-sm text-blue-500 hover:text-blue-600">Forgot Password?</a>
|
||||
</div>
|
||||
<div class="mt-3 text-center">
|
||||
<a href="/signup" class="text-sm text-slate-600 hover:text-slate-800">Non hai un account? Registrati</a>
|
||||
<div class="flex items-center justify-between text-sm">
|
||||
<a href="/forgot-password" class="text-blue-700 hover:underline">Forgot password?</a>
|
||||
<a href="/signup" class="text-gray-600 hover:underline">Create account</a>
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
{{end}}
|
||||
|
|
|
|||
|
|
@ -1,24 +1,21 @@
|
|||
{{define "content"}}
|
||||
<div class="mx-auto w-full max-w-96 rounded-xl border border-gray-200 px-6 py-8">
|
||||
<div class="mx-auto mb-6 flex h-16 w-16 items-center justify-center rounded-full bg-violet-100">
|
||||
<svg class="h-8 w-8 text-violet-500" fill="none" stroke="currentColor" viewBox="0 0 24 24" xmlns="http://www.w3.org/2000/svg">
|
||||
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M11 11V7a4 4 0 118 0v4m-8 0h8m-8 0H5m14 0v8a2 2 0 01-2 2H7a2 2 0 01-2-2v-8"></path>
|
||||
</svg>
|
||||
</div>
|
||||
|
||||
<h3 class="mb-6 text-center text-xl font-bold text-gray-800">Reset Password</h3>
|
||||
<div class="flex min-h-screen items-center justify-center">
|
||||
<div class="w-full max-w-md rounded-lg border border-gray-200 bg-white p-6 shadow-sm md:p-8">
|
||||
<h1 class="mb-1 text-2xl font-bold text-gray-900">Reset Password</h1>
|
||||
<p class="mb-6 text-sm text-gray-500">Imposta una nuova password.</p>
|
||||
|
||||
{{if .Token}}
|
||||
<form action="/reset-password?token={{.Token}}" method="post">
|
||||
<div class="mb-6">
|
||||
<label class="mb-1 block text-sm font-medium text-gray-700">Nuova password</label>
|
||||
<input type="password" name="password" class="w-full rounded-lg border border-gray-300 px-3 py-2 transition outline-none focus:border-violet-500 focus:ring-2 focus:ring-violet-500" required />
|
||||
<form action="/reset-password?token={{.Token}}" method="post" class="space-y-5">
|
||||
<div>
|
||||
<label for="reset-password" class="mb-2 block text-sm font-medium text-gray-900">Nuova password</label>
|
||||
<input id="reset-password" type="password" name="password" class="block w-full rounded-lg border border-gray-300 bg-gray-50 p-2.5 text-sm text-gray-900 focus:border-blue-500 focus:ring-blue-500" required />
|
||||
</div>
|
||||
|
||||
<button type="submit" class="w-full rounded-lg bg-violet-500 px-4 py-2 font-medium text-white transition duration-300 hover:bg-violet-600">Aggiorna password</button>
|
||||
<button type="submit" class="w-full rounded-lg bg-blue-700 px-5 py-2.5 text-center text-sm font-medium text-white hover:bg-blue-800 focus:outline-none focus:ring-4 focus:ring-blue-300">Aggiorna password</button>
|
||||
</form>
|
||||
{{else}}
|
||||
<p class="text-center text-sm text-gray-500">Token mancante o non valido.</p>
|
||||
<div class="rounded-lg border border-red-200 bg-red-50 p-4 text-sm text-red-800" role="alert">Token mancante o non valido.</div>
|
||||
{{end}}
|
||||
</div>
|
||||
</div>
|
||||
{{end}}
|
||||
|
|
|
|||
|
|
@ -1,29 +1,24 @@
|
|||
{{define "content"}}
|
||||
<div class="mx-auto w-full max-w-96 rounded-xl border border-gray-200 px-6 py-8">
|
||||
<div class="mx-auto mb-6 flex h-16 w-16 items-center justify-center rounded-full bg-emerald-100">
|
||||
<svg class="h-8 w-8 text-emerald-500" fill="none" stroke="currentColor" viewBox="0 0 24 24" xmlns="http://www.w3.org/2000/svg">
|
||||
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M18 9v3m0 0v3m0-3h3m-3 0h-3M5 7h8M5 11h4m1 10h8a2 2 0 002-2V5a2 2 0 00-2-2H6a2 2 0 00-2 2v14a2 2 0 002 2h4z"></path>
|
||||
</svg>
|
||||
<div class="flex min-h-screen items-center justify-center">
|
||||
<div class="w-full max-w-md rounded-lg border border-gray-200 bg-white p-6 shadow-sm md:p-8">
|
||||
<h1 class="mb-1 text-2xl font-bold text-gray-900">Signup</h1>
|
||||
<p class="mb-6 text-sm text-gray-500">Crea il tuo account.</p>
|
||||
|
||||
<form action="/signup" method="post" class="space-y-5">
|
||||
<div>
|
||||
<label for="signup-email" class="mb-2 block text-sm font-medium text-gray-900">Email</label>
|
||||
<input id="signup-email" type="email" name="email" value="{{.Email}}" class="block w-full rounded-lg border border-gray-300 bg-gray-50 p-2.5 text-sm text-gray-900 focus:border-blue-500 focus:ring-blue-500" required />
|
||||
</div>
|
||||
|
||||
<h3 class="mb-6 text-center text-xl font-bold text-gray-800">Create Account</h3>
|
||||
|
||||
<form action="/signup" method="post">
|
||||
<div class="mb-4">
|
||||
<label class="mb-1 block text-sm font-medium text-gray-700">Email</label>
|
||||
<input type="email" name="email" value="{{.Email}}" class="w-full rounded-lg border border-gray-300 px-3 py-2 transition outline-none focus:border-emerald-500 focus:ring-2 focus:ring-emerald-500" required />
|
||||
<div>
|
||||
<label for="signup-password" class="mb-2 block text-sm font-medium text-gray-900">Password</label>
|
||||
<input id="signup-password" type="password" name="password" class="block w-full rounded-lg border border-gray-300 bg-gray-50 p-2.5 text-sm text-gray-900 focus:border-blue-500 focus:ring-blue-500" required />
|
||||
</div>
|
||||
|
||||
<div class="mb-6">
|
||||
<label class="mb-1 block text-sm font-medium text-gray-700">Password</label>
|
||||
<input type="password" name="password" class="w-full rounded-lg border border-gray-300 px-3 py-2 transition outline-none focus:border-emerald-500 focus:ring-2 focus:ring-emerald-500" required />
|
||||
</div>
|
||||
<button type="submit" class="w-full rounded-lg bg-blue-700 px-5 py-2.5 text-center text-sm font-medium text-white hover:bg-blue-800 focus:outline-none focus:ring-4 focus:ring-blue-300">Sign up</button>
|
||||
|
||||
<button type="submit" class="w-full rounded-lg bg-emerald-500 px-4 py-2 font-medium text-white transition duration-300 hover:bg-emerald-600">Sign Up</button>
|
||||
|
||||
<div class="mt-4 text-center">
|
||||
<a href="/login" class="text-sm text-slate-600 hover:text-slate-800">Hai già un account? Accedi</a>
|
||||
</div>
|
||||
<p class="text-center text-sm text-gray-600">Hai già un account? <a href="/login" class="text-blue-700 hover:underline">Accedi</a></p>
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
{{end}}
|
||||
|
|
|
|||
|
|
@ -1,6 +1,8 @@
|
|||
{{define "content"}}
|
||||
<h1>Verifica email</h1>
|
||||
<p class="muted">Controlla la casella di posta e apri il link di verifica ricevuto.</p>
|
||||
<p class="muted">Se il link è scaduto, ripeti la registrazione o contatta supporto.</p>
|
||||
<p><a href="/login">Vai al login</a></p>
|
||||
<section class="rounded-lg border border-blue-200 bg-blue-50 p-8 shadow-sm" role="status" aria-live="polite">
|
||||
<h1 class="mb-2 text-2xl font-bold text-blue-900">Verifica email</h1>
|
||||
<p class="mb-2 text-blue-800">Controlla la casella di posta e apri il link di verifica ricevuto.</p>
|
||||
<p class="mb-4 text-blue-800">Se il link è scaduto, ripeti la registrazione o contatta supporto.</p>
|
||||
<a href="/login" class="inline-flex rounded-lg bg-blue-700 px-5 py-2.5 text-sm font-medium text-white hover:bg-blue-800">Vai al login</a>
|
||||
</section>
|
||||
{{end}}
|
||||
|
|
|
|||
Loading…
Reference in New Issue