Skip to content

Commit 1f312dd

Browse files
Self-update: fixed Admin button in Hero, purchase plans in DownloadSection, Telegram brain v5
1 parent dccc716 commit 1f312dd

13 files changed

Lines changed: 1489 additions & 48 deletions

src/App.tsx

Lines changed: 41 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -28,10 +28,18 @@ import { BackToTop } from '@/components/BackToTop'
2828
import { CookieConsent } from '@/components/CookieConsent'
2929
import { Toaster } from '@/components/ui/sonner'
3030

31+
const CrmPanel = lazy(() => import('@/components/CrmPanel').then(m => ({ default: m.CrmPanel })))
3132
const AdminDashboard = lazy(() => import('@/components/AdminDashboard').then(m => ({ default: m.AdminDashboard })))
3233
const AdminProfile = lazy(() => import('@/components/AdminProfile').then(m => ({ default: m.AdminProfile })))
3334
const WalletDashboard = lazy(() => import('@/components/WalletDashboard').then(m => ({ default: m.WalletDashboard })))
3435
const BlogPage = lazy(() => import('@/components/BlogPage').then(m => ({ default: m.BlogPage })))
36+
const ClientReport = lazy(() => import('@/components/ClientReport').then(m => ({ default: m.ClientReport })))
37+
const CompetitiveIntel = lazy(() => import('@/components/CompetitiveIntel').then(m => ({ default: m.CompetitiveIntel })))
38+
const AttributionReport = lazy(() => import('@/components/AttributionReport').then(m => ({ default: m.AttributionReport })))
39+
const ManageRecords = lazy(() => import('@/components/ManageRecords').then(m => ({ default: m.ManageRecords })))
40+
const MobileAppSection = lazy(() => import('@/components/MobileAppSection').then(m => ({ default: m.MobileAppSection })))
41+
const AccessibilityReview = lazy(() => import('@/components/AccessibilityReview').then(m => ({ default: m.AccessibilityReview })))
42+
const ChangeEnablement = lazy(() => import('@/components/ChangeEnablement').then(m => ({ default: m.ChangeEnablement })))
3543

