mirror of
https://github.com/marcogll/TaxHacker_s23.git
synced 2026-01-13 13:25:18 +00:00
feat: activate saas
This commit is contained in:
@@ -3,6 +3,7 @@ import { Card, CardContent, CardTitle } from "@/components/ui/card"
|
|||||||
import { ColoredText } from "@/components/ui/colored-text"
|
import { ColoredText } from "@/components/ui/colored-text"
|
||||||
import config from "@/lib/config"
|
import config from "@/lib/config"
|
||||||
import { PLANS } from "@/lib/stripe"
|
import { PLANS } from "@/lib/stripe"
|
||||||
|
import { Mail } from "lucide-react"
|
||||||
import Link from "next/link"
|
import Link from "next/link"
|
||||||
import { redirect } from "next/navigation"
|
import { redirect } from "next/navigation"
|
||||||
|
|
||||||
@@ -15,7 +16,8 @@ export default async function ChoosePlanPage() {
|
|||||||
<div className="container mx-auto px-4 py-8">
|
<div className="container mx-auto px-4 py-8">
|
||||||
<Card className="w-full max-w-4xl mx-auto p-8 flex flex-col items-center justify-center gap-8">
|
<Card className="w-full max-w-4xl mx-auto p-8 flex flex-col items-center justify-center gap-8">
|
||||||
<CardTitle className="text-4xl font-bold text-center">
|
<CardTitle className="text-4xl font-bold text-center">
|
||||||
<ColoredText>Choose your Cloud Edition plan</ColoredText>
|
<ColoredText>TaxHacker Cloud Edition</ColoredText>
|
||||||
|
<h2 className="mt-3 text-2xl font-semibold text-muted-foreground">Choose your plan</h2>
|
||||||
</CardTitle>
|
</CardTitle>
|
||||||
<CardContent className="w-full">
|
<CardContent className="w-full">
|
||||||
{config.auth.disableSignup ? (
|
{config.auth.disableSignup ? (
|
||||||
@@ -23,17 +25,39 @@ export default async function ChoosePlanPage() {
|
|||||||
Creating new account is disabled for now. Please use the self-hosted version.
|
Creating new account is disabled for now. Please use the self-hosted version.
|
||||||
</div>
|
</div>
|
||||||
) : (
|
) : (
|
||||||
<div className="flex flex-wrap justify-center gap-8">
|
<div className="space-y-8">
|
||||||
{Object.values(PLANS)
|
<div className="flex flex-wrap justify-center gap-8">
|
||||||
.filter((plan) => plan.isAvailable)
|
{Object.values(PLANS)
|
||||||
.map((plan) => (
|
.filter((plan) => plan.isAvailable)
|
||||||
<PricingCard key={plan.code} plan={plan} />
|
.map((plan) => (
|
||||||
))}
|
<PricingCard key={plan.code} plan={plan} />
|
||||||
|
))}
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div className="text-center text-muted-foreground">
|
||||||
|
By signing up, you agree to our{" "}
|
||||||
|
<Link href="/docs/terms" className="hover:text-primary transition-colors underline">
|
||||||
|
Terms of Service
|
||||||
|
</Link>
|
||||||
|
,{" "}
|
||||||
|
<Link href="/docs/privacy_policy" className="hover:text-primary transition-colors underline">
|
||||||
|
Privacy Policy
|
||||||
|
</Link>
|
||||||
|
, and{" "}
|
||||||
|
<Link href="/docs/ai" className="hover:text-primary transition-colors underline">
|
||||||
|
AI Usage Disclosure
|
||||||
|
</Link>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
)}
|
)}
|
||||||
</CardContent>
|
</CardContent>
|
||||||
|
|
||||||
<div className="text-center text-muted-foreground">
|
<div className="text-center text-muted-foreground">
|
||||||
<Link href={`mailto:${config.app.supportEmail}`} className="hover:text-primary transition-colors">
|
<Link
|
||||||
|
href={`mailto:${config.app.supportEmail}`}
|
||||||
|
className="flex flex-row gap-1 items-center hover:text-primary transition-colors underline"
|
||||||
|
>
|
||||||
|
<Mail className="w-4 h-4" />
|
||||||
Contact us for custom plans
|
Contact us for custom plans
|
||||||
</Link>
|
</Link>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@@ -45,7 +45,7 @@ export default async function AI() {
|
|||||||
</p>
|
</p>
|
||||||
<ul className="list-disc pl-6 space-y-2 mb-6 text-gray-700">
|
<ul className="list-disc pl-6 space-y-2 mb-6 text-gray-700">
|
||||||
<li>
|
<li>
|
||||||
<strong>GPT-4o</strong> and <strong>GPT-4.1</strong>
|
<strong>gpt-4o-mini</strong> and <strong>gpt-4.1-mini</strong>
|
||||||
</li>
|
</li>
|
||||||
</ul>
|
</ul>
|
||||||
<p className="text-gray-700 leading-relaxed mb-6">
|
<p className="text-gray-700 leading-relaxed mb-6">
|
||||||
|
|||||||
@@ -15,10 +15,16 @@ export default function LandingPage() {
|
|||||||
</Link>
|
</Link>
|
||||||
<div className="flex gap-4">
|
<div className="flex gap-4">
|
||||||
<Link
|
<Link
|
||||||
href="#start"
|
href="/enter"
|
||||||
|
className="text-sm font-medium px-4 py-2 rounded-full border border-gray-200 hover:bg-gray-50 transition-all"
|
||||||
|
>
|
||||||
|
Log In
|
||||||
|
</Link>
|
||||||
|
<Link
|
||||||
|
href="/cloud"
|
||||||
className="text-sm font-medium bg-gradient-to-r from-blue-600 to-indigo-600 text-white px-4 py-2 rounded-full hover:opacity-90 transition-all"
|
className="text-sm font-medium bg-gradient-to-r from-blue-600 to-indigo-600 text-white px-4 py-2 rounded-full hover:opacity-90 transition-all"
|
||||||
>
|
>
|
||||||
Get Started
|
Sign Up
|
||||||
</Link>
|
</Link>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
@@ -35,7 +41,7 @@ export default function LandingPage() {
|
|||||||
Let AI finally care about your taxes, scan your receipts and analyze your expenses
|
Let AI finally care about your taxes, scan your receipts and analyze your expenses
|
||||||
</h1>
|
</h1>
|
||||||
<p className="text-xl text-gray-600 mb-8 max-w-2xl mx-auto">
|
<p className="text-xl text-gray-600 mb-8 max-w-2xl mx-auto">
|
||||||
A self-hosted accounting app crafted with love for freelancers and small businesses.
|
A self-hosted accounting app crafted with love for freelancers and small businesses
|
||||||
</p>
|
</p>
|
||||||
<div className="flex gap-4 justify-center">
|
<div className="flex gap-4 justify-center">
|
||||||
<Link
|
<Link
|
||||||
@@ -237,42 +243,7 @@ export default function LandingPage() {
|
|||||||
Choose Your Version of TaxHacker
|
Choose Your Version of TaxHacker
|
||||||
</h2>
|
</h2>
|
||||||
</div>
|
</div>
|
||||||
<div className="grid md:grid-cols-2 gap-8">
|
<div className="grid md:grid-cols-2 gap-16">
|
||||||
{/* Self-Hosted Version */}
|
|
||||||
<div className="bg-gradient-to-b from-white to-gray-50 p-8 rounded-2xl shadow-lg ring-1 ring-gray-100">
|
|
||||||
<div className="inline-block px-3 py-1 rounded-full bg-violet-50 text-violet-600 text-sm font-medium mb-4">
|
|
||||||
Use Your Own Server
|
|
||||||
</div>
|
|
||||||
<h3 className="text-2xl font-semibold mb-4">
|
|
||||||
<ColoredText>Self-Hosted Edition</ColoredText>
|
|
||||||
</h3>
|
|
||||||
<ul className="space-y-3 text-gray-600 mb-8">
|
|
||||||
<li className="flex items-center">
|
|
||||||
<span className="text-blue-600 mr-2">✓</span>
|
|
||||||
Complete control over your data
|
|
||||||
</li>
|
|
||||||
<li className="flex items-center">
|
|
||||||
<span className="text-blue-600 mr-2">✓</span>
|
|
||||||
Use at your own infrastructure
|
|
||||||
</li>
|
|
||||||
<li className="flex items-center">
|
|
||||||
<span className="text-blue-600 mr-2">✓</span>
|
|
||||||
Free and open source
|
|
||||||
</li>
|
|
||||||
<li className="flex items-center">
|
|
||||||
<span className="text-blue-600 mr-2">✓</span>
|
|
||||||
Bring your own OpenAI keys
|
|
||||||
</li>
|
|
||||||
</ul>
|
|
||||||
<Link
|
|
||||||
href="https://github.com/vas3k/TaxHacker"
|
|
||||||
target="_blank"
|
|
||||||
className="block w-full text-center px-6 py-3 bg-gradient-to-r from-blue-600 to-indigo-600 text-white font-medium rounded-full hover:opacity-90 transition-all shadow-lg shadow-blue-500/20"
|
|
||||||
>
|
|
||||||
Github + Docker Compose
|
|
||||||
</Link>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
{/* Cloud Version */}
|
{/* Cloud Version */}
|
||||||
<div className="bg-gradient-to-b from-white to-gray-50 p-8 rounded-2xl shadow-lg ring-1 ring-gray-100">
|
<div className="bg-gradient-to-b from-white to-gray-50 p-8 rounded-2xl shadow-lg ring-1 ring-gray-100">
|
||||||
<div className="absolute top-4 right-4">
|
<div className="absolute top-4 right-4">
|
||||||
@@ -302,12 +273,47 @@ export default function LandingPage() {
|
|||||||
Automatic updates and new features
|
Automatic updates and new features
|
||||||
</li>
|
</li>
|
||||||
</ul>
|
</ul>
|
||||||
<button
|
<Link
|
||||||
disabled
|
href="/cloud"
|
||||||
className="block w-full text-center px-6 py-3 bg-gray-100 text-gray-400 font-medium rounded-full cursor-not-allowed"
|
className="block w-full text-center px-6 py-3 bg-gradient-to-r from-blue-600 to-indigo-600 text-white font-medium rounded-full hover:opacity-90 transition-all shadow-lg shadow-blue-500/20"
|
||||||
>
|
>
|
||||||
Coming Soon
|
LET'S GO!
|
||||||
</button>
|
</Link>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
{/* Self-Hosted Version */}
|
||||||
|
<div className="bg-gradient-to-b from-white to-gray-50 p-8 rounded-2xl shadow-lg ring-1 ring-gray-100">
|
||||||
|
<div className="inline-block px-3 py-1 rounded-full bg-violet-50 text-violet-600 text-sm font-medium mb-4">
|
||||||
|
Use Your Own Server
|
||||||
|
</div>
|
||||||
|
<h3 className="text-2xl font-semibold mb-4">
|
||||||
|
<ColoredText>Self-Hosted Edition</ColoredText>
|
||||||
|
</h3>
|
||||||
|
<ul className="space-y-3 text-gray-600 mb-8">
|
||||||
|
<li className="flex items-center">
|
||||||
|
<span className="text-blue-600 mr-2">✓</span>
|
||||||
|
Free and open source
|
||||||
|
</li>
|
||||||
|
<li className="flex items-center">
|
||||||
|
<span className="text-blue-600 mr-2">✓</span>
|
||||||
|
Complete control over your data
|
||||||
|
</li>
|
||||||
|
<li className="flex items-center">
|
||||||
|
<span className="text-blue-600 mr-2">✓</span>
|
||||||
|
Use at your own infrastructure
|
||||||
|
</li>
|
||||||
|
<li className="flex items-center">
|
||||||
|
<span className="text-blue-600 mr-2">✓</span>
|
||||||
|
Bring your own OpenAI keys
|
||||||
|
</li>
|
||||||
|
</ul>
|
||||||
|
<Link
|
||||||
|
href="https://github.com/vas3k/TaxHacker"
|
||||||
|
target="_blank"
|
||||||
|
className="block w-full text-center px-6 py-3 bg-gradient-to-r from-blue-600 to-indigo-600 text-white font-medium rounded-full hover:opacity-90 transition-all shadow-lg shadow-blue-500/20"
|
||||||
|
>
|
||||||
|
Github + Docker Compose
|
||||||
|
</Link>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
@@ -431,7 +437,7 @@ export default function LandingPage() {
|
|||||||
<div className="max-w-7xl mx-auto text-center text-sm text-gray-500">
|
<div className="max-w-7xl mx-auto text-center text-sm text-gray-500">
|
||||||
Made with ❤️ in Berlin by{" "}
|
Made with ❤️ in Berlin by{" "}
|
||||||
<Link href="https://github.com/vas3k" className="underline">
|
<Link href="https://github.com/vas3k" className="underline">
|
||||||
vas3k
|
@vas3k
|
||||||
</Link>
|
</Link>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
@@ -453,6 +459,13 @@ export default function LandingPage() {
|
|||||||
<Link href="/docs/cookie" className="text-sm text-gray-600 hover:text-gray-900">
|
<Link href="/docs/cookie" className="text-sm text-gray-600 hover:text-gray-900">
|
||||||
Cookie Policy
|
Cookie Policy
|
||||||
</Link>
|
</Link>
|
||||||
|
<Link
|
||||||
|
href="https://github.com/vas3k/TaxHacker"
|
||||||
|
target="_blank"
|
||||||
|
className="text-sm text-gray-600 hover:text-gray-900"
|
||||||
|
>
|
||||||
|
Source Code
|
||||||
|
</Link>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</section>
|
</section>
|
||||||
|
|||||||
@@ -1,96 +0,0 @@
|
|||||||
"use client"
|
|
||||||
|
|
||||||
import { FormInput } from "@/components/forms/simple"
|
|
||||||
import { Button } from "@/components/ui/button"
|
|
||||||
import { authClient } from "@/lib/auth-client"
|
|
||||||
import { useRouter } from "next/navigation"
|
|
||||||
import { useState } from "react"
|
|
||||||
|
|
||||||
export default function SignupForm() {
|
|
||||||
const [name, setName] = useState("")
|
|
||||||
const [email, setEmail] = useState("")
|
|
||||||
const [otp, setOtp] = useState("")
|
|
||||||
const [isOtpSent, setIsOtpSent] = useState(false)
|
|
||||||
const [isLoading, setIsLoading] = useState(false)
|
|
||||||
const [error, setError] = useState<string | null>(null)
|
|
||||||
const router = useRouter()
|
|
||||||
|
|
||||||
const handleSendOtp = async (e: React.FormEvent) => {
|
|
||||||
e.preventDefault()
|
|
||||||
setIsLoading(true)
|
|
||||||
setError(null)
|
|
||||||
|
|
||||||
try {
|
|
||||||
await authClient.emailOtp.sendVerificationOtp({
|
|
||||||
email,
|
|
||||||
type: "sign-in",
|
|
||||||
})
|
|
||||||
setIsOtpSent(true)
|
|
||||||
} catch (err) {
|
|
||||||
setError(err instanceof Error ? err.message : "Failed to send OTP")
|
|
||||||
} finally {
|
|
||||||
setIsLoading(false)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
const handleVerifyOtp = async (e: React.FormEvent) => {
|
|
||||||
e.preventDefault()
|
|
||||||
setIsLoading(true)
|
|
||||||
setError(null)
|
|
||||||
|
|
||||||
try {
|
|
||||||
await authClient.signIn.emailOtp({
|
|
||||||
email,
|
|
||||||
otp,
|
|
||||||
})
|
|
||||||
await authClient.updateUser({
|
|
||||||
name,
|
|
||||||
})
|
|
||||||
router.push("/dashboard")
|
|
||||||
} catch (err) {
|
|
||||||
setError(err instanceof Error ? err.message : "Failed to verify OTP")
|
|
||||||
} finally {
|
|
||||||
setIsLoading(false)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return (
|
|
||||||
<form onSubmit={isOtpSent ? handleVerifyOtp : handleSendOtp} className="flex flex-col gap-4 w-full">
|
|
||||||
<FormInput
|
|
||||||
title="Your Name"
|
|
||||||
type="text"
|
|
||||||
value={name}
|
|
||||||
onChange={(e) => setName(e.target.value)}
|
|
||||||
required
|
|
||||||
disabled={isOtpSent}
|
|
||||||
/>
|
|
||||||
|
|
||||||
<FormInput
|
|
||||||
title="Email"
|
|
||||||
type="email"
|
|
||||||
value={email}
|
|
||||||
onChange={(e) => setEmail(e.target.value)}
|
|
||||||
required
|
|
||||||
disabled={isOtpSent}
|
|
||||||
/>
|
|
||||||
|
|
||||||
{isOtpSent && (
|
|
||||||
<FormInput
|
|
||||||
title="Check your email for the verification code"
|
|
||||||
type="text"
|
|
||||||
value={otp}
|
|
||||||
onChange={(e) => setOtp(e.target.value)}
|
|
||||||
required
|
|
||||||
maxLength={6}
|
|
||||||
pattern="[0-9]{6}"
|
|
||||||
/>
|
|
||||||
)}
|
|
||||||
|
|
||||||
{error && <p className="text-red-500 text-sm">{error}</p>}
|
|
||||||
|
|
||||||
<Button type="submit" disabled={isLoading}>
|
|
||||||
{isLoading ? "Loading..." : isOtpSent ? "Verify Code" : "Create Account"}
|
|
||||||
</Button>
|
|
||||||
</form>
|
|
||||||
)
|
|
||||||
}
|
|
||||||
@@ -34,8 +34,8 @@ export async function WelcomeWidget() {
|
|||||||
</CardTitle>
|
</CardTitle>
|
||||||
<CardDescription className="mt-5">
|
<CardDescription className="mt-5">
|
||||||
<p className="mb-3">
|
<p className="mb-3">
|
||||||
I'm a little accountant app that tries to help you deal with endless receipts, checks and invoices with
|
I'm a little accountant app that helps you deal with endless receipts, checks and invoices with (you
|
||||||
(you guessed it) GenAI. Here's what I can do:
|
guessed it) AI. Here's what I can do:
|
||||||
</p>
|
</p>
|
||||||
<ul className="mb-5 list-disc pl-5 space-y-1">
|
<ul className="mb-5 list-disc pl-5 space-y-1">
|
||||||
<li>
|
<li>
|
||||||
@@ -50,7 +50,7 @@ export async function WelcomeWidget() {
|
|||||||
</li>
|
</li>
|
||||||
<li>
|
<li>
|
||||||
All <strong>LLM prompts are configurable</strong>: for fields, categories and projects. You can go to
|
All <strong>LLM prompts are configurable</strong>: for fields, categories and projects. You can go to
|
||||||
settings and change them.
|
settings and change them however you want.
|
||||||
</li>
|
</li>
|
||||||
<li>
|
<li>
|
||||||
I save data in a <strong>local SQLite database</strong> and can export it to CSV and ZIP archives.
|
I save data in a <strong>local SQLite database</strong> and can export it to CSV and ZIP archives.
|
||||||
|
|||||||
@@ -51,16 +51,9 @@ export default function SidebarUser({ profile, isSelfHosted }: { profile: UserPr
|
|||||||
<DropdownMenuItem asChild>
|
<DropdownMenuItem asChild>
|
||||||
<Link href="/settings/profile" className="flex items-center gap-2">
|
<Link href="/settings/profile" className="flex items-center gap-2">
|
||||||
<User className="h-4 w-4" />
|
<User className="h-4 w-4" />
|
||||||
Profile Settings
|
Profile & Plan
|
||||||
</Link>
|
</Link>
|
||||||
</DropdownMenuItem>
|
</DropdownMenuItem>
|
||||||
{/* <DropdownMenuItem asChild>
|
|
||||||
<Link href="/settings/billing" className="flex items-center gap-2">
|
|
||||||
<CreditCard className="h-4 w-4" />
|
|
||||||
Your Subscription
|
|
||||||
</Link>
|
|
||||||
</DropdownMenuItem> */}
|
|
||||||
|
|
||||||
<DropdownMenuItem asChild>
|
<DropdownMenuItem asChild>
|
||||||
<Link href="/settings/profile" className="flex items-center gap-2">
|
<Link href="/settings/profile" className="flex items-center gap-2">
|
||||||
<HardDrive className="h-4 w-4" />
|
<HardDrive className="h-4 w-4" />
|
||||||
|
|||||||
@@ -38,15 +38,16 @@ export const PLANS: Record<string, Plan> = {
|
|||||||
early: {
|
early: {
|
||||||
code: "early",
|
code: "early",
|
||||||
name: "Early Adopter",
|
name: "Early Adopter",
|
||||||
description: "Special plan for our early users",
|
description: "Discounted plan for our first users who can forgive us bugs and childish problems :)",
|
||||||
benefits: [
|
benefits: [
|
||||||
|
"Special price for early adopters",
|
||||||
"512 Mb of storage",
|
"512 Mb of storage",
|
||||||
"1000 AI file analysis",
|
"1000 AI file analysis",
|
||||||
"Unlimited transactions",
|
"Unlimited transactions",
|
||||||
"Unlimited fields, categories and projects",
|
"Unlimited fields, categories and projects",
|
||||||
],
|
],
|
||||||
price: "€50/year",
|
price: "€35 for a year",
|
||||||
stripePriceId: "price_1RGzKUPKOUEUzVB3hVyo2n57",
|
stripePriceId: "price_1RHTmTAs8DS4NhOzGnWqxvZC",
|
||||||
limits: {
|
limits: {
|
||||||
storage: 512 * 1024 * 1024,
|
storage: 512 * 1024 * 1024,
|
||||||
ai: 1000,
|
ai: 1000,
|
||||||
|
|||||||
Reference in New Issue
Block a user