Add shared legal pages and wire Privacy Policy and Terms across the app.

Extract privacy and terms content into reusable components, replace auth modals with links to /privacy and /terms, add settings legal section, and remove duplicate legal-modal markup.

Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
2026-06-18 01:33:34 -04:00
parent b7380f4348
commit 69da2bf71d
19 changed files with 1116 additions and 1149 deletions
+2
View File
@@ -1,5 +1,7 @@
# beenvoice - AI Assistant Rules
> **Canonical architecture reference:** [docs/ARCHITECTURE.md](./docs/ARCHITECTURE.md) (stack, routers, schema, auth). This file may lag behind; prefer ARCHITECTURE.md for facts.
## Project Overview
beenvoice is a professional invoicing application built with the T3 stack (Next.js 15, tRPC, Drizzle/LibSQL, NextAuth.js) and shadcn/ui components. This is a business-critical application where reliability, security, and professional user experience are paramount.
+36 -38
View File
@@ -1,14 +1,29 @@
![beenvoice Logo](public/beenvoice-logo.png)
# beenvoice - Invoicing Made Simple
# beenvoice Invoicing Made Simple
A modern, professional invoicing application built for freelancers and small businesses. beenvoice provides a clean, efficient way to manage clients and create professional invoices with ease.
Modern invoicing for freelancers and small businesses: clients, businesses, invoices, time tracking, expenses, recurring billing, PDF/email delivery, and optional SSO.
![beenvoice Logo](https://img.shields.io/badge/beenvoice-Invoicing%20Made%20Simple-green?style=for-the-badge)
**Architecture (dense):** [docs/ARCHITECTURE.md](./docs/ARCHITECTURE.md)
**Mobile companion:** [../beenvoice-app/README.md](../beenvoice-app/README.md)
## ✨ Features
## Stack at a glance
- **🔐 Secure Authentication** - Email/password registration and sign-in with better-auth, plus SSO via Authentik OIDC
| Layer | Tech |
|-------|------|
| App | Next.js 16 App Router, React 19 |
| API | tRPC 11 + SuperJSON |
| DB | PostgreSQL, Drizzle ORM |
| Auth | better-auth (email/password, Authentik OIDC, Expo mobile) |
| UI | shadcn/ui, Tailwind v4 |
| Email / PDF | Resend, @react-pdf/renderer |
| Package manager | Bun |
## Features
- **🔐 Authentication** — better-auth: email/password, password reset, optional Authentik OIDC, Expo mobile sessions
- **⏱ Time clock** — running timer, one per user; clock-out can append invoice line items
- **🤖 MCP API** — `/api/mcp` for automation via API keys (`bv_…`)
- **👥 Client Management** - Create, edit, and manage client information
- **🏢 Business Profiles** - Manage your business details, logo, and email settings
- **📄 Professional Invoices** - Generate detailed invoices with line items
@@ -103,35 +118,19 @@ A modern, professional invoicing application built for freelancers and small bus
7. **Open your browser**
Navigate to [http://localhost:3000](http://localhost:3000)
## 🏗️ Project Structure
## 🏗️ Project structure
See [docs/ARCHITECTURE.md](./docs/ARCHITECTURE.md) for routers, schema, auth, and MCP.
```
beenvoice/
├── src/
├── app/ # Next.js App Router pages
│ │ ├── api/ # API routes (better-auth, tRPC)
│ │ ├── auth/ # Authentication pages
│ │ ├── dashboard/ # Main app pages
│ │ │ ├── clients/ # Client management pages
├── invoices/ # Invoice management pages
│ │ │ └── businesses/ # Business profile pages
│ │ └── _components/ # Page-specific components
│ ├── components/ # Shared UI components
│ │ ├── ui/ # shadcn/ui components
│ │ ├── data/ # Data display components
│ │ ├── forms/ # Form components
│ │ └── layout/ # Layout components
│ ├── server/ # Server-side code
│ │ ├── api/ # tRPC routers
│ │ └── db/ # Database schema and connection
│ ├── lib/ # Utilities (auth, pdf export, etc.)
│ ├── styles/ # Global styles
│ └── trpc/ # tRPC client configuration
├── drizzle/ # Database migrations
├── public/ # Static assets
├── docs/ # Documentation
├── docker-compose.yml # Deployment compose stack
└── docker-compose.dev.yml # Development overrides with exposed PostgreSQL
├── src/app/ # Pages + /api (auth, trpc, mcp, cron, public PDF)
├── src/server/api/ # tRPC routers
├── src/server/db/ # Drizzle schema + pool
├── src/components/ # UI + domain components
├── src/lib/ # auth, PDF, email, branding
├── drizzle/ # SQL migrations
└── docs/ # Architecture + UI guides
```
## 🎯 Usage
@@ -250,15 +249,14 @@ The application uses the following core tables:
- **invoices** - Invoice headers with client and business relationships
- **invoice_items** - Individual line items with pricing and position ordering
### API Development
### API surface
All API endpoints are built with tRPC for type safety:
- **tRPC** — `/api/trpc` — primary API for web and mobile (session cookies)
- **MCP** — `/api/mcp` — JSON-RPC tools for integrations (API key only)
- **REST auth** — `/api/auth/register`, forgot/reset password (mobile + custom flows)
- **Public** — `/i/[token]`, `/api/i/[token]/pdf`
- **Authentication**: better-auth integration (email/password + OIDC)
- **Clients**: CRUD operations for client management
- **Businesses**: Business profile management
- **Invoices**: Invoice creation, management, and status tracking
- **Validation**: Zod schemas for input validation
All business logic lives in `src/server/api/routers/`. Input validation via Zod.
## 🎨 Customization
+211
View File
@@ -0,0 +1,211 @@
# beenvoice server architecture
Dense reference for the Next.js web application and API in `beenvoice/`. Package manager: **Bun**. Database: **PostgreSQL** via Drizzle ORM.
## Stack
| Layer | Technology |
|-------|------------|
| Framework | Next.js 16 App Router (`src/app/`) |
| API | tRPC 11 (`/api/trpc`), SuperJSON transformer |
| ORM | Drizzle + `pg` pool |
| Auth | better-auth (email/password, optional Authentik OIDC, Expo plugin for mobile) |
| UI | shadcn/ui, Tailwind CSS v4, Radix primitives |
| Email | Resend |
| PDF | `@react-pdf/renderer` |
## Request flow
```
Browser / Mobile / MCP client
├─► /api/auth/* → better-auth handler (session cookies)
├─► /api/trpc/* → createContext() → appRouter
│ ├─ Bearer / x-api-key → api-key auth
│ └─ else → better-auth session
├─► /api/mcp → API key only → JSON-RPC tools → tRPC caller
├─► /api/i/[token]/pdf → public invoice PDF
└─► /dashboard/* → RSC + client components (session required in UI)
```
**Context** (`src/server/api/trpc.ts`): `protectedProcedure` requires `ctx.session.user`. API-key auth sets `authSource: "api-key"`; `apiKeys.*` mutations require session (cannot manage keys with a key).
## Directory layout
```
src/
├── app/ # Routes (pages + route handlers)
│ ├── api/
│ │ ├── auth/ # better-auth catch-all + custom register/reset REST
│ │ ├── trpc/[trpc]/ # tRPC HTTP adapter
│ │ ├── mcp/ # MCP over HTTP (API key)
│ │ ├── i/[token]/pdf/ # Public PDF
│ │ └── cron/ # Recurring invoice generation (CRON_SECRET)
│ ├── auth/ # sign-in, register, forgot/reset password
│ ├── dashboard/ # Authenticated app shell
│ └── i/[token]/ # Public invoice view
├── components/ # Shared UI (ui/, forms/, layout/, data/)
├── hooks/
├── lib/ # auth.ts, pdf-export, email templates, branding
├── server/
│ ├── api/
│ │ ├── root.ts # appRouter composition
│ │ ├── trpc.ts # procedures, context, timing middleware (dev)
│ │ ├── api-keys.ts
│ │ └── routers/ # one file per domain
│ └── db/
│ ├── schema.ts # all tables (prefix beenvoice_)
│ ├── index.ts # drizzle + pool
│ └── migrate.ts
├── trpc/ # react.tsx (client), server.ts (RSC)
├── env.js # @t3-oss/env-nextjs validation
└── styles/globals.css
drizzle/ # SQL migrations (00000014+)
```
## tRPC routers
Root: `src/server/api/root.ts`. All routers use Zod input validation.
| Namespace | File | Key procedures |
|-----------|------|----------------|
| `clients` | `routers/clients.ts` | getAll, getById, create, update, delete |
| `businesses` | `routers/businesses.ts` | getAll, getById, getDefault, create, update, delete, setDefault, getEmailConfig, updateEmailConfig |
| `invoices` | `routers/invoices.ts` | getAll, getBillable, getById, create, update, delete, updateStatus, bulk*, previewPdf, public token, **getByPublicToken** (public), sendReminder |
| `payments` | `routers/payments.ts` | getByInvoice, create, delete |
| `expenses` | `routers/expenses.ts` | getAll, getById, create, update, delete |
| `invoiceTemplates` | `routers/invoiceTemplates.ts` | CRUD by template type |
| `recurringInvoices` | `routers/recurring-invoices.ts` | CRUD, pause/resume, generateNow; cron helper `generateDueRecurringInvoices` |
| `timeEntries` | `routers/time-entries.ts` | getAll, getRunning, clockIn, updateRunning, clockOut, create, update, delete, getSummary |
| `dashboard` | `routers/dashboard.ts` | getStats |
| `email` | `routers/email.ts` | sendInvoice |
| `settings` | `routers/settings.ts` | profile, theme, animation prefs, export/import data, admin account roles |
| `apiKeys` | `routers/apiKeys.ts` | list, create, revoke (session-only) |
### Time clock semantics
- **One running entry per user** — partial unique index on `(createdById)` where `endedAt IS NULL`.
- `clockIn` — optional client, invoice, rate, backdated `startedAt`; resolves rate from input → client default → business default.
- `clockOut` — optional description update; computes hours; if `invoiceId` set, appends line item; else tries latest open invoice for client.
- Outcomes: `linked_to_invoice`, `saved_no_invoice`, `saved_no_client`, `zero_hours`.
## Database schema
Single file: `src/server/db/schema.ts`. Table names use `pgTableCreator` → prefix `beenvoice_`.
### Auth & platform
| Table | Notes |
|-------|-------|
| `beenvoice_user` | Core user; role for admin features |
| `beenvoice_account` | OAuth/credential accounts (better-auth) |
| `beenvoice_session` | Sessions; unique token |
| `beenvoice_verification_token` | Email verification / reset |
| `beenvoice_api_key` | `bv_` prefix keys; SHA-256 hash stored |
| `beenvoice_sso_provider` | OIDC/SAML config per user |
| `beenvoice_platform_setting` | Singleton (`id = global`) branding/PDF/appearance |
### Domain
| Table | FKs | Notes |
|-------|-----|-------|
| `beenvoice_client` | `createdById` → user | defaultHourlyRate, currency |
| `beenvoice_business` | `createdById` | Resend config, `isDefault` |
| `beenvoice_invoice` | client, business?, user | status draft/sent/paid; `publicToken` |
| `beenvoice_invoice_item` | invoice (cascade) | position ordering |
| `beenvoice_invoice_payment` | invoice, user | payment method enum |
| `beenvoice_expense` | business?, client?, invoice? | billable flags |
| `beenvoice_invoice_template` | user | notes/terms templates |
| `beenvoice_recurring_invoice` | client, business?, user | schedule, `nextDueAt` |
| `beenvoice_recurring_invoice_item` | recurring (cascade) | |
| `beenvoice_time_entry` | client?, invoice?, user | `endedAt` null = running |
Migrations: `bun run db:generate``drizzle/`; apply with `db:push` (dev) or `db:migrate` (prod script).
## Authentication
**Server**`src/lib/auth.ts`:
- `betterAuth` + `drizzleAdapter` (users, sessions, accounts, verification)
- Plugins: `@better-auth/expo` (mobile SecureStore cookies), `nextCookies()`, optional `genericOAuth` (Authentik)
- Email/password with bcrypt (12 rounds); `DISABLE_SIGNUPS=true` blocks registration
- `trustedOrigins`: production URL, `beenvoice://`, `exp://` (Expo)
**Web client**`src/lib/auth-client.ts`: `createAuthClient` + `genericOAuthClient`.
**Routes**:
- `src/app/api/auth/[...all]/route.ts` — better-auth handler
- Custom REST: `register`, `forgot-password`, `reset-password`, `validate-reset-token` (used by mobile and legacy flows)
**Session cookies**: `better-auth.session_token` or `__Secure-better-auth.session_token` in production.
## Mobile API contract
The Expo app (`beenvoice-app`) does **not** use API keys. It:
1. Calls the same tRPC endpoints with `Authorization` cookie header from `authClient.getCookie()`.
2. Stores session per account in SecureStore via `@better-auth/expo` (`storagePrefix`: `beenvoice:guest` or `beenvoice:auth:{accountId}`).
3. Requires `trustedOrigins` and matching `BETTER_AUTH_URL` for the host the device can reach.
Ensure `src/lib/auth.ts` keeps the `expo()` plugin enabled.
## MCP (machine clients)
`POST /api/mcp` — JSON-RPC 2.0, protocol `2025-11-25`.
- **Auth**: API key only (`Authorization: Bearer bv_…` or `x-api-key`). Session cookies rejected.
- **Tools**: ~50 tools mirroring tRPC (invoices, clients, time clock, expenses, etc.)
- Implemented in `src/app/api/mcp/route.ts`; delegates to `createCaller(createContext)`.
API keys: format `bv_<base64url>`; stored as SHA-256 hash (`src/server/api/api-keys.ts`).
## Environment variables
Validated in `src/env.js`. See `.env.example`.
| Variable | Required | Notes |
|----------|----------|-------|
| `DATABASE_URL` | yes | PostgreSQL connection string |
| `AUTH_SECRET` | prod | `openssl rand -base64 32` |
| `BETTER_AUTH_URL` | yes | Public URL of API (no trailing path) |
| `NEXT_PUBLIC_APP_URL` | yes | Browser-facing URL |
| `DB_DISABLE_SSL` | local | `true` for Docker dev DB |
| `RESEND_API_KEY`, `RESEND_DOMAIN` | optional | Email; blank disables send |
| `AUTHENTIK_*` | optional | OIDC SSO |
| `DISABLE_SIGNUPS` | optional | `true` blocks registration |
| `CRON_SECRET` | cron route | Protects `/api/cron/generate-recurring` |
| `NEXT_PUBLIC_BRAND_*` | optional | Build-time white-label defaults |
## Docker
| File | Use |
|------|-----|
| `docker-compose.yml` | Production: `app` + `db` (Postgres internal) |
| `docker-compose.dev.yml` | Dev: Postgres only, port `${POSTGRES_PORT:-5432}` |
App image built from `Dockerfile`; runs `next start` on port 3000.
## Scripts
```bash
bun run dev # next dev --turbo
bun run build # production build
bun run db:push # push schema (dev)
bun run db:migrate # run migrations
bun run db:studio # Drizzle Studio
bun run check # eslint + tsc
```
## Public / unauthenticated surfaces
- `invoices.getByPublicToken` (tRPC publicProcedure)
- `/i/[token]` page and `/api/i/[token]/pdf`
- Auth REST endpoints for register/reset
## Related docs
- [forms-guide.md](./forms-guide.md), [UI_UNIFORMITY_GUIDE.md](./UI_UNIFORMITY_GUIDE.md)
- [data-table-responsive-guide.md](./data-table-responsive-guide.md)
- [email-features.md](./email-features.md)
- Mobile companion: `../beenvoice-app/docs/ARCHITECTURE.md`
+33
View File
@@ -0,0 +1,33 @@
# beenvoice documentation
## Core
| Document | Description |
|----------|-------------|
| [ARCHITECTURE.md](./ARCHITECTURE.md) | Server stack, tRPC routers, schema, auth, MCP, Docker, mobile API contract |
| [../README.md](../README.md) | Install, scripts, deployment |
## UI & product guides
| Document | Description |
|----------|-------------|
| [forms-guide.md](./forms-guide.md) | Form patterns |
| [UI_UNIFORMITY_GUIDE.md](./UI_UNIFORMITY_GUIDE.md) | Visual consistency |
| [breadcrumbs-guide.md](./breadcrumbs-guide.md) | Navigation breadcrumbs |
| [data-table-responsive-guide.md](./data-table-responsive-guide.md) | Responsive tables |
| [data-table-improvements.md](./data-table-improvements.md) | Table enhancements |
| [RESPONSIVE_TABLE_EXAMPLES.md](./RESPONSIVE_TABLE_EXAMPLES.md) | Table examples |
| [email-features.md](./email-features.md) | Email composer / delivery |
## Mobile
| Document | Description |
|----------|-------------|
| [../../beenvoice-app/docs/ARCHITECTURE.md](../../beenvoice-app/docs/ARCHITECTURE.md) | Expo app architecture |
| [../../beenvoice-app/README.md](../../beenvoice-app/README.md) | Mobile setup |
## Workspace
| Document | Description |
|----------|-------------|
| [../../README.md](../../README.md) | Meta repo layout, full-stack quick start |
+18
View File
@@ -0,0 +1,18 @@
import type { Metadata } from "next";
import { brand } from "~/lib/branding";
export const metadata: Metadata = {
title: {
template: `%s | ${brand.name}`,
default: `Legal | ${brand.name}`,
},
};
export default function LegalLayout({
children,
}: {
children: React.ReactNode;
}) {
return children;
}
+13 -385
View File
@@ -1,390 +1,18 @@
import { Card, CardContent, CardHeader, CardTitle } from "~/components/ui/card";
import { Button } from "~/components/ui/button";
import Link from "next/link";
import { ArrowLeft } from "lucide-react";
import type { Metadata } from "next";
import { PrivacyPolicyContent } from "~/components/legal/privacy-policy-content";
import { LegalPageShell } from "~/components/legal/legal-page-shell";
import { brand } from "~/lib/branding";
export const metadata: Metadata = {
title: `Privacy Policy | ${brand.name}`,
description: `How ${brand.name} collects, uses, and protects your data.`,
};
export default function PrivacyPolicyPage() {
return (
<div className="bg-background min-h-screen">
{/* Header */}
<div className="bg-card border-b">
<div className="container mx-auto max-w-4xl px-6 py-6">
<div className="flex items-center space-x-4">
<Link href="/auth/signin">
<Button variant="outline" size="sm">
<ArrowLeft className="mr-2 h-4 w-4" />
Back
</Button>
</Link>
<div>
<h1 className="text-2xl font-bold">Privacy Policy</h1>
<p className="text-muted-foreground text-sm">
Last updated: {new Date().toLocaleDateString()}
</p>
</div>
</div>
</div>
</div>
{/* Content */}
<div className="container mx-auto max-w-4xl px-6 py-8">
<div className="space-y-6">
<Card>
<CardHeader>
<CardTitle>Introduction</CardTitle>
</CardHeader>
<CardContent className="prose prose-sm max-w-none">
<p>
beenvoice (&quot;we&quot;, &quot;our&quot;, or &quot;us&quot;)
is committed to protecting your privacy. This Privacy Policy
explains how we collect, use, disclose, and safeguard your
information when you use our invoicing platform and services.
</p>
<p>
Please read this Privacy Policy carefully. If you do not agree
with the terms of this Privacy Policy, please do not access or
use our Service.
</p>
</CardContent>
</Card>
<Card>
<CardHeader>
<CardTitle>Information We Collect</CardTitle>
</CardHeader>
<CardContent className="prose prose-sm max-w-none">
<h4>Personal Information</h4>
<p>
We may collect personal information that you voluntarily provide
to us when you:
</p>
<ul>
<li>Register for an account</li>
<li>Create invoices or manage client information</li>
<li>Contact us for support</li>
<li>Subscribe to our newsletters or communications</li>
</ul>
<p>This personal information may include:</p>
<ul>
<li>Name and contact information (email, phone, address)</li>
<li>Business information and tax details</li>
<li>Client information you input into the system</li>
<li>Financial information related to your invoices</li>
<li>
Payment information (processed securely by third-party
providers)
</li>
</ul>
<h4>Automatically Collected Information</h4>
<p>
We may automatically collect certain information when you visit
our Service:
</p>
<ul>
<li>
Device information (IP address, browser type, operating
system)
</li>
<li>Usage data (pages visited, time spent, features used)</li>
<li>Log files and analytics data</li>
<li>Cookies and similar tracking technologies</li>
</ul>
</CardContent>
</Card>
<Card>
<CardHeader>
<CardTitle>How We Use Your Information</CardTitle>
</CardHeader>
<CardContent className="prose prose-sm max-w-none">
<p>We use the information we collect to:</p>
<ul>
<li>Provide, operate, and maintain our Service</li>
<li>Process your transactions and manage your account</li>
<li>Improve and personalize your experience</li>
<li>
Communicate with you about your account and our services
</li>
<li>Send you technical notices and support messages</li>
<li>Respond to your comments, questions, and requests</li>
<li>Monitor usage and analyze trends</li>
<li>
Detect, prevent, and address technical issues and security
breaches
</li>
<li>Comply with legal obligations</li>
</ul>
</CardContent>
</Card>
<Card>
<CardHeader>
<CardTitle>How We Share Your Information</CardTitle>
</CardHeader>
<CardContent className="prose prose-sm max-w-none">
<p>
We do not sell, trade, or rent your personal information to
third parties. We may share your information in the following
circumstances:
</p>
<h4>Service Providers</h4>
<p>
We may share your information with trusted third-party service
providers who assist us in operating our Service, such as:
</p>
<ul>
<li>Cloud hosting and storage providers</li>
<li>Payment processors</li>
<li>Email service providers</li>
<li>Analytics and monitoring services</li>
</ul>
<h4>Legal Requirements</h4>
<p>
We may disclose your information if required to do so by law or
in response to:
</p>
<ul>
<li>Legal processes (subpoenas, court orders)</li>
<li>Government requests</li>
<li>Law enforcement investigations</li>
<li>Protection of our rights, property, or safety</li>
</ul>
<h4>Business Transfers</h4>
<p>
In the event of a merger, acquisition, or sale of assets, your
information may be transferred as part of that transaction.
</p>
</CardContent>
</Card>
<Card>
<CardHeader>
<CardTitle>Data Security</CardTitle>
</CardHeader>
<CardContent className="prose prose-sm max-w-none">
<p>
We implement appropriate technical and organizational security
measures to protect your information:
</p>
<ul>
<li>Encryption of data in transit and at rest</li>
<li>Secure access controls and authentication</li>
<li>Regular security assessments and updates</li>
<li>Employee training on data protection</li>
<li>Incident response procedures</li>
</ul>
<p>
However, no method of transmission over the internet or
electronic storage is 100% secure. While we strive to protect
your information, we cannot guarantee absolute security.
</p>
</CardContent>
</Card>
<Card>
<CardHeader>
<CardTitle>Data Retention</CardTitle>
</CardHeader>
<CardContent className="prose prose-sm max-w-none">
<p>
We retain your personal information only for as long as
necessary to fulfill the purposes outlined in this Privacy
Policy, unless a longer retention period is required by law.
</p>
<p>
Factors we consider when determining retention periods include:
</p>
<ul>
<li>The nature and sensitivity of the information</li>
<li>Legal and regulatory requirements</li>
<li>Business and operational needs</li>
<li>Your account status and activity</li>
</ul>
</CardContent>
</Card>
<Card>
<CardHeader>
<CardTitle>Your Rights and Choices</CardTitle>
</CardHeader>
<CardContent className="prose prose-sm max-w-none">
<p>
Depending on your location, you may have the following rights
regarding your personal information:
</p>
<h4>Access and Portability</h4>
<ul>
<li>Request access to your personal information</li>
<li>Receive a copy of your data in a portable format</li>
</ul>
<h4>Correction and Updates</h4>
<ul>
<li>Correct inaccurate or incomplete information</li>
<li>Update your account information at any time</li>
</ul>
<h4>Deletion</h4>
<ul>
<li>Request deletion of your personal information</li>
<li>Close your account and remove your data</li>
</ul>
<h4>Restriction and Objection</h4>
<ul>
<li>Restrict the processing of your information</li>
<li>Object to certain uses of your data</li>
</ul>
<p>
To exercise these rights, please contact us using the
information provided in the &quot;Contact Us&quot; section
below.
</p>
</CardContent>
</Card>
<Card>
<CardHeader>
<CardTitle>Cookies and Tracking Technologies</CardTitle>
</CardHeader>
<CardContent className="prose prose-sm max-w-none">
<p>We use cookies and similar technologies to:</p>
<ul>
<li>Remember your preferences and settings</li>
<li>Authenticate your account</li>
<li>Analyze usage patterns and improve our Service</li>
<li>Provide personalized content and features</li>
</ul>
<p>
You can control cookies through your browser settings. However,
disabling cookies may affect the functionality of our Service.
</p>
<h4>Types of Cookies We Use</h4>
<ul>
<li>
<strong>Essential Cookies:</strong> Required for the Service
to function properly
</li>
<li>
<strong>Analytics Cookies:</strong> Help us understand how you
use our Service
</li>
<li>
<strong>Preference Cookies:</strong> Remember your settings
and preferences
</li>
</ul>
</CardContent>
</Card>
<Card>
<CardHeader>
<CardTitle>Third-Party Links and Services</CardTitle>
</CardHeader>
<CardContent className="prose prose-sm max-w-none">
<p>
Our Service may contain links to third-party websites or
integrate with third-party services. We are not responsible for
the privacy practices of these third parties.
</p>
<p>
We encourage you to read the privacy policies of any third-party
services you use in connection with our Service.
</p>
</CardContent>
</Card>
<Card>
<CardHeader>
<CardTitle>Children&apos;s Privacy</CardTitle>
</CardHeader>
<CardContent className="prose prose-sm max-w-none">
<p>
Our Service is not intended for children under the age of 13. We
do not knowingly collect personal information from children
under 13.
</p>
<p>
If you are a parent or guardian and believe your child has
provided us with personal information, please contact us
immediately so we can remove such information.
</p>
</CardContent>
</Card>
<Card>
<CardHeader>
<CardTitle>International Data Transfers</CardTitle>
</CardHeader>
<CardContent className="prose prose-sm max-w-none">
<p>
Your information may be transferred to and processed in
countries other than your own. We ensure that such transfers
comply with applicable data protection laws.
</p>
<p>
When we transfer your information internationally, we implement
appropriate safeguards to protect your data, including:
</p>
<ul>
<li>Standard contractual clauses</li>
<li>Adequacy decisions by relevant authorities</li>
<li>Certified privacy frameworks</li>
</ul>
</CardContent>
</Card>
<Card>
<CardHeader>
<CardTitle>Changes to This Privacy Policy</CardTitle>
</CardHeader>
<CardContent className="prose prose-sm max-w-none">
<p>
We may update this Privacy Policy from time to time. We will
notify you of any material changes by:
</p>
<ul>
<li>Posting the updated policy on our Service</li>
<li>Sending you an email notification</li>
<li>Displaying a prominent notice on our Service</li>
</ul>
<p>
Your continued use of our Service after any changes indicates
your acceptance of the updated Privacy Policy.
</p>
</CardContent>
</Card>
<Card>
<CardHeader>
<CardTitle>Contact Us</CardTitle>
</CardHeader>
<CardContent className="prose prose-sm max-w-none">
<p>
If you have questions about this Privacy Policy or our privacy
practices, please contact us at:
</p>
<ul>
<li>Email: privacy@beenvoice.com</li>
<li>Address: [Your Business Address]</li>
</ul>
<p>
We will respond to your inquiries within a reasonable timeframe
and in accordance with applicable law.
</p>
</CardContent>
</Card>
</div>
</div>
</div>
<LegalPageShell title="Privacy Policy">
<PrivacyPolicyContent />
</LegalPageShell>
);
}
+13 -302
View File
@@ -1,307 +1,18 @@
import { Card, CardContent, CardHeader, CardTitle } from "~/components/ui/card";
import { Button } from "~/components/ui/button";
import Link from "next/link";
import { ArrowLeft } from "lucide-react";
import type { Metadata } from "next";
import { LegalPageShell } from "~/components/legal/legal-page-shell";
import { TermsOfServiceContent } from "~/components/legal/terms-of-service-content";
import { brand } from "~/lib/branding";
export const metadata: Metadata = {
title: `Terms of Service | ${brand.name}`,
description: `Terms governing your use of the ${brand.name} platform.`,
};
export default function TermsOfServicePage() {
return (
<div className="bg-background min-h-screen">
{/* Header */}
<div className="bg-card border-b">
<div className="container mx-auto max-w-4xl px-6 py-6">
<div className="flex items-center space-x-4">
<Link href="/auth/signin">
<Button variant="outline" size="sm">
<ArrowLeft className="mr-2 h-4 w-4" />
Back
</Button>
</Link>
<div>
<h1 className="text-2xl font-bold">Terms of Service</h1>
<p className="text-muted-foreground text-sm">
Last updated: {new Date().toLocaleDateString()}
</p>
</div>
</div>
</div>
</div>
{/* Content */}
<div className="container mx-auto max-w-4xl px-6 py-8">
<div className="space-y-6">
<Card>
<CardHeader>
<CardTitle>Agreement to Terms</CardTitle>
</CardHeader>
<CardContent className="prose prose-sm max-w-none">
<p>
These Terms of Service (&quot;Terms&quot;) govern your use of
the beenvoice platform and services (the &quot;Service&quot;)
operated by beenvoice (&quot;us&quot;, &quot;we&quot;, or
&quot;our&quot;).
</p>
<p>
By accessing or using our Service, you agree to be bound by
these Terms. If you disagree with any part of these terms, then
you may not access the Service.
</p>
</CardContent>
</Card>
<Card>
<CardHeader>
<CardTitle>Description of Service</CardTitle>
</CardHeader>
<CardContent className="prose prose-sm max-w-none">
<p>
beenvoice is a web-based invoicing platform that allows users
to:
</p>
<ul>
<li>Create and manage professional invoices</li>
<li>Track client information and billing details</li>
<li>Monitor payment status and financial metrics</li>
<li>Generate reports and analytics</li>
<li>Manage business profiles and settings</li>
</ul>
</CardContent>
</Card>
<Card>
<CardHeader>
<CardTitle>User Accounts</CardTitle>
</CardHeader>
<CardContent className="prose prose-sm max-w-none">
<p>
When you create an account with us, you must provide information
that is accurate, complete, and current at all times. You are
responsible for safeguarding the password and for all activities
that occur under your account.
</p>
<p>
You agree not to disclose your password to any third party. You
must notify us immediately upon becoming aware of any breach of
security or unauthorized use of your account.
</p>
</CardContent>
</Card>
<Card>
<CardHeader>
<CardTitle>Acceptable Use</CardTitle>
</CardHeader>
<CardContent className="prose prose-sm max-w-none">
<p>You agree not to use the Service:</p>
<ul>
<li>
For any unlawful purpose or to solicit others to perform
unlawful acts
</li>
<li>
To violate any international, federal, provincial, or state
regulations, rules, laws, or local ordinances
</li>
<li>
To infringe upon or violate our intellectual property rights
or the intellectual property rights of others
</li>
<li>
To harass, abuse, insult, harm, defame, slander, disparage,
intimidate, or discriminate
</li>
<li>To submit false or misleading information</li>
<li>
To upload or transmit viruses or any other type of malicious
code
</li>
<li>
To spam, phish, pharm, pretext, spider, crawl, or scrape
</li>
<li>For any obscene or immoral purpose</li>
<li>
To interfere with or circumvent the security features of the
Service
</li>
</ul>
</CardContent>
</Card>
<Card>
<CardHeader>
<CardTitle>Data and Privacy</CardTitle>
</CardHeader>
<CardContent className="prose prose-sm max-w-none">
<p>
Your privacy is important to us. Please review our Privacy
Policy, which also governs your use of the Service, to
understand our practices.
</p>
<p>
You retain ownership of your data. We will not sell, rent, or
share your personal information with third parties without your
explicit consent, except as described in our Privacy Policy.
</p>
<p>
You are responsible for backing up your data. While we implement
regular backups, we recommend you maintain your own copies of
important information.
</p>
</CardContent>
</Card>
<Card>
<CardHeader>
<CardTitle>Payment Terms</CardTitle>
</CardHeader>
<CardContent className="prose prose-sm max-w-none">
<p>
Some aspects of the Service may require payment. You will be
charged according to your subscription plan. All fees are
non-refundable unless otherwise stated.
</p>
<p>
We may change our fees at any time. We will provide you with
reasonable notice of any fee changes by posting the new fees on
the Service or sending you email notification.
</p>
<p>
If you fail to pay any fees when due, we may suspend or
terminate your access to the Service until payment is made.
</p>
</CardContent>
</Card>
<Card>
<CardHeader>
<CardTitle>Intellectual Property Rights</CardTitle>
</CardHeader>
<CardContent className="prose prose-sm max-w-none">
<p>
The Service and its original content, features, and
functionality are and will remain the exclusive property of
beenvoice and its licensors. The Service is protected by
copyright, trademark, and other laws.
</p>
<p>
Our trademarks and trade dress may not be used in connection
with any product or service without our prior written consent.
</p>
</CardContent>
</Card>
<Card>
<CardHeader>
<CardTitle>Termination</CardTitle>
</CardHeader>
<CardContent className="prose prose-sm max-w-none">
<p>
We may terminate or suspend your account and bar access to the
Service immediately, without prior notice or liability, under
our sole discretion, for any reason whatsoever and without
limitation, including but not limited to a breach of the Terms.
</p>
<p>
If you wish to terminate your account, you may simply
discontinue using the Service and contact us to request account
deletion.
</p>
<p>
Upon termination, your right to use the Service will cease
immediately. If you wish to terminate your account, you may
simply discontinue using the Service.
</p>
</CardContent>
</Card>
<Card>
<CardHeader>
<CardTitle>Disclaimer of Warranties</CardTitle>
</CardHeader>
<CardContent className="prose prose-sm max-w-none">
<p>
The information on this Service is provided on an &quot;as
is&quot; basis. To the fullest extent permitted by law, we
exclude all representations, warranties, and conditions relating
to our Service and the use of this Service.
</p>
<p>
Nothing in this disclaimer will limit or exclude our or your
liability for death or personal injury resulting from
negligence, fraud, or fraudulent misrepresentation.
</p>
</CardContent>
</Card>
<Card>
<CardHeader>
<CardTitle>Limitation of Liability</CardTitle>
</CardHeader>
<CardContent className="prose prose-sm max-w-none">
<p>
In no event shall beenvoice, nor its directors, employees,
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
use of the Service.
</p>
</CardContent>
</Card>
<Card>
<CardHeader>
<CardTitle>Governing Law</CardTitle>
</CardHeader>
<CardContent className="prose prose-sm max-w-none">
<p>
These Terms shall be interpreted and governed by the laws of the
jurisdiction in which beenvoice operates, without regard to its
conflict of law provisions.
</p>
<p>
Our failure to enforce any right or provision of these Terms
will not be considered a waiver of those rights.
</p>
</CardContent>
</Card>
<Card>
<CardHeader>
<CardTitle>Changes to Terms</CardTitle>
</CardHeader>
<CardContent className="prose prose-sm max-w-none">
<p>
We reserve the right, at our sole discretion, to modify or
replace these Terms at any time. If a revision is material, we
will provide at least 30 days notice prior to any new terms
taking effect.
</p>
<p>
What constitutes a material change will be determined at our
sole discretion. By continuing to access or use our Service
after any revisions become effective, you agree to be bound by
the revised terms.
</p>
</CardContent>
</Card>
<Card>
<CardHeader>
<CardTitle>Contact Information</CardTitle>
</CardHeader>
<CardContent className="prose prose-sm max-w-none">
<p>
If you have any questions about these Terms of Service, please
contact us at:
</p>
<ul>
<li>Email: legal@beenvoice.com</li>
<li>Address: [Your Business Address]</li>
</ul>
</CardContent>
</Card>
</div>
</div>
</div>
<LegalPageShell title="Terms of Service">
<TermsOfServiceContent />
</LegalPageShell>
);
}
+5 -22
View File
@@ -7,7 +7,7 @@ import { Button } from "~/components/ui/button";
import { Label } from "~/components/ui/label";
import { toast } from "sonner";
import { Logo } from "~/components/branding/logo";
import { LegalModal } from "~/components/ui/legal-modal";
import { LegalAgreementNotice } from "~/components/legal/legal-links";
import {
Mail,
ArrowRight,
@@ -347,27 +347,10 @@ function ForgotPasswordForm() {
</a>
</div>
<div className="text-muted-foreground text-center text-xs leading-relaxed">
By using our service, you agree to our{" "}
<LegalModal
type="terms"
trigger={
<span className="text-primary inline cursor-pointer hover:underline">
Terms of Service
</span>
}
/>{" "}
and{" "}
<LegalModal
type="privacy"
trigger={
<span className="text-primary inline cursor-pointer hover:underline">
Privacy Policy
</span>
}
/>
.
</div>
<LegalAgreementNotice
action="using our service"
className="leading-relaxed"
/>
</div>
</div>
</CardContent>
+2 -14
View File
@@ -8,7 +8,7 @@ import { Button } from "~/components/ui/button";
import { Label } from "~/components/ui/label";
import { toast } from "sonner";
import { Logo } from "~/components/branding/logo";
import { LegalModal } from "~/components/ui/legal-modal";
import { LegalAgreementNotice } from "~/components/legal/legal-links";
import { Mail, Lock, ArrowRight, User } from "lucide-react";
function RegisterForm() {
@@ -151,19 +151,7 @@ function RegisterForm() {
</a>
</p>
<p className="text-muted-foreground text-center text-xs">
By creating an account you agree to our{" "}
<LegalModal
type="terms"
trigger={<span className="text-foreground cursor-pointer hover:underline">Terms</span>}
/>{" "}
and{" "}
<LegalModal
type="privacy"
trigger={<span className="text-foreground cursor-pointer hover:underline">Privacy Policy</span>}
/>
.
</p>
<LegalAgreementNotice action="creating an account" />
</div>
</CardContent>
</Card>
+5 -22
View File
@@ -8,7 +8,7 @@ import { Button } from "~/components/ui/button";
import { Label } from "~/components/ui/label";
import { toast } from "sonner";
import { Logo } from "~/components/branding/logo";
import { LegalModal } from "~/components/ui/legal-modal";
import { LegalAgreementNotice } from "~/components/legal/legal-links";
import {
Lock,
ArrowRight,
@@ -425,27 +425,10 @@ function ResetPasswordForm() {
</a>
</div>
<div className="text-muted-foreground text-center text-xs leading-relaxed">
By resetting your password, you agree to our{" "}
<LegalModal
type="terms"
trigger={
<span className="text-primary inline cursor-pointer hover:underline">
Terms of Service
</span>
}
/>{" "}
and{" "}
<LegalModal
type="privacy"
trigger={
<span className="text-primary inline cursor-pointer hover:underline">
Privacy Policy
</span>
}
/>
.
</div>
<LegalAgreementNotice
action="resetting your password"
className="leading-relaxed"
/>
</div>
</div>
</CardContent>
+2 -14
View File
@@ -9,7 +9,7 @@ import { Button } from "~/components/ui/button";
import { Label } from "~/components/ui/label";
import { toast } from "sonner";
import { Logo } from "~/components/branding/logo";
import { LegalModal } from "~/components/ui/legal-modal";
import { LegalAgreementNotice } from "~/components/legal/legal-links";
import { env } from "~/env";
import { Mail, Lock, ArrowRight, Shield } from "lucide-react";
@@ -167,19 +167,7 @@ export function SignInForm({ allowRegistration }: SignInFormProps) {
</p>
)}
<p className="text-muted-foreground text-center text-xs">
By signing in you agree to our{" "}
<LegalModal
type="terms"
trigger={<span className="text-foreground cursor-pointer hover:underline">Terms</span>}
/>{" "}
and{" "}
<LegalModal
type="privacy"
trigger={<span className="text-foreground cursor-pointer hover:underline">Privacy Policy</span>}
/>
.
</p>
<LegalAgreementNotice action="signing in" />
</div>
</CardContent>
</Card>
@@ -29,6 +29,7 @@ import { useAuthSession } from "~/hooks/use-auth-session";
import * as React from "react";
import { useState } from "react";
import Link from "next/link";
import { toast } from "sonner";
import {
AlertDialog,
@@ -84,6 +85,7 @@ import {
import { useAppearance } from "~/components/providers/appearance-provider";
import {
bodyFontPreferences,
brand,
colorModes,
colorThemes,
type ColorTheme,
@@ -701,6 +703,27 @@ export function SettingsContent() {
</Card>
)}
</div>
<Card className="bg-card border-border border">
<CardHeader>
<CardTitle className="text-foreground flex items-center gap-2">
<FileText className="text-primary h-5 w-5" />
Legal
</CardTitle>
<CardDescription>
Review how we handle your data and the terms for using{" "}
{brand.name}
</CardDescription>
</CardHeader>
<CardContent className="flex flex-col gap-3 sm:flex-row">
<Button variant="outline" asChild>
<Link href="/terms">Terms of Service</Link>
</Button>
<Button variant="outline" asChild>
<Link href="/privacy">Privacy Policy</Link>
</Button>
</CardContent>
</Card>
</TabsContent>
<TabsContent value="preferences" className="space-y-8">
+2 -2
View File
@@ -97,10 +97,10 @@ export default function HomePage() {
<span>© 2026 {brand.name}</span>
<div className="flex gap-5">
<Link href="/privacy" className="hover:text-foreground">
Privacy
Privacy Policy
</Link>
<Link href="/terms" className="hover:text-foreground">
Terms
Terms of Service
</Link>
</div>
</footer>
+43
View File
@@ -0,0 +1,43 @@
import Link from "next/link";
import { cn } from "~/lib/utils";
type LegalLinksProps = {
className?: string;
linkClassName?: string;
};
export function LegalLinks({ className, linkClassName }: LegalLinksProps) {
const linkStyles = cn(
"text-foreground font-medium hover:underline",
linkClassName,
);
return (
<span className={className}>
<Link href="/terms" className={linkStyles}>
Terms of Service
</Link>
{" and "}
<Link href="/privacy" className={linkStyles}>
Privacy Policy
</Link>
</span>
);
}
type LegalAgreementNoticeProps = {
action: string;
className?: string;
};
export function LegalAgreementNotice({
action,
className,
}: LegalAgreementNoticeProps) {
return (
<p className={cn("text-muted-foreground text-center text-xs", className)}>
By {action}, you agree to our <LegalLinks />.
</p>
);
}
+39
View File
@@ -0,0 +1,39 @@
import Link from "next/link";
import { ArrowLeft } from "lucide-react";
import { Button } from "~/components/ui/button";
import { LEGAL_LAST_UPDATED } from "~/lib/legal";
type LegalPageShellProps = {
title: string;
children: React.ReactNode;
};
export function LegalPageShell({ title, children }: LegalPageShellProps) {
return (
<div className="bg-background min-h-screen">
<div className="bg-card border-b">
<div className="container mx-auto max-w-4xl px-6 py-6">
<div className="flex items-center gap-4">
<Link href="/">
<Button variant="outline" size="sm">
<ArrowLeft className="mr-2 h-4 w-4" />
Back
</Button>
</Link>
<div>
<h1 className="text-2xl font-bold">{title}</h1>
<p className="text-muted-foreground text-sm">
Last updated: {LEGAL_LAST_UPDATED}
</p>
</div>
</div>
</div>
</div>
<div className="container mx-auto max-w-4xl px-6 py-8">
<div className="space-y-6">{children}</div>
</div>
</div>
);
}
@@ -0,0 +1,373 @@
import Link from "next/link";
import { Card, CardContent, CardHeader, CardTitle } from "~/components/ui/card";
import {
LEGAL_PRIVACY_EMAIL,
LEGAL_TERMS_EMAIL,
LEGAL_WEBSITE,
} from "~/lib/legal";
import { brand } from "~/lib/branding";
export function PrivacyPolicyContent() {
return (
<>
<Card>
<CardHeader>
<CardTitle>Introduction</CardTitle>
</CardHeader>
<CardContent className="prose prose-sm max-w-none dark:prose-invert">
<p>
{brand.name} (&quot;we&quot;, &quot;our&quot;, or &quot;us&quot;) is
committed to protecting your privacy. This Privacy Policy explains
how we collect, use, disclose, and safeguard your information when
you use our invoicing platform and services.
</p>
<p>
Please read this Privacy Policy carefully. If you do not agree with
the terms of this Privacy Policy, please do not access or use our
Service.
</p>
</CardContent>
</Card>
<Card>
<CardHeader>
<CardTitle>Information We Collect</CardTitle>
</CardHeader>
<CardContent className="prose prose-sm max-w-none dark:prose-invert">
<h4>Personal Information</h4>
<p>
We may collect personal information that you voluntarily provide to
us when you:
</p>
<ul>
<li>Register for an account</li>
<li>Create invoices or manage client information</li>
<li>Track time entries and billing activity</li>
<li>Contact us for support</li>
<li>Subscribe to our newsletters or communications</li>
</ul>
<p>This personal information may include:</p>
<ul>
<li>Name and contact information (email, phone, address)</li>
<li>Business information and tax details</li>
<li>Client information you input into the system</li>
<li>Financial information related to your invoices</li>
<li>
Payment information (processed securely by third-party providers)
</li>
</ul>
<h4>Automatically Collected Information</h4>
<p>
We may automatically collect certain information when you visit our
Service:
</p>
<ul>
<li>
Device information (IP address, browser type, operating system)
</li>
<li>Usage data (pages visited, time spent, features used)</li>
<li>Log files and analytics data</li>
<li>Cookies and similar tracking technologies</li>
</ul>
</CardContent>
</Card>
<Card>
<CardHeader>
<CardTitle>How We Use Your Information</CardTitle>
</CardHeader>
<CardContent className="prose prose-sm max-w-none dark:prose-invert">
<p>We use the information we collect to:</p>
<ul>
<li>Provide, operate, and maintain our Service</li>
<li>Process your transactions and manage your account</li>
<li>Improve and personalize your experience</li>
<li>Communicate with you about your account and our services</li>
<li>Send you technical notices and support messages</li>
<li>Respond to your comments, questions, and requests</li>
<li>Monitor usage and analyze trends</li>
<li>
Detect, prevent, and address technical issues and security
breaches
</li>
<li>Comply with legal obligations</li>
</ul>
</CardContent>
</Card>
<Card>
<CardHeader>
<CardTitle>How We Share Your Information</CardTitle>
</CardHeader>
<CardContent className="prose prose-sm max-w-none dark:prose-invert">
<p>
We do not sell, trade, or rent your personal information to third
parties. We may share your information in the following
circumstances:
</p>
<h4>Service Providers</h4>
<p>
We may share your information with trusted third-party service
providers who assist us in operating our Service, such as:
</p>
<ul>
<li>Cloud hosting and storage providers</li>
<li>Payment processors</li>
<li>Email service providers</li>
<li>Analytics and monitoring services</li>
</ul>
<h4>Legal Requirements</h4>
<p>
We may disclose your information if required to do so by law or in
response to:
</p>
<ul>
<li>Legal processes (subpoenas, court orders)</li>
<li>Government requests</li>
<li>Law enforcement investigations</li>
<li>Protection of our rights, property, or safety</li>
</ul>
<h4>Business Transfers</h4>
<p>
In the event of a merger, acquisition, or sale of assets, your
information may be transferred as part of that transaction.
</p>
</CardContent>
</Card>
<Card>
<CardHeader>
<CardTitle>Data Security</CardTitle>
</CardHeader>
<CardContent className="prose prose-sm max-w-none dark:prose-invert">
<p>
We implement appropriate technical and organizational security
measures to protect your information:
</p>
<ul>
<li>Encryption of data in transit and at rest</li>
<li>Secure access controls and authentication</li>
<li>Regular security assessments and updates</li>
<li>Employee training on data protection</li>
<li>Incident response procedures</li>
</ul>
<p>
However, no method of transmission over the internet or electronic
storage is 100% secure. While we strive to protect your information,
we cannot guarantee absolute security.
</p>
</CardContent>
</Card>
<Card>
<CardHeader>
<CardTitle>Data Retention</CardTitle>
</CardHeader>
<CardContent className="prose prose-sm max-w-none dark:prose-invert">
<p>
We retain your personal information only for as long as necessary to
fulfill the purposes outlined in this Privacy Policy, unless a
longer retention period is required by law.
</p>
<p>
Factors we consider when determining retention periods include:
</p>
<ul>
<li>The nature and sensitivity of the information</li>
<li>Legal and regulatory requirements</li>
<li>Business and operational needs</li>
<li>Your account status and activity</li>
</ul>
</CardContent>
</Card>
<Card>
<CardHeader>
<CardTitle>Your Rights and Choices</CardTitle>
</CardHeader>
<CardContent className="prose prose-sm max-w-none dark:prose-invert">
<p>
Depending on your location, you may have the following rights
regarding your personal information:
</p>
<h4>Access and Portability</h4>
<ul>
<li>Request access to your personal information</li>
<li>Receive a copy of your data in a portable format</li>
</ul>
<h4>Correction and Updates</h4>
<ul>
<li>Correct inaccurate or incomplete information</li>
<li>Update your account information at any time</li>
</ul>
<h4>Deletion</h4>
<ul>
<li>Request deletion of your personal information</li>
<li>Close your account and remove your data</li>
</ul>
<h4>Restriction and Objection</h4>
<ul>
<li>Restrict the processing of your information</li>
<li>Object to certain uses of your data</li>
</ul>
<p>
To exercise these rights, please contact us using the information
provided in the &quot;Contact Us&quot; section below.
</p>
</CardContent>
</Card>
<Card>
<CardHeader>
<CardTitle>Cookies and Tracking Technologies</CardTitle>
</CardHeader>
<CardContent className="prose prose-sm max-w-none dark:prose-invert">
<p>We use cookies and similar technologies to:</p>
<ul>
<li>Remember your preferences and settings</li>
<li>Authenticate your account</li>
<li>Analyze usage patterns and improve our Service</li>
<li>Provide personalized content and features</li>
</ul>
<p>
You can control cookies through your browser settings. However,
disabling cookies may affect the functionality of our Service.
</p>
<h4>Types of Cookies We Use</h4>
<ul>
<li>
<strong>Essential Cookies:</strong> Required for the Service to
function properly
</li>
<li>
<strong>Analytics Cookies:</strong> Help us understand how you use
our Service
</li>
<li>
<strong>Preference Cookies:</strong> Remember your settings and
preferences
</li>
</ul>
</CardContent>
</Card>
<Card>
<CardHeader>
<CardTitle>Third-Party Links and Services</CardTitle>
</CardHeader>
<CardContent className="prose prose-sm max-w-none dark:prose-invert">
<p>
Our Service may contain links to third-party websites or integrate
with third-party services. We are not responsible for the privacy
practices of these third parties.
</p>
<p>
We encourage you to read the privacy policies of any third-party
services you use in connection with our Service.
</p>
</CardContent>
</Card>
<Card>
<CardHeader>
<CardTitle>Children&apos;s Privacy</CardTitle>
</CardHeader>
<CardContent className="prose prose-sm max-w-none dark:prose-invert">
<p>
Our Service is not intended for children under the age of 13. We do
not knowingly collect personal information from children under 13.
</p>
<p>
If you are a parent or guardian and believe your child has provided
us with personal information, please contact us immediately so we
can remove such information.
</p>
</CardContent>
</Card>
<Card>
<CardHeader>
<CardTitle>International Data Transfers</CardTitle>
</CardHeader>
<CardContent className="prose prose-sm max-w-none dark:prose-invert">
<p>
Your information may be transferred to and processed in countries
other than your own. We ensure that such transfers comply with
applicable data protection laws.
</p>
<p>
When we transfer your information internationally, we implement
appropriate safeguards to protect your data, including:
</p>
<ul>
<li>Standard contractual clauses</li>
<li>Adequacy decisions by relevant authorities</li>
<li>Certified privacy frameworks</li>
</ul>
</CardContent>
</Card>
<Card>
<CardHeader>
<CardTitle>Changes to This Privacy Policy</CardTitle>
</CardHeader>
<CardContent className="prose prose-sm max-w-none dark:prose-invert">
<p>
We may update this Privacy Policy from time to time. We will notify
you of any material changes by:
</p>
<ul>
<li>Posting the updated policy on our Service</li>
<li>Sending you an email notification</li>
<li>Displaying a prominent notice on our Service</li>
</ul>
<p>
Your continued use of our Service after any changes indicates your
acceptance of the updated Privacy Policy.
</p>
</CardContent>
</Card>
<Card>
<CardHeader>
<CardTitle>Contact Us</CardTitle>
</CardHeader>
<CardContent className="prose prose-sm max-w-none dark:prose-invert">
<p>
If you have questions about this Privacy Policy or our privacy
practices, please contact us at:
</p>
<ul>
<li>
Email:{" "}
<a href={`mailto:${LEGAL_PRIVACY_EMAIL}`}>{LEGAL_PRIVACY_EMAIL}</a>
</li>
<li>
Website:{" "}
<a href={LEGAL_WEBSITE} target="_blank" rel="noopener noreferrer">
{LEGAL_WEBSITE}
</a>
</li>
</ul>
<p>
We will respond to your inquiries within a reasonable timeframe and
in accordance with applicable law.
</p>
</CardContent>
</Card>
</>
);
}
@@ -0,0 +1,291 @@
import Link from "next/link";
import { Card, CardContent, CardHeader, CardTitle } from "~/components/ui/card";
import {
LEGAL_PRIVACY_EMAIL,
LEGAL_TERMS_EMAIL,
LEGAL_WEBSITE,
} from "~/lib/legal";
import { brand } from "~/lib/branding";
export function TermsOfServiceContent() {
return (
<>
<Card>
<CardHeader>
<CardTitle>Agreement to Terms</CardTitle>
</CardHeader>
<CardContent className="prose prose-sm max-w-none dark:prose-invert">
<p>
These Terms of Service (&quot;Terms&quot;) govern your use of the{" "}
{brand.name} platform and services (the &quot;Service&quot;) operated
by {brand.name} (&quot;us&quot;, &quot;we&quot;, or &quot;our&quot;).
</p>
<p>
By accessing or using our Service, you agree to be bound by these
Terms. If you disagree with any part of these terms, then you may not
access the Service.
</p>
</CardContent>
</Card>
<Card>
<CardHeader>
<CardTitle>Description of Service</CardTitle>
</CardHeader>
<CardContent className="prose prose-sm max-w-none dark:prose-invert">
<p>
{brand.name} is a web-based invoicing platform that allows users to:
</p>
<ul>
<li>Create and manage professional invoices</li>
<li>Track client information and billing details</li>
<li>Clock time and convert entries into billable work</li>
<li>Monitor payment status and financial metrics</li>
<li>Generate reports and analytics</li>
<li>Manage business profiles and settings</li>
</ul>
</CardContent>
</Card>
<Card>
<CardHeader>
<CardTitle>User Accounts</CardTitle>
</CardHeader>
<CardContent className="prose prose-sm max-w-none dark:prose-invert">
<p>
When you create an account with us, you must provide information that
is accurate, complete, and current at all times. You are responsible
for safeguarding the password and for all activities that occur
under your account.
</p>
<p>
You agree not to disclose your password to any third party. You must
notify us immediately upon becoming aware of any breach of security
or unauthorized use of your account.
</p>
</CardContent>
</Card>
<Card>
<CardHeader>
<CardTitle>Acceptable Use</CardTitle>
</CardHeader>
<CardContent className="prose prose-sm max-w-none dark:prose-invert">
<p>You agree not to use the Service:</p>
<ul>
<li>
For any unlawful purpose or to solicit others to perform unlawful
acts
</li>
<li>
To violate any international, federal, provincial, or state
regulations, rules, laws, or local ordinances
</li>
<li>
To infringe upon or violate our intellectual property rights or
the intellectual property rights of others
</li>
<li>
To harass, abuse, insult, harm, defame, slander, disparage,
intimidate, or discriminate
</li>
<li>To submit false or misleading information</li>
<li>
To upload or transmit viruses or any other type of malicious code
</li>
<li>To spam, phish, pharm, pretext, spider, crawl, or scrape</li>
<li>For any obscene or immoral purpose</li>
<li>
To interfere with or circumvent the security features of the
Service
</li>
</ul>
</CardContent>
</Card>
<Card>
<CardHeader>
<CardTitle>Data and Privacy</CardTitle>
</CardHeader>
<CardContent className="prose prose-sm max-w-none dark:prose-invert">
<p>
Your privacy is important to us. Please review our{" "}
<Link href="/privacy">Privacy Policy</Link>, which also governs your
use of the Service, to understand our practices.
</p>
<p>
You retain ownership of your data. We will not sell, rent, or share
your personal information with third parties without your explicit
consent, except as described in our Privacy Policy.
</p>
<p>
You are responsible for backing up your data. While we implement
regular backups, we recommend you maintain your own copies of
important information.
</p>
</CardContent>
</Card>
<Card>
<CardHeader>
<CardTitle>Payment Terms</CardTitle>
</CardHeader>
<CardContent className="prose prose-sm max-w-none dark:prose-invert">
<p>
Some aspects of the Service may require payment. You will be charged
according to your subscription plan. All fees are non-refundable
unless otherwise stated.
</p>
<p>
We may change our fees at any time. We will provide you with
reasonable notice of any fee changes by posting the new fees on the
Service or sending you email notification.
</p>
<p>
If you fail to pay any fees when due, we may suspend or terminate
your access to the Service until payment is made.
</p>
</CardContent>
</Card>
<Card>
<CardHeader>
<CardTitle>Intellectual Property Rights</CardTitle>
</CardHeader>
<CardContent className="prose prose-sm max-w-none dark:prose-invert">
<p>
The Service and its original content, features, and functionality
are and will remain the exclusive property of {brand.name} and its
licensors. The Service is protected by copyright, trademark, and
other laws.
</p>
<p>
Our trademarks and trade dress may not be used in connection with
any product or service without our prior written consent.
</p>
</CardContent>
</Card>
<Card>
<CardHeader>
<CardTitle>Termination</CardTitle>
</CardHeader>
<CardContent className="prose prose-sm max-w-none dark:prose-invert">
<p>
We may terminate or suspend your account and bar access to the
Service immediately, without prior notice or liability, under our
sole discretion, for any reason whatsoever and without limitation,
including but not limited to a breach of the Terms.
</p>
<p>
If you wish to terminate your account, you may discontinue using the
Service and contact us to request account deletion.
</p>
<p>
Upon termination, your right to use the Service will cease
immediately.
</p>
</CardContent>
</Card>
<Card>
<CardHeader>
<CardTitle>Disclaimer of Warranties</CardTitle>
</CardHeader>
<CardContent className="prose prose-sm max-w-none dark:prose-invert">
<p>
The information on this Service is provided on an &quot;as is&quot;
basis. To the fullest extent permitted by law, we exclude all
representations, warranties, and conditions relating to our Service
and the use of this Service.
</p>
<p>
Nothing in this disclaimer will limit or exclude our or your
liability for death or personal injury resulting from negligence,
fraud, or fraudulent misrepresentation.
</p>
</CardContent>
</Card>
<Card>
<CardHeader>
<CardTitle>Limitation of Liability</CardTitle>
</CardHeader>
<CardContent className="prose prose-sm max-w-none dark:prose-invert">
<p>
In no event shall {brand.name}, nor its directors, employees,
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 use of the Service.
</p>
</CardContent>
</Card>
<Card>
<CardHeader>
<CardTitle>Governing Law</CardTitle>
</CardHeader>
<CardContent className="prose prose-sm max-w-none dark:prose-invert">
<p>
These Terms shall be interpreted and governed by the laws of the
jurisdiction in which {brand.name} operates, without regard to its
conflict of law provisions.
</p>
<p>
Our failure to enforce any right or provision of these Terms will
not be considered a waiver of those rights.
</p>
</CardContent>
</Card>
<Card>
<CardHeader>
<CardTitle>Changes to Terms</CardTitle>
</CardHeader>
<CardContent className="prose prose-sm max-w-none dark:prose-invert">
<p>
We reserve the right, at our sole discretion, to modify or replace
these Terms at any time. If a revision is material, we will provide
at least 30 days notice prior to any new terms taking effect.
</p>
<p>
What constitutes a material change will be determined at our sole
discretion. By continuing to access or use our Service after any
revisions become effective, you agree to be bound by the revised
terms.
</p>
</CardContent>
</Card>
<Card>
<CardHeader>
<CardTitle>Contact Information</CardTitle>
</CardHeader>
<CardContent className="prose prose-sm max-w-none dark:prose-invert">
<p>
If you have any questions about these Terms of Service, please
contact us at:
</p>
<ul>
<li>
Email:{" "}
<a href={`mailto:${LEGAL_TERMS_EMAIL}`}>{LEGAL_TERMS_EMAIL}</a>
</li>
<li>
Privacy inquiries:{" "}
<a href={`mailto:${LEGAL_PRIVACY_EMAIL}`}>{LEGAL_PRIVACY_EMAIL}</a>
</li>
<li>
Website:{" "}
<a href={LEGAL_WEBSITE} target="_blank" rel="noopener noreferrer">
{LEGAL_WEBSITE}
</a>
</li>
</ul>
</CardContent>
</Card>
</>
);
}
-350
View File
@@ -1,350 +0,0 @@
"use client";
import { useState } from "react";
import {
Dialog,
DialogContent,
DialogHeader,
DialogTitle,
} from "~/components/ui/dialog";
import { Button } from "~/components/ui/button";
import { ScrollArea } from "~/components/ui/scroll-area";
import { Card, CardContent, CardHeader, CardTitle } from "~/components/ui/card";
import { X } from "lucide-react";
interface LegalModalProps {
type: "terms" | "privacy";
trigger: React.ReactNode;
}
export function LegalModal({ type, trigger }: LegalModalProps) {
const [open, setOpen] = useState(false);
const isTerms = type === "terms";
const title = isTerms ? "Terms of Service" : "Privacy Policy";
return (
<Dialog open={open} onOpenChange={setOpen}>
<span className="inline" onClick={() => setOpen(true)}>
{trigger}
</span>
<DialogContent className="max-h-[80vh] max-w-6xl">
<DialogHeader>
<DialogTitle className="flex items-center justify-between">
{title}
<Button
variant="ghost"
size="sm"
onClick={() => setOpen(false)}
className="h-6 w-6 p-0"
>
<X className="h-4 w-4" />
</Button>
</DialogTitle>
</DialogHeader>
<ScrollArea className="h-full max-h-[60vh] pr-4">
{isTerms ? <TermsContent /> : <PrivacyContent />}
</ScrollArea>
<div className="flex justify-end pt-4">
<Button onClick={() => setOpen(false)}>Close</Button>
</div>
</DialogContent>
</Dialog>
);
}
function TermsContent() {
return (
<div className="space-y-6">
<Card>
<CardHeader>
<CardTitle>Agreement to Terms</CardTitle>
</CardHeader>
<CardContent className="prose prose-sm max-w-none">
<p>
These Terms of Service (&quot;Terms&quot;) govern your use of the
beenvoice platform and services (the &quot;Service&quot;) operated
by beenvoice (&quot;us&quot;, &quot;we&quot;, or &quot;our&quot;).
</p>
<p>
By accessing or using our Service, you agree to be bound by these
Terms. If you disagree with any part of these terms, then you may
not access the Service.
</p>
</CardContent>
</Card>
<Card>
<CardHeader>
<CardTitle>Description of Service</CardTitle>
</CardHeader>
<CardContent className="prose prose-sm max-w-none">
<p>
beenvoice is a web-based invoicing platform that allows users to:
</p>
<ul>
<li>Create and manage professional invoices</li>
<li>Track client information and billing details</li>
<li>Monitor payment status and financial metrics</li>
<li>Generate reports and analytics</li>
<li>Manage business profiles and settings</li>
</ul>
</CardContent>
</Card>
<Card>
<CardHeader>
<CardTitle>User Accounts</CardTitle>
</CardHeader>
<CardContent className="prose prose-sm max-w-none">
<p>
When you create an account with us, you must provide information
that is accurate, complete, and current at all times. You are
responsible for safeguarding the password and for all activities
that occur under your account.
</p>
<p>
You agree not to disclose your password to any third party. You must
notify us immediately upon becoming aware of any breach of security
or unauthorized use of your account.
</p>
</CardContent>
</Card>
<Card>
<CardHeader>
<CardTitle>Acceptable Use</CardTitle>
</CardHeader>
<CardContent className="prose prose-sm max-w-none">
<p>You agree not to use the Service:</p>
<ul>
<li>
For any unlawful purpose or to solicit others to perform unlawful
acts
</li>
<li>
To violate any international, federal, provincial, or state
regulations, rules, laws, or local ordinances
</li>
<li>
To infringe upon or violate our intellectual property rights or
the intellectual property rights of others
</li>
<li>
To harass, abuse, insult, harm, defame, slander, disparage,
intimidate, or discriminate
</li>
<li>To submit false or misleading information</li>
<li>
To upload or transmit viruses or any other type of malicious code
</li>
<li>To spam, phish, pharm, pretext, spider, crawl, or scrape</li>
<li>For any obscene or immoral purpose</li>
<li>
To interfere with or circumvent the security features of the
Service
</li>
</ul>
</CardContent>
</Card>
<Card>
<CardHeader>
<CardTitle>Payment Terms</CardTitle>
</CardHeader>
<CardContent className="prose prose-sm max-w-none">
<p>
Some aspects of the Service may require payment. You will be charged
according to your subscription plan. All fees are non-refundable
unless otherwise stated.
</p>
<p>
We may change our fees at any time. We will provide you with
reasonable notice of any fee changes by posting the new fees on the
Service or sending you email notification.
</p>
</CardContent>
</Card>
<Card>
<CardHeader>
<CardTitle>Termination</CardTitle>
</CardHeader>
<CardContent className="prose prose-sm max-w-none">
<p>
We may terminate or suspend your account and bar access to the
Service immediately, without prior notice or liability, under our
sole discretion, for any reason whatsoever and without limitation,
including but not limited to a breach of the Terms.
</p>
<p>
If you wish to terminate your account, you may simply discontinue
using the Service and contact us to request account deletion.
</p>
</CardContent>
</Card>
<Card>
<CardHeader>
<CardTitle>Contact Information</CardTitle>
</CardHeader>
<CardContent className="prose prose-sm max-w-none">
<p>
If you have any questions about these Terms of Service, please
contact us at:
</p>
<ul>
<li>Email: legal@beenvoice.com</li>
</ul>
</CardContent>
</Card>
</div>
);
}
function PrivacyContent() {
return (
<div className="space-y-6">
<Card>
<CardHeader>
<CardTitle>Information We Collect</CardTitle>
</CardHeader>
<CardContent className="prose prose-sm max-w-none">
<h4>Personal Information</h4>
<p>
We may collect personal information that you voluntarily provide to
us when you:
</p>
<ul>
<li>Register for an account</li>
<li>Create invoices or manage client information</li>
<li>Contact us for support</li>
</ul>
<p>This personal information may include:</p>
<ul>
<li>Name and contact information (email, phone, address)</li>
<li>Business information and tax details</li>
<li>Client information you input into the system</li>
<li>Financial information related to your invoices</li>
</ul>
</CardContent>
</Card>
<Card>
<CardHeader>
<CardTitle>How We Use Your Information</CardTitle>
</CardHeader>
<CardContent className="prose prose-sm max-w-none">
<p>We use the information we collect to:</p>
<ul>
<li>Provide, operate, and maintain our Service</li>
<li>Process your transactions and manage your account</li>
<li>Improve and personalize your experience</li>
<li>Communicate with you about your account and our services</li>
<li>Send you technical notices and support messages</li>
<li>Respond to your comments, questions, and requests</li>
<li>Monitor usage and analyze trends</li>
<li>
Detect, prevent, and address technical issues and security
breaches
</li>
</ul>
</CardContent>
</Card>
<Card>
<CardHeader>
<CardTitle>How We Share Your Information</CardTitle>
</CardHeader>
<CardContent className="prose prose-sm max-w-none">
<p>
We do not sell, trade, or rent your personal information to third
parties. We may share your information in the following
circumstances:
</p>
<h4>Service Providers</h4>
<p>
We may share your information with trusted third-party service
providers who assist us in operating our Service, such as:
</p>
<ul>
<li>Cloud hosting and storage providers</li>
<li>Payment processors</li>
<li>Email service providers</li>
<li>Analytics and monitoring services</li>
</ul>
<h4>Legal Requirements</h4>
<p>
We may disclose your information if required to do so by law or in
response to:
</p>
<ul>
<li>Legal processes (subpoenas, court orders)</li>
<li>Government requests</li>
<li>Law enforcement investigations</li>
<li>Protection of our rights, property, or safety</li>
</ul>
</CardContent>
</Card>
<Card>
<CardHeader>
<CardTitle>Data Security</CardTitle>
</CardHeader>
<CardContent className="prose prose-sm max-w-none">
<p>
We implement appropriate technical and organizational security
measures to protect your information:
</p>
<ul>
<li>Encryption of data in transit and at rest</li>
<li>Secure access controls and authentication</li>
<li>Regular security assessments and updates</li>
<li>Employee training on data protection</li>
<li>Incident response procedures</li>
</ul>
</CardContent>
</Card>
<Card>
<CardHeader>
<CardTitle>Your Rights and Choices</CardTitle>
</CardHeader>
<CardContent className="prose prose-sm max-w-none">
<p>
Depending on your location, you may have the following rights
regarding your personal information:
</p>
<ul>
<li>Request access to your personal information</li>
<li>Correct inaccurate or incomplete information</li>
<li>Request deletion of your personal information</li>
<li>Restrict the processing of your information</li>
<li>Object to certain uses of your data</li>
</ul>
<p>
To exercise these rights, please contact us at
privacy@beenvoice.com.
</p>
</CardContent>
</Card>
<Card>
<CardHeader>
<CardTitle>Contact Us</CardTitle>
</CardHeader>
<CardContent className="prose prose-sm max-w-none">
<p>
If you have questions about this Privacy Policy or our privacy
practices, please contact us at:
</p>
<ul>
<li>Email: privacy@beenvoice.com</li>
</ul>
</CardContent>
</Card>
</div>
);
}
+5
View File
@@ -0,0 +1,5 @@
export const LEGAL_LAST_UPDATED = "June 18, 2026";
export const LEGAL_PRIVACY_EMAIL = "privacy@beenvoice.com";
export const LEGAL_TERMS_EMAIL = "legal@beenvoice.com";
export const LEGAL_WEBSITE = "https://beenvoice.com";