feat: desktop image thumbnails, gallery lightbox portal, client-side compression, clickable table rows, fix header gap
This commit is contained in:
@@ -0,0 +1,134 @@
|
||||
"use client";
|
||||
|
||||
import { useState, useEffect } from "react";
|
||||
import { CheckCircle, Circle } from '@/lib/icons';
|
||||
import { cn } from "@/lib/utils";
|
||||
import { ACADEMY_MODULES } from "@/lib/academy/tours";
|
||||
import { getCompletedModules, resetProgress } from "@/lib/academy/progress";
|
||||
import { AcademyTourButton } from "./academy-tour-button";
|
||||
import { Button } from "@/components/ui/button";
|
||||
|
||||
export function AcademyClient() {
|
||||
const [completed, setCompleted] = useState<string[]>([]);
|
||||
|
||||
useEffect(() => {
|
||||
setCompleted(getCompletedModules());
|
||||
}, []);
|
||||
|
||||
function handleComplete() {
|
||||
setCompleted(getCompletedModules());
|
||||
}
|
||||
|
||||
function handleReset() {
|
||||
resetProgress();
|
||||
setCompleted([]);
|
||||
}
|
||||
|
||||
const percent = Math.round((completed.length / ACADEMY_MODULES.length) * 100);
|
||||
const allDone = completed.length === ACADEMY_MODULES.length;
|
||||
|
||||
return (
|
||||
<div className="space-y-6">
|
||||
{/* Progress header */}
|
||||
<div className="bg-card border rounded-xl p-5 space-y-3">
|
||||
<div className="flex items-center justify-between">
|
||||
<div>
|
||||
<p className="font-semibold text-sm">Genel İlerleme</p>
|
||||
<p className="text-muted-foreground text-xs mt-0.5">
|
||||
{completed.length} / {ACADEMY_MODULES.length} modül tamamlandı
|
||||
</p>
|
||||
</div>
|
||||
<span className={cn(
|
||||
"text-2xl font-bold",
|
||||
allDone ? "text-green-600" : "text-primary"
|
||||
)}>
|
||||
%{percent}
|
||||
</span>
|
||||
</div>
|
||||
{/* Progress bar */}
|
||||
<div className="h-2 bg-muted rounded-full overflow-hidden">
|
||||
<div
|
||||
className={cn(
|
||||
"h-full rounded-full transition-all duration-500",
|
||||
allDone ? "bg-green-500" : "bg-primary"
|
||||
)}
|
||||
style={{ width: `${percent}%` }}
|
||||
/>
|
||||
</div>
|
||||
{allDone && (
|
||||
<p className="text-green-600 text-sm font-medium">
|
||||
🎉 Tüm modülleri tamamladınız! Artık KovakEmlak CRM'i tam verimle kullanabilirsiniz.
|
||||
</p>
|
||||
)}
|
||||
</div>
|
||||
|
||||
{/* Module grid */}
|
||||
<div className="grid grid-cols-1 sm:grid-cols-2 lg:grid-cols-3 gap-4">
|
||||
{ACADEMY_MODULES.map((mod) => {
|
||||
const isDone = completed.includes(mod.id);
|
||||
return (
|
||||
<div
|
||||
key={mod.id}
|
||||
className={cn(
|
||||
"bg-card border rounded-xl p-5 flex flex-col gap-3 transition-colors",
|
||||
isDone && "border-green-200 bg-green-50/40 dark:bg-green-950/20 dark:border-green-900"
|
||||
)}
|
||||
>
|
||||
<div className="flex items-start justify-between gap-2">
|
||||
<div className="flex items-center gap-2.5">
|
||||
<span className="text-2xl">{mod.icon}</span>
|
||||
<div>
|
||||
<p className="font-semibold text-sm leading-tight">{mod.title}</p>
|
||||
<p className="text-muted-foreground text-xs mt-0.5">
|
||||
{mod.steps.length} adım
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
{isDone ? (
|
||||
<CheckCircle className="size-5 text-green-500 shrink-0 mt-0.5" />
|
||||
) : (
|
||||
<Circle className="size-5 text-muted-foreground/40 shrink-0 mt-0.5" />
|
||||
)}
|
||||
</div>
|
||||
|
||||
<p className="text-muted-foreground text-xs leading-relaxed flex-1">
|
||||
{mod.description}
|
||||
</p>
|
||||
|
||||
{/* Step previews */}
|
||||
<div className="space-y-1">
|
||||
{mod.steps.slice(0, 3).map((step, i) => (
|
||||
<div key={i} className="flex items-center gap-2 text-xs text-muted-foreground">
|
||||
<span className={cn(
|
||||
"size-4 rounded-full flex items-center justify-center text-[10px] font-medium shrink-0",
|
||||
isDone
|
||||
? "bg-green-100 text-green-700 dark:bg-green-900 dark:text-green-300"
|
||||
: "bg-muted text-muted-foreground"
|
||||
)}>
|
||||
{i + 1}
|
||||
</span>
|
||||
<span className="truncate">{step.title}</span>
|
||||
</div>
|
||||
))}
|
||||
</div>
|
||||
|
||||
<AcademyTourButton
|
||||
module={mod}
|
||||
onComplete={handleComplete}
|
||||
variant={isDone ? "ghost" : "outline"}
|
||||
/>
|
||||
</div>
|
||||
);
|
||||
})}
|
||||
</div>
|
||||
|
||||
{completed.length > 0 && (
|
||||
<div className="flex justify-end">
|
||||
<Button variant="ghost" size="sm" onClick={handleReset} className="text-muted-foreground text-xs">
|
||||
İlerlemeyi sıfırla
|
||||
</Button>
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
);
|
||||
}
|
||||
Reference in New Issue
Block a user