first commit

This commit is contained in:
Blake Ridgway
2026-03-27 23:18:17 -05:00
commit d8cb1d277f
19 changed files with 2557 additions and 0 deletions

View File

@@ -0,0 +1,39 @@
{{define "base"}}<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>{{block "title" .}}Arcline IT — Client Portal{{end}}</title>
<link rel="stylesheet" href="/static/css/style.css">
</head>
<body>
<nav class="nav">
<div class="nav__inner">
<a href="/" class="nav__logo">
<span class="nav__logo-bracket">[</span>arcline<span class="nav__logo-accent">IT</span><span class="nav__logo-bracket">]</span>
</a>
<span class="nav__label">client portal</span>
<div class="nav__actions">
{{block "nav-actions" .}}{{end}}
</div>
</div>
</nav>
<main class="main">
{{block "content" .}}{{end}}
</main>
<footer class="footer">
<div class="footer__inner">
<span class="footer__copy">&copy; 2026 Arcline IT</span>
<span class="footer__sep">·</span>
<a href="https://arclineit.com/tos" class="footer__link">Terms</a>
<span class="footer__sep">·</span>
<a href="https://arclineit.com/privacy" class="footer__link">Privacy</a>
<span class="footer__sep">·</span>
<a href="https://arclineit.com/contact" class="footer__link">Support</a>
</div>
</footer>
</body>
</html>
{{end}}

View File

@@ -0,0 +1,144 @@
{{template "base" .}}
{{define "title"}}Dashboard — Arcline IT Client Portal{{end}}
{{define "nav-actions"}}
<form method="POST" action="/logout" style="display:inline;">
<button class="btn btn--ghost btn--sm" type="submit">logout</button>
</form>
{{end}}
{{define "content"}}
<div class="dashboard">
<div class="container">
{{if .Flash}}
<div class="alert alert--ok flash">{{.Flash}}</div>
{{end}}
<div class="dash-header">
<h1 class="dash-header__title">
Welcome back, <span class="dash-header__name">{{.Customer.FirstName}}</span><span class="cursor"></span>
</h1>
<p class="dash-header__email">{{.Customer.Email}}</p>
</div>
<!-- Subscription panel -->
<section class="dash-section">
<h2 class="dash-section__title">// subscription</h2>
{{if .Subscription}}
<div class="term-window">
<div class="term-window__chrome">
<span class="term-window__dot term-window__dot--red"></span>
<span class="term-window__dot term-window__dot--yellow"></span>
<span class="term-window__dot term-window__dot--green"></span>
<span class="term-window__title">subscription.status</span>
</div>
<div class="term-window__body">
<div class="status-row">
<span class="status-row__label">plan</span>
<span class="status-row__dots"></span>
<span class="status-row__value">{{if .Subscription.PlanName}}{{.Subscription.PlanName}}{{else}}active plan{{end}}</span>
</div>
<div class="status-row">
<span class="status-row__label">status</span>
<span class="status-row__dots"></span>
<span class="status-badge status-badge--{{.Subscription.Status}}">{{.Subscription.Status}}</span>
</div>
{{if .Subscription.CurrentPeriodEnd}}
<div class="status-row">
<span class="status-row__label">next billing date</span>
<span class="status-row__dots"></span>
<span class="status-row__value">{{.Subscription.CurrentPeriodEnd}}</span>
</div>
{{end}}
</div>
</div>
{{if eq .Subscription.Status "active"}}
<div class="dash-actions">
<form method="POST" action="/cancel"
onsubmit="return confirm('Are you sure you want to cancel your subscription? This cannot be undone.')">
<button class="btn btn--danger btn--sm" type="submit">cancel subscription</button>
</form>
</div>
{{end}}
{{else}}
<div class="term-window">
<div class="term-window__chrome">
<span class="term-window__dot term-window__dot--red"></span>
<span class="term-window__dot term-window__dot--yellow"></span>
<span class="term-window__dot term-window__dot--green"></span>
<span class="term-window__title">subscription.status</span>
</div>
<div class="term-window__body">
<p class="dash-empty">No active subscription.</p>
<p class="dash-empty-sub">
<a href="https://arclineit.com/pricing" class="link">View plans</a> to get started.
</p>
</div>
</div>
{{end}}
</section>
<!-- Invoices panel -->
<section class="dash-section">
<h2 class="dash-section__title">// recent invoices</h2>
{{if .Invoices}}
<div class="term-window">
<div class="term-window__chrome">
<span class="term-window__dot term-window__dot--red"></span>
<span class="term-window__dot term-window__dot--yellow"></span>
<span class="term-window__dot term-window__dot--green"></span>
<span class="term-window__title">invoices</span>
</div>
<div class="term-window__body">
<table class="inv-table">
<thead>
<tr>
<th class="inv-table__th">date</th>
<th class="inv-table__th">period</th>
<th class="inv-table__th inv-table__th--right">amount</th>
<th class="inv-table__th">status</th>
<th class="inv-table__th">pdf</th>
</tr>
</thead>
<tbody>
{{range .Invoices}}
<tr class="inv-table__row">
<td class="inv-table__td inv-table__td--muted">{{.CreatedAt | slice 0 10}}</td>
<td class="inv-table__td inv-table__td--muted">{{.PeriodStart | slice 0 10}} {{.PeriodEnd | slice 0 10}}</td>
<td class="inv-table__td inv-table__td--right">{{.AmountDisplay}}</td>
<td class="inv-table__td"><span class="status-badge status-badge--{{.Status}}">{{.Status}}</span></td>
<td class="inv-table__td">
{{if .InvoicePDFURL}}
<a href="{{.InvoicePDFURL}}" class="link link--sm" target="_blank" rel="noopener">download</a>
{{else}}—{{end}}
</td>
</tr>
{{end}}
</tbody>
</table>
</div>
</div>
{{else}}
<div class="term-window">
<div class="term-window__chrome">
<span class="term-window__dot term-window__dot--red"></span>
<span class="term-window__dot term-window__dot--yellow"></span>
<span class="term-window__dot term-window__dot--green"></span>
<span class="term-window__title">invoices</span>
</div>
<div class="term-window__body">
<p class="dash-empty">No invoices yet.</p>
</div>
</div>
{{end}}
</section>
</div>
</div>
{{end}}

View File

@@ -0,0 +1,49 @@
{{template "base" .}}
{{define "title"}}Sign In — Arcline IT Client Portal{{end}}
{{define "content"}}
<div class="auth-wrap">
<div class="auth-card">
<div class="auth-card__header">
<span class="auth-card__prompt">$ arcline-billing --login</span>
</div>
<div class="auth-card__body">
<h1 class="auth-card__title">Sign in to your account</h1>
{{if .Error}}
<div class="alert alert--error">
{{if eq .Error "invalid_credentials"}}Invalid email or password.
{{else if eq .Error "missing_fields"}}Please fill in all fields.
{{else if eq .Error "server_error"}}A server error occurred. Please try again.
{{else}}An error occurred. Please try again.{{end}}
</div>
{{end}}
{{if eq (index . "reset") "1"}}
<div class="alert alert--ok">Password updated. You can now sign in.</div>
{{end}}
<form method="POST" action="/login" class="form">
<div class="form__group">
<label class="form__label" for="email">Email</label>
<input class="form__input" type="email" id="email" name="email"
autocomplete="email" required placeholder="you@example.com">
</div>
<div class="form__group">
<label class="form__label" for="password">Password</label>
<input class="form__input" type="password" id="password" name="password"
autocomplete="current-password" required placeholder="••••••••">
</div>
<button class="btn btn--primary btn--full" type="submit">Sign in</button>
</form>
<div class="auth-card__links">
<a href="/reset" class="auth-card__link">Forgot password?</a>
<span class="auth-card__sep">·</span>
<a href="/register" class="auth-card__link">Create account</a>
</div>
</div>
</div>
</div>
{{end}}

View File

