Initial commit
This commit is contained in:
114
src/components/krow/TalentLoop.jsx
Normal file
114
src/components/krow/TalentLoop.jsx
Normal file
@@ -0,0 +1,114 @@
|
||||
import React from 'react';
|
||||
import { Link } from 'react-router-dom';
|
||||
import { motion } from 'framer-motion';
|
||||
import {
|
||||
Fingerprint, GraduationCap, Mic, BadgeCheck, Handshake, Briefcase,
|
||||
LineChart, Star, TrendingUp, Crown, Presentation, Repeat, ArrowRight,
|
||||
} from 'lucide-react';
|
||||
|
||||
// The Credit Bureau for Human Talent — the career flywheel.
|
||||
const STEPS = [
|
||||
{ key: 'identity', label: 'Create Identity', to: '/identity', icon: Fingerprint },
|
||||
{ key: 'learn', label: 'Learn', to: '/university', icon: GraduationCap },
|
||||
{ key: 'interview', label: 'Interview', to: '/owliver', icon: Mic },
|
||||
{ key: 'verify', label: 'Verify Skills', to: '/identity', icon: BadgeCheck },
|
||||
{ key: 'hired', label: 'Get Hired', to: '/apply', icon: Handshake },
|
||||
{ key: 'work', label: 'Work', to: '/me', icon: Briefcase },
|
||||
{ key: 'track', label: 'Track Performance', to: '/me', icon: LineChart },
|
||||
{ key: 'reputation', label: 'Earn Reputation', to: '/me', icon: Star },
|
||||
{ key: 'promoted', label: 'Get Promoted', to: '/university', icon: TrendingUp },
|
||||
{ key: 'leader', label: 'Become Leader', to: '/university', icon: Crown },
|
||||
{ key: 'train', label: 'Train Others', to: '/university', icon: Presentation },
|
||||
{ key: 'repeat', label: 'Repeat', to: '/university', icon: Repeat, loop: true },
|
||||
];
|
||||
|
||||
export function stepStatus(profile, apps = []) {
|
||||
const hired = apps.some(a => a.status === 'hired');
|
||||
const worked = (profile.experience || []).length > 0 || hired;
|
||||
const isDone = {
|
||||
identity: !!profile.selfie_url && (profile.skills || []).length > 0,
|
||||
learn: (profile.completed_courses || []).length > 0,
|
||||
interview: (profile.ai_interview_score || 0) > 0 || !!(profile.personality || profile.strengths?.length),
|
||||
verify: (profile.certifications || []).length > 0 || (profile.earned_badges || []).length > 0,
|
||||
hired,
|
||||
work: worked,
|
||||
track: (profile.performance_score || 0) > 0 || (profile.attendance_score || 0) > 0 && worked,
|
||||
reputation: (profile.client_rating || 0) > 0 || (profile.supervisor_rating || 0) > 0 || (profile.krow_score || 0) >= 50,
|
||||
promoted: (profile.leadership_potential || 0) >= 40,
|
||||
leader: (profile.leadership_potential || 0) >= 70,
|
||||
train: (profile.leadership_potential || 0) >= 85 && (profile.completed_courses || []).length >= 3,
|
||||
repeat: false,
|
||||
};
|
||||
return isDone;
|
||||
}
|
||||
|
||||
export default function TalentLoop({ profile, applications = [] }) {
|
||||
const done = stepStatus(profile, applications);
|
||||
const reached = STEPS.filter(s => done[s.key]).length;
|
||||
const current = STEPS.find(s => !done[s.key]) || STEPS[STEPS.length - 1];
|
||||
const progress = Math.round((reached / (STEPS.length - 1)) * 100);
|
||||
|
||||
return (
|
||||
<section className="glass-card border border-white/60 rounded-2xl p-6 sm:p-8 shadow-sm">
|
||||
<div className="flex items-end justify-between flex-wrap gap-3 mb-1">
|
||||
<div>
|
||||
<span className="text-[11px] font-medium uppercase tracking-[0.18em] text-[#0A39DF]">The Talent Loop</span>
|
||||
<h2 className="text-[22px] font-heading font-bold text-[#0F172A] mt-1 tracking-tight">Build your reputation. Not just your resume.</h2>
|
||||
</div>
|
||||
<span className="text-[12px] text-[#64748B]">{reached}/{STEPS.length - 1} stages</span>
|
||||
</div>
|
||||
|
||||
<div className="h-1.5 bg-[#EEF2F7] rounded-full overflow-hidden mt-4 mb-7">
|
||||
<motion.div
|
||||
initial={{ width: 0 }}
|
||||
animate={{ width: `${progress}%` }}
|
||||
transition={{ duration: 0.8, ease: 'easeOut' }}
|
||||
className="h-full bg-[#0A39DF] rounded-full"
|
||||
/>
|
||||
</div>
|
||||
|
||||
<div className="grid grid-cols-2 sm:grid-cols-3 lg:grid-cols-6 gap-3">
|
||||
{STEPS.map((s, i) => {
|
||||
const isDone = done[s.key];
|
||||
const isCurrent = s.key === current.key && !s.loop;
|
||||
const Icon = s.icon;
|
||||
return (
|
||||
<Link
|
||||
key={s.key}
|
||||
to={s.to}
|
||||
className={`group relative flex flex-col items-start gap-2 rounded-xl border p-3 transition-all ${
|
||||
isDone
|
||||
? 'bg-[#0A39DF] border-[#0A39DF] text-white'
|
||||
: isCurrent
|
||||
? 'bg-white border-[#0A39DF] ring-2 ring-[#0A39DF]/20'
|
||||
: 'bg-white border-[#E5E7EB] hover:border-[#0A39DF]/50'
|
||||
}`}
|
||||
>
|
||||
<div className="flex items-center justify-between w-full">
|
||||
<span className={`text-[10px] font-semibold ${isDone ? 'text-white/70' : 'text-[#94A3B8]'}`}>{String(i + 1).padStart(2, '0')}</span>
|
||||
{s.loop && <Repeat className={`w-3.5 h-3.5 ${isDone ? 'text-white/70' : 'text-[#94A3B8]'}`} />}
|
||||
</div>
|
||||
<Icon className={`w-5 h-5 ${isDone ? 'text-white' : isCurrent ? 'text-[#0A39DF]' : 'text-[#64748B]'}`} />
|
||||
<span className={`text-[12px] font-medium leading-tight ${isDone ? 'text-white' : isCurrent ? 'text-[#0A39DF]' : 'text-[#334155]'}`}>
|
||||
{s.label}
|
||||
</span>
|
||||
</Link>
|
||||
);
|
||||
})}
|
||||
</div>
|
||||
|
||||
<div className="flex items-center justify-between gap-3 mt-6 pt-5 border-t border-[#EEF2F7] flex-wrap">
|
||||
<div>
|
||||
<p className="text-[11px] text-[#94A3B8] font-medium">UP NEXT</p>
|
||||
<p className="text-[15px] font-semibold text-[#0F172A]">{current.label}</p>
|
||||
</div>
|
||||
<Link
|
||||
to={current.to}
|
||||
className="inline-flex items-center gap-1.5 text-[13px] font-semibold text-[#0A39DF] hover:gap-2.5 transition-all"
|
||||
>
|
||||
Continue <ArrowRight className="w-4 h-4" />
|
||||
</Link>
|
||||
</div>
|
||||
</section>
|
||||
);
|
||||
}
|
||||
Reference in New Issue
Block a user