Add dashboard page with login header
A lot of things here don't work yet, but this is the basic layout.
This commit is contained in:
parent
4d55a391c9
commit
ac736cced1
6 changed files with 207 additions and 8 deletions
4
go.mod
4
go.mod
|
|
@ -9,11 +9,14 @@ require (
|
||||||
github.com/go-chi/chi/v5 v5.2.3
|
github.com/go-chi/chi/v5 v5.2.3
|
||||||
github.com/go-webauthn/webauthn v0.14.0
|
github.com/go-webauthn/webauthn v0.14.0
|
||||||
github.com/golang-jwt/jwt/v5 v5.3.0
|
github.com/golang-jwt/jwt/v5 v5.3.0
|
||||||
|
github.com/google/go-cmp v0.7.0
|
||||||
github.com/jackc/pgx/v5 v5.7.6
|
github.com/jackc/pgx/v5 v5.7.6
|
||||||
github.com/jaswdr/faker/v2 v2.8.1
|
github.com/jaswdr/faker/v2 v2.8.1
|
||||||
github.com/lib/pq v1.10.9
|
github.com/lib/pq v1.10.9
|
||||||
github.com/pressly/goose/v3 v3.26.0
|
github.com/pressly/goose/v3 v3.26.0
|
||||||
|
github.com/riverqueue/river/rivershared v0.26.0
|
||||||
github.com/stephenafamo/bob v0.41.1
|
github.com/stephenafamo/bob v0.41.1
|
||||||
|
github.com/stephenafamo/scan v0.7.0
|
||||||
github.com/wasilibs/go-pgquery v0.0.0-20250409022910-10ac41983c07
|
github.com/wasilibs/go-pgquery v0.0.0-20250409022910-10ac41983c07
|
||||||
golang.org/x/crypto v0.42.0
|
golang.org/x/crypto v0.42.0
|
||||||
)
|
)
|
||||||
|
|
@ -31,7 +34,6 @@ require (
|
||||||
github.com/pganalyze/pg_query_go/v6 v6.1.0 // indirect
|
github.com/pganalyze/pg_query_go/v6 v6.1.0 // indirect
|
||||||
github.com/qdm12/reprint v0.0.0-20200326205758-722754a53494 // indirect
|
github.com/qdm12/reprint v0.0.0-20200326205758-722754a53494 // indirect
|
||||||
github.com/sethvargo/go-retry v0.3.0 // indirect
|
github.com/sethvargo/go-retry v0.3.0 // indirect
|
||||||
github.com/stephenafamo/scan v0.7.0 // indirect
|
|
||||||
github.com/tetratelabs/wazero v1.9.0 // indirect
|
github.com/tetratelabs/wazero v1.9.0 // indirect
|
||||||
github.com/wasilibs/wazero-helpers v0.0.0-20240620070341-3dff1577cd52 // indirect
|
github.com/wasilibs/wazero-helpers v0.0.0-20240620070341-3dff1577cd52 // indirect
|
||||||
github.com/x448/float16 v0.8.4 // indirect
|
github.com/x448/float16 v0.8.4 // indirect
|
||||||
|
|
|
||||||
2
go.sum
2
go.sum
|
|
@ -135,6 +135,8 @@ github.com/qdm12/reprint v0.0.0-20200326205758-722754a53494 h1:wSmWgpuccqS2IOfmY
|
||||||
github.com/qdm12/reprint v0.0.0-20200326205758-722754a53494/go.mod h1:yipyliwI08eQ6XwDm1fEwKPdF/xdbkiHtrU+1Hg+vc4=
|
github.com/qdm12/reprint v0.0.0-20200326205758-722754a53494/go.mod h1:yipyliwI08eQ6XwDm1fEwKPdF/xdbkiHtrU+1Hg+vc4=
|
||||||
github.com/remyoudompheng/bigfft v0.0.0-20230129092748-24d4a6f8daec h1:W09IVJc94icq4NjY3clb7Lk8O1qJ8BdBEF8z0ibU0rE=
|
github.com/remyoudompheng/bigfft v0.0.0-20230129092748-24d4a6f8daec h1:W09IVJc94icq4NjY3clb7Lk8O1qJ8BdBEF8z0ibU0rE=
|
||||||
github.com/remyoudompheng/bigfft v0.0.0-20230129092748-24d4a6f8daec/go.mod h1:qqbHyh8v60DhA7CoWK5oRCqLrMHRGoxYCSS9EjAz6Eo=
|
github.com/remyoudompheng/bigfft v0.0.0-20230129092748-24d4a6f8daec/go.mod h1:qqbHyh8v60DhA7CoWK5oRCqLrMHRGoxYCSS9EjAz6Eo=
|
||||||
|
github.com/riverqueue/river/rivershared v0.26.0 h1:tsMvxTIdG58GoYXd3788DwjNq87Y7CcfRlV7TAzeuhw=
|
||||||
|
github.com/riverqueue/river/rivershared v0.26.0/go.mod h1:/BEdbdGEqfcFP9FtChwK81e2AWF8e82RC6z5mwQ3y1g=
|
||||||
github.com/rogpeppe/go-internal v1.6.1/go.mod h1:xXDCJY+GAPziupqXw64V24skbSoqbTEfhy4qGm1nDQc=
|
github.com/rogpeppe/go-internal v1.6.1/go.mod h1:xXDCJY+GAPziupqXw64V24skbSoqbTEfhy4qGm1nDQc=
|
||||||
github.com/sethvargo/go-retry v0.3.0 h1:EEt31A35QhrcRZtrYFDTBg91cqZVnFL2navjDrah2SE=
|
github.com/sethvargo/go-retry v0.3.0 h1:EEt31A35QhrcRZtrYFDTBg91cqZVnFL2navjDrah2SE=
|
||||||
github.com/sethvargo/go-retry v0.3.0/go.mod h1:mNX17F0C/HguQMyMyJxcnU471gOZGxCLyYaFyAZraas=
|
github.com/sethvargo/go-retry v0.3.0/go.mod h1:mNX17F0C/HguQMyMyJxcnU471gOZGxCLyYaFyAZraas=
|
||||||
|
|
|
||||||
37
html.go
37
html.go
|
|
@ -5,16 +5,20 @@ import (
|
||||||
"fmt"
|
"fmt"
|
||||||
"html/template"
|
"html/template"
|
||||||
"io"
|
"io"
|
||||||
|
"log/slog"
|
||||||
"os"
|
"os"
|
||||||
|
"strings"
|
||||||
|
|
||||||
|
"github.com/riverqueue/river/rivershared/util/slogutil"
|
||||||
"github.com/Gleipnir-Technology/nidus-sync/models"
|
"github.com/Gleipnir-Technology/nidus-sync/models"
|
||||||
)
|
)
|
||||||
|
|
||||||
var (
|
var (
|
||||||
dashboard = newBuiltTemplate("dashboard", "base")
|
dashboard = newBuiltTemplate("dashboard", "authenticated")
|
||||||
signin = newBuiltTemplate("signin", "base")
|
signin = newBuiltTemplate("signin", "base")
|
||||||
signup = newBuiltTemplate("signup", "base")
|
signup = newBuiltTemplate("signup", "base")
|
||||||
)
|
)
|
||||||
|
var components = [ ... ]string{"header"}
|
||||||
|
|
||||||
type BuiltTemplate struct {
|
type BuiltTemplate struct {
|
||||||
files []string
|
files []string
|
||||||
|
|
@ -26,13 +30,17 @@ type Link struct {
|
||||||
Title string
|
Title string
|
||||||
}
|
}
|
||||||
type ContentDashboard struct {
|
type ContentDashboard struct {
|
||||||
BabbleLinks []Link
|
User User
|
||||||
Username string
|
|
||||||
}
|
}
|
||||||
type ContentSignin struct {
|
type ContentSignin struct {
|
||||||
InvalidCredentials bool
|
InvalidCredentials bool
|
||||||
}
|
}
|
||||||
type ContentSignup struct { }
|
type ContentSignup struct { }
|
||||||
|
type User struct {
|
||||||
|
DisplayName string
|
||||||
|
Initials string
|
||||||
|
Username string
|
||||||
|
}
|
||||||
|
|
||||||
func (bt *BuiltTemplate) ExecuteTemplate(w io.Writer, data any) error {
|
func (bt *BuiltTemplate) ExecuteTemplate(w io.Writer, data any) error {
|
||||||
name := bt.files[0] + ".html"
|
name := bt.files[0] + ".html"
|
||||||
|
|
@ -51,9 +59,26 @@ func (bt *BuiltTemplate) ExecuteTemplate(w io.Writer, data any) error {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func extractInitials(name string) string {
|
||||||
|
parts := strings.Fields(name)
|
||||||
|
var initials strings.Builder
|
||||||
|
|
||||||
|
for _, part := range parts {
|
||||||
|
if len(part) > 0 {
|
||||||
|
initials.WriteString(strings.ToUpper(string(part[0])))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return initials.String()
|
||||||
|
}
|
||||||
|
|
||||||
func htmlDashboard(w io.Writer, user *models.User) error {
|
func htmlDashboard(w io.Writer, user *models.User) error {
|
||||||
data := ContentDashboard{
|
data := ContentDashboard{
|
||||||
Username: user.Username,
|
User: User{
|
||||||
|
DisplayName: user.DisplayName,
|
||||||
|
Initials: extractInitials(user.DisplayName),
|
||||||
|
Username: user.Username,
|
||||||
|
},
|
||||||
}
|
}
|
||||||
return dashboard.ExecuteTemplate(w, data)
|
return dashboard.ExecuteTemplate(w, data)
|
||||||
}
|
}
|
||||||
|
|
@ -108,6 +133,10 @@ func parseFromDisk(files []string) (*template.Template, error) {
|
||||||
paths = append(paths, "templates/"+f+".html")
|
paths = append(paths, "templates/"+f+".html")
|
||||||
}
|
}
|
||||||
name := files[0] + ".html"
|
name := files[0] + ".html"
|
||||||
|
for _, f := range components {
|
||||||
|
paths = append(paths, "templates/components/"+f+".html")
|
||||||
|
}
|
||||||
|
slog.Info("Rendering templates from disk", slog.Any("paths", slogutil.SliceString(paths)))
|
||||||
templ, err := template.New(name).Funcs(funcMap).ParseFiles(paths...)
|
templ, err := template.New(name).Funcs(funcMap).ParseFiles(paths...)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, fmt.Errorf("Failed to parse %s: %v", paths, err)
|
return nil, fmt.Errorf("Failed to parse %s: %v", paths, err)
|
||||||
|
|
|
||||||
19
templates/authenticated.html
Normal file
19
templates/authenticated.html
Normal file
|
|
@ -0,0 +1,19 @@
|
||||||
|
<!DOCTYPE html>
|
||||||
|
<html lang="en">
|
||||||
|
<head>
|
||||||
|
<meta charset="utf-8">
|
||||||
|
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||||
|
<title>{{template "title" .}} - Nidus Sync</title>
|
||||||
|
<link href="/static/vendor/css/bootstrap.min.css" rel="stylesheet">
|
||||||
|
<style>
|
||||||
|
{{template "style" .}}
|
||||||
|
</style>
|
||||||
|
</head>
|
||||||
|
<body>
|
||||||
|
{{if .User}}
|
||||||
|
{{template "header" .User}}
|
||||||
|
{{end}}
|
||||||
|
{{template "content" .}}
|
||||||
|
<script src="/static/vendor/js/bootstrap.bundle.min.js"></script>
|
||||||
|
</body>
|
||||||
|
</html>
|
||||||
50
templates/components/header.html
Normal file
50
templates/components/header.html
Normal file
|
|
@ -0,0 +1,50 @@
|
||||||
|
{{define "header"}}
|
||||||
|
<header class="navbar navbar-expand-lg navbar-light bg-white shadow-sm">
|
||||||
|
<div class="container">
|
||||||
|
<!-- Logo -->
|
||||||
|
<a class="navbar-brand" href="dashboard.html">
|
||||||
|
<div class="logo-placeholder" style="width: 100px; height: 40px; background-color: #e9ecef; display: flex; align-items: center; justify-content: center; border-radius: 4px;">
|
||||||
|
<span class="text-muted small">Your Logo</span>
|
||||||
|
</div>
|
||||||
|
</a>
|
||||||
|
|
||||||
|
<!-- Toggle Button for Mobile -->
|
||||||
|
<button class="navbar-toggler" type="button" data-bs-toggle="collapse" data-bs-target="#navbarNav" aria-controls="navbarNav" aria-expanded="false" aria-label="Toggle navigation">
|
||||||
|
<span class="navbar-toggler-icon"></span>
|
||||||
|
</button>
|
||||||
|
|
||||||
|
<!-- Nav Items -->
|
||||||
|
<div class="collapse navbar-collapse" id="navbarNav">
|
||||||
|
<ul class="navbar-nav me-auto">
|
||||||
|
<li class="nav-item">
|
||||||
|
<a class="nav-link" href="dashboard.html">Dashboard</a>
|
||||||
|
</li>
|
||||||
|
<li class="nav-item">
|
||||||
|
<a class="nav-link" href="projects.html">Projects</a>
|
||||||
|
</li>
|
||||||
|
<li class="nav-item">
|
||||||
|
<a class="nav-link" href="maps.html">Maps</a>
|
||||||
|
</li>
|
||||||
|
</ul>
|
||||||
|
|
||||||
|
<!-- User Info & Logout -->
|
||||||
|
<div class="d-flex align-items-center">
|
||||||
|
<div class="dropdown">
|
||||||
|
<a class="text-decoration-none dropdown-toggle d-flex align-items-center" href="#" role="button" id="userDropdown" data-bs-toggle="dropdown" aria-expanded="false">
|
||||||
|
<div class="avatar me-2 bg-primary rounded-circle d-flex align-items-center justify-content-center" style="width: 36px; height: 36px;">
|
||||||
|
<span class="text-white">{{ .Initials }}</span>
|
||||||
|
</div>
|
||||||
|
<span class="me-2">{{ .DisplayName }}</span>
|
||||||
|
</a>
|
||||||
|
<ul class="dropdown-menu dropdown-menu-end" aria-labelledby="userDropdown">
|
||||||
|
<li><a class="dropdown-item" href="profile.html">Profile</a></li>
|
||||||
|
<li><a class="dropdown-item" href="settings.html">Settings</a></li>
|
||||||
|
<li><hr class="dropdown-divider"></li>
|
||||||
|
<li><a class="dropdown-item text-danger" href="login.html">Logout</a></li>
|
||||||
|
</ul>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</header>
|
||||||
|
{{end}}
|
||||||
|
|
@ -1,9 +1,106 @@
|
||||||
{{template "base.html" .}}
|
{{template "authenticated.html" .}}
|
||||||
|
|
||||||
{{define "title"}}Dash{{end}}
|
{{define "title"}}Dash{{end}}
|
||||||
{{define "style"}}
|
{{define "style"}}
|
||||||
|
.connect-container {
|
||||||
|
max-width: 800px;
|
||||||
|
margin: 0 auto;
|
||||||
|
}
|
||||||
|
.connect-box {
|
||||||
|
box-shadow: 0 0 15px rgba(0,0,0,0.1);
|
||||||
|
border-radius: 10px;
|
||||||
|
padding: 40px;
|
||||||
|
background-color: #fff;
|
||||||
|
}
|
||||||
|
.connect-header {
|
||||||
|
margin-bottom: 25px;
|
||||||
|
text-align: center;
|
||||||
|
}
|
||||||
|
.logo-area {
|
||||||
|
text-align: center;
|
||||||
|
margin-bottom: 30px;
|
||||||
|
}
|
||||||
|
.logo-placeholder {
|
||||||
|
width: 120px;
|
||||||
|
height: 60px;
|
||||||
|
background-color: #e9ecef;
|
||||||
|
margin: 0 auto;
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: center;
|
||||||
|
border-radius: 6px;
|
||||||
|
}
|
||||||
|
.steps-container {
|
||||||
|
margin: 30px 0;
|
||||||
|
}
|
||||||
|
.step {
|
||||||
|
margin-bottom: 20px;
|
||||||
|
padding: 15px;
|
||||||
|
border-left: 3px solid #0d6efd;
|
||||||
|
background-color: #f8f9fa;
|
||||||
|
}
|
||||||
|
.connect-btn {
|
||||||
|
margin-top: 30px;
|
||||||
|
}
|
||||||
{{end}}
|
{{end}}
|
||||||
{{define "content"}}
|
{{define "content"}}
|
||||||
<h1>Hey {{ .Username }}</h1>
|
<div class="container min-vh-100 d-flex align-items-center justify-content-center py-5">
|
||||||
<p>At this point, pretend I'm showing you the result of some ArcGIS data.</p>
|
<div class="connect-container">
|
||||||
|
<!-- Logo Area -->
|
||||||
|
<div class="logo-area">
|
||||||
|
<div class="logo-placeholder">
|
||||||
|
<span class="text-muted">Your Logo</span>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="connect-box">
|
||||||
|
<div class="connect-header">
|
||||||
|
<h1>Connect Your ArcGIS Account</h1>
|
||||||
|
<p class="text-muted">Link your data to get started</p>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="connect-content">
|
||||||
|
<p>To provide you with the best experience, we need to connect to your ArcGIS account. This allows us to securely access and visualize your spatial data within our platform.</p>
|
||||||
|
|
||||||
|
<div class="steps-container">
|
||||||
|
<h4>What to expect:</h4>
|
||||||
|
|
||||||
|
<div class="step">
|
||||||
|
<h5>1. Secure Authentication</h5>
|
||||||
|
<p>When you click the "Connect to ArcGIS" button below, you'll be redirected to the official ArcGIS login page. This connection is secure and uses OAuth 2.0 protocol.</p>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="step">
|
||||||
|
<h5>2. Grant Permissions</h5>
|
||||||
|
<p>After logging in with your ArcGIS credentials, you'll be asked to approve permissions for our application to access your data. We only request access to what's needed for the platform to function.</p>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="step">
|
||||||
|
<h5>3. Return to Platform</h5>
|
||||||
|
<p>Once authentication is complete, you'll be automatically redirected back to our platform where your data will be available to work with.</p>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="alert alert-info">
|
||||||
|
<strong>Note:</strong> You'll need an active ArcGIS Online account or ArcGIS Enterprise account to proceed. If you don't have one, you can <a href="https://www.arcgis.com/home/signin.html" target="_blank">create an ArcGIS account here</a>.
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<p>By connecting your ArcGIS account, you'll be able to:</p>
|
||||||
|
<ul>
|
||||||
|
<li>Access and visualize your spatial data</li>
|
||||||
|
<li>Perform advanced analysis using our integrated tools</li>
|
||||||
|
<li>Share results with team members securely</li>
|
||||||
|
<li>Keep your data synchronized across platforms</li>
|
||||||
|
</ul>
|
||||||
|
|
||||||
|
<div class="text-center connect-btn">
|
||||||
|
<button type="button" class="btn btn-primary btn-lg">
|
||||||
|
Connect to ArcGIS
|
||||||
|
</button>
|
||||||
|
<p class="mt-2 text-muted"><small>You can disconnect your account at any time in settings</small></p>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
{{end}}
|
{{end}}
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue