feat: add terms and privacy pages
This commit is contained in:
parent
0da5aadbbe
commit
c473c88c33
9 changed files with 240 additions and 6 deletions
|
|
@ -24,6 +24,14 @@ func (h *homeHandler) HomePage(w http.ResponseWriter, r *http.Request) {
|
||||||
http.Redirect(w, r, "/app/dashboard", http.StatusSeeOther)
|
http.Redirect(w, r, "/app/dashboard", http.StatusSeeOther)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (h *homeHandler) PrivacyPage(w http.ResponseWriter, r *http.Request) {
|
||||||
|
ui.Render(w, r, pages.Privacy())
|
||||||
|
}
|
||||||
|
|
||||||
|
func (h *homeHandler) TermsPage(w http.ResponseWriter, r *http.Request) {
|
||||||
|
ui.Render(w, r, pages.Terms())
|
||||||
|
}
|
||||||
|
|
||||||
func (home *homeHandler) NotFoundPage(w http.ResponseWriter, r *http.Request) {
|
func (home *homeHandler) NotFoundPage(w http.ResponseWriter, r *http.Request) {
|
||||||
ui.Render(w, r, pages.NotFound())
|
ui.Render(w, r, pages.NotFound())
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -29,6 +29,8 @@ func SetupRoutes(a *app.App) http.Handler {
|
||||||
// Home
|
// Home
|
||||||
mux.HandleFunc("GET /{$}", home.HomePage)
|
mux.HandleFunc("GET /{$}", home.HomePage)
|
||||||
mux.HandleFunc("GET /forbidden", home.ForbiddenPage)
|
mux.HandleFunc("GET /forbidden", home.ForbiddenPage)
|
||||||
|
mux.HandleFunc("GET /privacy", home.PrivacyPage)
|
||||||
|
mux.HandleFunc("GET /terms", home.TermsPage)
|
||||||
|
|
||||||
// Auth pages
|
// Auth pages
|
||||||
authRateLimiter := middleware.RateLimitAuth()
|
authRateLimiter := middleware.RateLimitAuth()
|
||||||
|
|
|
||||||
|
|
@ -1,6 +1,7 @@
|
||||||
// templui component icon - version: v1.2.0 installed by templui v1.2.0
|
// templui component icon - version: v1.2.0 installed by templui v1.2.0
|
||||||
// 📚 Documentation: https://templui.io/docs/components/icon
|
// 📚 Documentation: https://templui.io/docs/components/icon
|
||||||
package icon
|
package icon
|
||||||
|
|
||||||
// This file is auto generated
|
// This file is auto generated
|
||||||
// Using Lucide icons version 0.544.0
|
// Using Lucide icons version 0.544.0
|
||||||
var AlarmClockOff = Icon("alarm-clock-off")
|
var AlarmClockOff = Icon("alarm-clock-off")
|
||||||
|
|
|
||||||
|
|
@ -70,6 +70,14 @@ templ Auth(errorMsg string) {
|
||||||
Sign in with password instead
|
Sign in with password instead
|
||||||
</a>
|
</a>
|
||||||
</p>
|
</p>
|
||||||
|
<!-- Legal Links -->
|
||||||
|
<p class="mt-8 text-center text-xs text-muted-foreground">
|
||||||
|
By continuing, you agree to our
|
||||||
|
<br/>
|
||||||
|
<a href="/terms" class="underline hover:text-foreground">Terms of Service</a>
|
||||||
|
and
|
||||||
|
<a href="/privacy" class="underline hover:text-foreground">Privacy Policy</a>.
|
||||||
|
</p>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -90,6 +90,14 @@ templ AuthPassword(errorMsg string) {
|
||||||
Sign up with magic link
|
Sign up with magic link
|
||||||
</a>
|
</a>
|
||||||
</p>
|
</p>
|
||||||
|
<!-- Legal Links -->
|
||||||
|
<p class="mt-8 text-center text-xs text-muted-foreground">
|
||||||
|
By continuing, you agree to our
|
||||||
|
<br/>
|
||||||
|
<a href="/terms" class="underline hover:text-foreground">Terms of Service</a>
|
||||||
|
and
|
||||||
|
<a href="/privacy" class="underline hover:text-foreground">Privacy Policy</a>.
|
||||||
|
</p>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
}
|
}
|
||||||
|
|
|
||||||
103
internal/ui/pages/privacy.templ
Normal file
103
internal/ui/pages/privacy.templ
Normal file
|
|
@ -0,0 +1,103 @@
|
||||||
|
package pages
|
||||||
|
|
||||||
|
import (
|
||||||
|
"git.juancwu.dev/juancwu/budgit/internal/ctxkeys"
|
||||||
|
"git.juancwu.dev/juancwu/budgit/internal/ui/layouts"
|
||||||
|
"git.juancwu.dev/juancwu/budgit/internal/ui/components/button"
|
||||||
|
"git.juancwu.dev/juancwu/budgit/internal/ui/components/icon"
|
||||||
|
)
|
||||||
|
|
||||||
|
templ Privacy() {
|
||||||
|
{{ cfg := ctxkeys.Config(ctx) }}
|
||||||
|
@layouts.Auth(layouts.SEOProps{
|
||||||
|
Title: "Privacy Policy",
|
||||||
|
Description: "Privacy Policy for " + cfg.AppName,
|
||||||
|
Path: "/privacy",
|
||||||
|
}) {
|
||||||
|
<div class="max-w-4xl mx-auto py-12 px-4 sm:px-6 lg:px-8">
|
||||||
|
<div class="mb-8">
|
||||||
|
@button.Button(button.Props{
|
||||||
|
Variant: button.VariantGhost,
|
||||||
|
Size: button.SizeSm,
|
||||||
|
Href: "/",
|
||||||
|
Class: "mb-4",
|
||||||
|
}) {
|
||||||
|
@icon.ArrowLeft()
|
||||||
|
Back to Home
|
||||||
|
}
|
||||||
|
<h1 class="text-3xl font-bold tracking-tight sm:text-4xl mb-2">Privacy Policy</h1>
|
||||||
|
<p class="text-muted-foreground">Last updated: January 30, 2026</p>
|
||||||
|
</div>
|
||||||
|
<div class="prose dark:prose-invert max-w-none space-y-8">
|
||||||
|
<section>
|
||||||
|
<h2 class="text-2xl font-semibold mb-4">1. Introduction</h2>
|
||||||
|
<p class="text-muted-foreground mb-4">
|
||||||
|
Welcome to { cfg.AppName } ("we," "our," or "us"). We are committed to protecting your personal information and your right to privacy.
|
||||||
|
This Privacy Policy applies to our website and any related services (collectively, the "Service").
|
||||||
|
</p>
|
||||||
|
<p class="text-muted-foreground">
|
||||||
|
{ cfg.AppName } is operated by an individual developer based in Canada as a hobby project.
|
||||||
|
</p>
|
||||||
|
</section>
|
||||||
|
<section>
|
||||||
|
<h2 class="text-2xl font-semibold mb-4">2. Information We Collect</h2>
|
||||||
|
<p class="text-muted-foreground mb-4">We collect information that you provide directly to us when you use the Service:</p>
|
||||||
|
<ul class="list-disc pl-6 space-y-2 text-muted-foreground">
|
||||||
|
<li><strong>Account Information:</strong> When you register, we collect your email address and name (if provided).</li>
|
||||||
|
<li>
|
||||||
|
<strong>User Content:</strong> We store the financial data, shopping lists, and other content you manually enter into the Service.
|
||||||
|
<span class="block mt-1 italic text-sm">Note: We do not connect to your bank accounts or financial institutions. The "financial" data stored is purely what you choose to type in, similar to a spreadsheet.</span>
|
||||||
|
</li>
|
||||||
|
<li><strong>Usage Data:</strong> We may collect anonymous, aggregated information about how the Service is accessed and used (e.g., pages viewed, time spent) to help us improve the product.</li>
|
||||||
|
</ul>
|
||||||
|
</section>
|
||||||
|
<section>
|
||||||
|
<h2 class="text-2xl font-semibold mb-4">3. How We Use Your Information</h2>
|
||||||
|
<p class="text-muted-foreground mb-4">We use the information we collect to:</p>
|
||||||
|
<ul class="list-disc pl-6 space-y-2 text-muted-foreground">
|
||||||
|
<li>Provide, maintain, and improve the Service.</li>
|
||||||
|
<li>Authenticate your identity (via Magic Links or password).</li>
|
||||||
|
<li>Send you administrative emails (e.g., login links, space invitations, welcome emails).</li>
|
||||||
|
<li>Respond to your comments or questions.</li>
|
||||||
|
</ul>
|
||||||
|
</section>
|
||||||
|
<section>
|
||||||
|
<h2 class="text-2xl font-semibold mb-4">4. Cookies and Tracking</h2>
|
||||||
|
<p class="text-muted-foreground">
|
||||||
|
We use cookies and similar technologies primarily for essential functions, such as keeping you logged in.
|
||||||
|
By using the Service, you consent to the use of these essential cookies.
|
||||||
|
</p>
|
||||||
|
</section>
|
||||||
|
<section>
|
||||||
|
<h2 class="text-2xl font-semibold mb-4">5. Data Storage and Third Parties</h2>
|
||||||
|
<p class="text-muted-foreground mb-4">
|
||||||
|
Our Service is hosted on virtual private servers provided by <strong>Hostinger</strong> located in the <strong>United States</strong>.
|
||||||
|
While we are based in Canada, your data may be processed and stored in the United States and subject to its laws.
|
||||||
|
</p>
|
||||||
|
<p class="text-muted-foreground">
|
||||||
|
We self-host our email delivery system. We do not sell, trade, or otherwise transfer your personally identifiable information to outside parties for marketing purposes.
|
||||||
|
</p>
|
||||||
|
</section>
|
||||||
|
<section>
|
||||||
|
<h2 class="text-2xl font-semibold mb-4">6. Children's Privacy</h2>
|
||||||
|
<p class="text-muted-foreground">
|
||||||
|
Our Service is not directed to anyone under the age of 13. We do not knowingly collect personal information from children under 13.
|
||||||
|
</p>
|
||||||
|
</section>
|
||||||
|
<section>
|
||||||
|
<h2 class="text-2xl font-semibold mb-4">7. Your Rights</h2>
|
||||||
|
<p class="text-muted-foreground mb-4">
|
||||||
|
Depending on your location, you may have rights regarding your personal data, including the right to access, correct, or delete your data.
|
||||||
|
You can delete your data by deleting your account within the application or by contacting us.
|
||||||
|
</p>
|
||||||
|
</section>
|
||||||
|
<section>
|
||||||
|
<h2 class="text-2xl font-semibold mb-4">8. Contact Us</h2>
|
||||||
|
<p class="text-muted-foreground">
|
||||||
|
If you have any questions about this Privacy Policy, please contact us at <a href={ templ.SafeURL("mailto:" + cfg.SupportEmail) } class="text-primary hover:underline">{ cfg.SupportEmail }</a>.
|
||||||
|
</p>
|
||||||
|
</section>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -1,6 +1,7 @@
|
||||||
package pages
|
package pages
|
||||||
|
|
||||||
import "git.juancwu.dev/juancwu/budgit/internal/ui/layouts"
|
import "git.juancwu.dev/juancwu/budgit/internal/ui/layouts"
|
||||||
|
import "git.juancwu.dev/juancwu/budgit/internal/ctxkeys"
|
||||||
|
|
||||||
templ NotFound() {
|
templ NotFound() {
|
||||||
@layouts.Base() {
|
@layouts.Base() {
|
||||||
|
|
@ -12,6 +13,12 @@ templ NotFound() {
|
||||||
<a href="/" class="text-primary hover:underline">
|
<a href="/" class="text-primary hover:underline">
|
||||||
← Back to Home
|
← Back to Home
|
||||||
</a>
|
</a>
|
||||||
|
{{ cfg := ctxkeys.Config(ctx) }}
|
||||||
|
if cfg.SupportEmail != "" {
|
||||||
|
<p class="mt-8 text-sm text-muted-foreground">
|
||||||
|
Need help? Contact <a href={ templ.SafeURL("mailto:" + cfg.SupportEmail) } class="hover:underline text-foreground">{ cfg.SupportEmail }</a>
|
||||||
|
</p>
|
||||||
|
}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
}
|
}
|
||||||
|
|
|
||||||
97
internal/ui/pages/terms.templ
Normal file
97
internal/ui/pages/terms.templ
Normal file
|
|
@ -0,0 +1,97 @@
|
||||||
|
package pages
|
||||||
|
|
||||||
|
import (
|
||||||
|
"git.juancwu.dev/juancwu/budgit/internal/ctxkeys"
|
||||||
|
"git.juancwu.dev/juancwu/budgit/internal/ui/layouts"
|
||||||
|
"git.juancwu.dev/juancwu/budgit/internal/ui/components/button"
|
||||||
|
"git.juancwu.dev/juancwu/budgit/internal/ui/components/icon"
|
||||||
|
)
|
||||||
|
|
||||||
|
templ Terms() {
|
||||||
|
{{ cfg := ctxkeys.Config(ctx) }}
|
||||||
|
@layouts.Auth(layouts.SEOProps{
|
||||||
|
Title: "Terms and Conditions",
|
||||||
|
Description: "Terms and Conditions for " + cfg.AppName,
|
||||||
|
Path: "/terms",
|
||||||
|
}) {
|
||||||
|
<div class="max-w-4xl mx-auto py-12 px-4 sm:px-6 lg:px-8">
|
||||||
|
<div class="mb-8">
|
||||||
|
@button.Button(button.Props{
|
||||||
|
Variant: button.VariantGhost,
|
||||||
|
Size: button.SizeSm,
|
||||||
|
Href: "/",
|
||||||
|
Class: "mb-4",
|
||||||
|
}) {
|
||||||
|
@icon.ArrowLeft()
|
||||||
|
Back to Home
|
||||||
|
}
|
||||||
|
<h1 class="text-3xl font-bold tracking-tight sm:text-4xl mb-2">Terms and Conditions</h1>
|
||||||
|
<p class="text-muted-foreground">Last updated: January 30, 2026</p>
|
||||||
|
</div>
|
||||||
|
<div class="prose dark:prose-invert max-w-none space-y-8">
|
||||||
|
<section>
|
||||||
|
<h2 class="text-2xl font-semibold mb-4">1. Agreement to Terms</h2>
|
||||||
|
<p class="text-muted-foreground">
|
||||||
|
By accessing or using { cfg.AppName } (the "Service"), you agree to be bound by these Terms and Conditions.
|
||||||
|
If you disagree with any part of the terms, you may not access the Service.
|
||||||
|
</p>
|
||||||
|
</section>
|
||||||
|
<section>
|
||||||
|
<h2 class="text-2xl font-semibold mb-4">2. Description of Service</h2>
|
||||||
|
<p class="text-muted-foreground mb-4">
|
||||||
|
{ cfg.AppName } is a personal budgeting and expense tracking tool provided as a hobby project.
|
||||||
|
It allows users to manually input and track financial data, shopping lists, and related information.
|
||||||
|
</p>
|
||||||
|
<div class="bg-muted p-4 rounded-lg border border-border">
|
||||||
|
<p class="font-medium text-foreground">Disclaimer: Not Financial Advice</p>
|
||||||
|
<p class="text-sm text-muted-foreground mt-1">
|
||||||
|
The Service is for informational and organizational purposes only. It is not intended to provide legal, tax, or financial advice.
|
||||||
|
We are not responsible for any financial decisions you make based on the information you track within the Service.
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
|
</section>
|
||||||
|
<section>
|
||||||
|
<h2 class="text-2xl font-semibold mb-4">3. Accounts</h2>
|
||||||
|
<p class="text-muted-foreground mb-4">
|
||||||
|
When you create an account with us, you must provide accurate and complete information.
|
||||||
|
You are responsible for safeguarding the password or magic link used to access the Service and for any activities or actions under your account.
|
||||||
|
</p>
|
||||||
|
</section>
|
||||||
|
<section>
|
||||||
|
<h2 class="text-2xl font-semibold mb-4">4. "As Is" and "As Available" Disclaimer</h2>
|
||||||
|
<p class="text-muted-foreground mb-4">
|
||||||
|
The Service is provided on an "AS IS" and "AS AVAILABLE" basis. The Service is provided without warranties of any kind, whether express or implied, including, but not limited to, implied warranties of merchantability, fitness for a particular purpose, non-infringement, or course of performance.
|
||||||
|
</p>
|
||||||
|
<p class="text-muted-foreground">
|
||||||
|
As a hobby project, we do not warrant that the Service will function uninterrupted, secure, or available at any particular time or location, or that any errors or defects will be corrected.
|
||||||
|
</p>
|
||||||
|
</section>
|
||||||
|
<section>
|
||||||
|
<h2 class="text-2xl font-semibold mb-4">5. Limitation of Liability</h2>
|
||||||
|
<p class="text-muted-foreground">
|
||||||
|
In no event shall the operator, nor any partners, agents, suppliers, or affiliates, be liable for any indirect, incidental, special, consequential or punitive damages, including without limitation, loss of profits, data, use, goodwill, or other intangible losses, resulting from your access to or use of or inability to access or use the Service.
|
||||||
|
</p>
|
||||||
|
</section>
|
||||||
|
<section>
|
||||||
|
<h2 class="text-2xl font-semibold mb-4">6. User Conduct</h2>
|
||||||
|
<p class="text-muted-foreground">
|
||||||
|
You agree not to use the Service for any unlawful purpose or in any way that interrupts, damages, or impairs the service.
|
||||||
|
We reserve the right to terminate or suspend your account immediately, without prior notice or liability, for any reason whatsoever, including without limitation if you breach the Terms.
|
||||||
|
</p>
|
||||||
|
</section>
|
||||||
|
<section>
|
||||||
|
<h2 class="text-2xl font-semibold mb-4">7. Governing Law</h2>
|
||||||
|
<p class="text-muted-foreground">
|
||||||
|
These Terms shall be governed and construed in accordance with the laws of Canada, without regard to its conflict of law provisions.
|
||||||
|
</p>
|
||||||
|
</section>
|
||||||
|
<section>
|
||||||
|
<h2 class="text-2xl font-semibold mb-4">8. Changes to Terms</h2>
|
||||||
|
<p class="text-muted-foreground">
|
||||||
|
We reserve the right, at our sole discretion, to modify or replace these Terms at any time. By continuing to access or use our Service after those revisions become effective, you agree to be bound by the revised terms.
|
||||||
|
</p>
|
||||||
|
</section>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
}
|
||||||
|
}
|
||||||
Loading…
Add table
Add a link
Reference in a new issue