feat: Make Auctions and Intelligence pages publicly accessible
CHANGES: - Auctions page now uses public layout (Header/Footer) instead of CommandCenterLayout - Intelligence page now uses public layout (Header/Footer) instead of CommandCenterLayout - Both pages accessible without login - Login CTA banner shown to non-authenticated users - Opportunities tab locked for non-authenticated users (shows ?) - Price alerts feature requires login - Consistent layout between both public pages: - Same hero section with title and refresh button - Same 4-column stats grid - Same CTA banner design - Same filter/search layout - Same table component - Same pagination design
This commit is contained in:
166
analysis_3.md
Normal file
166
analysis_3.md
Normal file
@ -0,0 +1,166 @@
|
||||
Um die Churn Rate (Absprungrate) zu senken und den Umsatz pro Kunde (LTV - Lifetime Value) zu steigern, musst du das Mindset des Nutzers ändern:
|
||||
|
||||
**Von:** *"Ich nutze Pounce, um eine Domain zu **finden**."* (Einmaliges Projekt)
|
||||
**Zu:** *"Ich nutze Pounce, um mein Domain-Business zu **betreiben**."* (Laufender Prozess)
|
||||
|
||||
Wenn Pounce nur ein "Such-Tool" ist, kündigen die Leute, sobald sie fündig wurden. Wenn Pounce aber ihr "Betriebssystem" wird, bleiben sie für immer.
|
||||
|
||||
Hier sind 4 Strategien, um Pounce unverzichtbar zu machen:
|
||||
|
||||
---
|
||||
|
||||
### 1. Strategie: Vom "Jäger" zum "Wächter" (Portfolio Monitoring)
|
||||
*Ziel: Den Nutzer binden, auch wenn er gerade nichts kaufen will.*
|
||||
|
||||
Viele Domainer und Agenturen besitzen bereits 50-500 Domains. Sie haben Angst, eine Verlängerung zu verpassen oder technische Fehler nicht zu bemerken.
|
||||
|
||||
* **Das Feature:** **"My Portfolio Health"**
|
||||
Der Nutzer importiert seine *eigenen* Domains in Pounce (nicht um sie zu kaufen, sondern zu verwalten).
|
||||
* **Uptime Monitor:** Ist meine Seite noch online?
|
||||
* **SSL Monitor:** Läuft mein Zertifikat ab?
|
||||
* **Expiration Alert:** Erinnere mich 30 Tage vor Ablauf (besser als die Spam-Mails der Registrare).
|
||||
* **Blacklist Check:** Landet meine Domain auf einer Spam-Liste?
|
||||
|
||||
* **Der Lock-in Effekt:**
|
||||
Niemand kündigt das Tool, das seine Assets überwacht ("Versicherungs-Psychologie"). Wenn du ihre 50 Domains überwachst, bist du unverzichtbar.
|
||||
|
||||
### 2. Strategie: Der "Micro-Marktplatz" (Liquidity)
|
||||
*Ziel: Mehr Umsatz durch Transaktionen.*
|
||||
|
||||
Wenn ein "Hunter" eine Domain über Pounce findet, will er sie oft später wieder verkaufen (Flipping). Aktuell schickst du ihn dafür weg zu Sedo. Warum nicht im Haus behalten?
|
||||
|
||||
* **Das Feature:** **"Pounce 'For Sale' Landing Pages"**
|
||||
Ein User (Trader/Tycoon) kann für seine Domains mit einem Klick eine schicke Verkaufsseite erstellen.
|
||||
* *Domain:* `super-startup.ai`
|
||||
* *Pounce generiert:* `pounce.ch/buy/super-startup-ai`
|
||||
* *Design:* Hochwertig, zeigt deine "Valuation Daten" (Pounce Score) an, um den Preis zu rechtfertigen.
|
||||
* *Kontakt:* Ein einfaches Kontaktformular, das die Anfrage direkt an den User leitet.
|
||||
|
||||
* **Das Geld:**
|
||||
* Entweder Teil des Abo-Preises ("Erstelle 5 Verkaufsseiten kostenlos").
|
||||
* Oder: Du nimmst keine Provision, aber der Käufer muss sich bei Pounce registrieren, um den Verkäufer zu kontaktieren (Lead Gen).
|
||||
|
||||
### 3. Strategie: SEO-Daten & Backlinks (Neue Zielgruppe)
|
||||
*Ziel: Kunden mit hohem Budget gewinnen (Agenturen).*
|
||||
|
||||
SEO-Agenturen kündigen fast nie, weil sie monatliche Budgets für Tools haben. Sie suchen Domains nicht wegen dem Namen, sondern wegen der **Power** (Backlinks).
|
||||
|
||||
* **Das Feature:** **"SEO Juice Detector"**
|
||||
Wenn eine Domain droppt, prüfst du nicht nur den Namen, sondern (über günstige APIs wie Moz oder durch Scraping öffentlicher Daten), ob Backlinks existieren.
|
||||
* *Anzeige:* "Domain `alte-bäckerei-münchen.de` ist frei. Hat Links von `sueddeutsche.de` und `wikipedia.org`."
|
||||
* **Der Wert:** Solche Domains sind für SEOs 100€ - 500€ wert, auch wenn der Name hässlich ist.
|
||||
* **Monetarisierung:** Das ist ein reines **Tycoon-Feature ($29 oder sogar $49/Monat)**.
|
||||
|
||||
### 4. Strategie: Alerts "nach Maß" (Hyper-Personalisierung)
|
||||
*Ziel: Den Nutzer täglich zurückholen.*
|
||||
|
||||
Wenn ich nur eine Mail bekomme "Hier sind 100 neue Domains", ist das oft Spam für mich. Ich will nur *genau das*, was ich suche.
|
||||
|
||||
* **Das Feature:** **"Sniper Alerts"**
|
||||
Der User kann extrem spezifische Filter speichern:
|
||||
* *"Informiere mich NUR, wenn eine 4-Letter .com Domain droppt, die kein 'q' oder 'x' enthält."*
|
||||
* *"Informiere mich, wenn eine .ch Domain droppt, die das Wort 'Immo' enthält."*
|
||||
* **Der Effekt:** Wenn die SMS/Mail kommt, weiß der User: "Das ist relevant". Er klickt, loggt sich ein, bleibt aktiv.
|
||||
|
||||
---
|
||||
|
||||
### Zusammenfassung des erweiterten Business-Modells
|
||||
|
||||
So sieht deine Umsatz-Maschine dann aus:
|
||||
|
||||
| Stufe | Was der User tut | Warum er bleibt (Retention) | Dein Umsatz |
|
||||
| :--- | :--- | :--- | :--- |
|
||||
| **Phase 1: Finding** | Sucht freie/droppende Domains. | Findet bessere Deals durch Spam-Filter. | $9 / Monat |
|
||||
| **Phase 2: Monitoring** | Überwacht Wettbewerber & eigene Domains. | Angst, Status-Änderungen zu verpassen (Versicherung). | Churn sinkt drastisch. |
|
||||
| **Phase 3: Selling** | Erstellt Verkaufs-Landings via Pounce. | Nutzt Pounce als Schaufenster für sein Business. | User ist "locked in". |
|
||||
| **Phase 4: SEO** | Sucht Backlink-Monster. | Verdient Geld mit deinen Daten (ROI). | $29 - $49 / Monat |
|
||||
|
||||
### Mein Tipp für den Start:
|
||||
Konzentriere dich auf **Strategie 1 (Portfolio Monitoring)** als erstes Zusatz-Feature nach dem Launch.
|
||||
|
||||
Warum?
|
||||
Es ist technisch einfach (du hast die Ping-Skripte ja schon für die Analyse gebaut). Du erlaubst dem User einfach, Domains *manuell* hinzuzufügen.
|
||||
Sobald ein User mal 50 seiner eigenen Domains eingetragen hat, wird er sein Abo **niemals kündigen**, weil er sonst seine Überwachung verliert. Das ist der ultimative "Golden Handcuff".
|
||||
|
||||
Vertrauen ist im Domain-Business tatsächlich die **härteste Währung**. Die Branche ist leider voll von Betrügern (Domain-Diebstahl, Phishing, Fake-Auktionen).
|
||||
|
||||
Wenn `pounce.ch` als "Command Center" wahrgenommen werden soll, muss die Plattform **sauberer sein als der Rest**.
|
||||
|
||||
Hier ist ein **4-Säulen-Sicherheitskonzept**, mit dem du Missbrauch verhinderst und gleichzeitig massives Vertrauen bei deinen echten Nutzern aufbaust.
|
||||
|
||||
---
|
||||
|
||||
### Säule 1: Identity Verification (Wer bist du?)
|
||||
*Hürde: Betrüger hassen Identifikation.*
|
||||
|
||||
Du darfst "Tycoon"-Features (und vor allem Verkaufs-Features) nicht einfach jedem geben, der eine E-Mail-Adresse hat.
|
||||
|
||||
1. **Stripe Identity / Radar:**
|
||||
Nutze für die Zahlungsabwicklung Stripe. Stripe hat eingebaute Betrugserkennung ("Radar"). Wenn jemand eine gestohlene Kreditkarte nutzt, blockiert Stripe ihn meist sofort. Das ist deine erste Firewall.
|
||||
2. **SMS-Verifizierung (2FA):**
|
||||
Jeder Account, der Domains verkaufen oder überwachen will, muss eine **Handynummer verifizieren**. Wegwerf-Nummern (VoIP) werden blockiert. Das erhöht die Hürde für Spammer massiv.
|
||||
3. **LinkedIn-Login (Optional für Trust):**
|
||||
Biete an: "Verbinde dein LinkedIn für den 'Verified Professional' Status". Ein Profil mit 500+ Kontakten und Historie ist selten ein Fake.
|
||||
|
||||
---
|
||||
|
||||
### Säule 2: Asset Verification (Gehört dir das wirklich?)
|
||||
*Hürde: Verhindern, dass Leute fremde Domains als ihre eigenen ausgeben.*
|
||||
|
||||
Das ist der wichtigste Punkt, wenn du Features wie "Portfolio Monitoring" oder "For Sale Pages" anbietest.
|
||||
|
||||
**Die technische Lösung: DNS Ownership Verify**
|
||||
Bevor ein Nutzer eine Domain in sein Portfolio aufnehmen kann, um sie zu verkaufen oder tief zu analysieren, muss er beweisen, dass er der Admin ist.
|
||||
* **Wie es funktioniert:**
|
||||
1. User fügt `mein-startup.ch` hinzu.
|
||||
2. Pounce sagt: "Bitte erstelle einen TXT-Record in deinen DNS-Einstellungen mit dem Inhalt: `pounce-verification=847392`."
|
||||
3. Dein System prüft den Record.
|
||||
4. Nur wenn er da ist -> **Domain Verified ✅**.
|
||||
|
||||
*Das ist der Industriestandard (macht Google auch). Wer keinen Zugriff auf die DNS hat, kann die Domain nicht claimen.*
|
||||
|
||||
---
|
||||
|
||||
### Säule 3: Content Monitoring (Was machst du damit?)
|
||||
*Hürde: Verhindern, dass deine "For Sale"-Seiten für Phishing genutzt werden.*
|
||||
|
||||
Wenn User über Pounce Verkaufsseiten ("Landers") erstellen können, könnten sie dort versuchen, Bankdaten abzugreifen.
|
||||
|
||||
1. **Automatischer Blacklist-Scan:**
|
||||
Jede Domain, die ins System kommt, wird sofort gegen **Google Safe Browsing** und **Spamhaus** geprüft. Ist die Domain dort als "Malware" gelistet? -> **Sofortiger Ban.**
|
||||
2. **Keyword-Blocking:**
|
||||
Erlaube keine Titel oder Texte auf Verkaufsseiten, die Wörter enthalten wie: "Login", "Bank", "Verify", "Paypal", "Password".
|
||||
3. **No Custom HTML:**
|
||||
Erlaube Usern auf ihren Verkaufsseiten *kein* eigenes HTML/JavaScript. Nur Text und vordefinierte Buttons. So können sie keine Schadsoftware einschleusen.
|
||||
|
||||
---
|
||||
|
||||
### Säule 4: The "Safe Harbor" Badge (Marketing)
|
||||
*Nutzen: Du machst die Sicherheit zu deinem Verkaufsargument.*
|
||||
|
||||
Du kommunizierst diese Strenge nicht als "Nervigkeit", sondern als **Qualitätsmerkmal**.
|
||||
|
||||
* **Das "Pounce Verified" Siegel:**
|
||||
Auf jeder Verkaufsseite oder in jedem Profil zeigst du an:
|
||||
* ✅ **ID Verified** (Handy/Zahlung geprüft)
|
||||
* ✅ **Owner Verified** (DNS geprüft)
|
||||
* ✅ **Clean History** (Keine Spam-Reports)
|
||||
|
||||
---
|
||||
|
||||
### Prozess bei Verstößen ("Zero Tolerance")
|
||||
|
||||
Du brauchst klare AGBs ("Terms of Service"):
|
||||
1. **One Strike Policy:** Wer versucht, Phishing zu betreiben oder gestohlene Domains anzubieten, wird sofort permanent gesperrt. Keine Diskussion.
|
||||
2. **Reporting Button:** Gib der Community Macht. Ein "Report Abuse"-Button auf jeder Seite. Wenn 2-3 unabhängige User etwas melden, wird das Asset automatisch offline genommen, bis du es geprüft hast.
|
||||
|
||||
### Zusammenfassung: Der "Trust Stack"
|
||||
|
||||
| Ebene | Maßnahme | Effekt |
|
||||
| :--- | :--- | :--- |
|
||||
| **Login** | SMS / 2FA + Stripe Radar | Hält Bots und Kreditkartenbetrüger fern. |
|
||||
| **Portfolio** | **DNS TXT Record (Zwingend)** | Nur der echte Besitzer kann Domains verwalten. |
|
||||
| **Marktplatz** | Google Safe Browsing Check | Verhindert Malware/Phishing auf deiner Plattform. |
|
||||
| **Frontend** | "Verified Owner" Badge | Käufer wissen: Das hier ist sicher. |
|
||||
|
||||
**Damit positionierst du Pounce als den "Safe Space" im wilden Westen des Domain-Handels.** Das ist für seriöse Investoren oft wichtiger als der Preis.
|
||||
@ -3,7 +3,8 @@
|
||||
import { useEffect, useState } from 'react'
|
||||
import { useStore } from '@/lib/store'
|
||||
import { api } from '@/lib/api'
|
||||
import { CommandCenterLayout } from '@/components/CommandCenterLayout'
|
||||
import { Header } from '@/components/Header'
|
||||
import { Footer } from '@/components/Footer'
|
||||
import { PremiumTable, Badge, PlatformBadge, StatCard, PageContainer } from '@/components/PremiumTable'
|
||||
import {
|
||||
Clock,
|
||||
@ -12,15 +13,13 @@ import {
|
||||
Flame,
|
||||
Timer,
|
||||
Gavel,
|
||||
ChevronUp,
|
||||
ChevronDown,
|
||||
ChevronsUpDown,
|
||||
DollarSign,
|
||||
RefreshCw,
|
||||
Target,
|
||||
X,
|
||||
TrendingUp,
|
||||
Loader2,
|
||||
Lock,
|
||||
Sparkles,
|
||||
ArrowRight,
|
||||
} from 'lucide-react'
|
||||
import Link from 'next/link'
|
||||
import clsx from 'clsx'
|
||||
@ -66,7 +65,7 @@ const PLATFORMS = [
|
||||
]
|
||||
|
||||
export default function AuctionsPage() {
|
||||
const { isAuthenticated, subscription } = useStore()
|
||||
const { isAuthenticated } = useStore()
|
||||
|
||||
const [allAuctions, setAllAuctions] = useState<Auction[]>([])
|
||||
const [endingSoon, setEndingSoon] = useState<Auction[]>([])
|
||||
@ -202,43 +201,91 @@ export default function AuctionsPage() {
|
||||
}
|
||||
|
||||
return (
|
||||
<CommandCenterLayout
|
||||
title="Auctions"
|
||||
subtitle={getSubtitle()}
|
||||
actions={
|
||||
<div className="min-h-screen bg-background flex flex-col">
|
||||
<Header />
|
||||
|
||||
{/* Hero Section */}
|
||||
<section className="relative pt-24 pb-12 overflow-hidden">
|
||||
{/* Background Effects */}
|
||||
<div className="absolute inset-0 pointer-events-none">
|
||||
<div className="absolute top-[-20%] left-1/2 -translate-x-1/2 w-[1200px] h-[800px] bg-accent/[0.03] rounded-full blur-[120px]" />
|
||||
</div>
|
||||
|
||||
<div className="relative max-w-7xl mx-auto px-4 sm:px-6 lg:px-8">
|
||||
<div className="flex flex-col sm:flex-row sm:items-center sm:justify-between gap-4 mb-8">
|
||||
<div>
|
||||
<h1 className="text-3xl sm:text-4xl font-display tracking-tight text-foreground">
|
||||
Domain Auctions
|
||||
</h1>
|
||||
<p className="text-foreground-muted mt-2">{getSubtitle()}</p>
|
||||
</div>
|
||||
<button
|
||||
onClick={handleRefresh}
|
||||
disabled={refreshing}
|
||||
className="flex items-center gap-2 px-4 py-2 text-sm text-foreground-muted hover:text-foreground
|
||||
hover:bg-foreground/5 rounded-lg transition-all disabled:opacity-50"
|
||||
className="flex items-center gap-2 px-4 py-2.5 text-sm text-foreground-muted hover:text-foreground
|
||||
bg-foreground/5 hover:bg-foreground/10 border border-border/50 rounded-xl
|
||||
transition-all disabled:opacity-50"
|
||||
>
|
||||
<RefreshCw className={clsx("w-4 h-4", refreshing && "animate-spin")} />
|
||||
<span className="hidden sm:inline">Refresh</span>
|
||||
Refresh
|
||||
</button>
|
||||
}
|
||||
>
|
||||
<PageContainer>
|
||||
</div>
|
||||
|
||||
{/* Stats */}
|
||||
<div className="grid grid-cols-2 lg:grid-cols-4 gap-4">
|
||||
<div className="grid grid-cols-2 lg:grid-cols-4 gap-4 mb-8">
|
||||
<StatCard title="All Auctions" value={allAuctions.length} icon={Gavel} />
|
||||
<StatCard title="Ending Soon" value={endingSoon.length} icon={Timer} accent />
|
||||
<StatCard title="Hot Auctions" value={hotAuctions.length} subtitle="20+ bids" icon={Flame} />
|
||||
<StatCard title="Opportunities" value={opportunities.length} icon={Target} />
|
||||
<StatCard
|
||||
title="Opportunities"
|
||||
value={isAuthenticated ? opportunities.length : '—'}
|
||||
subtitle={isAuthenticated ? undefined : 'Login required'}
|
||||
icon={Target}
|
||||
/>
|
||||
</div>
|
||||
|
||||
{/* Login Banner for Opportunities */}
|
||||
{!isAuthenticated && (
|
||||
<div className="mb-8 p-5 bg-gradient-to-r from-accent/10 via-accent/5 to-transparent border border-accent/20 rounded-2xl">
|
||||
<div className="flex flex-col sm:flex-row sm:items-center sm:justify-between gap-4">
|
||||
<div className="flex items-center gap-4">
|
||||
<div className="w-12 h-12 bg-accent/20 rounded-xl flex items-center justify-center">
|
||||
<Sparkles className="w-6 h-6 text-accent" />
|
||||
</div>
|
||||
<div>
|
||||
<h3 className="text-lg font-semibold text-foreground">Unlock Smart Opportunities</h3>
|
||||
<p className="text-sm text-foreground-muted">Get AI-powered auction analysis and personalized recommendations</p>
|
||||
</div>
|
||||
</div>
|
||||
<Link
|
||||
href="/login"
|
||||
className="flex items-center justify-center gap-2 px-6 py-3 bg-gradient-to-r from-accent to-accent/80
|
||||
text-background rounded-xl font-medium hover:shadow-[0_0_20px_-5px_rgba(16,185,129,0.4)] transition-all"
|
||||
>
|
||||
Sign In <ArrowRight className="w-4 h-4" />
|
||||
</Link>
|
||||
</div>
|
||||
</div>
|
||||
)}
|
||||
|
||||
{/* Tabs */}
|
||||
<div className="flex flex-wrap items-center gap-2 p-1.5 bg-background-secondary/30 border border-border/30 rounded-2xl w-fit">
|
||||
<div className="flex flex-wrap items-center gap-2 p-1.5 bg-background-secondary/30 border border-border/30 rounded-2xl w-fit mb-6">
|
||||
{[
|
||||
{ id: 'all' as const, label: 'All', icon: Gavel, count: allAuctions.length },
|
||||
{ id: 'ending' as const, label: 'Ending Soon', icon: Timer, count: endingSoon.length, color: 'warning' },
|
||||
{ id: 'hot' as const, label: 'Hot', icon: Flame, count: hotAuctions.length },
|
||||
{ id: 'opportunities' as const, label: 'Opportunities', icon: Target, count: opportunities.length },
|
||||
{ id: 'opportunities' as const, label: 'Opportunities', icon: Target, count: opportunities.length, locked: !isAuthenticated },
|
||||
].map((tab) => (
|
||||
<button
|
||||
key={tab.id}
|
||||
onClick={() => setActiveTab(tab.id)}
|
||||
onClick={() => {
|
||||
if (tab.locked) return
|
||||
setActiveTab(tab.id)
|
||||
}}
|
||||
disabled={tab.locked}
|
||||
className={clsx(
|
||||
"flex items-center gap-2 px-4 py-2.5 text-sm font-medium rounded-xl transition-all",
|
||||
tab.locked && "opacity-50 cursor-not-allowed",
|
||||
activeTab === tab.id
|
||||
? tab.color === 'warning'
|
||||
? "bg-amber-500 text-background"
|
||||
@ -246,18 +293,18 @@ export default function AuctionsPage() {
|
||||
: "text-foreground-muted hover:text-foreground hover:bg-foreground/5"
|
||||
)}
|
||||
>
|
||||
<tab.icon className="w-4 h-4" />
|
||||
{tab.locked ? <Lock className="w-4 h-4" /> : <tab.icon className="w-4 h-4" />}
|
||||
<span className="hidden sm:inline">{tab.label}</span>
|
||||
<span className={clsx(
|
||||
"text-xs px-1.5 py-0.5 rounded",
|
||||
"text-xs px-1.5 py-0.5 rounded tabular-nums",
|
||||
activeTab === tab.id ? "bg-background/20" : "bg-foreground/10"
|
||||
)}>{tab.count}</span>
|
||||
)}>{tab.locked ? '?' : tab.count}</span>
|
||||
</button>
|
||||
))}
|
||||
</div>
|
||||
|
||||
{/* Filters */}
|
||||
<div className="flex flex-wrap gap-3">
|
||||
<div className="flex flex-wrap gap-3 mb-6">
|
||||
<div className="relative flex-1 min-w-[200px] max-w-md">
|
||||
<Search className="absolute left-4 top-1/2 -translate-y-1/2 w-4 h-4 text-foreground-subtle" />
|
||||
<input
|
||||
@ -352,7 +399,7 @@ export default function AuctionsPage() {
|
||||
align: 'right',
|
||||
render: (a) => (
|
||||
<div>
|
||||
<span className="font-medium text-foreground">{formatCurrency(a.current_bid)}</span>
|
||||
<span className="font-medium text-foreground tabular-nums">{formatCurrency(a.current_bid)}</span>
|
||||
{a.buy_now_price && (
|
||||
<p className="text-xs text-accent">Buy: {formatCurrency(a.buy_now_price)}</p>
|
||||
)}
|
||||
@ -367,7 +414,7 @@ export default function AuctionsPage() {
|
||||
hideOnMobile: true,
|
||||
render: (a) => (
|
||||
<span className={clsx(
|
||||
"font-medium flex items-center justify-end gap-1",
|
||||
"font-medium flex items-center justify-end gap-1 tabular-nums",
|
||||
a.num_bids >= 20 ? "text-accent" : a.num_bids >= 10 ? "text-amber-400" : "text-foreground-muted"
|
||||
)}>
|
||||
{a.num_bids}
|
||||
@ -419,7 +466,11 @@ export default function AuctionsPage() {
|
||||
},
|
||||
]}
|
||||
/>
|
||||
</PageContainer>
|
||||
</CommandCenterLayout>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<div className="flex-1" />
|
||||
<Footer />
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
@ -3,7 +3,8 @@
|
||||
import { useEffect, useState } from 'react'
|
||||
import { useStore } from '@/lib/store'
|
||||
import { api } from '@/lib/api'
|
||||
import { CommandCenterLayout } from '@/components/CommandCenterLayout'
|
||||
import { Header } from '@/components/Header'
|
||||
import { Footer } from '@/components/Footer'
|
||||
import { PremiumTable, Badge, StatCard, PageContainer } from '@/components/PremiumTable'
|
||||
import {
|
||||
Search,
|
||||
@ -18,6 +19,8 @@ import {
|
||||
RefreshCw,
|
||||
Bell,
|
||||
X,
|
||||
Sparkles,
|
||||
ArrowRight,
|
||||
} from 'lucide-react'
|
||||
import clsx from 'clsx'
|
||||
import Link from 'next/link'
|
||||
@ -34,7 +37,7 @@ interface TLDData {
|
||||
}
|
||||
|
||||
export default function IntelligencePage() {
|
||||
const { subscription } = useStore()
|
||||
const { isAuthenticated } = useStore()
|
||||
|
||||
const [tldData, setTldData] = useState<TLDData[]>([])
|
||||
const [loading, setLoading] = useState(true)
|
||||
@ -96,24 +99,38 @@ export default function IntelligencePage() {
|
||||
}
|
||||
|
||||
return (
|
||||
<CommandCenterLayout
|
||||
title="TLD Intelligence"
|
||||
subtitle={getSubtitle()}
|
||||
actions={
|
||||
<div className="min-h-screen bg-background flex flex-col">
|
||||
<Header />
|
||||
|
||||
{/* Hero Section */}
|
||||
<section className="relative pt-24 pb-12 overflow-hidden">
|
||||
{/* Background Effects */}
|
||||
<div className="absolute inset-0 pointer-events-none">
|
||||
<div className="absolute top-[-20%] left-1/2 -translate-x-1/2 w-[1200px] h-[800px] bg-accent/[0.03] rounded-full blur-[120px]" />
|
||||
</div>
|
||||
|
||||
<div className="relative max-w-7xl mx-auto px-4 sm:px-6 lg:px-8">
|
||||
<div className="flex flex-col sm:flex-row sm:items-center sm:justify-between gap-4 mb-8">
|
||||
<div>
|
||||
<h1 className="text-3xl sm:text-4xl font-display tracking-tight text-foreground">
|
||||
TLD Intelligence
|
||||
</h1>
|
||||
<p className="text-foreground-muted mt-2">{getSubtitle()}</p>
|
||||
</div>
|
||||
<button
|
||||
onClick={handleRefresh}
|
||||
disabled={refreshing}
|
||||
className="flex items-center gap-2 px-4 py-2 text-sm text-foreground-muted hover:text-foreground
|
||||
hover:bg-foreground/5 rounded-lg transition-all disabled:opacity-50"
|
||||
className="flex items-center gap-2 px-4 py-2.5 text-sm text-foreground-muted hover:text-foreground
|
||||
bg-foreground/5 hover:bg-foreground/10 border border-border/50 rounded-xl
|
||||
transition-all disabled:opacity-50"
|
||||
>
|
||||
<RefreshCw className={clsx("w-4 h-4", refreshing && "animate-spin")} />
|
||||
<span className="hidden sm:inline">Refresh</span>
|
||||
Refresh
|
||||
</button>
|
||||
}
|
||||
>
|
||||
<PageContainer>
|
||||
</div>
|
||||
|
||||
{/* Stats Overview */}
|
||||
<div className="grid grid-cols-2 lg:grid-cols-4 gap-4">
|
||||
<div className="grid grid-cols-2 lg:grid-cols-4 gap-4 mb-8">
|
||||
<StatCard
|
||||
title="TLDs Tracked"
|
||||
value={total > 0 ? total.toLocaleString() : '—'}
|
||||
@ -140,8 +157,32 @@ export default function IntelligencePage() {
|
||||
/>
|
||||
</div>
|
||||
|
||||
{/* CTA Banner for non-authenticated users */}
|
||||
{!isAuthenticated && (
|
||||
<div className="mb-8 p-5 bg-gradient-to-r from-accent/10 via-accent/5 to-transparent border border-accent/20 rounded-2xl">
|
||||
<div className="flex flex-col sm:flex-row sm:items-center sm:justify-between gap-4">
|
||||
<div className="flex items-center gap-4">
|
||||
<div className="w-12 h-12 bg-accent/20 rounded-xl flex items-center justify-center">
|
||||
<Bell className="w-6 h-6 text-accent" />
|
||||
</div>
|
||||
<div>
|
||||
<h3 className="text-lg font-semibold text-foreground">Set Price Alerts</h3>
|
||||
<p className="text-sm text-foreground-muted">Get notified when TLD prices drop to your target</p>
|
||||
</div>
|
||||
</div>
|
||||
<Link
|
||||
href="/login"
|
||||
className="flex items-center justify-center gap-2 px-6 py-3 bg-gradient-to-r from-accent to-accent/80
|
||||
text-background rounded-xl font-medium hover:shadow-[0_0_20px_-5px_rgba(16,185,129,0.4)] transition-all"
|
||||
>
|
||||
Sign In <ArrowRight className="w-4 h-4" />
|
||||
</Link>
|
||||
</div>
|
||||
</div>
|
||||
)}
|
||||
|
||||
{/* Filters */}
|
||||
<div className="flex flex-col sm:flex-row gap-3">
|
||||
<div className="flex flex-col sm:flex-row gap-3 mb-6">
|
||||
<div className="relative flex-1 max-w-md">
|
||||
<Search className="absolute left-4 top-1/2 -translate-y-1/2 w-4 h-4 text-foreground-muted" />
|
||||
<input
|
||||
@ -255,7 +296,7 @@ export default function IntelligencePage() {
|
||||
href={`/tld-pricing/${tld.tld}`}
|
||||
className="p-2 text-foreground-subtle hover:text-foreground hover:bg-foreground/5 rounded-lg transition-colors"
|
||||
onClick={(e) => e.stopPropagation()}
|
||||
title="Set price alert"
|
||||
title="View details"
|
||||
>
|
||||
<Bell className="w-4 h-4" />
|
||||
</Link>
|
||||
@ -268,7 +309,7 @@ export default function IntelligencePage() {
|
||||
|
||||
{/* Pagination */}
|
||||
{total > 50 && (
|
||||
<div className="flex items-center justify-center gap-4 pt-2">
|
||||
<div className="flex items-center justify-center gap-4 pt-6">
|
||||
<button
|
||||
onClick={() => setPage(Math.max(0, page - 1))}
|
||||
disabled={page === 0}
|
||||
@ -292,7 +333,11 @@ export default function IntelligencePage() {
|
||||
</button>
|
||||
</div>
|
||||
)}
|
||||
</PageContainer>
|
||||
</CommandCenterLayout>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<div className="flex-1" />
|
||||
<Footer />
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
0
frontend/src/app/watchlist/page.tsx
Normal file → Executable file
0
frontend/src/app/watchlist/page.tsx
Normal file → Executable file
Reference in New Issue
Block a user