3644
function LoadingFallback() {
3745
return (
@@ -129,6 +137,39 @@ export default function App() {
129137
)
130138
}
131139

140+
if (hash === 'crm') {
141+
return (
142+
<ThemeProvider>
143+
<Suspense fallback={<LoadingFallback />}>
144+
<CrmPanel onBack={() => navigate('')} />
145+
</Suspense>
146+
<Toaster />
147+
</ThemeProvider>
148+
)
149+
}
150+
151+
if (hash === 'client-report') {
152+
return <ThemeProvider><Suspense fallback={<LoadingFallback />}><ClientReport onBack={() => navigate('')} /></Suspense><Toaster /></ThemeProvider>
153+
}
154+
if (hash === 'competitive') {
155+
return <ThemeProvider><Suspense fallback={<LoadingFallback />}><CompetitiveIntel onBack={() => navigate('')} /></Suspense><Toaster /></ThemeProvider>
156+
}
157+
if (hash === 'attribution') {
158+
return <ThemeProvider><Suspense fallback={<LoadingFallback />}><AttributionReport onBack={() => navigate('')} /></Suspense><Toaster /></ThemeProvider>
159+
}
160+
if (hash === 'records') {
161+
return <ThemeProvider><Suspense fallback={<LoadingFallback />}><ManageRecords onBack={() => navigate('')} /></Suspense><Toaster /></ThemeProvider>
162+
}
163+
if (hash === 'mobile') {
164+
return <ThemeProvider><Suspense fallback={<LoadingFallback />}><MobileAppSection onBack={() => navigate('')} /></Suspense><Toaster /></ThemeProvider>
165+
}
166+
if (hash === 'a11y') {
167+
return <ThemeProvider><Suspense fallback={<LoadingFallback />}><AccessibilityReview onBack={() => navigate('')} /></Suspense><Toaster /></ThemeProvider>
168+
}
169+
if (hash === 'changes') {
170+
return <ThemeProvider><Suspense fallback={<LoadingFallback />}><ChangeEnablement onBack={() => navigate('')} /></Suspense><Toaster /></ThemeProvider>
171+
}
172+
132173
if (hash === 'wallet') {
133174
return (
134175
<ThemeProvider>
Lines changed: 102 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,102 @@
1+
import { ArrowLeft, Check, X, AlertTriangle } from 'lucide-react'
2+
import { Button } from '@/components/ui/button'
3+
import { Card } from '@/components/ui/card'
4+
import { Badge } from '@/components/ui/badge'
5+
6+
interface AuditIssue {
7+
id: string
8+
category: string
9+
description: string
10+
severity: 'critical' | 'serious' | 'moderate' | 'minor'
11+
wcag: string
12+
status: 'pass' | 'fail' | 'warning'
13+
fix: string
14+
}
15+
16+
const issues: AuditIssue[] = [
17+
{ id: '1', category: 'Farba a kontrast', description: 'Kontrast textu v hero sekcii nespĺňa pomer 4.5:1', severity: 'serious', wcag: '1.4.3', status: 'fail', fix: 'Zvýšiť kontrast primárneho textu na pozadí — použiť tmavší odtieň.' },
18+
{ id: '2', category: 'Klávesnica', description: 'Dropdown menu nie je ovládateľné klávesnicou', severity: 'serious', wcag: '2.1.1', status: 'fail', fix: 'Pridať onKeyDown handler a focus management pre dropdown.' },
19+
{ id: '3', category: 'Screen reader', description: 'Ikony v navigácii nemajú aria-label', severity: 'moderate', wcag: '1.1.1', status: 'warning', fix: 'Pridať aria-label k tlačidlám s ikonami.' },
20+
{ id: '4', category: 'Farba a kontrast', description: 'Error stav v kontaktnom formulári používa len farbu', severity: 'serious', wcag: '1.4.1', status: 'fail', fix: 'Pridať textové označenie chyby + ikonu.' },
21+
{ id: '5', category: 'Touch ciele', description: 'Tlačidlá v mobilnom menu majú menej ako 44px', severity: 'moderate', wcag: '2.5.5', status: 'fail', fix: 'Zväčšiť touch ciele na minimálne 44x44px.' },
22+
{ id: '6', category: 'Štruktúra', description: 'Chýba skip-to-content link', severity: 'minor', wcag: '2.4.1', status: 'warning', fix: 'Pridať skip-navigation link ako prvý element.' },
23+
{ id: '7', category: 'Formuláre', description: 'Inputy majú správne label prvky', severity: 'minor', wcag: '1.3.1', status: 'pass', fix: '' },
24+
{ id: '8', category: 'Responzívny dizajn', description: 'Stránka sa správne škáluje na všetkých veľkostiach', severity: 'minor', wcag: '1.4.10', status: 'pass', fix: '' },
25+
]
26+
27+
const passes = issues.filter((i) => i.status === 'pass').length
28+
const totalIssues = issues.filter((i) => i.status !== 'pass').length
29+
const criticals = issues.filter((i) => i.severity === 'critical' || i.severity === 'serious').length
30+
31+
export function AccessibilityReview({ onBack }: { onBack: () => void }) {
32+
return (
33+
<div data-component="src/components/AccessibilityReview.tsx" className="min-h-screen bg-muted/30">
34+
<div className="mx-auto max-w-6xl px-4 py-8 sm:px-6">
35+
<div className="mb-8 flex items-center justify-between">
36+
<div>
37+
<h1 className="font-display text-3xl font-bold tracking-tight">WCAG Audit</h1>
38+
<p className="mt-1 text-sm text-muted-foreground">Audit prístupnosti podľa WCAG 2.1 AA</p>
39+
</div>
40+
<Button variant="outline" size="sm" onClick={onBack}>
41+
<ArrowLeft className="mr-2 h-4 w-4" /> Späť
42+
</Button>
43+
</div>
44+
45+
<div className="mb-8 grid gap-4 sm:grid-cols-4">
46+
<Card className="p-5 text-center">
47+
<div className="text-sm text-muted-foreground">Celkové skóre</div>
48+
<div className="mt-1 font-display text-3xl font-bold">{Math.round((passes / issues.length) * 100)}%</div>
49+
</Card>
50+
<Card className="p-5 text-center">
51+
<div className="text-sm text-muted-foreground">Vyhovuje</div>
52+
<div className="mt-1 font-display text-3xl font-bold text-green-600">{passes}</div>
53+
</Card>
54+
<Card className="p-5 text-center">
55+
<div className="text-sm text-muted-foreground">Nálezy</div>
56+
<div className="mt-1 font-display text-3xl font-bold text-orange-600">{totalIssues}</div>
57+
</Card>
58+
<Card className="p-5 text-center">
59+
<div className="text-sm text-muted-foreground">Vážne</div>
60+
<div className="mt-1 font-display text-3xl font-bold text-red-600">{criticals}</div>
61+
</Card>
62+
</div>
63+
64+
<div className="space-y-3">
65+
{issues.map((issue) => (
66+
<Card key={issue.id} className={`p-5 transition-shadow hover:shadow-sm ${issue.status === 'fail' ? 'border-l-4 border-l-red-500' : issue.status === 'warning' ? 'border-l-4 border-l-yellow-500' : 'border-l-4 border-l-green-500'}`}>
67+
<div className="flex items-start gap-4">
68+
<div className={`flex h-10 w-10 shrink-0 items-center justify-center rounded-xl ${issue.status === 'pass' ? 'bg-green-100 text-green-600' : issue.status === 'fail' ? 'bg-red-100 text-red-600' : 'bg-yellow-100 text-yellow-600'}`}>
69+
{issue.status === 'pass' ? <Check className="h-5 w-5" /> : issue.status === 'fail' ? <X className="h-5 w-5" /> : <AlertTriangle className="h-5 w-5" />}
70+
</div>
71+
<div className="min-w-0 flex-1">
72+
<div className="flex flex-wrap items-center gap-2">
73+
<span className="font-medium">{issue.description}</span>
74+
<Badge variant="outline">{issue.category}</Badge>
75+
<Badge variant="secondary">WCAG {issue.wcag}</Badge>
76+
</div>
77+
{issue.fix && (
78+
<p className="mt-1 text-sm text-muted-foreground">
79+
<span className="font-medium text-foreground">Oprava:</span> {issue.fix}
80+
</p>
81+
)}
82+
</div>
83+
<Badge className={issue.status === 'pass' ? 'bg-green-100 text-green-700' : issue.status === 'fail' ? 'bg-red-100 text-red-700' : 'bg-yellow-100 text-yellow-700'}>
84+
{issue.status === 'pass' ? 'OK' : issue.status === 'fail' ? 'Chyba' : 'Varovanie'}
85+
</Badge>
86+
</div>
87+
</Card>
88+
))}
89+
</div>
90+
91+
<Card className="mt-8 p-6">
92+
<h3 className="mb-2 font-display text-lg font-semibold">Zhrnutie auditu</h3>
93+
<p className="text-sm text-muted-foreground">
94+
Stránka dosahuje <strong>{Math.round((passes / issues.length) * 100)}%</strong> z WCAG 2.1 AA kritérií.
95+
Zostáva <strong>{criticals} vážnych</strong> nálezov na opravu.
96+
Hlavné oblasti na zlepšenie: kontrast farieb, ovládanie klávesnicou, a screen reader podpora.
97+
</p>
98+
</Card>
99+
</div>
100+
</div>
101+
)
102+
}
Lines changed: 147 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,147 @@
1+
import { useState } from 'react'
2+
import { ArrowLeft, BarChart3, Globe, Mail, MessageCircle } from 'lucide-react'
3+
import { Button } from '@/components/ui/button'
4+
import { Card } from '@/components/ui/card'
5+
import { Badge } from '@/components/ui/badge'
6+
7+
interface Channel {
8+
name: string
9+
icon: typeof Globe
10+
conversions: number
11+
revenue: number
12+
firstTouch: number
13+
lastTouch: number
14+
linear: number
15+
trend: 'up' | 'down' | 'flat'
16+
}
17+
18+
const channels: Channel[] = [
19+
{ name: 'Organické vyhľadávanie', icon: Globe, conversions: 342, revenue: 89_200, firstTouch: 28, lastTouch: 35, linear: 31, trend: 'up' },
20+
{ name: 'Platená reklama', icon: BarChart3, conversions: 218, revenue: 64_500, firstTouch: 22, lastTouch: 28, linear: 25, trend: 'flat' },
21+
{ name: 'Email marketing', icon: Mail, conversions: 156, revenue: 41_000, firstTouch: 18, lastTouch: 15, linear: 17, trend: 'up' },
22+
{ name: 'Sociálne siete', icon: MessageCircle, conversions: 98, revenue: 28_400, firstTouch: 32, lastTouch: 22, linear: 27, trend: 'down' },
23+
]
24+
25+
const totalRevenue = channels.reduce((s, c) => s + c.revenue, 0)
26+
const totalConversions = channels.reduce((s, c) => s + c.conversions, 0)
27+
28+
export function AttributionReport({ onBack }: { onBack: () => void }) {
29+
const [model, setModel] = useState<'firstTouch' | 'lastTouch' | 'linear'>('linear')
30+
31+
return (
32+
<div data-component="src/components/AttributionReport.tsx" className="min-h-screen bg-muted/30">
33+
<div className="mx-auto max-w-6xl px-4 py-8 sm:px-6">
34+
<div className="mb-8 flex items-center justify-between">
35+
<div>
36+
<h1 className="font-display text-3xl font-bold tracking-tight">Atribučný report</h1>
37+
<p className="mt-1 text-sm text-muted-foreground">Ktoré kanály prinášajú najviac hodnoty</p>
38+
</div>
39+
<Button variant="outline" size="sm" onClick={onBack}>
40+
<ArrowLeft className="mr-2 h-4 w-4" /> Späť
41+
</Button>
42+
</div>
43+
44+
<div className="mb-8 grid gap-4 sm:grid-cols-3">
45+
<Card className="p-5">
46+
<div className="text-sm text-muted-foreground">Celkové konverzie</div>
47+
<div className="mt-1 font-display text-2xl font-bold">{totalConversions}</div>
48+
</Card>
49+
<Card className="p-5">
50+
<div className="text-sm text-muted-foreground">Celkový revenue</div>
51+
<div className="mt-1 font-display text-2xl font-bold">{totalRevenue.toLocaleString('sk-SK')} EUR</div>
52+
</Card>
53+
<Card className="p-5">
54+
<div className="text-sm text-muted-foreground">Priemerná hodnota konverzie</div>
55+
<div className="mt-1 font-display text-2xl font-bold">
56+
{Math.round(totalRevenue / totalConversions).toLocaleString('sk-SK')} EUR
57+
</div>
58+
</Card>
59+
</div>
60+
61+
<div className="mb-6 flex gap-2">
62+
<Button variant={model === 'firstTouch' ? 'default' : 'outline'} size="sm" onClick={() => setModel('firstTouch')}>
63+
First Touch
64+
</Button>
65+
<Button variant={model === 'lastTouch' ? 'default' : 'outline'} size="sm" onClick={() => setModel('lastTouch')}>
66+
Last Touch
67+
</Button>
68+
<Button variant={model === 'linear' ? 'default' : 'outline'} size="sm" onClick={() => setModel('linear')}>
69+
Lineárny
70+
</Button>
71+
</div>
72+
73+
<div className="mb-8 grid gap-4 sm:grid-cols-2 lg:grid-cols-4">
74+
{channels.map((c) => {
75+
const attributedRevenue = (totalRevenue * c[model]) / 100
76+
const Icon = c.icon
77+
return (
78+
<Card key={c.name} className="p-5">
79+
<div className="mb-3 flex items-center gap-3">
80+
<div className="flex h-10 w-10 items-center justify-center rounded-xl bg-primary/10 text-primary">
81+
<Icon className="h-5 w-5" />
82+
</div>
83+
<Badge variant={c.trend === 'up' ? 'default' : c.trend === 'down' ? 'outline' : 'secondary'}>
84+
{c.trend === 'up' ? 'Rastie' : c.trend === 'down' ? 'Klesá' : 'Stabilné'}
85+
</Badge>
86+
</div>
87+
<div className="font-medium">{c.name}</div>
88+
<div className="mt-1 text-2xl font-bold">{c[model]}%</div>
89+
<div className="mt-1 text-sm text-muted-foreground">
90+
{Math.round(attributedRevenue).toLocaleString('sk-SK')} EUR
91+
</div>
92+
<div className="mt-2">
93+
<div className="h-2 w-full overflow-hidden rounded-full bg-muted">
94+
<div className="h-full rounded-full bg-primary transition-all" style={{ width: `${c[model]}%` }} />
95+
</div>
96+
</div>
97+
<div className="mt-2 text-xs text-muted-foreground">{c.conversions} konverzií</div>
98+
</Card>
99+
)
100+
})}
101+
</div>
102+
103+
<Card className="overflow-hidden">
104+
<div className="overflow-x-auto">
105+
<table className="w-full text-sm">
106+
<thead>
107+
<tr className="border-b border-border bg-muted/50 text-left">
108+
<th className="px-4 py-3 font-medium">Kanál</th>
109+
<th className="px-4 py-3 font-medium">Konverzie</th>
110+
<th className="px-4 py-3 font-medium">Revenue</th>
111+
<th className="px-4 py-3 font-medium">First Touch</th>
112+
<th className="px-4 py-3 font-medium">Last Touch</th>
113+
<th className="px-4 py-3 font-medium">Lineárny</th>
114+
<th className="px-4 py-3 font-medium">Trend</th>
115+
</tr>
116+
</thead>
117+
<tbody>
118+
{channels.map((c) => {
119+
const Icon = c.icon
120+
return (
121+
<tr key={c.name} className="border-b border-border transition-colors hover:bg-muted/30">
122+
<td className="px-4 py-3">
123+
<div className="flex items-center gap-2 font-medium">
124+
<Icon className="h-4 w-4 text-muted-foreground" /> {c.name}
125+
</div>
126+
</td>
127+
<td className="px-4 py-3">{c.conversions}</td>
128+
<td className="px-4 py-3 font-medium">{c.revenue.toLocaleString('sk-SK')} EUR</td>
129+
<td className="px-4 py-3">{c.firstTouch}%</td>
130+
<td className="px-4 py-3">{c.lastTouch}%</td>
131+
<td className="px-4 py-3 font-semibold">{c.linear}%</td>
132+
<td className="px-4 py-3">
133+
<Badge variant={c.trend === 'up' ? 'default' : c.trend === 'down' ? 'outline' : 'secondary'}>
134+
{c.trend === 'up' ? '↑' : c.trend === 'down' ? '↓' : '→'}
135+
</Badge>
136+
</td>
137+
</tr>
138+
)
139+
})}
140+
</tbody>
141+
</table>
142+
</div>
143+
</Card>
144+
</div>
145+
</div>
146+
)
147+
}

0 commit comments

Comments
 (0)