@@ -0,0 +1,63 @@
{{template "base" .}}
{{define "title"}}Create Account — Arcline IT Client Portal{{end}}
{{define "content"}}
<div class="auth-wrap">
<div class="auth-card">
<div class="auth-card__header">
<span class="auth-card__prompt">$ arcline-billing --register</span>
</div>
<div class="auth-card__body">
<h1 class="auth-card__title">Create your account</h1>
{{if .Error}}
<div class="alert alert--error">
{{if eq .Error "missing_fields"}}Please fill in all required fields.
{{else if eq .Error "password_mismatch"}}Passwords do not match.
{{else if eq .Error "password_too_short"}}Password must be at least 8 characters.
{{else if eq .Error "email_taken"}}An account with that email already exists.
{{else if eq .Error "server_error"}}A server error occurred. Please try again.
{{else}}An error occurred. Please try again.{{end}}
</div>
{{end}}
<form method="POST" action="/register" class="form">
<div class="form__row">
<div class="form__group">
<label class="form__label" for="first_name">First name</label>
<input class="form__input" type="text" id="first_name" name="first_name"
autocomplete="given-name" required placeholder="Blake">
</div>
<div class="form__group">
<label class="form__label" for="last_name">Last name</label>
<input class="form__input" type="text" id="last_name" name="last_name"
autocomplete="family-name" required placeholder="Smith">
</div>
</div>
<div class="form__group">
<label class="form__label" for="email">Email</label>
<input class="form__input" type="email" id="email" name="email"
autocomplete="email" required placeholder="you@example.com">
</div>
<div class="form__group">
<label class="form__label" for="password">Password <span class="form__hint">(min. 8 characters)</span></label>
<input class="form__input" type="password" id="password" name="password"
autocomplete="new-password" required placeholder="••••••••" minlength="8">
</div>
<div class="form__group">
<label class="form__label" for="confirm_password">Confirm password</label>
<input class="form__input" type="password" id="confirm_password" name="confirm_password"
autocomplete="new-password" required placeholder="••••••••" minlength="8">
</div>
<button class="btn btn--primary btn--full" type="submit">Create account</button>
</form>
<div class="auth-card__links">
<span class="auth-card__text">Already have an account?</span>
<a href="/login" class="auth-card__link">Sign in</a>
</div>
</div>
</div>
</div>
{{end}}

View File

@@ -0,0 +1,45 @@
{{template "base" .}}
{{define "title"}}Set New Password — Arcline IT Client Portal{{end}}
{{define "content"}}
<div class="auth-wrap">
<div class="auth-card">
<div class="auth-card__header">
<span class="auth-card__prompt">$ arcline-billing --set-password</span>
</div>
<div class="auth-card__body">
<h1 class="auth-card__title">Set a new password</h1>
{{if .Error}}
<div class="alert alert--error">
{{if eq .Error "invalid_token"}}This reset link is invalid or has expired.
{{else if eq .Error "missing_password"}}Please enter a new password.
{{else if eq .Error "password_mismatch"}}Passwords do not match.
{{else if eq .Error "password_too_short"}}Password must be at least 8 characters.
{{else if eq .Error "server_error"}}A server error occurred. Please try again.
{{else}}An error occurred. Please try again.{{end}}
</div>
{{end}}
<form method="POST" action="/reset/{{.Token}}" class="form">
<div class="form__group">
<label class="form__label" for="password">New password <span class="form__hint">(min. 8 characters)</span></label>
<input class="form__input" type="password" id="password" name="password"
autocomplete="new-password" required placeholder="••••••••" minlength="8">
</div>
<div class="form__group">
<label class="form__label" for="confirm_password">Confirm new password</label>
<input class="form__input" type="password" id="confirm_password" name="confirm_password"
autocomplete="new-password" required placeholder="••••••••" minlength="8">
</div>
<button class="btn btn--primary btn--full" type="submit">Update password</button>
</form>
<div class="auth-card__links">
<a href="/login" class="auth-card__link">Back to sign in</a>
</div>
</div>
</div>
</div>
{{end}}

View File

@@ -0,0 +1,48 @@
{{template "base" .}}
{{define "title"}}Reset Password — Arcline IT Client Portal{{end}}
{{define "content"}}
<div class="auth-wrap">
<div class="auth-card">
<div class="auth-card__header">
<span class="auth-card__prompt">$ arcline-billing --reset-password</span>
</div>
<div class="auth-card__body">
<h1 class="auth-card__title">Reset your password</h1>
{{if .Sent}}
<div class="alert alert--ok">
If an account exists for that email, a reset link has been sent. Check your inbox.
</div>
{{else}}
{{if .Error}}
<div class="alert alert--error">
{{if eq .Error "missing_email"}}Please enter your email address.
{{else}}An error occurred. Please try again.{{end}}
</div>
{{end}}
<p class="auth-card__desc">
Enter your account email and we'll send you a link to reset your password.
</p>
<form method="POST" action="/reset" class="form">
<div class="form__group">
<label class="form__label" for="email">Email</label>
<input class="form__input" type="email" id="email" name="email"
autocomplete="email" required placeholder="you@example.com">
</div>
<button class="btn btn--primary btn--full" type="submit">Send reset link</button>
</form>
{{end}}
<div class="auth-card__links">
<a href="/login" class="auth-card__link">Back to sign in</a>
</div>
</div>
</div>
</div>
{{end}}