Initial commit

This commit is contained in:
2026-08-07 23:53:15 +05:30
commit 90f14a73b2
222 changed files with 35000 additions and 0 deletions

View File

@@ -0,0 +1,136 @@
import React from 'react';
import { useWorkerProfile } from '@/lib/krowHooks';
import { getTalentScore, getScoreBand, getGrowth } from '@/lib/talentHome';
import { ShieldCheck, Smartphone, Image as ImageIcon, QrCode, Sparkles } from 'lucide-react';
import { toast } from 'react-hot-toast';
import { Image } from '@/components/ui/image';
const clamp = (n, min = 0, max = 100) => Math.max(min, Math.min(max, Math.round(Number(n) || 0)));
const toFICO = (s) => 300 + Math.round((clamp(s) / 100) * 550);
function buildVCard(profile) {
const name = profile.full_name || 'KROW Talent';
const [first, ...rest] = name.split(' ');
const last = rest.join(' ');
const lines = [
'BEGIN:VCARD', 'VERSION:3.0',
`N:${last};${first};;;`, `FN:${name}`,
profile.current_position ? `TITLE:${profile.current_position}` : '',
profile.phone ? `TEL;TYPE=CELL:${profile.phone}` : '',
profile.email ? `EMAIL:${profile.email}` : '',
`URL:https://krow.app/me`,
`NOTE:KROW ID ${(profile.id || '').slice(-6).toUpperCase()}`,
'END:VCARD',
].filter(Boolean);
return lines.join('\n');
}
function download(filename, content, type) {
const blob = new Blob([content], { type });
const url = URL.createObjectURL(blob);
const a = document.createElement('a');
a.href = url; a.download = filename; a.click();
URL.revokeObjectURL(url);
}
export default function KrowIdPreview() {
const { data: profile, isLoading } = useWorkerProfile();
if (isLoading || !profile) {
return <div className="flex justify-center py-10 text-xs text-[#6B7280]">Loading KROW ID…</div>;
}
const score = getTalentScore(profile);
const band = getScoreBand(score);
const growth = getGrowth(profile);
const fico = toFICO(score);
const shortId = (profile.id || '').slice(-6).toUpperCase();
const name = profile.full_name || 'KROW Talent';
const title = profile.current_position || profile.desired_position || 'Open to work';
const initials = name.split(' ').map(p => p[0]).slice(0, 2).join('').toUpperCase();
const vcard = buildVCard(profile);
const qrSrc = `https://api.qrserver.com/v1/create-qr-code/?margin=0&size=120x120&data=${encodeURIComponent(vcard)}`;
const addToContacts = () => {
download(`${name.replace(/\s+/g, '_')}.vcf`, vcard, 'text/vcard');
toast.success('Contact file downloaded.');
};
return (
<div className="flex justify-center">
<div className="relative w-full max-w-[360px]" style={{ perspective: '1200px' }}>
{/* depth layers */}
<div aria-hidden className="absolute inset-0 rounded-3xl bg-white/50 border border-white/60 backdrop-blur-xl shadow-[0_18px_50px_-20px_rgba(10,57,223,0.3)]"
style={{ transform: 'translateY(12px) translateX(10px) rotateY(-8deg) rotateX(5deg)' }} />
<div aria-hidden className="absolute inset-0 rounded-3xl bg-white/40 border border-white/70 backdrop-blur-xl shadow-[0_14px_40px_-16px_rgba(10,57,223,0.25)]"
style={{ transform: 'translateY(6px) translateX(5px) rotateY(-4deg) rotateX(2deg)' }} />
{/* front pass */}
<div className="relative rounded-3xl border border-white/80 backdrop-blur-2xl shadow-[0_20px_60px_-22px_rgba(10,57,223,0.4)] overflow-hidden"
style={{ background: 'linear-gradient(160deg, rgba(255,255,255,0.94) 0%, rgba(244,247,255,0.9) 100%)' }}>
<div aria-hidden className="pointer-events-none absolute inset-0" style={{ background: 'linear-gradient(115deg, transparent 30%, rgba(10,57,223,0.05) 45%, rgba(249,229,71,0.06) 55%, transparent 70%)' }} />
<div className="relative p-5">
{/* header row */}
<div className="flex items-center gap-3">
<div className="w-14 h-14 rounded-full overflow-hidden border-2 border-white shadow bg-[#EFF6FF] flex items-center justify-center shrink-0">
{profile.selfie_url ? (
<Image src={profile.selfie_url} alt={name} className="w-full h-full object-cover" fittingType="fill" />
) : (
<span className="text-[18px] font-bold text-[#0A39DF]">{initials}</span>
)}
</div>
<div className="min-w-0 text-left">
<p className="text-[9px] font-semibold uppercase tracking-[0.12em] text-[#0A39DF]">KROW ID · {shortId}</p>
<h2 className="text-[16px] font-bold text-[#0F172A] tracking-tight leading-tight truncate">{name}</h2>
<p className="text-[11px] text-[#6B7280] truncate">{title}</p>
</div>
<ShieldCheck className="w-4 h-4 text-[#10B981] shrink-0" />
</div>
{/* score + factors row */}
<div className="mt-4 flex items-center gap-4">
<div className="text-center shrink-0">
<p className="text-[30px] font-bold text-[#0F172A] leading-none tracking-tight">{fico}</p>
<span className="inline-flex items-center gap-1 rounded-full px-2 py-0.5 text-[10px] font-bold mt-1"
style={{ color: band.color, background: `${band.color}14` }}>
<Sparkles className="w-2.5 h-2.5" /> {band.label}
</span>
<p className="text-[10px] font-semibold text-[#16A34A] mt-1">↑ +{growth} mo</p>
</div>
<div className="flex-1 grid grid-cols-2 gap-1.5">
{[
['Reliability', clamp(profile.reliability_score)],
['Attendance', clamp(profile.attendance_score ?? 100)],
['Performance', clamp(profile.performance_score)],
['XP', Number(profile.xp) || 0],
].map(([label, value]) => (
<span key={label} className="inline-flex items-center justify-between gap-1 rounded-lg bg-white/70 border border-[#E6ECF5] px-2 py-1 text-[10px]">
<span className="text-[#9CA3AF]">{label}</span>
<span className="font-semibold text-[#0A39DF]">{value}</span>
</span>
))}
</div>
</div>
{/* footer: QR + actions */}
<div className="mt-4 flex items-center gap-3">
<div className="rounded-lg bg-white p-1 border border-[#E6ECF5] shadow-sm shrink-0">
<img src={qrSrc} alt="KROW Identity QR" width={48} height={48} className="block" />
</div>
<div className="flex-1 grid grid-cols-2 gap-1.5">
<button onClick={addToContacts} className="flex items-center justify-center gap-1 h-8 rounded-lg bg-[#0A39DF] text-white text-[10px] font-semibold hover:bg-[#082EB4] transition-colors">
<Smartphone className="w-3 h-3" /> Contacts
</button>
<button onClick={() => toast('Open My KROW ID page to save the full pass.', { icon: 'ℹ️' })} className="flex items-center justify-center gap-1 h-8 rounded-lg bg-white border border-[#E6ECF5] text-[#0F172A] text-[10px] font-semibold hover:border-[#0A39DF] transition-colors">
<ImageIcon className="w-3 h-3 text-[#0A39DF]" /> Save
</button>
</div>
</div>
<p className="text-[9px] text-[#9CA3AF] mt-2 flex items-center gap-1 justify-center"><QrCode className="w-2.5 h-2.5" /> Scan to share your verified KROW Identity</p>
</div>
</div>
</div>
</div>
);
}