nxtgauge-frontend-solid/src/components/admin/DashboardDesignPreview.tsx
Ashwin Kumar 3703d66df5 feat: auth provider, route guards, forgot password, API client
- Add AuthProvider context, RequireAuth guard
- Create API client with endpoint helpers
- Add forgot-password route wired to backend
- Remove dummy login button, add forgot-password link
- Dashboard: RequireAuth, auth context integration
- DashboardDesignPreview: replace Beeceptor with internal payments API
- DashboardDesignPreview: add coupon state, validation, apply
- DashboardDesignPreview: credit wallet on verify, ledger entry
- DashboardDesignPreview: adjust total with discount
- misc: getToken import, API constant
- fix: token storage (use getToken)
2026-04-06 08:24:22 +02:00

6776 lines
458 KiB
TypeScript
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

import { For, Show, createEffect, createMemo, createResource, createSignal, onMount } from 'solid-js';
import {
Plus, Search, ArrowRight, Clock, User, BookOpen, AlertCircle, Save, X,
LayoutGrid, List, CheckCircle, Edit2, Trash2, Globe, Lock,
Rocket, TrendingUp, ShieldCheck, Mail, Phone, MapPin,
ChevronRight, ChevronLeft, ChevronDown, ChevronUp,
Download, Filter, MoreVertical, Star, HelpCircle,
Award, Bell, BadgeCheck, Bookmark, BriefcaseBusiness, Calendar,
Camera, CheckCircle2, Clapperboard, Compass, Coins, Code2,
Dumbbell, Eye, FileText, GraduationCap, HandHelping,
HeadphonesIcon, Image, LogOut, Megaphone, PenTool,
RefreshCw, Scissors, Settings, Settings2, UtensilsCrossed,
UserCircle2, Users,
} from 'lucide-solid';
import { RuntimeKBConfig, RuntimeKBArticle } from '../../lib/runtime/types';
import { getToken } from '~/lib/auth';
function titleCase(value: string) {
return String(value || '')
.replace(/_/g, ' ')
.replace(/\b\w/g, (c) => c.toUpperCase());
}
const ORANGE_ICON_FILTER = 'invert(51%) sepia(86%) saturate(2445%) hue-rotate(353deg) brightness(101%) contrast(103%)';
const BLUE_ICON_FILTER = 'invert(11%) sepia(85%) saturate(2462%) hue-rotate(233deg) brightness(91%) contrast(101%)';
const API = '/api/gateway';
function StatusBadge(props: { status: 'ACTIVE' | 'INACTIVE' }) {
const active = () => props.status === 'ACTIVE';
return (
<span style={`display:inline-flex;align-items:center;border-radius:9999px;border:1px solid ${active() ? '#FFD8C2' : '#D1D5DB'};background:${active() ? '#FFF1EB' : '#F3F4F6'};color:${active() ? '#FF5E13' : '#4B5563'};padding:2px 10px;font-size:12px;font-weight:500`}>
{active() ? 'Active' : 'Inactive'}
</span>
);
}
function sidebarIcon(label: string) {
const key = String(label || '').toLowerCase().trim();
if (key.includes('dashboard')) return LayoutGrid;
if (key.includes('portfolio')) return Image;
if (key.includes('profile')) return UserCircle2;
if (key.includes('lead')) return HandHelping;
if (key.includes('response')) return FileText;
if (key.includes('credit')) return Coins;
if (key.includes('explore')) return Compass;
if (key.includes('verification')) return BadgeCheck;
if (key.includes('support') || key.includes('help center')) return HeadphonesIcon;
if (key === 'settings') return Settings2;
if (key.includes('switch role') || key.includes('switch services') || key.includes('switch service')) return RefreshCw;
if (key.includes('logout')) return LogOut;
if (key.includes('job')) return BriefcaseBusiness;
if (key.includes('application')) return FileText;
if (key.includes('shortlisted')) return Users;
if (key.includes('saved')) return Bookmark;
if (key.includes('requirement')) return FileText;
return LayoutGrid;
}
type CustomerView = {
title: string;
subtitle: string;
tabs: string[];
cta?: string;
};
type ProfileSpec = {
title: string;
subtitle: string;
tabs: string[];
tabFields: Record<string, string[]>;
};
function normalizeRoleKey(roleKey: string): string {
const key = String(roleKey || '').toUpperCase();
if (key.includes('COMPANY')) return 'COMPANY';
if (key.includes('CUSTOMER')) return 'CUSTOMER';
if (key.includes('SERVICE_SEEKER')) return 'CUSTOMER';
if (key.includes('JOB_SEEKER') || key.includes('JOBSEEKER')) return 'JOB_SEEKER';
if (key.includes('PHOTOGRAPHER')) return 'PHOTOGRAPHER';
if (key.includes('MAKEUP')) return 'MAKEUP_ARTIST';
if (key.includes('DEVELOPER')) return 'DEVELOPER';
if (key.includes('VIDEO')) return 'VIDEO_EDITOR';
if (key.includes('UGC') || (key.includes('CONTENT') && key.includes('CREATOR'))) return 'UGC_CONTENT_CREATOR';
if (key.includes('GRAPHIC')) return 'GRAPHIC_DESIGNER';
if (key.includes('SOCIAL')) return 'SOCIAL_MEDIA_MANAGER';
if (key.includes('FITNESS')) return 'FITNESS_TRAINER';
if (key.includes('TUTOR')) return 'TUTOR';
if (key.includes('CATER')) return 'CATERING_SERVICES';
return 'PROFESSIONAL';
}
const PROFILE_SPECS: Record<string, ProfileSpec> = {
CUSTOMER: {
title: 'Service Seeker Profile',
subtitle: 'Manage your personal details, service preferences, documents, and account settings.',
tabs: ['basic information', 'documents', 'settings'],
tabFields: {
'basic information': ['First Name', 'Last Name', 'Email Address', 'Mobile Number', 'Area', 'Place', 'PIN Code', 'Service Category'],
documents: ['Identity Proof', 'Address Proof'],
settings: ['Password & Login', 'Notification Preferences', 'Privacy Controls'],
},
},
COMPANY: {
title: 'Company Profile',
subtitle: 'Configure organization details, hiring preferences, compliance documents, and settings.',
tabs: ['basic information', 'documents', 'settings'],
tabFields: {
'basic information': ['Company Name', 'Company Email', 'Company Phone', 'City', 'Area', 'Place', 'PIN Code', 'Website URL'],
documents: ['GST Certificate', 'PAN Card', 'Incorporation Certificate'],
settings: ['Account Security', 'Team Access', 'Notification Preferences'],
},
},
JOB_SEEKER: {
title: 'Job Seeker Profile',
subtitle: 'Maintain your career profile, resume, preferences, and verification docs.',
tabs: ['basic information', 'documents', 'settings'],
tabFields: {
'basic information': ['First Name', 'Last Name', 'Email Address', 'Mobile Number', 'Current Role', 'Total Experience', 'City', 'Area', 'Place'],
documents: ['Identity Proof', 'Address Proof', 'Education Proof'],
settings: ['Password & Login', 'Notification Preferences', 'Privacy Controls'],
},
},
PHOTOGRAPHER: {
title: 'Photographer Profile',
subtitle: 'Manage your photography details, pricing, portfolio, and documents.',
tabs: ['basic information', 'documents', 'settings'],
tabFields: {
'basic information': ['First Name', 'Last Name', 'Email Address', 'Mobile Number', 'Gender', 'Address Line 1', 'Address Line 2 (Optional)', 'City', 'Area', 'State', 'PIN Code'],
documents: ['Identity Proof', 'Address Proof', 'Portfolio Ownership Proof'],
settings: ['Password & Login', 'Notification Preferences', 'Privacy Controls'],
},
},
MAKEUP_ARTIST: {
title: 'Makeup Artist Profile',
subtitle: 'Manage makeup specialization, services, portfolio, and compliance documents.',
tabs: ['basic information', 'documents', 'settings'],
tabFields: {
'basic information': ['First Name', 'Last Name', 'Email Address', 'Mobile Number', 'Gender', 'Address Line 1', 'Address Line 2 (Optional)', 'City', 'Area', 'State', 'PIN Code'],
documents: ['Identity Proof', 'Address Proof', 'Professional Certifications'],
settings: ['Password & Login', 'Notification Preferences', 'Privacy Controls'],
},
},
DEVELOPER: {
title: 'Developer Profile',
subtitle: 'Showcase technical profile, pricing models, portfolio projects, and documents.',
tabs: ['basic information', 'documents', 'settings'],
tabFields: {
'basic information': ['First Name', 'Last Name', 'Email Address', 'Mobile Number', 'Gender', 'Address Line 1', 'Address Line 2 (Optional)', 'City', 'Area', 'State', 'PIN Code'],
documents: ['Identity Proof', 'Address Proof', 'Tax Document'],
settings: ['Password & Login', 'Notification Preferences', 'Privacy Controls'],
},
},
VIDEO_EDITOR: {
title: 'Video Editor Profile',
subtitle: 'Manage editing profile, services, portfolio, and verification documents.',
tabs: ['basic information', 'documents', 'settings'],
tabFields: {
'basic information': ['First Name', 'Last Name', 'Email Address', 'Mobile Number', 'Gender', 'Address Line 1', 'Address Line 2 (Optional)', 'City', 'Area', 'State', 'PIN Code'],
documents: ['Identity Proof', 'Address Proof', 'Tax Document'],
settings: ['Password & Login', 'Notification Preferences', 'Privacy Controls'],
},
},
UGC_CONTENT_CREATOR: {
title: 'UGC Content Creator Profile',
subtitle: 'Manage your creator profile, content style, pricing, and portfolio deliverables.',
tabs: ['basic information', 'documents', 'settings'],
tabFields: {
'basic information': ['First Name', 'Last Name', 'Email Address', 'Mobile Number', 'Gender', 'Address Line 1', 'Address Line 2 (Optional)', 'City', 'Area', 'State', 'PIN Code'],
documents: ['Identity Proof', 'Address Proof', 'Tax Document'],
settings: ['Password & Login', 'Notification Preferences', 'Privacy Controls'],
},
},
GRAPHIC_DESIGNER: {
title: 'Graphic Designer Profile',
subtitle: 'Manage design profile, service pricing, portfolio assets, and documents.',
tabs: ['basic information', 'documents', 'settings'],
tabFields: {
'basic information': ['First Name', 'Last Name', 'Email Address', 'Mobile Number', 'Gender', 'Address Line 1', 'Address Line 2 (Optional)', 'City', 'Area', 'State', 'PIN Code'],
documents: ['Identity Proof', 'Address Proof', 'Tax Document'],
settings: ['Password & Login', 'Notification Preferences', 'Privacy Controls'],
},
},
SOCIAL_MEDIA_MANAGER: {
title: 'Social Media Manager Profile',
subtitle: 'Manage social profile details, service plans, case studies, and documents.',
tabs: ['basic information', 'documents', 'settings'],
tabFields: {
'basic information': ['First Name', 'Last Name', 'Email Address', 'Mobile Number', 'Gender', 'Address Line 1', 'Address Line 2 (Optional)', 'City', 'Area', 'State', 'PIN Code'],
documents: ['Identity Proof', 'Address Proof', 'Tax Document'],
settings: ['Password & Login', 'Notification Preferences', 'Privacy Controls'],
},
},
FITNESS_TRAINER: {
title: 'Fitness Trainer Profile',
subtitle: 'Manage training details, plans, certifications, and profile settings.',
tabs: ['basic information', 'documents', 'settings'],
tabFields: {
'basic information': ['First Name', 'Last Name', 'Email Address', 'Mobile Number', 'Gender', 'Address Line 1', 'Address Line 2 (Optional)', 'City', 'Area', 'State', 'PIN Code'],
documents: ['Identity Proof', 'Address Proof', 'Certification Proof'],
settings: ['Password & Login', 'Notification Preferences', 'Privacy Controls'],
},
},
TUTOR: {
title: 'Tutor Profile',
subtitle: 'Manage teaching details, subjects, pricing, documents, and settings.',
tabs: ['basic information', 'documents', 'settings'],
tabFields: {
'basic information': ['First Name', 'Last Name', 'Email Address', 'Mobile Number', 'Gender', 'Address Line 1', 'Address Line 2 (Optional)', 'City', 'Area', 'State', 'PIN Code'],
documents: ['Identity Proof', 'Address Proof', 'Educational Proof'],
settings: ['Password & Login', 'Notification Preferences', 'Privacy Controls'],
},
},
CATERING_SERVICES: {
title: 'Catering Services Profile',
subtitle: 'Manage business details, menu packages, gallery, and compliance docs.',
tabs: ['basic information', 'documents', 'settings'],
tabFields: {
'basic information': ['Business Name', 'Contact Person Name', 'Email Address', 'Mobile Number', 'Gender', 'Address Line 1', 'Address Line 2 (Optional)', 'City', 'Area', 'State', 'PIN Code'],
documents: ['Identity Proof', 'Address Proof', 'Food License'],
settings: ['Password & Login', 'Notification Preferences', 'Privacy Controls'],
},
},
PROFESSIONAL: {
title: 'Professional Profile',
subtitle: 'Manage professional details, pricing, portfolio, and account settings.',
tabs: ['basic information', 'documents', 'settings'],
tabFields: {
'basic information': ['Full Name', 'Email Address', 'Mobile Number', 'Gender', 'Address Line 1', 'Address Line 2 (Optional)', 'City', 'Area', 'State', 'PIN Code'],
documents: ['Identity Proof', 'Address Proof', 'Tax Document'],
settings: ['Password & Login', 'Notification Preferences', 'Privacy Controls'],
},
},
};
function profileSpecForRole(roleKey: string): ProfileSpec {
const normalized = normalizeRoleKey(roleKey);
const base = PROFILE_SPECS[normalized] || PROFILE_SPECS.PROFESSIONAL;
const existingDocuments = Array.isArray(base.tabFields?.documents) ? base.tabFields.documents : [];
const documents = Array.from(new Set(['Address', 'Address Proof', ...existingDocuments]));
return {
...base,
tabFields: {
...base.tabFields,
documents,
},
};
}
type PortfolioSpec = {
roleLabel: string;
tabs: string[];
specialties: string[];
statsLabels: string[];
equipmentLabel: string;
galleryTabLabel: string;
experienceTabLabel: string;
serviceTabLabel: string;
faqItems: Array<{ q: string; a: string }>;
packages: Array<{ name: string; price: string; items: string[] }>;
};
const PORTFOLIO_SPECS: Record<string, PortfolioSpec> = {
PHOTOGRAPHER: {
roleLabel: 'Photographer',
tabs: ['about', 'services & pricing', 'portfolio gallery', 'experience & equipment', 'testimonials', 'faqs'],
specialties: ['Wedding', 'Pre-Wedding', 'Candid', 'Event', 'Portrait', 'Lifestyle'],
statsLabels: ['Shoots Done', 'Years Exp', 'Verified Pro', 'Last Delivery', 'Will Travel'],
equipmentLabel: 'Camera & Equipment',
galleryTabLabel: 'portfolio gallery',
experienceTabLabel: 'experience & equipment',
serviceTabLabel: 'services & pricing',
faqItems: [
{ q: 'Do you travel for shoots?', a: 'Yes, I cover Pan-India and select international locations. Travel charges apply beyond 50km.' },
{ q: 'How long before I receive the final photos?', a: 'Edited photos are delivered within 1014 working days after the shoot.' },
{ q: 'What equipment do you use?', a: 'I shoot with Canon EOS R6 and Sony A7IV, with a full range of prime and zoom lenses.' },
{ q: 'Do you provide raw files?', a: 'Raw files are not included by default. They can be added as an optional upgrade.' },
],
packages: [
{ name: 'Essential', price: '₹15,000', items: ['4-hour shoot', '100 edited photos', 'Online gallery', '1 location'] },
{ name: 'Premium', price: '₹28,000', items: ['8-hour shoot', '250 edited photos', 'Online gallery', '2 locations', 'Drone shots'] },
{ name: 'Signature', price: '₹50,000', items: ['Full-day shoot', '500 edited photos', 'USB delivery', '3 locations', 'Drone + Reel'] },
],
},
MAKEUP_ARTIST: {
roleLabel: 'Makeup Artist',
tabs: ['about', 'services & pricing', 'gallery', 'experience & certifications', 'testimonials', 'faqs'],
specialties: ['Bridal', 'Party', 'Editorial', 'SFX', 'Airbrush', 'Natural'],
statsLabels: ['Sessions Done', 'Years Exp', 'Verified Pro', 'Last Booking', 'Will Travel'],
equipmentLabel: 'Kit & Products',
galleryTabLabel: 'gallery',
experienceTabLabel: 'experience & certifications',
serviceTabLabel: 'services & pricing',
faqItems: [
{ q: 'Do you do trials before the wedding?', a: 'Yes, a bridal trial is mandatory. It helps finalise the look and ensures comfort on the big day.' },
{ q: 'Which brands do you use?', a: 'I work with MAC, Huda Beauty, Fenty, and NARS for face. Products are selected based on skin type.' },
{ q: 'Do you travel for events?', a: 'Yes. Travel is available with prior notice. Charges apply for locations beyond 30km.' },
{ q: 'How long does bridal makeup take?', a: 'Typically 2.53 hours for full bridal. Party makeup takes around 4560 minutes.' },
],
packages: [
{ name: 'Party', price: '₹3,500', items: ['Full face makeup', 'Hair styling', '45-min session', 'Touch-up kit'] },
{ name: 'Bridal', price: '₹18,000', items: ['Trial session', 'Full bridal look', 'HD & airbrush', 'Touch-up on-site'] },
{ name: 'Bridal Party', price: '₹35,000', items: ['Bride + 4 family', 'Full day', 'Airbrush finish', 'On-site artist'] },
],
},
TUTOR: {
roleLabel: 'Tutor',
tabs: ['about', 'subjects & pricing', 'student work', 'qualifications', 'testimonials', 'faqs'],
specialties: ['Mathematics', 'Physics', 'Chemistry', 'Biology', 'English', 'Programming'],
statsLabels: ['Students Taught', 'Years Exp', 'Verified Pro', 'Next Slot', 'Online OK'],
equipmentLabel: 'Teaching Tools',
galleryTabLabel: 'student work',
experienceTabLabel: 'qualifications',
serviceTabLabel: 'subjects & pricing',
faqItems: [
{ q: 'Do you teach online or in-person?', a: 'Both modes available. Online via Zoom or Google Meet. In-person in Chennai and surrounding areas.' },
{ q: 'What boards do you cover?', a: 'CBSE, ICSE, State Board, IB, and A-Levels. I also prepare students for JEE and NEET.' },
{ q: 'How many students per batch?', a: 'Group batches have max 5 students. Individual sessions are 1-on-1 for focused learning.' },
{ q: 'Do you provide study material?', a: 'Yes. Custom notes, practice sheets, and mock tests are included in all plans.' },
],
packages: [
{ name: 'Crash Course', price: '₹5,000/mo', items: ['10 sessions/month', '1 subject', 'Practice tests', 'Online only'] },
{ name: 'Core Plan', price: '₹8,500/mo', items: ['16 sessions/month', '2 subjects', 'Study materials', 'Doubt clearing'] },
{ name: 'Intensive', price: '₹14,000/mo', items: ['24 sessions/month', '3 subjects', 'Mock exams', 'Progress reports'] },
],
},
DEVELOPER: {
roleLabel: 'Developer',
tabs: ['about', 'services & pricing', 'projects', 'tech stack & experience', 'testimonials', 'faqs'],
specialties: ['Web Development', 'Mobile Apps', 'API / Backend', 'UI/UX', 'DevOps', 'Consulting'],
statsLabels: ['Projects Done', 'Years Exp', 'Verified Pro', 'Last Delivery', 'Remote OK'],
equipmentLabel: 'Tech Stack',
galleryTabLabel: 'projects',
experienceTabLabel: 'tech stack & experience',
serviceTabLabel: 'services & pricing',
faqItems: [
{ q: 'Do you work on fixed price or hourly?', a: 'Both models available. Small projects are fixed-price; larger engagements use hourly or milestone billing.' },
{ q: 'Do you sign NDAs?', a: 'Yes. NDAs are standard for all client projects involving proprietary data or unreleased products.' },
{ q: 'Can you handle full-stack?', a: 'Yes. I cover React/SolidJS frontend, Rust/Node backend, PostgreSQL databases, and cloud deployment.' },
{ q: 'What is your typical delivery time?', a: 'Landing pages in 35 days. Full apps depend on scope. Detailed estimate provided after briefing.' },
],
packages: [
{ name: 'Starter', price: '₹20,000', items: ['Landing page / MVP', '5-day delivery', '2 revisions', 'Basic SEO'] },
{ name: 'Business', price: '₹65,000', items: ['Full web app', '3-week delivery', 'API integration', 'Admin panel', 'Deployment'] },
{ name: 'Enterprise', price: 'Custom', items: ['Complex systems', 'Dedicated sprint', 'Team collaboration', 'SLA included'] },
],
},
VIDEO_EDITOR: {
roleLabel: 'Video Editor',
tabs: ['about', 'services & pricing', 'showreel', 'experience & tools', 'testimonials', 'faqs'],
specialties: ['Wedding Films', 'Corporate', 'Music Videos', 'Reels', 'Documentary', 'Product'],
statsLabels: ['Videos Done', 'Years Exp', 'Verified Pro', 'Last Delivery', 'Remote OK'],
equipmentLabel: 'Editing Suite',
galleryTabLabel: 'showreel',
experienceTabLabel: 'experience & tools',
serviceTabLabel: 'services & pricing',
faqItems: [
{ q: 'Which editing software do you use?', a: 'Adobe Premiere Pro, DaVinci Resolve, and After Effects for motion graphics and colour grading.' },
{ q: 'What is your turnaround time?', a: 'Short reels in 23 days. Full event films take 1015 working days depending on footage length.' },
{ q: 'Do you accept raw footage from other cameras?', a: 'Yes. Any format is accepted — I handle transcoding as part of the workflow.' },
{ q: 'How many revisions are included?', a: '2 revisions are included in all packages. Additional revisions are charged at ₹500 per round.' },
],
packages: [
{ name: 'Reel Edit', price: '₹3,000', items: ['Up to 60s reel', 'Music sync', '2 revisions', '48hr delivery'] },
{ name: 'Event Film', price: '₹12,000', items: ['5-min highlight', 'Colour grade', 'Titles + music', '10-day delivery'] },
{ name: 'Feature Film', price: '₹28,000', items: ['Full-length film', 'Cinematic grade', 'Motion graphics', 'Multi-format export'] },
],
},
UGC_CONTENT_CREATOR: {
roleLabel: 'UGC Content Creator',
tabs: ['about', 'services & pricing', 'content portfolio', 'experience & tools', 'testimonials', 'faqs'],
specialties: ['UGC Ads', 'Product Demos', 'Lifestyle UGC', 'Voiceover', 'Scripted Reels', 'Performance Creatives'],
statsLabels: ['Campaigns Done', 'Years Exp', 'Verified Pro', 'Last Delivery', 'Remote OK'],
equipmentLabel: 'Creator Setup',
galleryTabLabel: 'content portfolio',
experienceTabLabel: 'experience & tools',
serviceTabLabel: 'services & pricing',
faqItems: [
{ q: 'Do you create scripts for UGC ads?', a: 'Yes. Script ideation and hooks are included in campaign packages based on your brand goals.' },
{ q: 'Can you deliver platform-specific formats?', a: 'Yes. I deliver optimized cuts for Instagram, YouTube Shorts, and TikTok formats.' },
{ q: 'Are raw files included?', a: 'Raw files are optional and available as an add-on depending on package scope.' },
{ q: 'What is your turnaround time?', a: 'Most UGC deliverables are completed in 35 business days after brief and product receipt.' },
],
packages: [
{ name: 'Starter UGC', price: '₹7,500', items: ['1 video creative', 'Script + hook', 'Vertical format', '2 revisions'] },
{ name: 'Growth UGC', price: '₹18,000', items: ['3 video creatives', 'Platform variations', 'Usage rights', '4 revisions'] },
{ name: 'Scale UGC', price: '₹35,000', items: ['6 video creatives', 'A/B hook variants', 'Ad-ready exports', 'Priority delivery'] },
],
},
GRAPHIC_DESIGNER: {
roleLabel: 'Graphic Designer',
tabs: ['about', 'services & pricing', 'portfolio', 'experience & tools', 'testimonials', 'faqs'],
specialties: ['Brand Identity', 'UI/UX Design', 'Print', 'Social Media', 'Motion', 'Packaging'],
statsLabels: ['Projects Done', 'Years Exp', 'Verified Pro', 'Last Delivery', 'Remote OK'],
equipmentLabel: 'Design Tools',
galleryTabLabel: 'portfolio',
experienceTabLabel: 'experience & tools',
serviceTabLabel: 'services & pricing',
faqItems: [
{ q: 'What file formats do you deliver?', a: 'Final files in AI, PDF, PNG, SVG, and any format required. Print-ready and web-optimised variants included.' },
{ q: 'Do you create brand guidelines?', a: 'Yes. Brand identity packages include a full style guide covering colour, typography, and usage rules.' },
{ q: 'How many concepts do you provide initially?', a: '3 initial concepts are presented for branding. UI/UX starts with wireframes before visual designs.' },
{ q: 'Do you handle printing coordination?', a: 'Yes. I work with trusted print vendors and can manage end-to-end print production.' },
],
packages: [
{ name: 'Logo Pack', price: '₹8,000', items: ['3 logo concepts', '2 revisions', 'All file formats', 'Usage rights'] },
{ name: 'Brand Kit', price: '₹22,000', items: ['Logo + palette', 'Typography', 'Brand guidelines', 'Social templates'] },
{ name: 'Full Identity', price: '₹45,000', items: ['Complete brand', 'UI kit', 'Print collateral', 'Motion logo'] },
],
},
SOCIAL_MEDIA_MANAGER: {
roleLabel: 'Social Media Manager',
tabs: ['about', 'services & pricing', 'case studies', 'experience & tools', 'testimonials', 'faqs'],
specialties: ['Instagram', 'YouTube', 'LinkedIn', 'Twitter/X', 'Facebook', 'Content Strategy'],
statsLabels: ['Brands Managed', 'Years Exp', 'Verified Pro', 'Last Campaign', 'Remote OK'],
equipmentLabel: 'Platforms & Tools',
galleryTabLabel: 'case studies',
experienceTabLabel: 'experience & tools',
serviceTabLabel: 'services & pricing',
faqItems: [
{ q: 'Do you create the content too?', a: 'Yes. Content creation — including copy, graphics, and reels — is included in all monthly retainer plans.' },
{ q: 'How do you measure results?', a: 'Monthly reports covering reach, engagement rate, follower growth, and link clicks are shared via dashboard.' },
{ q: 'Do you handle paid ads?', a: 'Yes. Meta and Google ad campaigns are available as add-ons with dedicated reporting.' },
{ q: 'What industries do you specialise in?', a: 'Fashion, F&B, real estate, personal brands, and D2C e-commerce. Industry-specific content strategy provided.' },
],
packages: [
{ name: 'Starter', price: '₹12,000/mo', items: ['2 platforms', '12 posts/month', 'Captions + hashtags', 'Monthly report'] },
{ name: 'Growth', price: '₹22,000/mo', items: ['3 platforms', '24 posts/month', 'Reels + Stories', 'Ad management'] },
{ name: 'Premium', price: '₹40,000/mo', items: ['5 platforms', 'Daily posting', 'Full content calendar', 'Influencer outreach'] },
],
},
FITNESS_TRAINER: {
roleLabel: 'Fitness Trainer',
tabs: ['about', 'training plans', 'client results', 'certifications', 'testimonials', 'faqs'],
specialties: ['Weight Loss', 'Strength', 'Yoga', 'HIIT', 'Nutrition', 'Rehabilitation'],
statsLabels: ['Clients Trained', 'Years Exp', 'Certified Pro', 'Next Slot', 'Online OK'],
equipmentLabel: 'Certifications & Tools',
galleryTabLabel: 'client results',
experienceTabLabel: 'certifications',
serviceTabLabel: 'training plans',
faqItems: [
{ q: 'Do you offer online training?', a: 'Yes. Online sessions via Zoom with personalised programs tracked through fitness apps.' },
{ q: 'Is nutrition guidance included?', a: 'Basic nutrition guidance is included. A detailed meal plan is available as an add-on.' },
{ q: 'How long until I see results?', a: 'Visible progress typically begins in 46 weeks with consistent training and diet adherence.' },
{ q: 'Do you design custom workout plans?', a: 'Yes. Every client gets a custom plan based on fitness level, goals, and equipment access.' },
],
packages: [
{ name: 'Starter', price: '₹4,000/mo', items: ['8 sessions/month', 'Workout plan', 'WhatsApp check-ins', 'Progress tracking'] },
{ name: 'Transform', price: '₹8,000/mo', items: ['16 sessions/month', 'Custom diet plan', 'Weekly reviews', 'App access'] },
{ name: 'Elite', price: '₹15,000/mo', items: ['Daily check-in', 'Full nutrition plan', 'Body composition tracking', 'Priority access'] },
],
},
CATERING_SERVICES: {
roleLabel: 'Catering Services',
tabs: ['about', 'packages & pricing', 'gallery', 'experience & certifications', 'testimonials', 'faqs'],
specialties: ['Weddings', 'Corporate Events', 'Private Parties', 'Buffet', 'Live Counters', 'Theme Catering'],
statsLabels: ['Events Done', 'Years Active', 'Certified', 'Last Event', 'Pan-India'],
equipmentLabel: 'Equipment & Certifications',
galleryTabLabel: 'gallery',
experienceTabLabel: 'experience & certifications',
serviceTabLabel: 'packages & pricing',
faqItems: [
{ q: 'What is your minimum guest count?', a: 'Minimum 50 guests for standard bookings. Intimate private dining available for 20+ guests.' },
{ q: 'Do you handle equipment setup?', a: 'Yes. Full setup including chafing dishes, serving counters, and staff is included in all packages.' },
{ q: 'Are custom menus available?', a: 'Absolutely. Menus are fully customisable — dietary restrictions, regional cuisines, and themed menus accommodated.' },
{ q: 'How far in advance should I book?', a: 'At least 34 weeks for events under 200 guests; 68 weeks for large-scale events.' },
],
packages: [
{ name: 'Essential', price: '₹350/plate', items: ['3-course menu', 'Serving staff', 'Basic setup', 'Buffet style'] },
{ name: 'Premium', price: '₹650/plate', items: ['5-course menu', 'Live counters', 'Themed decor', 'Dedicated manager'] },
{ name: 'Royal', price: '₹1,200/plate', items: ['Custom menu', 'Chef station', 'Premium décor', 'Full-day service'] },
],
},
PROFESSIONAL: {
roleLabel: 'Professional',
tabs: ['about', 'services & pricing', 'portfolio', 'experience', 'testimonials', 'faqs'],
specialties: ['Primary Service', 'Secondary Service', 'Consulting', 'Training', 'Events', 'Custom'],
statsLabels: ['Projects Done', 'Years Exp', 'Verified Pro', 'Last Delivery', 'Remote OK'],
equipmentLabel: 'Tools & Equipment',
galleryTabLabel: 'portfolio',
experienceTabLabel: 'experience',
serviceTabLabel: 'services & pricing',
faqItems: [
{ q: 'How do I get started?', a: 'Send a requirement, review the professional\'s profile, and accept the request to receive their contact details.' },
{ q: 'What is your availability?', a: 'Availability is updated regularly on the profile. Contact for specific date confirmation.' },
{ q: 'Do you provide a contract?', a: 'Yes. A service agreement is shared before any project begins for mutual clarity.' },
{ q: 'What is your revision policy?', a: 'Revisions are included as per the selected package. Additional revisions are billed separately.' },
],
packages: [
{ name: 'Basic', price: 'From ₹5,000', items: ['Core deliverables', 'Standard timeline', '1 revision', 'Email support'] },
{ name: 'Standard', price: 'From ₹15,000', items: ['Full scope', 'Priority delivery', '3 revisions', 'Call support'] },
{ name: 'Premium', price: 'Custom', items: ['Custom scope', 'Dedicated support', 'Unlimited revisions', 'SLA'] },
],
},
};
function portfolioSpecForRole(roleKey: string): PortfolioSpec {
const normalized = normalizeRoleKey(roleKey);
return PORTFOLIO_SPECS[normalized] || PORTFOLIO_SPECS.PROFESSIONAL;
}
function portfolioMediaConfig(roleKey: string): {
mode: 'visual' | 'non_visual';
ctaLabel: string;
items: string[];
} {
const normalized = normalizeRoleKey(roleKey);
if (
normalized === 'PHOTOGRAPHER'
|| normalized === 'MAKEUP_ARTIST'
|| normalized === 'VIDEO_EDITOR'
|| normalized === 'UGC_CONTENT_CREATOR'
|| normalized === 'GRAPHIC_DESIGNER'
|| normalized === 'SOCIAL_MEDIA_MANAGER'
|| normalized === 'CATERING_SERVICES'
|| normalized === 'DEVELOPER'
|| normalized === 'FITNESS_TRAINER'
) {
return {
mode: 'visual',
ctaLabel: 'Upload Images',
items: ['Campaign Shot', 'Result Highlight', 'Before/After', 'Client Output', 'Style Board', 'Final Delivery', 'Portfolio Select', 'Recent Work'],
};
}
return {
mode: 'non_visual',
ctaLabel: 'Add Work Entry',
items: ['Case Study Summary', 'Outcome & Metrics', 'Client Feedback Snippet', 'Delivery Timeline', 'Method & Process', 'Proof of Work'],
};
}
const PORTFOLIO_TESTIMONIALS = [
{ name: 'Priya S.', rating: 5, text: 'Absolutely brilliant work. The results exceeded every expectation. Highly recommended.', category: 'Verified Booking' },
{ name: 'Rohan M.', rating: 5, text: 'Professional, punctual, and highly creative. Loved the final deliverables.', category: 'Verified Booking' },
{ name: 'Ananya K.', rating: 4, text: 'Great communication throughout the project. Delivered on time with excellent quality.', category: 'Verified Booking' },
{ name: 'Kiran T.', rating: 5, text: 'An absolute pleasure to work with. Will definitely hire again for our next project.', category: 'Verified Booking' },
];
function customerViewFor(sidebar: string, roleKey: string): CustomerView {
const key = String(sidebar || '').toLowerCase().trim();
const role = normalizeRoleKey(roleKey);
const isProfessionalRole = role !== 'COMPANY' && role !== 'JOB_SEEKER' && role !== 'CUSTOMER';
if (key === 'my dashboard') return { title: 'Service Seeker Dashboard Overview', subtitle: 'Manage your requirements and track professional responses in real-time.', tabs: ['overview', 'recent requirements', 'quick actions'], cta: 'Post New Requirement' };
if (key === 'leads') return { title: 'Leads', subtitle: 'Browse marketplace requirements and request contact access for current opportunities.', tabs: [], cta: 'Buy Credits' };
if (key === 'jobs') {
if (role === 'JOB_SEEKER') return { title: 'Jobs', subtitle: 'Scroll through approved jobs, filter opportunities, and apply with your job seeker profile.', tabs: ['all jobs', 'recommended', 'saved', 'applied', 'expiring soon'], cta: 'Post A Resume' };
return { title: 'Post Job', subtitle: 'Create a job, submit it for approval, and publish it to attract the right candidates.', tabs: [], cta: 'Post New Job' };
}
if (key === 'my requirements') return { title: 'My Requirements Management', subtitle: 'Manage and track active service requests, drafts, and closed items.', tabs: ['all requirements', 'open', 'closed', 'drafts'], cta: 'Post New Requirement' };
if (key === 'received responses' || key === 'my responses') {
if (role !== 'CUSTOMER') return { title: 'My Responses', subtitle: 'Track requested and pending leads with current request status.', tabs: ['requested leads', 'pending leads'] };
return { title: 'Received Professional Responses', subtitle: 'Review and manage professional applications for active requirements.', tabs: ['all responses', 'new', 'shortlisted', 'rejected'] };
}
if (key === 'shortlisted responses') return { title: 'Shortlisted Responses', subtitle: 'Focus on high-intent responses and convert them to confirmed engagements.', tabs: ['all shortlisted', 'interview scheduled', 'finalized'] };
if (key === 'applications') return { title: 'Applications', subtitle: 'Review all candidate applications received for your active job postings.', tabs: ['all applications', 'shortlisted', 'under review', 'rejected'], cta: 'View Job Postings' };
if (key === 'shortlisted candidates') return { title: 'Shortlisted Candidates', subtitle: 'Manage candidates you have shortlisted across all job postings.', tabs: ['shortlisted', 'interview scheduled', 'offer extended'] };
if (key === 'my applications') return { title: 'My Applications', subtitle: 'Track the status of all jobs you have applied to.', tabs: ['all', 'under review', 'shortlisted', 'rejected'], cta: 'Browse Jobs' };
if (key === 'saved jobs') return { title: 'Saved Jobs', subtitle: 'Jobs you have bookmarked. Apply before they expire.', tabs: ['saved', 'expiring soon'], cta: 'Browse Jobs' };
if (key.includes('profile')) {
const spec = profileSpecForRole(roleKey);
return { title: spec.title, subtitle: spec.subtitle, tabs: spec.tabs, cta: 'Save Changes' };
}
if (key === 'my portfolio') {
if (!isProfessionalRole) {
return { title: 'Portfolio Unavailable', subtitle: 'My Portfolio is available only for professional roles.', tabs: [] };
}
const spec = portfolioSpecForRole(roleKey);
return { title: `${spec.roleLabel} Portfolio`, subtitle: 'Manage your public portfolio, showcase your work, and control what service seekers see before they accept your contact request.', tabs: spec.tabs, cta: 'Edit Portfolio' };
}
if (key === 'credits') return { title: 'Credits & Billing', subtitle: 'View credit balance, top-up packages, and transaction history.', tabs: ['overview', 'buy credits', 'transactions', 'usage history'], cta: 'Buy Credits' };
if (key === 'explore nxtgauge') return { title: 'Explore Marketplace', subtitle: 'Discover top-tier professionals and specialized services.', tabs: [] };
if (key === 'verification') return { title: 'Verification Portal', subtitle: 'Track verification progress, documents, and updates.', tabs: ['approval status', 'documents', 'activity'] };
if (key === 'help center' || key === 'support') return { title: 'Help Center', subtitle: 'Get help, manage tickets, and contact support.', tabs: [] };
if (key === 'settings') return { title: 'Account & Privacy Settings', subtitle: 'Configure account security and privacy preferences.', tabs: ['account', 'privacy', 'notifications'] };
if (key === 'switch role' || key === 'switch services' || key === 'switch service') return { title: 'Service Switcher Portal', subtitle: 'Switch to approved services without logging out.', tabs: ['available services', 'pending approvals', 'onboarding'] };
if (key === 'logout') return { title: 'Logout Confirmation', subtitle: 'Confirm before ending your current session.', tabs: ['confirm logout', 'cancel'] };
return { title: 'Service Seeker Dashboard', subtitle: 'Preview service seeker dashboard flow.', tabs: ['overview'] };
}
const REQUIREMENT_ROWS = [
{
id: '#REQ-9012',
title: 'Wedding Shoot in Goa',
summary: 'Traditional & drone coverage',
category: 'PHOTOGRAPHER',
amount: '₹50,000',
budget: '₹50,000 - ₹80,000',
location: 'Goa (On-site)',
submission: 'Oct 24, 2023',
status: 'approved',
responses: 5,
responseTag: 'Active (5 Responses)',
},
{
id: '#REQ-8945',
title: 'E-commerce Portal Redesign',
summary: 'Next.js + Tailwind CSS specialist',
category: 'DEVELOPER',
amount: '₹1,20,000',
budget: '₹1,20,000 - ₹2,00,000',
location: 'Remote',
submission: 'Nov 02, 2023',
status: 'under review',
responses: 0,
responseTag: 'In Review',
},
{
id: '#REQ-8832',
title: 'Corporate Brand Identity',
summary: 'Logo, stationery & guidelines',
category: 'UI DESIGNER',
amount: '₹35,000',
budget: '₹35,000 Flat',
location: 'Mumbai (Hybrid)',
submission: 'Oct 15, 2023',
status: 'rejected',
responses: 0,
responseTag: 'Closed',
},
{
id: '#REQ-9108',
title: 'Home Fitness Program',
summary: '12-week transformation plan',
category: 'FITNESS TRAINER',
amount: '₹18,000',
budget: '₹18,000 - ₹28,000',
location: 'Chennai (Online + Offline)',
submission: 'Nov 08, 2023',
status: 'active',
responses: 3,
responseTag: 'New (3 Responses)',
},
{
id: '#REQ-9121',
title: 'Product Launch Promo Video',
summary: 'Scripted edit + motion graphics',
category: 'VIDEO EDITOR',
amount: '₹40,000',
budget: '₹40,000 - ₹70,000',
location: 'Remote',
submission: 'Nov 10, 2023',
status: 'draft',
responses: 0,
responseTag: 'Draft',
},
{
id: '#REQ-8660',
title: 'Weekly Maths Coaching',
summary: 'Class 10 board-focused tutoring',
category: 'TUTOR',
amount: '₹8,000',
budget: '₹8,000 - ₹14,000',
location: 'Pune (Online)',
submission: 'Sep 28, 2023',
status: 'closed',
responses: 12,
responseTag: 'Completed',
},
];
const REQUIREMENT_ROLE_OPTIONS = [
{ key: 'PHOTOGRAPHER', title: 'Photographer', desc: 'Capture moments with professional event, portrait, or commercial photography.', icon: '/sidebar-icons/photographer.svg' },
{ key: 'MAKEUP_ARTIST', title: 'Makeup Artist', desc: 'Professional styling for weddings, film, or editorial shoots.', icon: '/sidebar-icons/makeup-artist.svg' },
{ key: 'TUTOR', title: 'Tutor', desc: 'Expert guidance for academic subjects, languages, or specialized skills.', icon: '/sidebar-icons/tutor.svg' },
{ key: 'DEVELOPER', title: 'Developer', desc: 'Build websites, mobile apps, or enterprise software solutions.', icon: '/sidebar-icons/developers.svg' },
{ key: 'VIDEO_EDITOR', title: 'Video Editor', desc: 'Professional post-production for YouTube, film, or social media content.', icon: '/sidebar-icons/report.svg' },
{ key: 'UGC_CONTENT_CREATOR', title: 'UGC Content Creator', desc: 'Create user-generated content for ads, social campaigns, and product promotions.', icon: '/sidebar-icons/report.svg' },
{ key: 'FITNESS_TRAINER', title: 'Fitness Trainer', desc: 'Personalized workout plans and health coaching for your goals.', icon: '/sidebar-icons/users.svg' },
{ key: 'CATERING_SERVICES', title: 'Catering Services', desc: 'High-quality food services for events, corporate parties, and weddings.', icon: '/sidebar-icons/order.svg' },
{ key: 'GRAPHIC_DESIGNER', title: 'Graphic Designer', desc: 'Creative branding, logo design, and visual marketing assets.', icon: '/sidebar-icons/designation.svg' },
{ key: 'SOCIAL_MEDIA_MANAGER', title: 'Social Media Manager', desc: 'Strategic growth and content management for your online presence.', icon: '/sidebar-icons/leads.svg' },
];
const REQUIREMENT_ROLE_DETAILS: Record<string, { title: string; subtitle: string; fields: string[]; toggles: string[]; styles: string[] }> = {
PHOTOGRAPHER: {
title: 'Photography Details',
subtitle: 'Tell us more about the shoot to get accurate quotes.',
fields: ['Event Type', 'Shoot Type', 'Event Date & Time', 'Event Duration (Hours)', 'Venue / Location', 'Number of People', 'Delivery Deadline'],
toggles: ['Outdoor Shoot', 'Drone Required', 'Physical Album'],
styles: ['Black & White', 'Vintage', 'Cinematic', 'Fine Art'],
},
MAKEUP_ARTIST: {
title: 'Makeup Service Details',
subtitle: 'Share look preferences and event details for precise proposals.',
fields: ['Event Type', 'Makeup Category', 'Event Date & Time', 'Artists Required', 'Venue / Location', 'Skin Tone Preference', 'Ready By Time'],
toggles: ['Trial Session Required', 'Hair Styling Included', 'Travel Included'],
styles: ['Natural', 'Glam', 'HD', 'Airbrush'],
},
TUTOR: {
title: 'Tutoring Requirement Details',
subtitle: 'Specify class details so tutors can send the right plan.',
fields: ['Subject', 'Class / Grade', 'Mode (Online / Offline)', 'Sessions Per Week', 'Preferred Start Date', 'Student Location', 'Exam Goal'],
toggles: ['1-on-1 Sessions', 'Homework Support', 'Weekly Progress Reports'],
styles: ['Concept Focused', 'Exam Focused', 'Crash Course', 'Practice Heavy'],
},
DEVELOPER: {
title: 'Development Requirement Details',
subtitle: 'Add technical scope details for better estimates.',
fields: ['Project Type', 'Platform', 'Preferred Stack', 'Project Duration', 'Launch Deadline', 'Team Size Needed', 'Support Duration'],
toggles: ['UI/UX Included', 'API Development', 'Post-launch Maintenance'],
styles: ['MVP', 'Scale-ready', 'Enterprise', 'Performance-first'],
},
VIDEO_EDITOR: {
title: 'Video Editing Details',
subtitle: 'Tell us the output style and timeline for quick delivery.',
fields: ['Video Category', 'Final Duration', 'Footage Volume', 'Delivery Date', 'Editing Style', 'Platform', 'Revision Rounds'],
toggles: ['Color Grading', 'Subtitles', 'Motion Graphics'],
styles: ['Cinematic', 'Social Reels', 'Corporate', 'Documentary'],
},
UGC_CONTENT_CREATOR: {
title: 'UGC Campaign Details',
subtitle: 'Share campaign objective and deliverables for accurate creator proposals.',
fields: ['Campaign Goal', 'Platform', 'Deliverables Needed', 'Brand Category', 'Delivery Deadline', 'Target Audience', 'Usage Rights Duration'],
toggles: ['Script Support', 'Voiceover Needed', 'Product Shipping Required'],
styles: ['Product Demo', 'Lifestyle UGC', 'Hook-first Ads', 'Testimonial Style'],
},
FITNESS_TRAINER: {
title: 'Fitness Coaching Details',
subtitle: 'Share your goals and routine to match with the right coach.',
fields: ['Primary Goal', 'Current Activity Level', 'Preferred Mode', 'Training Days Per Week', 'Preferred Timings', 'Health Conditions', 'Goal Timeline'],
toggles: ['Nutrition Plan', 'Home Equipment Plan', 'Weekly Tracking'],
styles: ['Weight Loss', 'Strength', 'Mobility', 'Athletic'],
},
CATERING_SERVICES: {
title: 'Catering Requirement Details',
subtitle: 'Provide event and menu details to receive accurate quotes.',
fields: ['Event Type', 'Cuisine Preference', 'Guest Count', 'Service Date', 'Venue / Location', 'Meal Slot', 'Serving Style'],
toggles: ['Live Counters', 'Dessert Station', 'Serving Staff Included'],
styles: ['South Indian', 'North Indian', 'Continental', 'Fusion'],
},
GRAPHIC_DESIGNER: {
title: 'Design Requirement Details',
subtitle: 'Define your creative brief for better design proposals.',
fields: ['Project Type', 'Brand Industry', 'Deliverables Needed', 'Deadline', 'Target Audience', 'Reference Links', 'Output Formats'],
toggles: ['Brand Guidelines', 'Source Files', 'Print-ready Assets'],
styles: ['Minimal', 'Bold', 'Luxury', 'Playful'],
},
SOCIAL_MEDIA_MANAGER: {
title: 'Social Media Requirement Details',
subtitle: 'Tell us campaign goals and channels for the best fit.',
fields: ['Primary Goal', 'Platforms', 'Posting Frequency', 'Campaign Duration', 'Start Date', 'Brand Category', 'Monthly Budget'],
toggles: ['Reels Production', 'Ad Management', 'Community Management'],
styles: ['Growth Focused', 'Brand Awareness', 'Lead Generation', 'Content-first'],
},
};
const RESPONSE_ROWS = [
{ name: 'Julianne Vance', status: 'new', quote: '₹12,86,500' },
{ name: 'Marcus Holloway', status: 'shortlisted', quote: '₹6,80,600' },
{ name: 'Sarah Jenkins', status: 'rejected', quote: '₹4,15,000' },
{ name: 'David Wu', status: 'new', quote: '₹8,30,000' },
];
const COMPANY_JOB_STEPS = ['Job Basics', 'Role & Requirements', 'Compensation & Location', 'Screening', 'Review & Checkout'];
const COMPANY_JOB_SKILLS = ['Cloud Architecture', 'Kubernetes', 'Go/Rust', 'PostgreSQL', 'CI/CD', 'System Design'];
const LEAD_MARKETPLACE_TABS = ['All Leads', 'Recommended'];
type LeadCardStatus = 'open' | 'requested' | 'unlocked' | 'closed';
const INITIAL_PRO_LEAD_CARDS = [
{
id: 'LD-29831',
title: 'Luxury Wedding Coverage - ECR',
category: 'Wedding Photography',
location: 'Chennai, India',
area: 'ECR',
dateRequired: 'Apr 14, 2026',
urgency: 'High / Immediate',
budget: '₹2,40,000',
budgetValue: 240000,
priceRange: '₹2,00,000 - ₹2,60,000',
cost: 25,
status: 'open' as LeadCardStatus,
match: '98% match',
contactCount: 7,
maxContacts: 10,
},
{
id: 'LD-29745',
title: 'Editorial Fashion Shoot - Studio Series',
category: 'Fashion / Editorial',
location: 'Chennai, India',
area: 'Nungambakkam',
dateRequired: 'Apr 22, 2026',
urgency: 'Medium',
budget: '₹1,10,000',
budgetValue: 110000,
priceRange: '₹90,000 - ₹1,30,000',
cost: 25,
status: 'requested' as LeadCardStatus,
match: '92% match',
contactCount: 9,
maxContacts: 10,
},
{
id: 'LD-29612',
title: 'Corporate Branding Shoot - OMR Campus',
category: 'Commercial Architecture',
location: 'Chennai, India',
area: 'Sholinganallur',
dateRequired: 'May 05, 2026',
urgency: 'Low / Flexible',
budget: '₹1,85,000',
budgetValue: 185000,
priceRange: '₹1,50,000 - ₹2,10,000',
cost: 25,
status: 'unlocked' as LeadCardStatus,
match: '85% match',
contactCount: 10,
maxContacts: 10,
},
{
id: 'LD-29588',
title: 'Temple Wedding Documentary',
category: 'Traditional Wedding',
location: 'Chennai, India',
area: 'Mylapore',
dateRequired: 'Apr 30, 2026',
urgency: 'High / Immediate',
budget: '₹95,000',
budgetValue: 95000,
priceRange: '₹70,000 - ₹1,10,000',
cost: 25,
status: 'open' as LeadCardStatus,
match: '90% match',
contactCount: 4,
maxContacts: 10,
},
{
id: 'LD-29477',
title: 'Product Photography - Electronics',
category: 'E-commerce',
location: 'Chennai, India',
area: 'T Nagar',
dateRequired: 'May 12, 2026',
urgency: 'Medium',
budget: '₹75,000',
budgetValue: 75000,
priceRange: '₹50,000 - ₹90,000',
cost: 25,
status: 'open' as LeadCardStatus,
match: '88% match',
contactCount: 6,
maxContacts: 10,
},
{
id: 'LD-29340',
title: 'Corporate Leadership Portraits',
category: 'Corporate Portrait',
location: 'Chennai, India',
area: 'Guindy',
dateRequired: 'May 22, 2026',
urgency: 'Low / Flexible',
budget: '₹1,35,000',
budgetValue: 135000,
priceRange: '₹1,00,000 - ₹1,50,000',
cost: 25,
status: 'closed' as LeadCardStatus,
match: '84% match',
contactCount: 10,
maxContacts: 10,
},
];
const JOB_SEEKER_OPEN_JOBS = [
{ id: 'JOB-1001', title: 'Senior UX/UI Architect', company: 'Design System Global', location: 'San Francisco, CA (Remote)', salary: '$160k - $210k', exp: '5-7 Years Exp.', type: 'Full-time', tags: ['FIGMA', 'REACT', 'TOKEN STUDIO'], match: '98% Match', posted: 'Posted 2 hours ago' },
{ id: 'JOB-1002', title: 'Engineering Manager (Cloud Infrastructure)', company: 'FinStream Tech', location: 'London, UK (Hybrid)', salary: '£120k - £150k', exp: '8+ Years Exp.', type: 'Hybrid', tags: ['AWS', 'KUBERNETES', 'TERRAFORM'], match: '92% Match', posted: 'Posted 5 hours ago' },
{ id: 'JOB-1003', title: 'Head of Talent Acquisition', company: 'GreenGrowth HR', location: 'Remote (North America)', salary: '$130k - $180k', exp: '10+ Years Exp.', type: 'Worldwide', tags: ['RECRUITING', 'STRATEGIC HR'], match: '85% Match', posted: 'Posted 1 day ago' },
{ id: 'JOB-1004', title: 'Senior Data Scientist (LLM Focus)', company: 'Aether Intelligence', location: 'New York, NY', salary: '$200k - $250k', exp: '4+ Years Exp.', type: 'Fast Hire', tags: ['PYTHON', 'PYTORCH', 'TRANSFORMERS'], match: '95% Match', posted: 'Posted 3 hours ago' },
];
type JobBoardCard = (typeof JOB_SEEKER_OPEN_JOBS)[number];
type CompanyJobSubmissionStatus = 'VERIFICATION_PENDING' | 'VERIFIED' | 'APPROVAL_PENDING' | 'APPROVED_LIVE';
type CompanyJobSubmission = {
id: string;
job: JobBoardCard;
status: CompanyJobSubmissionStatus;
};
type CompanyJobDraft = {
title: string;
company: string;
location: string;
salary: string;
exp: string;
type: string;
tags: string[];
match: string;
posted: string;
department: string;
openings: string;
};
type RequirementRow = (typeof REQUIREMENT_ROWS)[number];
const DEFAULT_COMPANY_JOB_DRAFT: CompanyJobDraft = {
title: 'Senior Technical Architect',
company: 'Your Company',
location: 'Remote',
salary: '$80,000 - $120,000',
exp: '8+ Years Exp.',
type: 'Full-time, Permanent',
tags: COMPANY_JOB_SKILLS.slice(0, 3),
match: 'New',
posted: 'Posted just now',
department: 'Engineering & Infrastructure',
openings: '1',
};
const JOB_SEEKER_APPLIED_ROWS = [
{ id: 'APP-NX-8293', title: 'Senior Product Designer', company: 'Stripe', location: 'Remote, USA', status: 'Shortlisted', note: 'Recruiter viewed 2h ago' },
{ id: 'APP-NX-7741', title: 'Staff UX Engineer', company: 'Airbnb', location: 'San Francisco, CA', status: 'Under Review', note: 'In initial screening' },
{ id: 'APP-NX-9011', title: 'Product Marketing Lead', company: 'Notion', location: 'New York, NY', status: 'Applied', note: 'Successfully submitted' },
{ id: 'APP-NX-5529', title: 'Growth Specialist', company: 'Meta', location: 'Remote', status: 'Not Selected', note: 'Closed on Oct 10' },
];
const HELP_CENTER_CATEGORIES = [
{ title: 'Account & Login', description: 'Trouble logging in? Manage your password and account access.', articles: 24, icon: '/sidebar-icons/users.svg' },
{ title: 'Profile & Verification', description: 'How to get verified and complete your profile faster.', articles: 18, icon: '/sidebar-icons/approval.svg' },
{ title: 'Jobs & Applications', description: 'Find work and manage your active project applications.', articles: 32, icon: '/sidebar-icons/jobs.svg' },
{ title: 'Leads & Responses', description: 'Learn how to respond to incoming business leads.', articles: 15, icon: '/sidebar-icons/leads.svg' },
{ title: 'Credits & Payments', description: 'Invoices, purchasing credits, and billing history.', articles: 21, icon: '/sidebar-icons/credits.svg' },
{ title: 'Settings & Security', description: 'Manage data, sessions, and notification preferences.', articles: 12, icon: '/sidebar-icons/role.svg' },
{ title: 'Service Switching', description: 'How to switch between approved service profiles.', articles: 9, icon: '/sidebar-icons/role.svg' },
{ title: 'Support & Tickets', description: 'Track your support history and ticket updates.', articles: 14, icon: '/sidebar-icons/support.svg' },
];
const HELP_CENTER_ARTICLES = [
'How to verify your business account',
'Setting up two-factor authentication',
'Understanding lead credit balances',
'Troubleshooting mobile notifications',
'Connecting your bank account',
];
const HELP_CENTER_FAQS = [
{
q: 'What are lead credits?',
a: 'Lead credits are the internal currency on Nxtgauge. You use them to respond to new inquiries and business opportunities.',
},
{
q: 'How long does verification take?',
a: 'Most verifications complete within 24 to 72 hours, depending on document quality and review queue.',
},
{
q: 'Can I change my account service?',
a: 'Yes. Use Switch Services once your additional service registration is approved.',
},
{
q: 'What is the refund policy for credits?',
a: 'Credits follow platform policy and may be eligible for adjustment when billing issues are validated.',
},
];
const HELP_TICKET_ROWS = [
{ id: 'TCK-1042', title: 'Verification clarification required', status: 'Open', updated: '2h ago', priority: 'High', lastMessage: 'Please share GST certificate copy.' },
{ id: 'TCK-1031', title: 'Unable to see credit invoice', status: 'In Progress', updated: 'Yesterday', priority: 'Medium', lastMessage: 'Invoice regenerated and shared via email.' },
{ id: 'TCK-1007', title: 'Need onboarding status update', status: 'Resolved', updated: '3 days ago', priority: 'Low', lastMessage: 'Profile approved successfully.' },
] as const;
const HELP_TICKET_DETAILS: Record<
string,
{
userMessage: string;
adminMessage: string;
requestedDocuments: string[];
receivedFiles: Array<{ file: string; state: 'Received' | 'Pending Upload' }>;
}
> = {
'TCK-1042': {
userMessage: 'Can you confirm if any additional document is required from my side?',
adminMessage: 'Your verification is active. Please upload the pending documents listed below to avoid delays.',
requestedDocuments: ['GST Certificate', 'Authorization Letter', 'Business PAN Copy'],
receivedFiles: [
{ file: 'gst_certificate.pdf', state: 'Received' },
{ file: 'authorization_letter.jpg', state: 'Received' },
{ file: 'business_pan.pdf', state: 'Pending Upload' },
],
},
'TCK-1031': {
userMessage: 'I cannot find my latest credit invoice in billing history.',
adminMessage: 'Invoice has been regenerated. Please verify if the attached copy is visible on your billing page now.',
requestedDocuments: ['Invoice Month Confirmation'],
receivedFiles: [{ file: 'invoice_month_confirmation.txt', state: 'Received' }],
},
'TCK-1007': {
userMessage: 'Can I get an update on my onboarding review status?',
adminMessage: 'Your onboarding has been approved. No further action is pending from your side.',
requestedDocuments: [],
receivedFiles: [],
},
};
function roleIcon(roleKey: string) {
const key = String(roleKey || '').toLowerCase();
if (key.includes('photo')) return Camera;
if (key.includes('makeup')) return Scissors;
if (key.includes('tutor')) return GraduationCap;
if (key.includes('developer')) return Code2;
if (key.includes('video')) return Clapperboard;
if (key.includes('ugc') || (key.includes('content') && key.includes('creator'))) return Clapperboard;
if (key.includes('graphic')) return PenTool;
if (key.includes('social')) return Megaphone;
if (key.includes('cater')) return UtensilsCrossed;
if (key.includes('fitness')) return Dumbbell;
return BriefcaseBusiness;
}
function roleIconAsset(roleKey: string) {
const key = String(roleKey || '').toLowerCase();
if (key.includes('photo')) return '/sidebar-icons/photographer.svg';
if (key.includes('makeup')) return '/sidebar-icons/makeup-artist.svg';
if (key.includes('tutor')) return '/sidebar-icons/tutor.svg';
if (key.includes('developer')) return '/sidebar-icons/developers.svg';
if (key.includes('video')) return '';
if (key.includes('ugc') || (key.includes('content') && key.includes('creator'))) return '/sidebar-icons/report.svg';
if (key.includes('graphic')) return '/sidebar-icons/designation.svg';
if (key.includes('social')) return '/sidebar-icons/leads.svg';
if (key.includes('cater')) return '';
if (key.includes('fitness')) return '/sidebar-icons/users.svg';
if (key.includes('company')) return '/sidebar-icons/company.svg';
return '/sidebar-icons/role.svg';
}
export default function DashboardDesignPreview(props: {
status: 'ACTIVE' | 'INACTIVE';
sidebarItems: string[];
activeSidebar: string;
onSidebarSelect: (item: string) => void;
tabs: string[];
activeTab: string;
onTabSelect: (item: string) => void;
widgets: string[];
fields: string[];
mode?: 'default' | 'customer_external';
roleKey?: string;
exploreRoles?: Array<{ key: string; name: string }>;
onOpenFullscreen?: () => void;
hidePreviewHeader?: boolean;
liveData?: { userName: string; userId: string; rolePrefix: string };
}) {
const [isVerified, setIsVerified] = createSignal(false);
const [verificationPending, setVerificationPending] = createSignal(false);
const isProfessionalRoleKey = (roleKey: string) => {
const role = normalizeRoleKey(roleKey);
return role !== 'COMPANY' && role !== 'JOB_SEEKER' && role !== 'CUSTOMER';
};
const normalizeTabKey = (value: string) => String(value || '').trim().toLowerCase();
const isCustomerExternalMode = createMemo(() => props.mode === 'customer_external');
const previewSidebarItems = createMemo(() => (props.sidebarItems.length ? props.sidebarItems : ['My Dashboard', 'My Profile', 'Switch Services', 'Logout']));
const customerView = createMemo(() => customerViewFor(props.activeSidebar || previewSidebarItems()[0] || 'My Dashboard', props.roleKey || ''));
const currentProfileSpec = createMemo(() => profileSpecForRole(props.roleKey || ''));
const isJobSeekerRole = createMemo(() => normalizeRoleKey(props.roleKey || '') === 'JOB_SEEKER');
const activeTabKey = createMemo(() => normalizeTabKey(props.activeTab));
const previewTabs = createMemo(() => {
if (isCustomerExternalMode()) return customerView().tabs;
return props.tabs.length ? props.tabs : ['overview'];
});
const resolvedTabKey = createMemo(() => {
const tabs = previewTabs();
const key = activeTabKey();
return tabs.some((item) => normalizeTabKey(item) === key) ? key : normalizeTabKey(tabs[0] || '');
});
const previewWidgets = createMemo(() => (props.widgets.length ? props.widgets : ['total_requirements', 'open', 'closed', 'responses', 'saved_pros']));
const previewFields = createMemo(() => (props.fields.length ? props.fields : ['full_name', 'email', 'verification_status', 'approval_status']));
const customerKey = createMemo(() => String(props.activeSidebar || '').toLowerCase().trim());
const portfolioJobsCompletedPreview = 1;
const portfolioFeedbackCountPreview = 0;
const portfolioTestimonialsUnlocked = createMemo(() => portfolioJobsCompletedPreview >= 3 && portfolioFeedbackCountPreview >= 2);
const exploreRoleCards = createMemo(() => {
const roles = Array.isArray(props.exploreRoles) ? props.exploreRoles : [];
const activeRoles = userRoles();
const professionalOnly = roles
.filter((r) => String(r?.key || '').trim())
.filter((r) => {
const roleKey = String(r?.key || '').toLowerCase();
return !roleKey.includes('company')
&& !roleKey.includes('job_seeker')
&& !roleKey.includes('jobseeker')
&& !roleKey.includes('customer')
&& !roleKey.includes('service_seeker');
})
.map((role) => {
const roleKey = String(role.key || '').trim();
const title = String(role.name || '').trim() || titleCase(roleKey.replace(/_/g, ' ').toLowerCase());
const isRegistered = activeRoles.some((ar) => normalizeRoleKey(ar.role_key || ar.key) === normalizeRoleKey(roleKey));
const isCurrent = normalizeRoleKey(props.roleKey || '') === normalizeRoleKey(roleKey);
return {
key: roleKey,
title,
subtitle: `Apply and onboard for ${title} to unlock more opportunities on Nxtgauge.`,
status: isCurrent ? 'Active' : isRegistered ? 'Registered' : 'Available',
action: isCurrent ? 'Active' : isRegistered ? 'Switch' : 'Register',
iconAsset: roleIconAsset(roleKey),
Icon: roleIcon(roleKey),
};
});
const defaults = [
{ key: 'PHOTOGRAPHER', title: 'Photographer', subtitle: 'Capture professional moments and monetize your creative vision.', iconAsset: '/sidebar-icons/photographer.svg', Icon: Camera },
{ key: 'MAKEUP_ARTIST', title: 'Makeup Artist', subtitle: 'Offer beauty services for events and premium clientele.', iconAsset: '/sidebar-icons/makeup-artist.svg', Icon: Scissors },
{ key: 'TUTOR', title: 'Tutor', subtitle: 'Share expertise through remote and flexible teaching.', iconAsset: '/sidebar-icons/tutor.svg', Icon: GraduationCap },
{ key: 'DEVELOPER', title: 'Developer', subtitle: 'Build scalable products for high-growth businesses.', iconAsset: '/sidebar-icons/developers.svg', Icon: Code2 },
{ key: 'VIDEO_EDITOR', title: 'Video Editor', subtitle: 'Craft compelling stories with world-class editing.', iconAsset: '/sidebar-icons/report.svg', Icon: Clapperboard },
{ key: 'UGC_CONTENT_CREATOR', title: 'UGC Content Creator', subtitle: 'Create ad-ready user-generated content for brand campaigns.', iconAsset: '/sidebar-icons/report.svg', Icon: Clapperboard },
{ key: 'GRAPHIC_DESIGNER', title: 'Graphic Designer', subtitle: 'Design visual systems that brands remember.', iconAsset: '/sidebar-icons/designation.svg', Icon: PenTool },
{ key: 'SOCIAL_MEDIA_MANAGER', title: 'Social Media Manager', subtitle: 'Manage digital presence and audience growth.', iconAsset: '/sidebar-icons/leads.svg', Icon: Megaphone },
{ key: 'FITNESS_TRAINER', title: 'Fitness Trainer', subtitle: 'Coach clients on health and performance goals.', iconAsset: '/sidebar-icons/users.svg', Icon: Dumbbell },
{ key: 'CATERING_SERVICES', title: 'Catering Services', subtitle: 'Deliver high-quality catering for events and celebrations.', iconAsset: '/sidebar-icons/order.svg', Icon: UtensilsCrossed },
];
const merged = [...professionalOnly];
defaults.forEach((item) => {
if (!merged.some((row) => normalizeRoleKey(row.key) === normalizeRoleKey(item.key))) {
const isRegistered = activeRoles.some((ar) => normalizeRoleKey(ar.role_key || ar.key) === normalizeRoleKey(item.key));
const isCurrent = normalizeRoleKey(props.roleKey || '') === normalizeRoleKey(item.key);
merged.push({
...item,
status: isCurrent ? 'Active' : isRegistered ? 'Registered' : 'Available',
action: isCurrent ? 'Active' : isRegistered ? 'Switch' : 'Register'
});
}
});
return merged.slice(0, 10);
});
const [dashboardWidgetOrder, setDashboardWidgetOrder] = createSignal<string[]>([]);
const [draggingDashboardWidget, setDraggingDashboardWidget] = createSignal<string | null>(null);
const [helpCenterTab, setHelpCenterTab] = createSignal<'help_center' | 'my_tickets' | 'create_ticket'>('help_center');
const [myTicketsTab, setMyTicketsTab] = createSignal<'all_tickets' | 'view_ticket'>('all_tickets');
const [activeTicketId, setActiveTicketId] = createSignal<string>('TCK-1042');
const [openFaqIndex, setOpenFaqIndex] = createSignal<number | null>(0);
const [ticketMessage, setTicketMessage] = createSignal('');
const [createTicketFiles, setCreateTicketFiles] = createSignal<string[]>([]);
const [viewTicketFiles, setViewTicketFiles] = createSignal<string[]>([]);
const [profileFormData, setProfileFormData] = createSignal<Record<string, string>>({});
const [profileSaving, setProfileSaving] = createSignal(false);
const [profileSaveStatus, setProfileSaveStatus] = createSignal<'idle' | 'saved' | 'error'>('idle');
const [profileSettingsTab, setProfileSettingsTab] = createSignal<'change_password' | 'notifications' | 'privacy'>('change_password');
const [showDeleteAccountModal, setShowDeleteAccountModal] = createSignal(false);
const [portfolioEditMode, setPortfolioEditMode] = createSignal(false);
const [portfolioTopTab, setPortfolioTopTab] = createSignal<'my_portfolio' | 'preview'>('my_portfolio');
const [profileApprovalState, setProfileApprovalState] = createSignal<'DRAFT' | 'SUBMITTED' | 'IN_REVIEW' | 'DOCUMENTS_REQUESTED' | 'APPROVED' | 'REJECTED'>('DRAFT');
const [portfolioApprovalState, setPortfolioApprovalState] = createSignal<'DRAFT' | 'SUBMITTED' | 'IN_REVIEW' | 'DOCUMENTS_REQUESTED' | 'APPROVED' | 'REJECTED'>('DRAFT');
const [portfolioSpecialties, setPortfolioSpecialties] = createSignal<string[]>([]);
const [portfolioLanguages, setPortfolioLanguages] = createSignal<string[]>([]);
const [portfolioServiceAreas, setPortfolioServiceAreas] = createSignal<string[]>([]);
const [portfolioSpecialtyInput, setPortfolioSpecialtyInput] = createSignal('');
const [portfolioLanguageInput, setPortfolioLanguageInput] = createSignal('');
const [portfolioAreaInput, setPortfolioAreaInput] = createSignal('');
const [profileDocumentType, setProfileDocumentType] = createSignal('Aadhar Card');
const [portfolioFormValues, setPortfolioFormValues] = createSignal<Record<string, string>>({});
const [portfolioFormErrors, setPortfolioFormErrors] = createSignal<Record<string, string>>({});
const [userRoles, setUserRoles] = createSignal<any[]>([]);
const [portfolioValidationNotice, setPortfolioValidationNotice] = createSignal('');
const [portfolioServices, setPortfolioServices] = createSignal<Array<{ name: string; model: string; price: string; details: string }>>([]);
const [portfolioServiceDraft, setPortfolioServiceDraft] = createSignal<{ name: string; model: string; price: string; details: string }>({ name: '', model: 'Flat', price: '', details: '' });
const [portfolioExperiences, setPortfolioExperiences] = createSignal<Array<{ year: string; title: string; details: string }>>([]);
const [portfolioExperienceDraft, setPortfolioExperienceDraft] = createSignal<{ year: string; title: string; details: string }>({ year: '', title: '', details: '' });
const [portfolioDesignTools, setPortfolioDesignTools] = createSignal<string[]>([]);
const [portfolioToolInput, setPortfolioToolInput] = createSignal('');
const [portfolioPhotos, setPortfolioPhotos] = createSignal<string[]>([]);
const [requirementsView, setRequirementsView] = createSignal<'list' | 'new' | 'detail'>('list');
const [requirementsStep, setRequirementsStep] = createSignal(1);
const [requirementRows, setRequirementRows] = createSignal<RequirementRow[]>(REQUIREMENT_ROWS);
const [selectedRequirementRole, setSelectedRequirementRole] = createSignal('PHOTOGRAPHER');
const [selectedRequirementId, setSelectedRequirementId] = createSignal('#REQ-9012');
const [jobPostView, setJobPostView] = createSignal<'form' | 'review' | 'success'>('form');
const [jobPostStep, setJobPostStep] = createSignal(1);
const [jobBoardJobs, setJobBoardJobs] = createSignal<JobBoardCard[]>(JOB_SEEKER_OPEN_JOBS);
const [companyJobSubmissions, setCompanyJobSubmissions] = createSignal<CompanyJobSubmission[]>([]);
const [companyJobDraft] = createSignal<CompanyJobDraft>(DEFAULT_COMPANY_JOB_DRAFT);
const [jobSeekerScreen, setJobSeekerScreen] = createSignal<'list' | 'detail' | 'apply'>('list');
const [jobSeekerSelectedId, setJobSeekerSelectedId] = createSignal(JOB_SEEKER_OPEN_JOBS[0]?.id || '');
const [jobSeekerApplyStep, setJobSeekerApplyStep] = createSignal(2);
const [lastJobSeekerTabKey, setLastJobSeekerTabKey] = createSignal('');
const [leadCards, setLeadCards] = createSignal(INITIAL_PRO_LEAD_CARDS);
const [leadMarketplaceTab, setLeadMarketplaceTab] = createSignal('All Leads');
const [leadSearch, setLeadSearch] = createSignal('');
const [leadAreaFilter, setLeadAreaFilter] = createSignal('All Areas');
const [leadBudgetFilter, setLeadBudgetFilter] = createSignal('All Budgets');
const [leadDateFilter, setLeadDateFilter] = createSignal('Any Date');
const [leadSortFilter, setLeadSortFilter] = createSignal('Newest First');
const [leadFiltersOpen, setLeadFiltersOpen] = createSignal(false);
const [leadSortOpen, setLeadSortOpen] = createSignal(false);
const [leadPage, setLeadPage] = createSignal(1);
const [activeLeadDetailId, setActiveLeadDetailId] = createSignal('');
const [leadContactConfirmId, setLeadContactConfirmId] = createSignal('');
const [activeResponseLeadId, setActiveResponseLeadId] = createSignal('');
const [responsesDetailMode, setResponsesDetailMode] = createSignal(false);
const [requestedSearch, setRequestedSearch] = createSignal('');
const [requestedStatusFilter, setRequestedStatusFilter] = createSignal('All Status');
const [requestedSortFilter, setRequestedSortFilter] = createSignal('Newest First');
const [requestedSortOpen, setRequestedSortOpen] = createSignal(false);
const [requestedFilterOpen, setRequestedFilterOpen] = createSignal(false);
const [requestedPage, setRequestedPage] = createSignal(1);
const [leadCredits, setLeadCredits] = createSignal(250);
const [checkoutPackage, setCheckoutPackage] = createSignal<any | null>(null);
const [paymentStep, setPaymentStep] = createSignal<'idle' | 'processing' | 'verifying' | 'success' | 'error'>('idle');
const [paymentRef, setPaymentRef] = createSignal<string | null>(null);
const [paymentResult, setPaymentResult] = createSignal<any>(null);
// Coupon state
const [couponCode, setCouponCode] = createSignal('');
const [appliedCoupon, setAppliedCoupon] = createSignal<{code: string; discount_type: string; discount_value: number; final_price_inr: number} | null>(null);
const [couponError, setCouponError] = createSignal('');
const [couponLoading, setCouponLoading] = createSignal(false);
const [creditManageView, setCreditManageView] = createSignal(false);
const [txRows, setTxRows] = createSignal<Array<[string, string, string, string, string, string]>>([
['#INV-2023-089', 'Enterprise Growth', '5,000', '₹1,20,000', 'Completed', 'Oct 24, 2023'],
['#INV-2023-074', 'Starter Kick', '500', '₹15,000', 'Pending', 'Oct 23, 2023'],
['#INV-2023-052', 'Pro Pack', '2,500', '₹65,000', 'Completed', 'Oct 21, 2023'],
['#INV-2023-031', 'Top-up', '1,000', '₹30,000', 'Failed', 'Oct 20, 2023'],
]);
const [leadRequestRows, setLeadRequestRows] = createSignal([
{ id: 'LD-29745', title: 'Editorial Fashion Shoot - Studio Series', city: 'Nungambakkam, Chennai', requestDate: 'Apr 02, 2026', status: 'request_sent', decisionDate: '--' },
{ id: 'LD-29612', title: 'Corporate Branding Shoot - OMR Campus', city: 'Sholinganallur, Chennai', requestDate: 'Apr 01, 2026', status: 'contact_unlocked', decisionDate: 'Apr 01, 2026' },
{ id: 'LD-29588', title: 'Temple Wedding Documentary', city: 'Mylapore, Chennai', requestDate: 'Mar 30, 2026', status: 'rejected', decisionDate: 'Mar 30, 2026' },
] as Array<{ id: string; title: string; city: string; requestDate: string; status: 'request_sent' | 'approved' | 'contact_unlocked' | 'rejected' | 'expired_refunded' | 'cancelled_by_professional'; decisionDate: string }>);
const [lastSidebarKey, setLastSidebarKey] = createSignal('');
const [profileSettingToggles, setProfileSettingToggles] = createSignal<Record<string, boolean>>({
email_updates: true,
in_app_alerts: true,
verification_reminders: true,
profile_visibility: true,
data_sharing_consent: false,
});
const toggleProfileSetting = (key: string) => {
setProfileSettingToggles((prev) => ({ ...prev, [key]: !prev[key] }));
};
const submitCompanyJobForReview = () => {
const draft = companyJobDraft();
const id = `JOB-COMP-${Date.now()}`;
const submittedJob: JobBoardCard = {
id,
title: draft.title,
company: draft.company,
location: draft.location,
salary: draft.salary,
exp: draft.exp,
type: draft.type,
tags: draft.tags,
match: draft.match,
posted: draft.posted,
};
setCompanyJobSubmissions((prev) => [{ id, job: submittedJob, status: 'VERIFICATION_PENDING' }, ...prev]);
setTimeout(() => {
setCompanyJobSubmissions((prev) => prev.map((row) => (row.id === id ? { ...row, status: 'VERIFIED' } : row)));
setTimeout(() => {
setCompanyJobSubmissions((prev) => prev.map((row) => (row.id === id ? { ...row, status: 'APPROVAL_PENDING' } : row)));
setTimeout(() => {
setCompanyJobSubmissions((prev) => prev.map((row) => (row.id === id ? { ...row, status: 'APPROVED_LIVE' } : row)));
setJobBoardJobs((prev) => [submittedJob, ...prev.filter((job) => job.id !== id)]);
setJobSeekerSelectedId(id);
}, 650);
}, 650);
}, 650);
};
const submitRequirementForReview = () => {
const roleKey = selectedRequirementRole();
const roleLabel = titleCase(roleKey.replace(/_/g, ' ').toLowerCase());
if (hasLive()) {
apiPost('/api/customers/requirements', {
profession_key: roleKey,
title: `${roleLabel} Requirement`,
description: 'Submitted via dashboard',
location: 'Chennai',
budget_min: 10000000,
budget_max: 20000000,
}).then((res: any) => res?.json?.().then((r: any) => {
// After creating, submit for approval
if (r?.id) apiPost(`/api/customers/requirements/${r.id}/submit`, {});
})).then(() => refetchRequirementsLive());
}
const id = `#REQ-${Math.floor(9200 + Math.random() * 899)}`;
const submission = new Date().toLocaleDateString('en-US', { month: 'short', day: '2-digit', year: 'numeric' });
const newRequirement: RequirementRow = {
id,
title: `${roleLabel} Requirement`,
summary: 'Submitted from customer requirement form',
category: roleKey,
amount: '₹1,50,000',
budget: '₹1,50,000 - ₹2,00,000',
location: 'Chennai (On-site)',
submission,
status: 'under review',
responses: 0,
responseTag: 'Verification Pending',
};
setRequirementRows((prev) => [newRequirement, ...prev]);
setSelectedRequirementId(id);
setRequirementsView('list');
setRequirementsStep(1);
setTimeout(() => {
setRequirementRows((prev) => prev.map((row) => (row.id === id ? { ...row, status: 'approved', responses: 0, responseTag: 'Approval Pending' } : row)));
setTimeout(() => {
setRequirementRows((prev) => prev.map((row) => (row.id === id ? { ...row, status: 'active', responses: 0, responseTag: 'Active (0 Responses)' } : row)));
setLeadCards((prev) => {
const leadId = id.replace('#REQ', 'LD');
if (prev.some((card) => card.id === leadId)) return prev;
return [
{
id: leadId,
title: newRequirement.title,
category: roleLabel,
location: 'Chennai, India',
area: 'Chennai',
dateRequired: 'May 30, 2026',
urgency: 'Medium',
budget: '₹1,50,000',
budgetValue: 150000,
priceRange: newRequirement.budget,
cost: 25,
status: 'open',
match: '90% match',
contactCount: 0,
maxContacts: 10,
},
...prev,
];
});
}, 650);
}, 650);
};
const isProfessionalRole = createMemo(() => isProfessionalRoleKey(props.roleKey || ''));
const bothApprovalsApproved = createMemo(() => {
if (!isProfessionalRole()) return profileApprovalState() === 'APPROVED';
return profileApprovalState() === 'APPROVED' && portfolioApprovalState() === 'APPROVED';
});
const approvalTone = (state: string) => {
if (state === 'APPROVED') return { border: '#E5E7EB', bg: '#F9FAFB', text: '#374151', label: 'Approved' };
if (state === 'IN_REVIEW' || state === 'SUBMITTED') return { border: '#E5E7EB', bg: '#F9FAFB', text: '#374151', label: 'In Review' };
if (state === 'DOCUMENTS_REQUESTED') return { border: '#E5E7EB', bg: '#F9FAFB', text: '#374151', label: 'Documents Requested' };
if (state === 'REJECTED') return { border: '#E5E7EB', bg: '#F9FAFB', text: '#374151', label: 'Rejected' };
return { border: '#E5E7EB', bg: '#F9FAFB', text: '#374151', label: 'Draft' };
};
// ─── Live API integration (customer_external mode with liveData) ──────────
const hasLive = () => isCustomerExternalMode() && !!props.liveData;
const livePrefix = () => props.liveData?.rolePrefix ?? '';
const GW = '/api/gateway';
const apiFetch = (path: string) =>
fetch(`${GW}${path}`, { credentials: 'include' })
.then((r) => (r.ok ? r.json() : null))
.catch(() => null);
const apiPost = (path: string, body: unknown) =>
fetch(`${GW}${path}`, {
method: 'POST',
credentials: 'include',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify(body),
}).catch(() => null);
const apiDelete = (path: string) =>
fetch(`${GW}${path}`, { method: 'DELETE', credentials: 'include' }).catch(() => null);
// Credits balance
const [creditsResource] = createResource(
() => (hasLive() ? livePrefix() : null),
(prefix) => apiFetch(`/api/${prefix}/wallet/balance`),
);
// Marketplace requirements (professionals)
const [marketplaceResource] = createResource(
() => (hasLive() && isProfessionalRole() ? livePrefix() : null),
(prefix) => apiFetch(`/api/${prefix}/marketplace?limit=50`),
);
// My lead requests (professionals)
const [leadRequestsResource, { refetch: refetchLeadRequestsLive }] = createResource(
() => (hasLive() && isProfessionalRole() ? livePrefix() : null),
(prefix) => apiFetch(`/api/${prefix}/leads/requests/me?limit=50`),
);
// Customer requirements
const [requirementsResource, { refetch: refetchRequirementsLive }] = createResource(
() => (hasLive() && normalizeRoleKey(props.roleKey ?? '') === 'CUSTOMER' ? 'yes' : null),
() => apiFetch('/api/customers/requirements?limit=50'),
);
// Jobs board
const [jobsResource] = createResource(
() => {
const r = normalizeRoleKey(props.roleKey ?? '');
return hasLive() && (r === 'JOB_SEEKER' || r === 'COMPANY') ? r : null;
},
() => apiFetch('/api/jobs?limit=50'),
);
// User profile (all roles)
const [profileResource] = createResource(
() => (hasLive() ? livePrefix() : null),
(prefix) => apiFetch(`/api/${prefix}/profile/me`),
);
// Professional services (Packages)
const [servicesResource, { refetch: refetchServicesLive }] = createResource(
() => (hasLive() && isProfessionalRole() ? livePrefix() : null),
(prefix) => apiFetch(`/api/${prefix}/services/me`),
);
// Professional portfolio
const [portfolioResource, { refetch: refetchPortfolioLive }] = createResource(
() => (hasLive() && isProfessionalRole() ? livePrefix() : null),
(prefix) => apiFetch(`/api/${prefix}/portfolio/me`),
);
// Current user roles (for switching and exploration status)
const [userRolesResource, { refetch: refetchUserRolesLive }] = createResource(
() => (hasLive() ? 'yes' : null),
() => apiFetch('/api/users/roles'),
);
// Tracecoin pricing packages
const [pricingPackagesResource] = createResource(
() => (hasLive() ? 'yes' : null),
() => apiFetch('/api/packages'),
);
// Dynamic Knowledge Base Resource
const [kbResource] = createResource(
() => (hasLive() ? 'yes' : null),
async () => {
const res = await apiFetch('/api/runtime-config/knowledge_base/GLOBAL_KB');
const data = (res?.payload as RuntimeKBConfig) || { articles: [] };
return data;
}
);
const [kbSearch, setKbSearch] = createSignal('');
const [kbCategory, setKbCategory] = createSignal('All');
const filteredKbArticles = createMemo(() => {
const data = kbResource()?.articles || [];
return data.filter(a => {
if (!a.isPublished) return false;
const matchesSearch = a.title.toLowerCase().includes(kbSearch().toLowerCase()) ||
a.content.toLowerCase().includes(kbSearch().toLowerCase());
const matchesCategory = kbCategory() === 'All' || a.category === kbCategory();
return matchesSearch && matchesCategory;
});
});
const kbCategoriesWithCounts = createMemo(() => {
const articles = kbResource()?.articles || [];
const counts: Record<string, number> = {};
articles.forEach(a => {
if (!a.isPublished) return;
counts[a.category] = (counts[a.category] || 0) + 1;
});
return Object.entries(counts).map(([title, articles]) => ({
title,
articles,
icon: '/icons/help-book.png', // Placeholder icon
description: `Guides and documentation for ${title}.`
}));
});
const applyCoupon = async () => {
const code = couponCode().trim();
if (!code) { setCouponError('Enter a coupon code'); return; }
setCouponLoading(true);
setCouponError('');
try {
const token = getToken();
const headers: HeadersInit = {
'Content-Type': 'application/json',
...(token ? { 'Authorization': `Bearer ${token}` } : {})
};
const res = await fetch(`${API}/coupons/validate`, {
method: 'POST',
headers,
credentials: 'include',
body: JSON.stringify({
coupon_code: code,
role_key: normalizeRoleKey(props.roleKey || ''),
package_price_inr: Number(checkoutPackage()?.price_paise || 0)
})
});
const data = await res.json();
if (res.ok && data.valid) {
setAppliedCoupon({
code,
discount_type: data.discount_type,
discount_value: data.discount_value,
final_price_inr: data.final_price_inr
});
} else {
setCouponError(data.message || 'Invalid coupon');
setAppliedCoupon(null);
}
} catch (e) {
setCouponError('Network error');
setAppliedCoupon(null);
} finally {
setCouponLoading(false);
}
};
const startPayment = async (pkg: any) => {
setPaymentStep('processing');
try {
const token = getToken();
const headers: HeadersInit = {
'Content-Type': 'application/json',
...(token ? { 'Authorization': `Bearer ${token}` } : {})
};
const ac = appliedCoupon();
const body: any = {
package_id: pkg.id,
amount: ac ? ac.final_price_inr : pkg.price_paise,
};
if (ac) body.coupon_code = ac.code;
const res = await fetch(`${API}/payments/create-order`, {
method: 'POST',
headers,
credentials: 'include',
body: JSON.stringify(body),
});
const data = await res.json().catch(() => ({}));
if (!res.ok) {
setPaymentStep('error');
setCouponError('Payment initiation failed');
return;
}
setPaymentRef(data.order_id || `ORD-${Date.now()}`);
setTimeout(() => verifyPayment(pkg), 1200);
} catch (e) {
setPaymentRef(`ORD-MOCK-${Date.now()}`);
setTimeout(() => verifyPayment(pkg), 1200);
}
};
const verifyPayment = async (pkg: any) => {
setPaymentStep('verifying');
try {
const token = getToken();
const headers: HeadersInit = {
'Content-Type': 'application/json',
...(token ? { 'Authorization': `Bearer ${token}` } : {})
};
const res = await fetch(`${API}/payments/verify`, {
method: 'POST',
headers,
credentials: 'include',
body: JSON.stringify({ order_id: paymentRef(), payment_id: `PAY-${Date.now()}` }),
});
if (res.ok) {
setPaymentStep('success');
const creditsToAdd = Number(pkg.credits) + Number(pkg.bonus_credits || 0);
setLeadCredits((prev) => prev + creditsToAdd);
const ac = appliedCoupon();
const amountPaid = ac ? ac.final_price_inr : pkg.price_paise;
const newRow: [string, string, string, string, string, string] = [
paymentRef() || `#INV-${Date.now()}`,
pkg.display_name || pkg.name,
Number(creditsToAdd).toLocaleString(),
`${(amountPaid / 100).toLocaleString('en-IN')}`,
'Completed',
new Date().toLocaleDateString('en-US', { month: 'short', day: '2-digit', year: 'numeric' }),
];
setTxRows((prev) => [newRow, ...prev]);
setAppliedCoupon(null);
setCouponCode('');
} else {
setPaymentStep('error');
}
} catch (e) {
setPaymentStep('error');
}
};
const adjustCreditsManually = (amount: number, reason: string) => {
setLeadCredits((prev) => prev + amount);
const newRow: [string, string, string, string, string, string] = [
`#ADJ-${Date.now().toString().slice(-6)}`,
`Adjustment: ${reason}`,
amount > 0 ? `+${amount.toLocaleString()}` : amount.toLocaleString(),
'₹0',
'Completed',
new Date().toLocaleDateString('en-US', { month: 'short', day: '2-digit', year: 'numeric' }),
];
setTxRows((prev) => [newRow, ...prev]);
};
// Sync resources → local signals
createEffect(() => {
const d = creditsResource();
if (d != null && typeof d.balance === 'number') setLeadCredits(d.balance);
});
createEffect(() => {
const d = marketplaceResource();
if (!d) return;
const items: any[] = Array.isArray(d.items) ? d.items : Array.isArray(d) ? d : [];
if (!items.length) return;
setLeadCards(items.map((item: any) => ({
id: String(item.id ?? item.requirement_id ?? ''),
title: String(item.title ?? item.description ?? 'Requirement'),
category: String(item.category ?? item.profession_key ?? props.roleKey ?? ''),
location: String(item.location ?? item.city ?? 'India'),
area: String(item.area ?? item.locality ?? ''),
dateRequired: item.required_by
? new Date(item.required_by).toLocaleDateString('en-US', { month: 'short', day: '2-digit', year: 'numeric' })
: 'TBD',
urgency: item.urgency === 'HIGH' ? 'High' : item.urgency === 'MEDIUM' ? 'Medium' : 'Low',
budget: item.budget_min != null
? `${Math.round(item.budget_min / 100).toLocaleString('en-IN')} - ₹${Math.round((item.budget_max ?? item.budget_min) / 100).toLocaleString('en-IN')}`
: '₹0',
budgetValue: Number(item.budget_max ?? item.budget_min ?? 0) / 100,
priceRange: item.budget_min != null
? `${Math.round(item.budget_min / 100).toLocaleString('en-IN')} - ₹${Math.round((item.budget_max ?? item.budget_min) / 100).toLocaleString('en-IN')}`
: '₹0',
cost: 25,
status: 'open' as const,
match: '80% match',
contactCount: Number(item.contact_count ?? 0),
maxContacts: Number(item.max_contacts ?? 10),
})));
});
createEffect(() => {
const d = leadRequestsResource();
if (!d) return;
const items: any[] = Array.isArray(d.items) ? d.items : Array.isArray(d) ? d : [];
if (!items.length) return;
const statusMap: Record<string, any> = {
PENDING: 'request_sent', CONTACT_UNLOCKED: 'contact_unlocked',
REJECTED: 'rejected', CANCELLED: 'cancelled_by_professional', EXPIRED: 'expired_refunded',
};
setLeadRequestRows(items.map((item: any) => ({
id: String(item.id ?? ''),
title: String(item.title ?? item.requirement_title ?? 'Lead Request'),
city: String(item.location ?? item.city ?? 'India'),
requestDate: item.created_at
? new Date(item.created_at).toLocaleDateString('en-US', { month: 'short', day: '2-digit', year: 'numeric' })
: '--',
status: statusMap[String(item.status ?? '')] ?? 'request_sent',
decisionDate: item.decision_date
? new Date(item.decision_date).toLocaleDateString('en-US', { month: 'short', day: '2-digit', year: 'numeric' })
: '--',
})));
});
createEffect(() => {
const d = requirementsResource();
if (!d) return;
const items: any[] = Array.isArray(d.items) ? d.items : Array.isArray(d) ? d : [];
if (!items.length) return;
const statusMap: Record<string, string> = {
PENDING: 'under review', APPROVED: 'approved', ACTIVE: 'active',
REJECTED: 'closed', CLOSED: 'closed',
};
setRequirementRows(items.map((item: any) => ({
id: String(item.id ?? ''),
title: String(item.title ?? item.description ?? 'Requirement'),
summary: String(item.description ?? ''),
category: String(item.category ?? item.profession_key ?? ''),
amount: item.budget_min != null
? `${Math.round(item.budget_min / 100).toLocaleString('en-IN')}`
: '₹0',
budget: item.budget_min != null
? `${Math.round(item.budget_min / 100).toLocaleString('en-IN')} - ₹${Math.round((item.budget_max ?? item.budget_min) / 100).toLocaleString('en-IN')}`
: '₹0',
location: String(item.location ?? 'India'),
submission: item.created_at
? new Date(item.created_at).toLocaleDateString('en-US', { month: 'short', day: '2-digit', year: 'numeric' })
: '--',
status: statusMap[String(item.status ?? '')] ?? 'under review',
responses: Number(item.response_count ?? 0),
responseTag: `Active (${Number(item.response_count ?? 0)} Responses)`,
})));
});
createEffect(() => {
const d = jobsResource();
if (!d) return;
const items: any[] = Array.isArray(d.items) ? d.items : Array.isArray(d) ? d : [];
if (!items.length) return;
setJobBoardJobs(items.map((item: any) => ({
id: String(item.id ?? ''),
title: String(item.title ?? 'Position'),
company: String(item.company_name ?? item.company ?? 'Company'),
location: String(item.location ?? 'India'),
salary: item.salary_min != null
? `${Math.round(item.salary_min / 100).toLocaleString('en-IN')}+`
: 'Negotiable',
exp: String(item.experience_required ?? item.experience ?? '0-2 yrs'),
type: String(item.employment_type ?? item.type ?? 'Full-Time'),
tags: Array.isArray(item.tags) ? item.tags : [],
match: '',
posted: item.created_at
? new Date(item.created_at).toLocaleDateString('en-US', { month: 'short', day: 'numeric' })
: '--',
})));
});
createEffect(() => {
const d = profileResource();
if (!d) return;
const parts = String(d.display_name || d.full_name || d.business_name || '').split(' ');
const map: Record<string, string> = {};
map['First Name'] = d.first_name || parts[0] || '';
map['Last Name'] = d.last_name || parts.slice(1).join(' ') || '';
map['Business Name'] = d.business_name || d.display_name || '';
map['Contact Person Name'] = d.contact_person || d.display_name || '';
map['Company Name'] = d.company_name || d.display_name || '';
map['Email Address'] = d.email || '';
map['Mobile Number'] = d.phone || d.mobile || '';
if (d.location) map['City'] = d.location;
if (d.area) map['Area'] = d.area;
if (d.state) map['State'] = d.state;
if (d.pin_code) map['PIN Code'] = d.pin_code;
if (d.bio) map['Bio'] = d.bio;
if (d.status) setProfileApprovalState(
d.status === 'APPROVED' ? 'APPROVED'
: d.status === 'REJECTED' ? 'REJECTED'
: d.status === 'PENDING' ? 'IN_REVIEW'
: 'DRAFT',
);
setProfileFormData((prev) => ({ ...prev, ...map }));
});
createEffect(() => {
const d = userRolesResource();
if (Array.isArray(d)) setUserRoles(d);
else if (d && Array.isArray(d.data)) setUserRoles(d.data);
});
createEffect(() => {
const d = servicesResource();
if (!d) return;
const items = Array.isArray(d.items) ? d.items : Array.isArray(d) ? d : [];
if (items.length > 0) {
setPortfolioServices(items.map((it: any) => ({
id: it.id,
name: it.name || '',
model: it.model || 'Flat',
price: it.price || '',
details: it.details || ''
})));
}
});
createEffect(() => {
const d = portfolioResource();
if (!d) return;
if (Array.isArray(d.photos)) setPortfolioPhotos(d.photos);
if (Array.isArray(d.experience)) setPortfolioExperiences(d.experience);
if (Array.isArray(d.specialties)) setPortfolioSpecialties(d.specialties);
if (Array.isArray(d.languages)) setPortfolioLanguages(d.languages);
if (Array.isArray(d.service_areas)) setPortfolioServiceAreas(d.service_areas);
});
// ─── End live API integration ─────────────────────────────────────────────
const registerRole = async (roleKey: string) => {
const res = await apiPost('/api/users/roles/register', { role_key: roleKey.toUpperCase() });
if (res && (res.ok || res.status === 200)) {
window.location.reload();
} else {
alert(`Failed to register as ${roleKey}. Please check if you are already registered.`);
}
};
const switchRole = async (roleKey: string) => {
const res = await apiPost('/api/users/roles/switch', { role: roleKey.toUpperCase() });
if (res && (res.ok || res.status === 200)) {
window.location.reload();
} else {
alert(`Failed to switch to ${roleKey}.`);
}
};
const submitProfileForApproval = async () => {
if (!hasLive() || !livePrefix()) return;
try {
const res = await fetch(`${GW}/api/${livePrefix()}/profile/submit`, {
method: 'POST',
credentials: 'include',
headers: { 'Content-Type': 'application/json' },
});
if (res.ok) {
setProfileApprovalState('SUBMITTED');
setTimeout(() => setProfileApprovalState('IN_REVIEW'), 250);
}
} catch {
setProfileApprovalState('SUBMITTED');
}
};
const submitPortfolioForApproval = () => {
setPortfolioApprovalState('SUBMITTED');
setTimeout(() => setPortfolioApprovalState('IN_REVIEW'), 250);
};
createEffect(() => {
const roleKey = normalizeRoleKey(props.roleKey || '');
const spec = portfolioSpecForRole(roleKey);
setPortfolioSpecialties(spec.specialties.slice(0, 6));
setPortfolioLanguages(['English', 'Hindi', 'Tamil']);
setPortfolioServiceAreas(['T. Nagar', 'Adyar', 'Velachery', 'Anna Nagar']);
setPortfolioSpecialtyInput('');
setPortfolioLanguageInput('');
setPortfolioAreaInput('');
setPortfolioFormValues({});
setPortfolioFormErrors({});
setPortfolioValidationNotice('');
setPortfolioServices([{ name: 'Core Service', model: 'Flat', price: '₹15,000', details: 'Includes planning and delivery' }]);
setPortfolioServiceDraft({ name: '', model: 'Flat', price: '', details: '' });
setPortfolioExperiences([{ year: '2022', title: 'Started professional practice', details: 'Handled 40+ projects successfully' }]);
setPortfolioExperienceDraft({ year: '', title: '', details: '' });
setPortfolioToolInput('');
setPortfolioDesignTools(['Figma', 'Adobe Photoshop', 'Illustrator']);
setPortfolioPhotos(['sample-1.jpg', 'sample-2.jpg']);
});
const addPortfolioTag = (
value: string,
setter: (next: (prev: string[]) => string[]) => void,
max = 6,
) => {
const normalized = String(value || '').trim();
if (!normalized) return false;
let added = false;
setter((prev) => {
if (prev.some((item) => item.toLowerCase() === normalized.toLowerCase()) || prev.length >= max) return prev;
added = true;
return [...prev, normalized];
});
return added;
};
const removePortfolioTag = (
value: string,
setter: (next: (prev: string[]) => string[]) => void,
) => setter((prev) => prev.filter((item) => item !== value));
const selectedTicket = createMemo(() => HELP_TICKET_ROWS.find((row) => row.id === activeTicketId()) || HELP_TICKET_ROWS[0]);
const selectedTicketDetails = createMemo(() => HELP_TICKET_DETAILS[selectedTicket().id] || HELP_TICKET_DETAILS['TCK-1042']);
const leadCostPerContact = 25;
const leadsPerPage = 3;
const requestedPerPage = 5;
const lockedLeadCredits = createMemo(() => leadCards().filter((card) => card.status === 'requested').length * leadCostPerContact);
const usableLeadCredits = createMemo(() => Math.max(0, leadCredits() - lockedLeadCredits()));
const filteredLeadCards = createMemo(() => {
const query = leadSearch().trim().toLowerCase();
const area = leadAreaFilter();
const budget = leadBudgetFilter();
const date = leadDateFilter();
const list = leadCards().filter((lead) => {
if (lead.status !== 'open') return false;
const matchesQuery = !query
|| lead.title.toLowerCase().includes(query)
|| lead.category.toLowerCase().includes(query)
|| lead.location.toLowerCase().includes(query)
|| String(lead.area || '').toLowerCase().includes(query);
if (!matchesQuery) return false;
if (area !== 'All Areas' && String(lead.area || '') !== area) return false;
if (date === 'Within 7 Days') {
const diff = (Date.parse(lead.dateRequired) - Date.now()) / (1000 * 60 * 60 * 24);
if (!(diff >= 0 && diff <= 7)) return false;
}
if (date === 'This Month') {
const value = new Date(lead.dateRequired);
const now = new Date();
if (!(value.getMonth() === now.getMonth() && value.getFullYear() === now.getFullYear())) return false;
}
if (budget === 'Under ₹1L' && lead.budgetValue >= 100000) return false;
if (budget === '₹1L - ₹2L' && !(lead.budgetValue >= 100000 && lead.budgetValue <= 200000)) return false;
if (budget === 'Above ₹2L' && lead.budgetValue <= 200000) return false;
return true;
});
const sorted = [...list];
if (leadSortFilter() === 'Budget High-Low') sorted.sort((a, b) => b.budgetValue - a.budgetValue);
if (leadSortFilter() === 'Budget Low-High') sorted.sort((a, b) => a.budgetValue - b.budgetValue);
if (leadSortFilter() === 'Newest First') sorted.sort((a, b) => Date.parse(b.dateRequired) - Date.parse(a.dateRequired));
return sorted;
});
const totalLeadPages = createMemo(() => Math.max(1, Math.ceil(filteredLeadCards().length / leadsPerPage)));
const pagedLeadCards = createMemo(() => {
const start = (leadPage() - 1) * leadsPerPage;
return filteredLeadCards().slice(start, start + leadsPerPage);
});
const filteredRequestedRows = createMemo(() => {
const query = requestedSearch().trim().toLowerCase();
const status = requestedStatusFilter();
const sorted = leadRequestRows().filter((row) => {
if (status !== 'All Status' && status !== titleCase(row.status.replace(/_/g, ' '))) return false;
if (!query) return true;
return row.id.toLowerCase().includes(query) || row.title.toLowerCase().includes(query) || row.city.toLowerCase().includes(query);
});
if (requestedSortFilter() === 'Oldest First') {
sorted.sort((a, b) => Date.parse(a.requestDate) - Date.parse(b.requestDate));
} else {
sorted.sort((a, b) => Date.parse(b.requestDate) - Date.parse(a.requestDate));
}
return sorted;
});
const totalRequestedPages = createMemo(() => Math.max(1, Math.ceil(filteredRequestedRows().length / requestedPerPage)));
const pagedRequestedRows = createMemo(() => {
const start = (requestedPage() - 1) * requestedPerPage;
return filteredRequestedRows().slice(start, start + requestedPerPage);
});
const ticketSummary = createMemo(() => {
const openCount = HELP_TICKET_ROWS.filter((row) => row.status !== 'Resolved').length;
const resolvedCount = HELP_TICKET_ROWS.length - openCount;
return { openCount, resolvedCount, total: HELP_TICKET_ROWS.length };
});
const openTicketDetails = (ticketId: string) => {
setHelpCenterTab('my_tickets');
setActiveTicketId(ticketId);
setMyTicketsTab('view_ticket');
setTicketMessage('');
setViewTicketFiles([]);
};
createEffect(() => {
const key = customerKey();
if (key !== lastSidebarKey()) {
setLastSidebarKey(key);
if (key === 'credits') props.onTabSelect('overview');
}
});
createEffect(() => {
setDashboardWidgetOrder(previewWidgets());
});
createEffect(() => {
if (customerKey() !== 'help center' && customerKey() !== 'support') setHelpCenterTab('help_center');
});
createEffect(() => {
if (customerKey() !== 'my requirements') {
setRequirementsView('list');
setRequirementsStep(1);
}
});
createEffect(() => {
if (customerKey() !== 'leads') {
setLeadMarketplaceTab('All Leads');
setLeadSearch('');
setLeadAreaFilter('All Areas');
setLeadBudgetFilter('All Budgets');
setLeadDateFilter('Any Date');
setLeadSortFilter('Newest First');
setLeadFiltersOpen(false);
setLeadSortOpen(false);
setLeadPage(1);
setActiveLeadDetailId('');
setLeadContactConfirmId('');
setActiveResponseLeadId('');
setResponsesDetailMode(false);
setRequestedSearch('');
setRequestedStatusFilter('All Status');
setRequestedSortFilter('Newest First');
setRequestedSortOpen(false);
setRequestedFilterOpen(false);
setRequestedPage(1);
}
});
createEffect(() => {
void leadSearch();
void leadAreaFilter();
void leadBudgetFilter();
void leadDateFilter();
void leadSortFilter();
setLeadPage(1);
});
createEffect(() => {
if (leadPage() > totalLeadPages()) setLeadPage(totalLeadPages());
});
createEffect(() => {
void requestedSearch();
void requestedStatusFilter();
void requestedSortFilter();
setRequestedPage(1);
});
createEffect(() => {
if (requestedPage() > totalRequestedPages()) setRequestedPage(totalRequestedPages());
});
createEffect(() => {
if (customerKey() !== 'jobs') {
setJobPostView('form');
setJobPostStep(1);
setJobSeekerScreen('list');
setJobSeekerSelectedId(jobBoardJobs()[0]?.id || '');
setJobSeekerApplyStep(2);
setLastJobSeekerTabKey('');
}
});
createEffect(() => {
if (customerKey() !== 'my portfolio') setPortfolioEditMode(false);
if (customerKey() !== 'my portfolio') setPortfolioTopTab('my_portfolio');
});
const openPortfolioPreviewInline = () => {
setPortfolioTopTab('preview');
props.onSidebarSelect('My Portfolio');
props.onTabSelect('about');
};
createEffect(() => {
if (customerKey() !== 'jobs' || !isJobSeekerRole()) return;
const tabKey = normalizeTabKey(resolvedTabKey());
if (tabKey === lastJobSeekerTabKey()) return;
setLastJobSeekerTabKey(tabKey);
setJobSeekerScreen('list');
setJobSeekerSelectedId(jobBoardJobs()[0]?.id || '');
});
createEffect(() => {
const rows = jobBoardJobs();
const active = jobSeekerSelectedId();
if (!rows.length) return;
if (!rows.some((job) => job.id === active)) {
setJobSeekerSelectedId(rows[0].id);
}
});
createEffect(() => {
if (helpCenterTab() !== 'my_tickets') {
setMyTicketsTab('all_tickets');
setActiveTicketId('TCK-1042');
setViewTicketFiles([]);
}
});
createEffect(() => {
if (helpCenterTab() !== 'create_ticket') setCreateTicketFiles([]);
});
const moveDashboardWidget = (movingKey: string, targetKey: string) => {
if (!movingKey || !targetKey || movingKey === targetKey) return;
setDashboardWidgetOrder((prev) => {
const from = prev.indexOf(movingKey);
const to = prev.indexOf(targetKey);
if (from === -1 || to === -1) return prev;
const next = [...prev];
next.splice(from, 1);
next.splice(to, 0, movingKey);
return next;
});
};
const statusChip = (value: string) => {
const key = value.toLowerCase();
if (key === 'active' || key === 'new') return { bg: '#FFF1EB', c: '#FF5E13' };
if (key === 'closed' || key === 'contacted') return { bg: '#ECFDF3', c: '#059669' };
if (key === 'shortlisted') return { bg: '#EEF2FF', c: '#4338CA' };
if (key === 'draft') return { bg: '#FEF3C7', c: '#B45309' };
return { bg: '#F3F4F6', c: '#6B7280' };
};
const requestLeadContact = (leadId: string) => {
if (usableLeadCredits() < leadCostPerContact) return;
if (hasLive()) {
apiPost(`/api/${livePrefix()}/leads/request`, { requirement_id: leadId })
.then(() => refetchLeadRequestsLive());
}
let changed = false;
setLeadCards((prev) => prev.map((card) => {
if (card.id !== leadId || card.status !== 'open' || card.contactCount >= card.maxContacts) return card;
changed = true;
const nextCount = Math.min(card.maxContacts, card.contactCount + 1);
return {
...card,
contactCount: nextCount,
status: (nextCount >= card.maxContacts ? 'closed' : 'requested') as LeadCardStatus,
};
}));
if (!changed) return;
setLeadRequestRows((prev) => {
const idx = prev.findIndex((row) => row.id === leadId);
if (idx >= 0) {
const next = [...prev];
next[idx] = { ...next[idx], status: 'request_sent', decisionDate: '--' };
return next;
}
const selected = leadCards().find((card) => card.id === leadId);
return [
{
id: leadId,
title: selected?.title || 'Lead Request',
city: selected ? `${selected.area || 'Central'}, ${selected.location}` : 'Chennai, India',
requestDate: 'Apr 02, 2026',
status: 'request_sent' as const,
decisionDate: '--',
},
...prev,
];
});
};
const leadDetailsSpec = (lead: { category: string }) => {
const c = lead.category.toLowerCase();
if (c.includes('photography') || c.includes('fashion')) {
return { timeframe: '3-5 shoot days + 7 days edit', scope: 'Pre-shoot planning, full day coverage, edited album delivery', highlights: ['Shot list alignment with customer brief', 'Venue and lighting plan finalization', 'Edited photos + social cuts'] };
}
if (c.includes('design') || c.includes('branding')) {
return { timeframe: '7-14 working days', scope: 'Brand exploration, visual concepts, revision rounds, handoff', highlights: ['Moodboard and style direction', 'Logo/system deliverables', 'Source files + usage formats'] };
}
if (c.includes('video')) {
return { timeframe: '5-10 working days', scope: 'Editing, color correction, sound polish, export variants', highlights: ['Storyboard-based edits', 'Platform-specific outputs', 'Two revision rounds'] };
}
return { timeframe: '5-10 working days', scope: 'Requirement discovery, execution plan, and final delivery', highlights: ['Clear milestone tracking', 'Weekly progress updates', 'Final quality checklist'] };
};
const openLeadDetailsInNewTab = (leadId: string) => {
setActiveLeadDetailId(leadId);
setLeadMarketplaceTab('View Details');
};
const openResponseLeadDetails = (leadId: string) => {
setActiveResponseLeadId(leadId);
setResponsesDetailMode(true);
};
const openLeadContactConfirm = (leadId: string) => setLeadContactConfirmId(leadId);
const confirmLeadContactRequest = () => {
const leadId = leadContactConfirmId();
if (!leadId) return;
requestLeadContact(leadId);
setLeadContactConfirmId('');
};
const approveLeadContact = (leadId: string) => {
setLeadCards((prev) => prev.map((card) => card.id === leadId && card.status === 'requested'
? { ...card, status: 'unlocked' as const }
: card));
setLeadCredits((prev) => Math.max(0, prev - leadCostPerContact));
setLeadRequestRows((prev) => prev.map((row) => row.id === leadId
? { ...row, status: 'contact_unlocked' as const, decisionDate: 'Apr 03, 2026' }
: row));
};
const cancelLeadRequest = (leadId: string) => {
if (hasLive()) {
apiDelete(`/api/${livePrefix()}/leads/requests/${leadId}`)
.then(() => refetchLeadRequestsLive());
}
setLeadCards((prev) => prev.map((card) => {
if (card.id !== leadId) return card;
if (card.status !== 'requested' && card.status !== 'closed') return card;
return { ...card, contactCount: Math.max(0, card.contactCount - 1), status: 'open' as LeadCardStatus };
}));
setLeadCredits((prev) => Math.max(0, prev - leadCostPerContact));
setLeadRequestRows((prev) => prev.map((row) => row.id === leadId
? { ...row, status: 'cancelled_by_professional' as const, decisionDate: 'Apr 03, 2026' }
: row));
};
const refundPendingLead = (leadId: string) => {
setLeadCards((prev) => prev.map((card) => {
if (card.id !== leadId) return card;
if (card.status !== 'requested' && card.status !== 'closed') return card;
return { ...card, contactCount: Math.max(0, card.contactCount - 1), status: 'open' as LeadCardStatus };
}));
setLeadRequestRows((prev) => prev.map((row) => row.id === leadId
? { ...row, status: 'expired_refunded' as const, decisionDate: 'Auto-refunded' }
: row));
};
const leadMatchPercent = (lead: { match: string }) => {
const value = Number.parseInt(String(lead.match || '').replace(/[^0-9]/g, ''), 10);
return Number.isFinite(value) ? value : 70;
};
const leadProbability = (lead: { match: string; urgency: string; contactCount: number; maxContacts: number }) => {
const urgencyBoost = lead.urgency.includes('High') ? 8 : lead.urgency.includes('Medium') ? 4 : 0;
const slotFactor = ((lead.maxContacts - lead.contactCount) / Math.max(1, lead.maxContacts)) * 40;
const score = Math.round(0.5 * leadMatchPercent(lead) + slotFactor + urgencyBoost);
return Math.min(95, Math.max(5, score));
};
const leadProbabilityColor = (score: number) => {
if (score >= 75) return '#16A34A';
if (score >= 50) return '#F59E0B';
return '#DC2626';
};
const leadProbabilityLabel = (score: number) => {
if (score >= 75) return 'High';
if (score >= 50) return 'Medium';
return 'Low';
};
const leadGaugeNeedlePoint = (score: number) => {
const clamped = Math.max(0, Math.min(100, score));
const angleDeg = 180 - (clamped * 180) / 100;
const angleRad = (angleDeg * Math.PI) / 180;
const radius = 34;
return {
x: 60 + Math.cos(angleRad) * radius,
y: 60 - Math.sin(angleRad) * radius,
};
};
const leadGaugeDash = (score: number) => {
const clamped = Math.max(0, Math.min(100, score));
const arcLength = Math.PI * 50;
return `${((clamped / 100) * arcLength).toFixed(1)} ${arcLength.toFixed(1)}`;
};
const renderPortfolioContent = () => {
const spec = portfolioSpecForRole(props.roleKey || '');
const mediaConfig = portfolioMediaConfig(props.roleKey || '');
const submissionTabs = spec.tabs.filter((item) => normalizeTabKey(item) !== normalizeTabKey('testimonials'));
const selectedPortfolioTab = submissionTabs.find((item) => normalizeTabKey(item) === normalizeTabKey(resolvedTabKey())) || submissionTabs[0] || 'about';
const selectedPortfolioTabKey = normalizeTabKey(selectedPortfolioTab);
const serviceTabKey = normalizeTabKey(spec.serviceTabLabel);
const galleryTabKey = normalizeTabKey(spec.galleryTabLabel);
const experienceTabKey = normalizeTabKey(spec.experienceTabLabel);
const testimonialsTabKey = normalizeTabKey('testimonials');
const faqsTabKey = normalizeTabKey('faqs');
const portfolioJobsCompleted = portfolioJobsCompletedPreview;
const portfolioFeedbackCount = portfolioFeedbackCountPreview;
const testimonialsUnlocked = portfolioTestimonialsUnlocked();
const isPreviewMode = portfolioTopTab() === 'preview';
const canEdit = !isPreviewMode;
const portfolioStepKeys = submissionTabs.map((item) => normalizeTabKey(item));
const activePortfolioStepIndex = Math.max(0, portfolioStepKeys.findIndex((key) => key === selectedPortfolioTabKey));
const goToPortfolioStep = (index: number) => {
const bounded = Math.max(0, Math.min(portfolioStepKeys.length - 1, index));
props.onTabSelect(submissionTabs[bounded] || submissionTabs[0] || 'about');
};
const portfolioTools = (() => {
const role = normalizeRoleKey(props.roleKey || '');
if (role === 'GRAPHIC_DESIGNER') return ['Figma', 'Adobe Photoshop', 'Illustrator', 'InDesign', 'After Effects'];
if (role === 'SOCIAL_MEDIA_MANAGER') return ['Meta Business Suite', 'Canva', 'Buffer', 'Hootsuite', 'Google Analytics'];
if (role === 'DEVELOPER') return ['React', 'TypeScript', 'Node.js', 'PostgreSQL', 'Docker'];
if (role === 'VIDEO_EDITOR') return ['Premiere Pro', 'After Effects', 'DaVinci Resolve', 'CapCut', 'Audition'];
if (role === 'PHOTOGRAPHER') return ['Lightroom', 'Photoshop', 'Capture One', 'Bridge', 'Snapseed'];
return ['Domain Tool 1', 'Domain Tool 2', 'Domain Tool 3'];
})();
const portfolioFormFieldsByTab: Record<string, string[]> = {
about: ['Professional Headline', 'About You', 'Area', 'Place', 'Travel Preference', 'Response Time'],
[serviceTabKey]: ['Primary Service', 'Pricing Model', 'Starting Price', 'Delivery Timeline', 'Includes', 'Additional Notes'],
[galleryTabKey]: ['Portfolio Item Title', 'Portfolio Link', 'Category', 'Project Summary', 'Outcome', 'Asset Upload'],
[experienceTabKey]: ['Years of Experience', 'Top Tools', 'Milestone 1', 'Milestone 2', 'Certifications', 'Working Style'],
[testimonialsTabKey]: ['Client Name', 'Client Feedback', 'Rating', 'Project Type', 'Client Location', 'Consent'],
[faqsTabKey]: ['Question 1', 'Answer 1', 'Question 2', 'Answer 2', 'Question 3', 'Answer 3'],
};
const selectedPortfolioFormFields = portfolioFormFieldsByTab[selectedPortfolioTabKey] || portfolioFormFieldsByTab.about;
const setPortfolioFieldValue = (field: string, value: string) => {
const fieldKey = `${selectedPortfolioTabKey}::${field}`;
setPortfolioFormValues((prev) => ({ ...prev, [fieldKey]: value }));
setPortfolioFormErrors((prev) => ({ ...prev, [fieldKey]: '' }));
setPortfolioValidationNotice('');
};
const renderPortfolioFormField = (field: string) => {
const key = String(field || '').toLowerCase();
const isSelect = /model|category|style|rating|consent|response time|travel|type|timeline/i.test(key);
const isLong = /about|summary|notes|answer|feedback|includes/i.test(key);
const fieldKey = `${selectedPortfolioTabKey}::${field}`;
const fieldValue = portfolioFormValues()[fieldKey] || '';
const fieldError = portfolioFormErrors()[fieldKey] || '';
const placeholder = (() => {
if (key.includes('area')) return 'Enter area in Chennai';
if (key.includes('place')) return 'Enter place in Chennai';
if (key.includes('price')) return 'Enter amount';
if (key.includes('link')) return 'Paste URL';
if (key.includes('upload')) return 'Upload file (PDF/JPG/PNG)';
return `${isSelect ? 'Select' : 'Enter'} ${field.toLowerCase()}`;
})();
return (
<div style="display:flex;flex-direction:column;gap:6px">
<p style="margin:0;font-size:11px;font-weight:700;color:#6B7280;letter-spacing:0.01em;text-transform:none">{field}</p>
<Show
when={isLong}
fallback={
<div style="position:relative">
<input type="text" value={fieldValue} onInput={(e) => setPortfolioFieldValue(field, e.currentTarget.value)} placeholder={placeholder} style={`width:100%;height:36px;border:1px solid ${fieldError ? '#FFD8C2' : '#E5E7EB'};border-radius:8px;background:white;padding:0 30px 0 10px;font-size:12px;color:#111827;outline:none`} />
<Show when={isSelect}>
<span style="position:absolute;right:10px;top:50%;transform:translateY(-50%);color:#9CA3AF;font-size:12px"></span>
</Show>
</div>
}
>
<textarea value={fieldValue} onInput={(e) => setPortfolioFieldValue(field, e.currentTarget.value)} placeholder={placeholder} style={`width:100%;min-height:74px;border:1px solid ${fieldError ? '#FFD8C2' : '#E5E7EB'};border-radius:8px;background:white;padding:8px 10px;font-size:12px;color:#111827;outline:none;resize:vertical`} />
</Show>
<Show when={!!fieldError}>
<p style="margin:0;font-size:11px;color:#C2410C">{fieldError}</p>
</Show>
</div>
);
};
const validatePortfolioStep = (stepKey: string) => {
const nextErrors: Record<string, string> = {};
const stepFields = portfolioFormFieldsByTab[stepKey] || [];
stepFields.forEach((field) => {
const fKey = `${stepKey}::${field}`;
const value = (portfolioFormValues()[fKey] || '').trim();
const optional = /optional/i.test(field);
const isUpload = /asset upload/i.test(field);
if (!optional && !isUpload && !value) nextErrors[fKey] = 'This field is required';
});
if (stepKey === serviceTabKey && portfolioServices().length < 1) {
setPortfolioValidationNotice('Add at least one service with pricing.');
} else if (stepKey === galleryTabKey && (portfolioPhotos().length < 1 || portfolioPhotos().length > 6)) {
setPortfolioValidationNotice('Add 1 to 6 portfolio photos.');
} else if (stepKey === experienceTabKey && portfolioExperiences().length < 1) {
setPortfolioValidationNotice('Add at least one experience entry.');
} else if (Object.keys(nextErrors).length) {
setPortfolioValidationNotice('Please fill all required fields in this tab.');
} else {
setPortfolioValidationNotice('');
}
setPortfolioFormErrors((prev) => ({ ...prev, ...nextErrors }));
return Object.keys(nextErrors).length === 0
&& !(stepKey === serviceTabKey && portfolioServices().length < 1)
&& !(stepKey === galleryTabKey && (portfolioPhotos().length < 1 || portfolioPhotos().length > 6))
&& !(stepKey === experienceTabKey && portfolioExperiences().length < 1);
};
const addServiceDraft = async () => {
const draft = portfolioServiceDraft();
if (!draft.name.trim() || !draft.price.trim()) {
setPortfolioValidationNotice('Add service name and price to continue.');
return;
}
if (hasLive()) {
try {
const res = await apiPost(`/api/${livePrefix()}/services`, {
name: draft.name.trim(),
model: draft.model.trim() || 'Flat',
price: draft.price.trim(),
details: draft.details.trim(),
});
if (res && (res.ok || res.status === 200)) {
refetchServicesLive();
setPortfolioServiceDraft({ name: '', model: 'Flat', price: '', details: '' });
setPortfolioValidationNotice('');
} else {
setPortfolioValidationNotice('Failed to save service to backend.');
}
} catch (err) {
console.error(err);
setPortfolioValidationNotice('Network error while saving service.');
}
} else {
setPortfolioServices((prev) => [...prev, {
name: draft.name.trim(),
model: draft.model.trim() || 'Flat',
price: draft.price.trim(),
details: draft.details.trim(),
}]);
setPortfolioServiceDraft({ name: '', model: 'Flat', price: '', details: '' });
setPortfolioValidationNotice('');
}
};
const removeServiceItem = async (index: number) => {
const target = portfolioServices()[index];
if (hasLive() && target && (target as any).id) {
try {
const res = await apiDelete(`/api/${livePrefix()}/services/${(target as any).id}`);
if (res && (res.ok || res.status === 200)) {
refetchServicesLive();
}
} catch (err) {
console.error('Failed to delete service:', err);
}
} else {
setPortfolioServices((prev) => prev.filter((_, i) => i !== index));
}
};
const addExperienceDraft = async () => {
const draft = portfolioExperienceDraft();
if (!draft.year.trim() || !draft.title.trim()) {
setPortfolioValidationNotice('Add year and title for each experience entry.');
return;
}
const nextExp = [...portfolioExperiences(), {
year: draft.year.trim(),
title: draft.title.trim(),
details: draft.details.trim(),
}];
if (hasLive()) {
try {
const res = await apiPost(`/api/${livePrefix()}/portfolio/experience`, nextExp);
if (res && (res.ok || res.status === 200)) {
refetchPortfolioLive();
setPortfolioExperienceDraft({ year: '', title: '', details: '' });
setPortfolioValidationNotice('');
}
} catch (err) {
console.error(err);
}
} else {
setPortfolioExperiences(nextExp);
setPortfolioExperienceDraft({ year: '', title: '', details: '' });
setPortfolioValidationNotice('');
}
};
const removeExperienceItem = async (index: number) => {
const nextExp = portfolioExperiences().filter((_, i) => i !== index);
if (hasLive()) {
try {
const res = await apiPost(`/api/${livePrefix()}/portfolio/experience`, nextExp);
if (res && (res.ok || res.status === 200)) refetchPortfolioLive();
} catch (err) {
console.error(err);
}
} else {
setPortfolioExperiences(nextExp);
}
};
const addPhotoItem = async () => {
const current = portfolioPhotos();
if (current.length >= 6) {
setPortfolioValidationNotice('Portfolio is limited to 6 photos.');
return;
}
const nextLabel = `portfolio-${current.length + 1}.jpg`;
const nextPhotos = [...current, nextLabel];
if (hasLive()) {
try {
const res = await apiPost(`/api/${livePrefix()}/portfolio/photos`, nextPhotos);
if (res && (res.ok || res.status === 200)) refetchPortfolioLive();
} catch (err) {
console.error(err);
}
} else {
setPortfolioPhotos(nextPhotos);
}
setPortfolioValidationNotice('');
};
const removePhotoItem = async (index: number) => {
const nextPhotos = portfolioPhotos().filter((_, i) => i !== index);
if (hasLive()) {
try {
const res = await apiPost(`/api/${livePrefix()}/portfolio/photos`, nextPhotos);
if (res && (res.ok || res.status === 200)) refetchPortfolioLive();
} catch (err) {
console.error(err);
}
} else {
setPortfolioPhotos(nextPhotos);
}
};
const showSection = (tabKey: string) => (isPreviewMode ? portfolioStepKeys.includes(tabKey) : selectedPortfolioTabKey === tabKey);
return (
<div style="display:flex;flex-direction:column;gap:14px">
<div style="border-radius:12px;border:1px solid #E5E7EB;background:white;padding:0 12px;box-shadow:0 1px 3px rgba(0,0,0,0.04)">
<div style="display:flex;align-items:center;gap:20px;border-bottom:1px solid #E5E7EB;padding-top:10px">
<button
type="button"
onClick={() => setPortfolioTopTab('my_portfolio')}
style={`padding:0 0 10px;font-size:13px;font-weight:500;background:none;border:none;white-space:nowrap;cursor:pointer;color:${portfolioTopTab() === 'my_portfolio' ? '#FF5E13' : '#6B7280'};border-bottom:${portfolioTopTab() === 'my_portfolio' ? '2px solid #FF5E13' : '2px solid transparent'};margin-bottom:-1px`}
>
My Portfolio
</button>
<button
type="button"
onClick={() => setPortfolioTopTab('preview')}
style={`padding:0 0 10px;font-size:13px;font-weight:500;background:none;border:none;white-space:nowrap;cursor:pointer;color:${portfolioTopTab() === 'preview' ? '#FF5E13' : '#6B7280'};border-bottom:${portfolioTopTab() === 'preview' ? '2px solid #FF5E13' : '2px solid transparent'};margin-bottom:-1px`}
>
Preview
</button>
</div>
</div>
{/* Profile Header */}
<div style="border-radius:14px;border:1px solid #E5E7EB;background:white;box-shadow:0 1px 3px rgba(0,0,0,0.06);overflow:hidden">
<div style="height:3px;background:linear-gradient(90deg,#FF5E13 0%,#FF8A4C 55%,#FFD0B5 100%)" />
<Show when={isPreviewMode}>
<div style="padding:16px 20px;border-bottom:1px solid #E5E7EB;display:flex;align-items:flex-start;justify-content:space-between;gap:12px">
<div style="display:grid;gap:8px">
<div style="display:flex;align-items:center;gap:8px;flex-wrap:wrap">
<p style="margin:0;font-size:18px;font-weight:800;color:#111827">Alex Morgan</p>
<span style="font-size:11px;font-weight:700;color:#374151;background:#F3F4F6;border:1px solid #E5E7EB;border-radius:999px;padding:2px 9px">Verified</span>
<span style="font-size:11px;font-weight:700;color:#C2410C;background:#FFF1EB;border:1px solid #FFD8C2;border-radius:999px;padding:2px 9px">Top Response Rate</span>
</div>
<p style="margin:0;font-size:13px;color:#4B5563">{spec.roleLabel}</p>
<div style="display:flex;align-items:center;gap:6px;flex-wrap:wrap">
<span style="height:24px;padding:0 10px;border-radius:999px;background:#FFF1EB;color:#C2410C;font-size:11px;font-weight:700;display:inline-flex;align-items:center">Area: South Chennai</span>
<span style="height:24px;padding:0 10px;border-radius:999px;background:#FFF1EB;color:#C2410C;font-size:11px;font-weight:700;display:inline-flex;align-items:center">Place: T. Nagar, Chennai</span>
<span style="height:24px;padding:0 10px;border-radius:999px;border:1px solid #E5E7EB;background:#F9FAFB;color:#374151;font-size:11px;font-weight:700;display:inline-flex;align-items:center">Travel: T. Nagar, Adyar, Velachery, Anna Nagar</span>
</div>
</div>
<div style="display:flex;gap:8px;flex-shrink:0;align-self:center" />
</div>
</Show>
<div style="padding:0 20px;border-bottom:1px solid #E5E7EB;background:#FFFFFF">
<div style="display:flex;align-items:center;gap:20px;overflow-x:auto;padding-top:10px">
<For each={[...submissionTabs, 'preview']}>
{(item) => {
const itemKey = normalizeTabKey(item);
const isPreviewStep = itemKey === 'preview';
const isLockedTestimonialsTab = itemKey === 'testimonials' && !testimonialsUnlocked;
const isActive = isPreviewStep ? isPreviewMode : (!isPreviewMode && selectedPortfolioTabKey === itemKey);
return (
<button
type="button"
disabled={isLockedTestimonialsTab}
onClick={() => {
if (isLockedTestimonialsTab) return;
if (isPreviewStep) {
setPortfolioTopTab('preview');
return;
}
setPortfolioTopTab('my_portfolio');
props.onTabSelect(item);
}}
title={isLockedTestimonialsTab ? 'Unlock after 3 completed jobs and 2 customer feedback entries' : (isPreviewStep ? 'Final preview before submit' : '')}
style={`padding:0 0 10px;font-size:13px;font-weight:500;background:none;border:none;white-space:nowrap;cursor:${isLockedTestimonialsTab ? 'not-allowed' : 'pointer'};opacity:${isLockedTestimonialsTab ? 0.5 : 1};color:${isActive ? '#FF5E13' : '#6B7280'};border-bottom:${isActive ? '2px solid #FF5E13' : '2px solid transparent'};margin-bottom:-1px;flex-shrink:0`}
>
{titleCase(item)}{isLockedTestimonialsTab ? ' • Locked' : ''}
</button>
);
}}
</For>
</div>
</div>
<Show when={isPreviewMode}>
<div style="padding:14px 16px;display:grid;grid-template-columns:repeat(4,minmax(0,1fr));gap:10px">
{spec.statsLabels.slice(0, 4).map((label, i) => (
<div style="border:1px solid #E5E7EB;border-radius:12px;background:#FAFBFD;padding:12px 12px">
<p style="margin:0;font-size:10px;font-weight:700;text-transform:uppercase;letter-spacing:0.05em;color:#9CA3AF">{label}</p>
<p style="margin:5px 0 0;font-size:14px;font-weight:800;color:#111827">{i===0?'248':i===1?'7+':i===2?'Verified':i===3?'2 days':'Yes'}</p>
</div>
))}
</div>
</Show>
</div>
<Show when={!isPreviewMode}>
<div style="display:grid;grid-template-columns:2fr 1fr;gap:10px">
<div style="border:1px solid #E5E7EB;background:white;border-radius:16px;padding:14px;box-shadow:0 1px 4px rgba(0,0,0,0.06)">
<div style="display:flex;justify-content:space-between;align-items:center;gap:10px">
<p style="margin:0;font-size:13px;font-weight:700;color:#111827">{titleCase(selectedPortfolioTab)}</p>
<span style="height:22px;padding:0 8px;border-radius:999px;border:1px solid #DDEBFF;background:#EEF4FF;display:inline-flex;align-items:center;font-size:10px;font-weight:700;color:#03004E">
{selectedPortfolioFormFields.length} fields
</span>
</div>
<Show when={!!portfolioValidationNotice()}>
<div style="margin-top:10px;border:1px solid #E5E7EB;background:#F9FAFB;border-radius:8px;padding:8px 10px;font-size:12px;color:#374151">
{portfolioValidationNotice()}
</div>
</Show>
<Show when={selectedPortfolioTabKey === serviceTabKey}>
<div style="display:grid;gap:10px;margin-top:10px">
<div style="display:grid;grid-template-columns:repeat(2,minmax(0,1fr));gap:10px">
<input type="text" value={portfolioServiceDraft().name} onInput={(e) => setPortfolioServiceDraft((prev) => ({ ...prev, name: e.currentTarget.value }))} placeholder="Service name" style="height:36px;border:1px solid #E5E7EB;border-radius:8px;background:white;padding:0 10px;font-size:12px;color:#111827;outline:none" />
<input type="text" value={portfolioServiceDraft().price} onInput={(e) => setPortfolioServiceDraft((prev) => ({ ...prev, price: e.currentTarget.value }))} placeholder="Starting price" style="height:36px;border:1px solid #E5E7EB;border-radius:8px;background:white;padding:0 10px;font-size:12px;color:#111827;outline:none" />
<input type="text" value={portfolioServiceDraft().model} onInput={(e) => setPortfolioServiceDraft((prev) => ({ ...prev, model: e.currentTarget.value }))} placeholder="Pricing model (Flat/Hourly)" style="height:36px;border:1px solid #E5E7EB;border-radius:8px;background:white;padding:0 10px;font-size:12px;color:#111827;outline:none" />
<input type="text" value={portfolioServiceDraft().details} onInput={(e) => setPortfolioServiceDraft((prev) => ({ ...prev, details: e.currentTarget.value }))} placeholder="Delivery timeline or details" style="height:36px;border:1px solid #E5E7EB;border-radius:8px;background:white;padding:0 10px;font-size:12px;color:#111827;outline:none" />
</div>
<div style="display:flex;justify-content:flex-start">
<button type="button" onClick={addServiceDraft} style="height:32px;border-radius:8px;border:1px solid #E5E7EB;background:white;padding:0 12px;font-size:12px;font-weight:700;color:#374151;cursor:pointer">Add Service</button>
</div>
<div style="display:grid;gap:8px">
<For each={portfolioServices()}>{(service, idx) => (
<div style="border:1px solid #E5E7EB;border-radius:10px;background:#FAFBFD;padding:10px;display:grid;grid-template-columns:1fr auto;gap:8px;align-items:start">
<div style="display:grid;gap:4px">
<p style="margin:0;font-size:12px;font-weight:700;color:#111827">{service.name}</p>
<p style="margin:0;font-size:11px;color:#6B7280">{service.model} {service.price}</p>
<p style="margin:0;font-size:11px;color:#374151">{service.details || 'No additional notes'}</p>
</div>
<button type="button" onClick={() => removeServiceItem(idx())} style="height:28px;border-radius:8px;border:1px solid #E5E7EB;background:white;padding:0 10px;font-size:11px;font-weight:700;color:#374151;cursor:pointer">Remove</button>
</div>
)}</For>
</div>
</div>
</Show>
<Show when={selectedPortfolioTabKey === galleryTabKey}>
<div style="display:grid;gap:10px;margin-top:10px">
<div style="display:flex;align-items:center;justify-content:space-between;gap:8px">
<p style="margin:0;font-size:12px;color:#374151">Portfolio photos ({portfolioPhotos().length}/6)</p>
<button type="button" onClick={addPhotoItem} disabled={portfolioPhotos().length >= 6} style={`height:32px;border-radius:8px;border:1px solid #E5E7EB;background:white;padding:0 12px;font-size:12px;font-weight:700;color:#374151;cursor:${portfolioPhotos().length >= 6 ? 'not-allowed' : 'pointer'};opacity:${portfolioPhotos().length >= 6 ? 0.6 : 1}`}>Add Photo</button>
</div>
<div style="display:grid;grid-template-columns:repeat(2,minmax(0,1fr));gap:8px">
<For each={portfolioPhotos()}>{(photo, idx) => (
<div style="border:1px solid #E5E7EB;border-radius:10px;background:#FAFBFD;padding:10px;display:flex;align-items:center;justify-content:space-between;gap:8px">
<span style="font-size:12px;font-weight:600;color:#374151">{photo}</span>
<button type="button" onClick={() => removePhotoItem(idx())} style="height:26px;border-radius:8px;border:1px solid #E5E7EB;background:white;padding:0 8px;font-size:11px;font-weight:700;color:#374151;cursor:pointer">Remove</button>
</div>
)}</For>
</div>
</div>
</Show>
<Show when={selectedPortfolioTabKey === experienceTabKey}>
<div style="display:grid;gap:10px;margin-top:10px">
<div style="display:grid;grid-template-columns:110px 1fr;gap:10px">
<input type="text" value={portfolioExperienceDraft().year} onInput={(e) => setPortfolioExperienceDraft((prev) => ({ ...prev, year: e.currentTarget.value }))} placeholder="Year" style="height:36px;border:1px solid #E5E7EB;border-radius:8px;background:white;padding:0 10px;font-size:12px;color:#111827;outline:none" />
<input type="text" value={portfolioExperienceDraft().title} onInput={(e) => setPortfolioExperienceDraft((prev) => ({ ...prev, title: e.currentTarget.value }))} placeholder="Role or milestone title" style="height:36px;border:1px solid #E5E7EB;border-radius:8px;background:white;padding:0 10px;font-size:12px;color:#111827;outline:none" />
</div>
<textarea value={portfolioExperienceDraft().details} onInput={(e) => setPortfolioExperienceDraft((prev) => ({ ...prev, details: e.currentTarget.value }))} placeholder="Brief details" style="min-height:72px;border:1px solid #E5E7EB;border-radius:8px;background:white;padding:8px 10px;font-size:12px;color:#111827;outline:none;resize:vertical" />
<div style="display:flex;justify-content:flex-start">
<button type="button" onClick={addExperienceDraft} style="height:32px;border-radius:8px;border:1px solid #E5E7EB;background:white;padding:0 12px;font-size:12px;font-weight:700;color:#374151;cursor:pointer">Add Experience</button>
</div>
<div style="border:1px solid #E5E7EB;border-radius:10px;background:#FAFBFD;padding:10px;display:grid;gap:8px">
<p style="margin:0;font-size:11px;font-weight:700;color:#6B7280;letter-spacing:0.01em;text-transform:none">Design Tools</p>
<div style="display:flex;flex-wrap:wrap;gap:6px">
<For each={portfolioDesignTools()}>
{(tool) => (
<span style="height:24px;padding:0 8px;border-radius:6px;border:1px solid #E5E7EB;background:white;font-size:11px;font-weight:600;color:#374151;display:inline-flex;align-items:center;gap:6px">
{tool}
<button type="button" onClick={() => removePortfolioTag(tool, setPortfolioDesignTools)} style="border:none;background:none;color:#6B7280;cursor:pointer;padding:0;font-size:11px;line-height:1">x</button>
</span>
)}
</For>
</div>
<div style="display:grid;grid-template-columns:1fr auto;gap:8px;align-items:center">
<input type="text" value={portfolioToolInput()} onInput={(e) => setPortfolioToolInput(e.currentTarget.value)} placeholder="Add tools (e.g. Figma, Photoshop, Illustrator)" style="height:34px;border:1px solid #E5E7EB;border-radius:8px;background:white;padding:0 10px;font-size:12px;color:#374151;outline:none" />
<button
type="button"
onClick={() => {
const added = addPortfolioTag(portfolioToolInput(), setPortfolioDesignTools, 10);
if (added) setPortfolioToolInput('');
}}
style="height:32px;border-radius:8px;border:1px solid #E5E7EB;background:white;padding:0 12px;font-size:12px;font-weight:700;color:#374151;cursor:pointer"
>
Add Tool
</button>
</div>
</div>
<div style="display:grid;gap:8px">
<For each={portfolioExperiences()}>{(entry, idx) => (
<div style="border:1px solid #E5E7EB;border-radius:10px;background:#FAFBFD;padding:10px;display:grid;grid-template-columns:1fr auto;gap:8px;align-items:start">
<div style="display:grid;gap:4px">
<p style="margin:0;font-size:12px;font-weight:700;color:#111827">{entry.year} - {entry.title}</p>
<p style="margin:0;font-size:11px;color:#374151">{entry.details || 'No additional notes'}</p>
</div>
<button type="button" onClick={() => removeExperienceItem(idx())} style="height:28px;border-radius:8px;border:1px solid #E5E7EB;background:white;padding:0 10px;font-size:11px;font-weight:700;color:#374151;cursor:pointer">Remove</button>
</div>
)}</For>
</div>
</div>
</Show>
<Show when={selectedPortfolioTabKey !== serviceTabKey && selectedPortfolioTabKey !== galleryTabKey && selectedPortfolioTabKey !== experienceTabKey}>
<div style="display:grid;grid-template-columns:repeat(2,minmax(0,1fr));gap:10px;margin-top:10px">
<For each={selectedPortfolioFormFields}>{(field) => renderPortfolioFormField(field)}</For>
</div>
</Show>
</div>
<div style="border:1px solid #E5E7EB;background:white;border-radius:16px;padding:14px;box-shadow:0 1px 4px rgba(0,0,0,0.06)">
<p style="margin:0;font-size:11px;letter-spacing:0.04em;text-transform:uppercase;color:#6B7280">Portfolio Form</p>
<p style="margin:8px 0 0;font-size:18px;font-weight:800;color:#111827">Simple step-by-step input</p>
<p style="margin:8px 0 0;font-size:12px;color:#6B7280;line-height:1.5">Fill one tab at a time. Use Next to move through sections, then check final Preview before submitting.</p>
<Show when={!!portfolioValidationNotice()}>
<div style="margin-top:10px;border:1px solid #E5E7EB;background:#F9FAFB;border-radius:8px;padding:8px 10px;font-size:12px;color:#374151">
{portfolioValidationNotice()}
</div>
</Show>
<div style="display:grid;gap:6px;margin-top:10px">
<For each={submissionTabs}>{(tabName, idx) => (
<div style={`height:28px;border-radius:8px;border:1px solid #E5E7EB;background:${idx() === activePortfolioStepIndex ? '#FFF8F4' : '#F9FAFB'};padding:0 10px;display:flex;align-items:center;font-size:11px;font-weight:700;color:${idx() === activePortfolioStepIndex ? '#C2410C' : '#374151'}`}>
{idx() + 1}. {titleCase(tabName)}
</div>
)}</For>
</div>
</div>
</div>
</Show>
<Show when={isPreviewMode}>
<Show when={showSection(normalizeTabKey('about'))}>
{/* About + Specialties */}
<div style="display:grid;grid-template-columns:2fr 1fr;gap:10px">
<div style="border-radius:14px;border:1px solid #E5E7EB;background:white;box-shadow:0 1px 3px rgba(0,0,0,0.05);overflow:hidden">
<div style="padding:12px 16px;border-bottom:1px solid #E5E7EB">
<p style="margin:0;font-size:13px;font-weight:700;color:#111827;display:flex;align-items:center;gap:6px"><UserCircle2 size={14} style="color:#FF5E13" /> About</p>
</div>
<div style="padding:14px 16px">
<Show
when={canEdit}
fallback={<p style="margin:0;font-size:13px;color:#374151;line-height:1.6">Professional {spec.roleLabel.toLowerCase()} with 7+ years of experience delivering high-quality work across India. Committed to excellence, creativity, and client satisfaction on every project.</p>}
>
<textarea
value={`Professional ${spec.roleLabel.toLowerCase()} with 7+ years of experience delivering high-quality work across India. Committed to excellence, creativity, and client satisfaction on every project.`}
style="width:100%;min-height:84px;border:1px solid #FFD8C2;border-radius:10px;background:#FFFCFA;padding:10px;font-size:13px;color:#374151;line-height:1.6;resize:vertical"
/>
</Show>
<div style="margin-top:10px;display:grid;grid-template-columns:repeat(3,minmax(0,1fr));gap:8px">
{['Fast response within 2 hours', 'Clear scope & milestone planning', 'Delivery-first execution'].map((line) => (
<div style="border:1px solid #F3F4F6;border-radius:8px;background:#FAFAFA;padding:8px;font-size:11px;font-weight:600;color:#374151">{line}</div>
))}
</div>
</div>
<div style="display:flex;border-top:1px solid #F3F4F6">
{[['7+','Years Exp'],['248','Projects'],['4.9','Rating'],['128','Reviews']].map(([val,lbl], i) => (
<div style={`flex:1;padding:12px 16px;${i<3?'border-right:1px solid #F3F4F6':''}`}>
<p style="margin:0;font-size:11px;font-weight:600;text-transform:uppercase;letter-spacing:0.05em;color:#9CA3AF">{lbl}</p>
<p style="margin:4px 0 0;font-size:14px;font-weight:700;color:#111827">{val}</p>
</div>
))}
</div>
</div>
<div style="border-radius:14px;border:1px solid #E5E7EB;background:white;box-shadow:0 1px 3px rgba(0,0,0,0.05);overflow:hidden">
<div style="padding:12px 16px;border-bottom:1px solid #E5E7EB">
<p style="margin:0;font-size:13px;font-weight:700;color:#111827;display:flex;align-items:center;gap:6px"><Rocket size={14} style="color:#FF5E13" /> Specialties</p>
</div>
<div style="padding:14px 16px;display:flex;flex-wrap:wrap;gap:6px">
<For each={portfolioSpecialties()}>
{(s) => (
<span style="height:24px;padding:0 8px;border-radius:6px;border:1px solid #E5E7EB;background:#F9FAFB;font-size:11px;font-weight:600;color:#374151;display:inline-flex;align-items:center;gap:6px">
{s}
<Show when={canEdit}>
<button
type="button"
onClick={() => removePortfolioTag(s, setPortfolioSpecialties)}
style="border:none;background:none;color:#6B7280;cursor:pointer;padding:0;font-size:11px;line-height:1"
aria-label={`Remove ${s}`}
>
x
</button>
</Show>
</span>
)}
</For>
</div>
<Show when={canEdit}>
<div style="padding:0 16px 12px;display:flex;gap:8px;align-items:center">
<input
type="text"
value={portfolioSpecialtyInput()}
onInput={(e) => setPortfolioSpecialtyInput(e.currentTarget.value)}
placeholder="Add specialty"
style="flex:1;height:32px;border:1px solid #E5E7EB;border-radius:8px;background:white;padding:0 10px;font-size:12px;color:#374151;outline:none"
/>
<button
type="button"
onClick={() => {
const added = addPortfolioTag(portfolioSpecialtyInput(), setPortfolioSpecialties, 6);
if (added) setPortfolioSpecialtyInput('');
}}
style="height:32px;border-radius:8px;border:1px solid #E5E7EB;background:white;padding:0 10px;font-size:11px;font-weight:700;color:#374151;cursor:pointer"
>
Add
</button>
</div>
</Show>
<div style="padding:0 16px 14px;border-top:1px solid #F3F4F6;margin-top:4px">
<p style="margin:10px 0 6px;font-size:11px;font-weight:600;text-transform:uppercase;letter-spacing:0.05em;color:#9CA3AF">Languages</p>
<div style="display:flex;gap:5px;flex-wrap:wrap">
<For each={portfolioLanguages()}>
{(l) => (
<span style="height:22px;padding:0 8px;border-radius:6px;border:1px solid #E5E7EB;background:#F9FAFB;font-size:11px;font-weight:600;color:#374151;display:inline-flex;align-items:center;gap:6px">
{l}
<Show when={canEdit}>
<button
type="button"
onClick={() => removePortfolioTag(l, setPortfolioLanguages)}
style="border:none;background:none;color:#6B7280;cursor:pointer;padding:0;font-size:11px;line-height:1"
aria-label={`Remove ${l}`}
>
x
</button>
</Show>
</span>
)}
</For>
</div>
<Show when={canEdit}>
<div style="margin-top:6px;display:flex;gap:8px;align-items:center">
<input
type="text"
value={portfolioLanguageInput()}
onInput={(e) => setPortfolioLanguageInput(e.currentTarget.value)}
placeholder="Add language"
style="flex:1;height:30px;border:1px solid #E5E7EB;border-radius:8px;background:white;padding:0 10px;font-size:12px;color:#374151;outline:none"
/>
<button
type="button"
onClick={() => {
const added = addPortfolioTag(portfolioLanguageInput(), setPortfolioLanguages, 6);
if (added) setPortfolioLanguageInput('');
}}
style="height:30px;border-radius:8px;border:1px solid #E5E7EB;background:white;padding:0 10px;font-size:11px;font-weight:700;color:#374151;cursor:pointer"
>
Add
</button>
</div>
</Show>
<p style="margin:10px 0 6px;font-size:11px;font-weight:600;text-transform:uppercase;letter-spacing:0.05em;color:#9CA3AF">Service Areas</p>
<div style="display:flex;flex-wrap:wrap;gap:5px">
<For each={portfolioServiceAreas()}>
{(c) => (
<span style="height:22px;padding:0 8px;border-radius:6px;border:1px solid #E5E7EB;background:#F9FAFB;font-size:11px;font-weight:600;color:#374151;display:inline-flex;align-items:center;gap:6px">
{c}
<Show when={canEdit}>
<button
type="button"
onClick={() => removePortfolioTag(c, setPortfolioServiceAreas)}
style="border:none;background:none;color:#6B7280;cursor:pointer;padding:0;font-size:11px;line-height:1"
aria-label={`Remove ${c}`}
>
x
</button>
</Show>
</span>
)}
</For>
</div>
<Show when={canEdit}>
<div style="margin-top:6px;display:flex;gap:8px;align-items:center">
<input
type="text"
value={portfolioAreaInput()}
onInput={(e) => setPortfolioAreaInput(e.currentTarget.value)}
placeholder="Add service area in Chennai"
style="flex:1;height:30px;border:1px solid #E5E7EB;border-radius:8px;background:white;padding:0 10px;font-size:12px;color:#374151;outline:none"
/>
<button
type="button"
onClick={() => {
const added = addPortfolioTag(portfolioAreaInput(), setPortfolioServiceAreas, 8);
if (added) setPortfolioAreaInput('');
}}
style="height:30px;border-radius:8px;border:1px solid #E5E7EB;background:white;padding:0 10px;font-size:11px;font-weight:700;color:#374151;cursor:pointer"
>
Add
</button>
</div>
</Show>
</div>
</div>
</div>
</Show>
<Show when={showSection(serviceTabKey)}>
{/* Packages */}
<div style="border-radius:12px;border:1px solid #E5E7EB;background:linear-gradient(180deg,#FFFFFF 0%,#FCFCFD 100%);box-shadow:0 1px 3px rgba(0,0,0,0.06);overflow:hidden">
<div style="padding:12px 16px;border-bottom:1px solid #E5E7EB;display:flex;justify-content:space-between;align-items:center">
<p style="margin:0;font-size:13px;font-weight:700;color:#111827;display:flex;align-items:center;gap:6px"><Coins size={14} style="color:#FF5E13" /> {titleCase(spec.serviceTabLabel)}</p>
<span style="height:22px;padding:0 8px;border-radius:999px;background:#FFF1EB;border:1px solid #FFD8C2;color:#C2410C;font-size:10px;font-weight:800;display:inline-flex;align-items:center">Transparent Pricing</span>
</div>
<Show when={canEdit}>
<div style="padding:10px 12px;border-bottom:1px solid #E5E7EB;background:#FFFFFF;display:grid;gap:8px">
<div style="display:grid;grid-template-columns:1.2fr 0.9fr 1fr 1.3fr auto;gap:8px;padding:8px;border:1px solid #E5E7EB;border-radius:10px;background:#F9FAFB">
<p style="margin:0;font-size:11px;font-weight:700;color:#6B7280;text-transform:uppercase">Service</p>
<p style="margin:0;font-size:11px;font-weight:700;color:#6B7280;text-transform:uppercase">Pricing Type</p>
<p style="margin:0;font-size:11px;font-weight:700;color:#6B7280;text-transform:uppercase">Amount</p>
<p style="margin:0;font-size:11px;font-weight:700;color:#6B7280;text-transform:uppercase">Details</p>
<p style="margin:0;font-size:11px;font-weight:700;color:#6B7280;text-transform:uppercase;text-align:center">Action</p>
</div>
{[1, 2].map((row) => (
<div style="display:grid;grid-template-columns:1.2fr 0.9fr 1fr 1.3fr auto;gap:8px;align-items:center;padding:8px;border:1px solid #E5E7EB;border-radius:10px;background:white">
<input type="text" value={row === 1 ? 'Wedding Coverage' : 'Pre-wedding Shoot'} style="height:34px;border:1px solid #E5E7EB;border-radius:8px;background:white;padding:0 10px;font-size:12px;color:#374151" />
<select style="height:34px;border:1px solid #E5E7EB;border-radius:8px;background:white;padding:0 10px;font-size:12px;color:#374151">
<option>Flat</option>
<option>Hourly</option>
<option>Per Day</option>
</select>
<input type="text" value={row === 1 ? '₹45,000' : '₹18,000'} style="height:34px;border:1px solid #E5E7EB;border-radius:8px;background:white;padding:0 10px;font-size:12px;color:#374151" />
<input type="text" value={row === 1 ? '8 hours, 300 photos' : '4 hours, 120 photos'} style="height:34px;border:1px solid #E5E7EB;border-radius:8px;background:white;padding:0 10px;font-size:12px;color:#374151" />
<button type="button" style="height:32px;border-radius:8px;border:1px solid #E5E7EB;background:white;padding:0 10px;font-size:11px;font-weight:700;color:#374151">Remove</button>
</div>
))}
<div style="display:flex;justify-content:flex-start">
<button type="button" style="height:32px;border-radius:8px;border:1px solid #E5E7EB;background:white;padding:0 12px;font-size:12px;font-weight:700;color:#374151">+ Add Service</button>
</div>
</div>
</Show>
<div style="display:grid;grid-template-columns:repeat(3,minmax(0,1fr));gap:10px;padding:12px">
{spec.packages.map((pkg, i) => (
<div style={`border:1px solid ${i === 1 ? '#FFD8C2' : '#E5E7EB'};background:${i === 1 ? '#FFF8F4' : '#FFFFFF'};border-radius:10px;padding:12px`}>
<Show when={i === 1}>
<span style="height:20px;padding:0 8px;border-radius:999px;background:#FF5E13;color:white;font-size:10px;font-weight:800;display:inline-flex;align-items:center">Most Chosen</span>
</Show>
<p style="margin:0;font-size:11px;font-weight:600;text-transform:uppercase;letter-spacing:0.05em;color:#9CA3AF">{pkg.name}</p>
<p style="margin:4px 0 0;font-size:16px;font-weight:700;color:#111827">{pkg.price}</p>
<div style="margin-top:8px;display:grid;gap:4px">
{pkg.items.map((item) => (
<div style="display:flex;align-items:center;gap:6px;font-size:12px;color:#374151">
<CheckCircle2 size={11} style="color:#9CA3AF;flex-shrink:0" /> {item}
</div>
))}
</div>
</div>
))}
</div>
</div>
</Show>
<Show when={showSection(galleryTabKey)}>
{/* Work Gallery */}
<div style="border-radius:12px;border:1px solid #E5E7EB;background:linear-gradient(180deg,#FFFFFF 0%,#FCFCFD 100%);box-shadow:0 1px 3px rgba(0,0,0,0.06);overflow:hidden">
<div style="padding:12px 16px;border-bottom:1px solid #E5E7EB;display:flex;align-items:center;justify-content:space-between">
<p style="margin:0;font-size:13px;font-weight:700;color:#111827;display:flex;align-items:center;gap:6px"><Image size={14} style="color:#FF5E13" /> {titleCase(spec.galleryTabLabel)}</p>
<button type="button" disabled={!canEdit} style={`height:30px;border-radius:8px;border:none;background:#0D0D2A;color:white;padding:0 12px;font-size:11px;font-weight:600;cursor:${canEdit ? 'pointer' : 'not-allowed'};opacity:${canEdit ? 1 : 0.6}`}>{mediaConfig.ctaLabel}</button>
</div>
<Show when={canEdit}>
<div style="padding:10px 12px;border-bottom:1px solid #E5E7EB;background:#FFF8F4">
<div style="height:34px;border:1px dashed #FFD8C2;border-radius:8px;background:#FFFCFA;display:flex;align-items:center;justify-content:center;font-size:12px;color:#9A3412;font-weight:600">
Drag files here or click upload to add portfolio samples (max 6 photos)
</div>
</div>
</Show>
<Show
when={mediaConfig.mode === 'visual'}
fallback={
<div style="padding:14px 16px;display:grid;grid-template-columns:repeat(2,minmax(0,1fr));gap:8px">
<For each={mediaConfig.items}>
{(item, idx) => (
<div style="min-height:84px;border-radius:10px;border:1px solid #E5E7EB;background:#F9FAFB;padding:10px;display:flex;gap:8px;align-items:flex-start">
<FileText size={16} style="color:#9CA3AF;flex-shrink:0;margin-top:2px" />
<div>
<p style="margin:0;font-size:12px;font-weight:700;color:#111827">{item}</p>
<p style="margin:4px 0 0;font-size:11px;color:#6B7280">Entry #{idx() + 1} • Structured proof without mandatory photos</p>
</div>
</div>
)}
</For>
</div>
}
>
<div style="padding:14px 16px;display:grid;grid-template-columns:repeat(3,1fr);gap:8px">
{[0,1,2,3,4,5].map((i) => (
<div style="height:110px;border-radius:10px;border:1px solid #E5E7EB;background:linear-gradient(180deg,#FFFFFF 0%,#F8FAFC 100%);display:flex;flex-direction:column;align-items:center;justify-content:center;gap:6px">
<Image size={20} style="color:#C5CCD5" />
<span style="font-size:10px;color:#9CA3AF;text-align:center;padding:0 6px;line-height:1.35">{mediaConfig.items[i % mediaConfig.items.length]}</span>
</div>
))}
</div>
</Show>
</div>
</Show>
<Show when={showSection(experienceTabKey)}>
{/* Experience */}
<div style="border-radius:12px;border:1px solid #E5E7EB;background:linear-gradient(180deg,#FFFFFF 0%,#FCFCFD 100%);box-shadow:0 1px 3px rgba(0,0,0,0.06);overflow:hidden">
<div style="padding:12px 16px;border-bottom:1px solid #E5E7EB">
<p style="margin:0;font-size:13px;font-weight:700;color:#111827;display:flex;align-items:center;gap:6px"><BriefcaseBusiness size={14} style="color:#FF5E13" /> {titleCase(spec.experienceTabLabel)}</p>
</div>
<Show when={canEdit}>
<div style="padding:10px 12px;border-bottom:1px solid #E5E7EB;background:#FFF8F4;display:grid;grid-template-columns:120px 1fr;gap:8px">
<input type="text" value="Year" style="height:34px;border:1px solid #FFD8C2;border-radius:8px;background:#FFFCFA;padding:0 10px;font-size:12px;color:#374151" />
<input type="text" value="Milestone description" style="height:34px;border:1px solid #FFD8C2;border-radius:8px;background:#FFFCFA;padding:0 10px;font-size:12px;color:#374151" />
</div>
</Show>
<div style="padding:14px 16px">
<p style="margin:0 0 8px;font-size:11px;font-weight:600;text-transform:uppercase;letter-spacing:0.05em;color:#9CA3AF">{spec.equipmentLabel}</p>
<div style="display:flex;flex-wrap:wrap;gap:6px">
{spec.specialties.map((item) => (
<span style="height:24px;padding:0 8px;border-radius:6px;border:1px solid #E5E7EB;background:#F9FAFB;font-size:11px;font-weight:600;color:#374151;display:inline-flex;align-items:center">{item}</span>
))}
</div>
<div style="margin-top:12px">
<p style="margin:0 0 8px;font-size:11px;font-weight:600;text-transform:uppercase;letter-spacing:0.05em;color:#9CA3AF">Design Tools</p>
<Show
when={canEdit}
fallback={
<div style="display:flex;flex-wrap:wrap;gap:6px">
{(portfolioDesignTools().length ? portfolioDesignTools() : portfolioTools).map((tool) => (
<span style="height:24px;padding:0 8px;border-radius:6px;border:1px solid #E5E7EB;background:#F9FAFB;font-size:11px;font-weight:600;color:#374151;display:inline-flex;align-items:center">{tool}</span>
))}
</div>
}
>
<div style="display:grid;grid-template-columns:1fr auto;gap:8px;align-items:center">
<input type="text" placeholder="Add tools (e.g. Figma, Photoshop, Illustrator)" style="height:34px;border:1px solid #E5E7EB;border-radius:8px;background:white;padding:0 10px;font-size:12px;color:#374151" />
<button type="button" style="height:32px;border-radius:8px;border:1px solid #E5E7EB;background:white;padding:0 12px;font-size:12px;font-weight:700;color:#374151">+ Add Tool</button>
</div>
<div style="display:flex;flex-wrap:wrap;gap:6px;margin-top:8px">
{(portfolioDesignTools().length ? portfolioDesignTools() : portfolioTools).map((tool) => (
<span style="height:24px;padding:0 8px;border-radius:6px;border:1px solid #E5E7EB;background:#F9FAFB;font-size:11px;font-weight:600;color:#374151;display:inline-flex;align-items:center;gap:6px">
{tool}
<button type="button" style="border:none;background:none;color:#9CA3AF;cursor:pointer;font-size:11px;line-height:1">✕</button>
</span>
))}
</div>
</Show>
</div>
</div>
<div style="border-top:1px solid #F3F4F6">
{[['2018','Started professional career as '+spec.roleLabel],['2020','Completed 100+ successful projects'],['2022','Won regional industry recognition'],['2024','Joined Nxtgauge marketplace']].map(([yr, desc], i, arr) => (
<div style={`display:flex;gap:12px;align-items:flex-start;padding:10px 16px;${i<arr.length-1?'border-bottom:1px solid #F3F4F6':''}`}>
<span style="margin-top:2px;width:8px;height:8px;border-radius:999px;background:#FF5E13;flex-shrink:0" />
<p style="margin:0;font-size:11px;font-weight:700;color:#9CA3AF;min-width:32px">{yr}</p>
<p style="margin:0;font-size:13px;color:#374151;line-height:1.5">{desc}</p>
</div>
))}
</div>
</div>
</Show>
<Show when={showSection(testimonialsTabKey)}>
{/* Testimonials */}
<div style="border-radius:12px;border:1px solid #E5E7EB;background:linear-gradient(180deg,#FFFFFF 0%,#FCFCFD 100%);box-shadow:0 1px 3px rgba(0,0,0,0.06);overflow:hidden">
<div style="padding:12px 16px;border-bottom:1px solid #E5E7EB;display:flex;align-items:center;justify-content:space-between;gap:10px">
<p style="margin:0;font-size:13px;font-weight:700;color:#111827;display:flex;align-items:center;gap:6px"><Star size={14} style="color:#FF5E13" /> Testimonials</p>
<Show
when={testimonialsUnlocked}
fallback={<span style="font-size:11px;font-weight:700;color:#C2410C;background:#FFF1EB;border:1px solid #FFD8C2;border-radius:6px;padding:1px 7px">Locked For New Profiles</span>}
>
<span style="font-size:11px;font-weight:600;color:#374151;background:#F3F4F6;border:1px solid #E5E7EB;border-radius:6px;padding:1px 7px">4.9 · 128 reviews</span>
</Show>
</div>
<Show
when={testimonialsUnlocked}
fallback={
<div style="padding:16px">
<div style="border:1px dashed #FFD8C2;background:#FFF8F4;border-radius:10px;padding:14px">
<p style="margin:0;font-size:13px;font-weight:700;color:#111827">Testimonials will be activated automatically.</p>
<p style="margin:6px 0 0;font-size:12px;line-height:1.5;color:#6B7280">
New professionals unlock testimonials after completing at least <strong>3 jobs</strong> and receiving at least <strong>2 customer feedback entries</strong>.
</p>
<p style="margin:8px 0 0;font-size:11px;color:#9CA3AF">Current progress: {portfolioJobsCompleted} jobs completed • {portfolioFeedbackCount} feedback received</p>
</div>
</div>
}
>
<div style="display:grid;grid-template-columns:1fr 1fr;gap:10px;padding:10px">
{PORTFOLIO_TESTIMONIALS.map((t, i) => (
<div style="padding:14px 16px;border:1px solid #E5E7EB;border-radius:10px;background:#FFFFFF">
<div style="display:flex;justify-content:space-between;align-items:center">
<p style="margin:0;font-size:13px;font-weight:700;color:#111827">{t.name}</p>
<div style="display:flex;gap:1px">
{Array.from({length: t.rating}).map(() => <Star size={11} style="color:#F59E0B" fill="#F59E0B" />)}
</div>
</div>
<p style="margin:2px 0 0;font-size:11px;color:#9CA3AF">{t.category}</p>
<p style="margin:8px 0 0;font-size:12px;color:#374151;line-height:1.5">{t.text}</p>
</div>
))}
</div>
</Show>
</div>
</Show>
<Show when={showSection(faqsTabKey)}>
{/* FAQs */}
<div style="border-radius:12px;border:1px solid #E5E7EB;background:linear-gradient(180deg,#FFFFFF 0%,#FCFCFD 100%);box-shadow:0 1px 3px rgba(0,0,0,0.06);overflow:hidden">
<div style="padding:12px 16px;border-bottom:1px solid #E5E7EB">
<p style="margin:0;font-size:13px;font-weight:700;color:#111827;display:flex;align-items:center;gap:6px"><HelpCircle size={14} style="color:#FF5E13" /> Frequently Asked Questions</p>
</div>
<Show when={canEdit}>
<div style="padding:10px 12px;border-bottom:1px solid #E5E7EB;background:#FFF8F4;display:grid;gap:8px">
<input type="text" value="Add question" style="height:34px;border:1px solid #FFD8C2;border-radius:8px;background:#FFFCFA;padding:0 10px;font-size:12px;color:#374151" />
<textarea style="min-height:66px;border:1px solid #FFD8C2;border-radius:8px;background:#FFFCFA;padding:8px 10px;font-size:12px;color:#374151;resize:vertical">Add answer</textarea>
</div>
</Show>
<For each={spec.faqItems}>{(faq, i) => (
<div style={`border-bottom:${i() < spec.faqItems.length - 1 ? '1px solid #F3F4F6' : 'none'}`}>
<button
type="button"
onClick={() => setOpenFaqIndex(openFaqIndex()===i() ? null : i())}
style={`width:100%;display:flex;justify-content:space-between;align-items:center;padding:12px 16px;background:${openFaqIndex()===i() ? '#FFF8F4' : 'none'};border:none;cursor:pointer;text-align:left`}
>
<span style="font-size:13px;font-weight:700;color:#111827">{faq.q}</span>
{openFaqIndex()===i() ? <ChevronUp size={15} style="color:#374151;flex-shrink:0" /> : <ChevronDown size={15} style="color:#9CA3AF;flex-shrink:0" />}
</button>
<Show when={openFaqIndex()===i()}>
<div style="padding:0 16px 12px">
<p style="margin:0;font-size:13px;color:#374151;line-height:1.6">{faq.a}</p>
</div>
</Show>
</div>
)}</For>
</div>
</Show>
</Show>
<div style="border-radius:12px;border:1px solid #E5E7EB;background:white;padding:10px 12px;display:flex;align-items:center;justify-content:space-between;gap:10px">
<p style="margin:0;font-size:12px;color:#6B7280">
<Show when={!isPreviewMode} fallback={<span>Final review mode: check all tabs, then submit for approval.</span>}>
<span>Step {activePortfolioStepIndex + 1} of {portfolioStepKeys.length}</span>
</Show>
</p>
<div style="display:flex;align-items:center;gap:8px">
<button
type="button"
onClick={() => {
if (isPreviewMode) {
setPortfolioTopTab('my_portfolio');
goToPortfolioStep(portfolioStepKeys.length - 1);
return;
}
goToPortfolioStep(activePortfolioStepIndex - 1);
}}
disabled={!isPreviewMode && activePortfolioStepIndex === 0}
style={`height:32px;border-radius:8px;border:1px solid #E5E7EB;background:white;padding:0 12px;font-size:12px;font-weight:700;color:#374151;cursor:${(!isPreviewMode && activePortfolioStepIndex === 0) ? 'not-allowed' : 'pointer'};opacity:${(!isPreviewMode && activePortfolioStepIndex === 0) ? 0.5 : 1}`}
>
Previous
</button>
<Show
when={!isPreviewMode}
fallback={
<button type="button" onClick={() => setPortfolioTopTab('my_portfolio')} style="height:32px;border-radius:8px;border:1px solid #E5E7EB;background:white;padding:0 12px;font-size:12px;font-weight:700;color:#374151">
Back To Edit
</button>
}
>
<button
type="button"
onClick={() => {
const currentStepKey = portfolioStepKeys[activePortfolioStepIndex];
if (!validatePortfolioStep(currentStepKey)) return;
if (activePortfolioStepIndex >= portfolioStepKeys.length - 1) {
setPortfolioTopTab('preview');
return;
}
goToPortfolioStep(activePortfolioStepIndex + 1);
}}
style="height:32px;border-radius:8px;border:none;background:#03004E;color:white;padding:0 12px;font-size:12px;font-weight:700"
>
{activePortfolioStepIndex >= portfolioStepKeys.length - 1 ? 'Final Preview' : 'Next'}
</button>
</Show>
</div>
</div>
</div>
);
};
const renderCustomerContent = () => {
const tab = resolvedTabKey();
if (customerKey() === 'my dashboard') {
if (tab === 'recent requirements') {
return (
<div style="border:1px solid #E5E7EB;background:white;border-radius:16px;padding:14px;box-shadow:0 1px 4px rgba(0,0,0,0.06)">
<p style="margin:0;font-size:14px;font-weight:700;color:#111827">Recent Requirements</p>
<div style="margin-top:10px;display:grid;gap:8px">
<For each={requirementRows()}>{(row) => {
const chip = statusChip(row.status);
return (
<div style="display:grid;grid-template-columns:2fr 1fr 1fr 1fr;gap:8px;padding:8px;border:1px solid #E5E7EB;border-radius:8px;background:#F9FAFB">
<span style="font-size:12px;font-weight:700;color:#111827">{row.title}</span>
<span style="font-size:12px;color:#6B7280">{row.amount}</span>
<span style={`display:inline-flex;align-items:center;justify-content:center;height:22px;border-radius:999px;background:${chip.bg};color:${chip.c};font-size:11px;font-weight:700;text-transform:uppercase`}>{row.status}</span>
<span style="font-size:12px;color:#374151">{row.responses} responses</span>
</div>
);
}}</For>
</div>
</div>
);
}
if (tab === 'quick actions') {
return (
<div style="display:grid;grid-template-columns:1fr 1fr;gap:10px">
<div style="border:1px solid #E5E7EB;background:white;border-radius:16px;padding:14px;box-shadow:0 1px 4px rgba(0,0,0,0.06)">
<p style="margin:0;font-size:14px;font-weight:700;color:#111827">Quick Actions</p>
<div style="display:grid;gap:8px;margin-top:8px">
{['Post New Requirement', 'View Responses', 'Buy Credits', 'Explore Professionals'].map((a, i) => (
<button type="button" style={`height:34px;border-radius:8px;border:${i===0?'none':'1px solid #E5E7EB'};background:${i===0?'#FF5E13':'white'};color:${i===0?'white':'#374151'};font-size:12px;font-weight:700;cursor:pointer;text-align:left;padding:0 10px`}>
{a}
</button>
))}
</div>
</div>
<div style="border:1px solid #E5E7EB;background:white;border-radius:16px;padding:14px;box-shadow:0 1px 4px rgba(0,0,0,0.06)">
<p style="margin:0;font-size:14px;font-weight:700;color:#111827">Recent Activity</p>
<div style="margin-top:8px;display:grid;gap:8px">
{['Requirement posted', 'New response received', 'Credit purchase completed'].map((a) => <div style="padding:8px;border:1px solid #E5E7EB;border-radius:8px;background:#F9FAFB;font-size:12px;color:#374151">{a}</div>)}
</div>
</div>
</div>
);
}
return (
<div style="display:flex;flex-direction:column;gap:10px">
<Show when={!bothApprovalsApproved()}>
<div style="border:1px solid #E5E7EB;background:white;border-radius:12px;padding:12px;box-shadow:0 1px 4px rgba(0,0,0,0.04)">
<p style="margin:0;font-size:14px;font-weight:800;color:#111827">Complete Verification</p>
<p style="margin:6px 0 0;font-size:12px;color:#6B7280;line-height:1.5">
To start receiving opportunities, submit your profile and portfolio for admin approval.
</p>
<div style="display:grid;grid-template-columns:1fr 1fr;gap:8px;margin-top:10px">
<div style="border:1px solid #E5E7EB;border-radius:8px;background:#F9FAFB;padding:8px">
<p style="margin:0;font-size:11px;color:#6B7280">Step 1</p>
<p style="margin:4px 0 0;font-size:12px;font-weight:800;color:#111827">Profile: {approvalTone(profileApprovalState()).label}</p>
</div>
<div style="border:1px solid #E5E7EB;border-radius:8px;background:#F9FAFB;padding:8px">
<p style="margin:0;font-size:11px;color:#6B7280">Step 2</p>
<p style="margin:4px 0 0;font-size:12px;font-weight:800;color:#111827">Portfolio: {approvalTone(portfolioApprovalState()).label}</p>
</div>
</div>
<div style="display:flex;gap:8px;flex-wrap:wrap;margin-top:10px">
<button type="button" onClick={() => { props.onSidebarSelect('My Profile'); props.onTabSelect('basic information'); }} style="height:32px;border-radius:8px;border:1px solid #E5E7EB;background:white;padding:0 12px;font-size:12px;font-weight:700;color:#374151">Open My Profile</button>
<Show when={isProfessionalRole()}>
<button type="button" onClick={() => { props.onSidebarSelect('My Portfolio'); props.onTabSelect('about'); }} style="height:32px;border-radius:8px;border:1px solid #E5E7EB;background:white;padding:0 12px;font-size:12px;font-weight:700;color:#374151">Open My Portfolio</button>
</Show>
<button type="button" onClick={() => props.onSidebarSelect('Verification')} style="height:32px;border-radius:8px;border:none;background:#03004E;color:white;padding:0 12px;font-size:12px;font-weight:700">Open Verification</button>
</div>
</div>
</Show>
<div style="border:1px solid #E5E7EB;background:white;border-radius:12px;padding:10px 12px;box-shadow:0 1px 4px rgba(0,0,0,0.04)">
<p style="margin:0;font-size:11px;letter-spacing:0.04em;text-transform:uppercase;color:#6B7280">Widget Customization</p>
<p style="margin:4px 0 0;font-size:12px;color:#374151">Drag and drop cards below to reorder your dashboard widgets.</p>
</div>
<div style="display:grid;grid-template-columns:2fr 1fr 1fr;gap:10px">
<div style="border:1px solid #E5E7EB;background:white;border-radius:16px;padding:14px;box-shadow:0 1px 4px rgba(0,0,0,0.06)">
<p style="margin:0;font-size:32px;font-weight:800;color:#111827;line-height:1.1">Welcome back, {props.liveData?.userName ?? 'Alex'}</p>
<p style="margin:6px 0 0;font-size:13px;color:#6B7280">To start receiving opportunities, complete My Profile and My Portfolio, then submit both for approval.</p>
<div style="display:flex;gap:8px;flex-wrap:wrap;margin-top:10px">
<button type="button" onClick={() => { props.onSidebarSelect('My Profile'); props.onTabSelect('basic information'); }} style="height:30px;border-radius:8px;border:1px solid #E5E7EB;background:white;padding:0 10px;font-size:11px;font-weight:700;color:#374151">Fill My Profile</button>
<Show when={isProfessionalRole()}>
<button type="button" onClick={() => { props.onSidebarSelect('My Portfolio'); props.onTabSelect('about'); }} style="height:30px;border-radius:8px;border:1px solid #E5E7EB;background:white;padding:0 10px;font-size:11px;font-weight:700;color:#374151">Fill My Portfolio</button>
</Show>
<button type="button" onClick={() => props.onSidebarSelect('Verification')} style="height:30px;border-radius:8px;border:none;background:#03004E;color:white;padding:0 10px;font-size:11px;font-weight:700">Submit For Approval</button>
</div>
</div>
<div style="border:1px solid #E5E7EB;background:white;border-radius:16px;padding:14px;box-shadow:0 1px 4px rgba(0,0,0,0.06)">
<p style="margin:0;font-size:11px;letter-spacing:0.04em;color:#6B7280;text-transform:uppercase">Profile Status</p>
<p style="margin:8px 0 0;font-size:34px;font-weight:800;color:#111827">85%</p>
</div>
<div style="border:1px solid #E5E7EB;background:white;border-radius:16px;padding:14px;box-shadow:0 1px 4px rgba(0,0,0,0.06)">
<p style="margin:0;font-size:11px;letter-spacing:0.04em;text-transform:uppercase;color:#6B7280">Credits Balance</p>
<p style="margin:8px 0 0;font-size:34px;font-weight:800;line-height:1;color:#111827">{leadCredits().toLocaleString('en-IN')}</p>
</div>
</div>
<div style="display:grid;grid-template-columns:repeat(5,minmax(0,1fr));gap:10px">
<For each={dashboardWidgetOrder().slice(0, 5)}>
{(w) => (
<div
draggable
onDragStart={(e) => {
setDraggingDashboardWidget(w);
e.dataTransfer?.setData('text/plain', w);
}}
onDragOver={(e) => e.preventDefault()}
onDrop={(e) => {
e.preventDefault();
moveDashboardWidget(draggingDashboardWidget() || '', w);
setDraggingDashboardWidget(null);
}}
style={`border:1px solid #E5E7EB;background:${draggingDashboardWidget() === w ? '#FFF8F4' : 'white'};border-radius:12px;padding:10px;min-height:92px;box-shadow:0 1px 3px rgba(0,0,0,0.05);cursor:grab`}
>
<p style="margin:0;font-size:11px;letter-spacing:0.04em;text-transform:uppercase;color:#6B7280">{titleCase(w)}</p>
<p style="margin:10px 0 0;font-size:22px;font-weight:800;color:#111827">42</p>
</div>
)}
</For>
</div>
</div>
);
}
if (customerKey().includes('profile')) {
const spec = currentProfileSpec();
const selectedTab = spec.tabs.find((item) => normalizeTabKey(item) === normalizeTabKey(tab)) || spec.tabs[0] || 'basic info';
const selectedTabKey = normalizeTabKey(selectedTab);
const defaultFields = ['First Name', 'Last Name', 'Email Address', 'Mobile Number', 'City'];
const fieldsForTab = spec.tabFields[selectedTab] || spec.tabFields[spec.tabs[0] || ''] || defaultFields;
const isPreferencesTab = normalizeTabKey(selectedTab) === 'preferences';
const isDocumentsTab = selectedTabKey === 'documents';
const isRequiredField = (field: string) => !/\(optional\)|optional/i.test(field);
const requiredCount = fieldsForTab.filter((f) => isRequiredField(f)).length;
const profileState = () => approvalTone(profileApprovalState());
const renderField = (field: string) => {
const required = isRequiredField(field);
const isSelect = /country|mode|type|category|level|gender|industry|subjects|platforms|budget|response time/i.test(field);
const isSpecialitiesField = /specialit|specializ|skills/i.test(field);
const cleanLabel = String(field || '').replace(/\(optional\)/ig, '').trim();
const safeLabel = cleanLabel || 'value';
const key = safeLabel.toLowerCase();
const labelText = (() => {
if (key.includes('specialt') || key.includes('specialit') || key.includes('specializ')) return 'Specialities';
return safeLabel;
})();
const isCityField = key === 'city';
const placeholderText = (() => {
if (key.includes('gender')) return 'Select gender';
if (key.includes('address line 1')) return 'Enter address line 1';
if (key.includes('address line 2')) return 'Enter address line 2';
if (key === 'city') return 'Chennai';
if (key === 'state') return 'Enter state';
if (key === 'address') return 'Enter full address in Chennai';
if (key.includes('address proof')) return 'Upload address proof (PDF/JPG/PNG)';
if (key.includes('proof') || key.includes('certificate') || key.includes('document')) return `Upload ${labelText} (PDF/JPG/PNG)`;
if (key.includes('pin code') || key.includes('pincode')) return 'Enter 6-digit pincode';
if (key.includes('area')) return 'Enter area in Chennai';
if (key.includes('place')) return 'Enter place in Chennai';
if (isSpecialitiesField) return 'e.g. Wedding, Product, Portrait';
return `${isSelect ? 'Select' : 'Enter'} ${labelText.toLowerCase()}`;
})();
return (
<div style="display:flex;flex-direction:column;gap:6px">
<p style="margin:0;font-size:11px;font-weight:700;color:#6B7280;letter-spacing:0.01em;text-transform:none">
{labelText}
<Show when={required}>
<span style="color:#FF5E13;margin-left:4px">*</span>
</Show>
</p>
<div style="position:relative">
<input
type="text"
value={profileFormData()[field] ?? (isCityField ? 'Chennai' : '')}
readOnly={isCityField}
onInput={(e) => !isCityField && setProfileFormData((prev) => ({ ...prev, [field]: e.currentTarget.value }))}
placeholder={placeholderText}
style="width:100%;height:36px;border:1px solid #E5E7EB;border-radius:8px;background:white;padding:0 30px 0 10px;font-size:12px;color:#111827;outline:none"
/>
<Show when={isSelect}>
<span style="position:absolute;right:10px;top:50%;transform:translateY(-50%);color:#9CA3AF;font-size:12px">▾</span>
</Show>
</div>
</div>
);
};
if (selectedTab === 'settings') {
const settingsTabs: Array<{ key: 'change_password' | 'notifications' | 'privacy'; label: string }> = [
{ key: 'change_password', label: 'Change Password' },
{ key: 'notifications', label: 'Notifications' },
{ key: 'privacy', label: 'Privacy' },
];
const activeSettingsTab = profileSettingsTab();
return (
<div style="display:grid;grid-template-columns:1fr;gap:10px">
<div style="border:1px solid #E5E7EB;background:white;border-radius:16px;padding:14px;box-shadow:0 1px 4px rgba(0,0,0,0.06)">
<p style="margin:0;font-size:13px;font-weight:700;color:#111827">Settings</p>
<div style="display:flex;align-items:center;gap:20px;margin-top:10px;border-bottom:1px solid #E5E7EB">
<For each={settingsTabs}>
{(item) => (
<button
type="button"
onClick={() => setProfileSettingsTab(item.key)}
style={`padding:0 0 10px;font-size:13px;font-weight:500;background:none;border:none;cursor:pointer;${activeSettingsTab === item.key ? 'color:#FF5E13;border-bottom:2px solid #FF5E13;margin-bottom:-1px' : 'color:#6B7280'}`}
>
{item.label}
</button>
)}
</For>
</div>
<Show when={activeSettingsTab === 'change_password'}>
<div style="display:grid;grid-template-columns:1fr 1fr;gap:10px;margin-top:10px">
<div style="display:flex;flex-direction:column;gap:6px">
<p style="margin:0;font-size:11px;font-weight:700;color:#6B7280;letter-spacing:0.04em;text-transform:uppercase">Current Password<span style="color:#FF5E13;margin-left:4px">*</span></p>
<input type="password" placeholder="Enter current password" style="height:36px;border:1px solid #E5E7EB;border-radius:8px;background:white;padding:0 10px;font-size:12px;color:#111827;outline:none" />
</div>
<div style="display:flex;flex-direction:column;gap:6px">
<p style="margin:0;font-size:11px;font-weight:700;color:#6B7280;letter-spacing:0.04em;text-transform:uppercase">New Password<span style="color:#FF5E13;margin-left:4px">*</span></p>
<input type="password" placeholder="Enter new password" style="height:36px;border:1px solid #E5E7EB;border-radius:8px;background:white;padding:0 10px;font-size:12px;color:#111827;outline:none" />
</div>
</div>
<div style="display:flex;flex-direction:column;gap:6px;margin-top:10px">
<p style="margin:0;font-size:11px;font-weight:700;color:#6B7280;letter-spacing:0.04em;text-transform:uppercase">Confirm Password<span style="color:#FF5E13;margin-left:4px">*</span></p>
<input type="password" placeholder="Confirm new password" style="height:36px;border:1px solid #E5E7EB;border-radius:8px;background:white;padding:0 10px;font-size:12px;color:#111827;outline:none" />
</div>
</Show>
<Show when={activeSettingsTab === 'notifications'}>
<div style="display:grid;gap:8px;margin-top:10px">
{[
['email_updates', 'Email Updates'],
['in_app_alerts', 'In-app Alerts'],
['verification_reminders', 'Verification Reminders'],
].map(([key, title]) => (
<div style="display:flex;justify-content:space-between;align-items:center;gap:10px;padding:10px;border:1px solid #E5E7EB;border-radius:10px;background:#F9FAFB">
<p style="margin:0;font-size:12px;font-weight:700;color:#111827">{title}</p>
<button
type="button"
onClick={() => toggleProfileSetting(String(key))}
style={`height:30px;border-radius:999px;padding:0 12px;font-size:11px;font-weight:700;cursor:pointer;border:1px solid ${profileSettingToggles()[String(key)] ? '#C7D2FE' : '#D1D5DB'};background:${profileSettingToggles()[String(key)] ? '#EEF2FF' : 'white'};color:${profileSettingToggles()[String(key)] ? '#03004E' : '#374151'}`}
>
{profileSettingToggles()[String(key)] ? 'On' : 'Off'}
</button>
</div>
))}
</div>
</Show>
<Show when={activeSettingsTab === 'privacy'}>
<div style="display:grid;gap:8px;margin-top:10px">
{[
['profile_visibility', 'Profile Visibility'],
['data_sharing_consent', 'Data Sharing Consent'],
].map(([key, title]) => (
<div style="display:flex;justify-content:space-between;align-items:center;gap:10px;padding:10px;border:1px solid #E5E7EB;border-radius:10px;background:#F9FAFB">
<p style="margin:0;font-size:12px;font-weight:700;color:#111827">{title}</p>
<button
type="button"
onClick={() => toggleProfileSetting(String(key))}
style={`height:30px;border-radius:999px;padding:0 12px;font-size:11px;font-weight:700;cursor:pointer;border:1px solid ${profileSettingToggles()[String(key)] ? '#C7D2FE' : '#D1D5DB'};background:${profileSettingToggles()[String(key)] ? '#EEF2FF' : 'white'};color:${profileSettingToggles()[String(key)] ? '#03004E' : '#374151'}`}
>
{profileSettingToggles()[String(key)] ? 'On' : 'Off'}
</button>
</div>
))}
<div style="display:flex;justify-content:space-between;align-items:center;gap:10px;padding:10px;border:1px solid #FFE2D3;border-radius:10px;background:#FFF8F4">
<div>
<p style="margin:0;font-size:12px;font-weight:700;color:#111827">Delete Account</p>
<p style="margin:2px 0 0;font-size:11px;color:#6B7280">Permanently remove your account and data.</p>
</div>
<button
type="button"
onClick={() => setShowDeleteAccountModal(true)}
style="height:30px;border-radius:8px;padding:0 12px;font-size:11px;font-weight:700;cursor:pointer;border:1px solid #FFD0BA;background:white;color:#FF5E13"
>
Delete
</button>
</div>
</div>
</Show>
<div style="display:flex;justify-content:flex-end;gap:8px;margin-top:12px;padding-top:12px;border-top:1px solid #E5E7EB">
<button type="button" style="height:34px;border-radius:8px;border:1px solid #D1D5DB;background:white;color:#374151;padding:0 14px;font-size:12px;font-weight:700">Cancel</button>
<button type="button" style="height:34px;border-radius:8px;border:none;background:#03004E;color:white;padding:0 14px;font-size:12px;font-weight:700">
{activeSettingsTab === 'change_password' ? 'Update Password' : 'Save Settings'}
</button>
</div>
</div>
<Show when={showDeleteAccountModal()}>
<div style="position:fixed;inset:0;background:rgba(15,23,42,0.4);display:flex;align-items:center;justify-content:center;z-index:50;padding:16px">
<div style="width:min(460px,100%);border:1px solid #E5E7EB;background:white;border-radius:14px;box-shadow:0 10px 30px rgba(0,0,0,0.18);padding:16px">
<div style="display:flex;align-items:center;gap:8px">
<span style="width:22px;height:22px;border-radius:999px;background:#FFF1EB;color:#FF5E13;display:inline-flex;align-items:center;justify-content:center;font-size:13px;font-weight:800">!</span>
<p style="margin:0;font-size:15px;font-weight:800;color:#111827">Delete account?</p>
</div>
<p style="margin:10px 0 0;font-size:13px;color:#374151;line-height:1.5">
This will permanently remove your account. This action cannot be undone.
</p>
<div style="display:flex;justify-content:flex-end;gap:8px;margin-top:14px">
<button
type="button"
onClick={() => setShowDeleteAccountModal(false)}
style="height:34px;border-radius:8px;border:1px solid #D1D5DB;background:white;color:#374151;padding:0 14px;font-size:12px;font-weight:700"
>
Cancel
</button>
<button
type="button"
onClick={() => setShowDeleteAccountModal(false)}
style="height:34px;border-radius:8px;border:none;background:#03004E;color:white;padding:0 14px;font-size:12px;font-weight:700"
>
Yes, Delete Account
</button>
</div>
</div>
</div>
</Show>
</div>
);
}
return (
<div style="display:grid;grid-template-columns:2fr 1fr;gap:10px">
<div style="border:1px solid #E5E7EB;background:white;border-radius:16px;padding:14px;box-shadow:0 1px 4px rgba(0,0,0,0.06)">
<div style="display:flex;justify-content:space-between;align-items:center;gap:10px">
<p style="margin:0;font-size:13px;font-weight:700;color:#111827">{isPreferencesTab ? 'Preference Details' : titleCase(selectedTab)}</p>
<span style="height:22px;padding:0 8px;border-radius:999px;border:1px solid #DDEBFF;background:#EEF4FF;display:inline-flex;align-items:center;font-size:10px;font-weight:700;color:#03004E">
{fieldsForTab.length} fields
</span>
</div>
<Show
when={isDocumentsTab}
fallback={
<Show
when={isPreferencesTab}
fallback={
<div style="display:grid;grid-template-columns:repeat(2,minmax(0,1fr));gap:10px;margin-top:10px">
<For each={fieldsForTab}>{(field) => renderField(field)}</For>
</div>
}
>
<div style="display:grid;grid-template-columns:1fr;gap:8px;margin-top:10px;max-width:560px">
<For each={fieldsForTab}>{(field) => renderField(field)}</For>
</div>
</Show>
}
>
<div style="display:grid;grid-template-columns:1fr;gap:10px;margin-top:10px;max-width:560px">
<div style="display:flex;flex-direction:column;gap:6px">
<p style="margin:0;font-size:11px;font-weight:700;color:#6B7280;letter-spacing:0.01em;text-transform:none">
Document type<span style="color:#FF5E13;margin-left:4px">*</span>
</p>
<select
value={profileDocumentType()}
onChange={(e) => setProfileDocumentType(e.currentTarget.value)}
style="width:100%;height:36px;border:1px solid #E5E7EB;border-radius:8px;background:white;padding:0 10px;font-size:12px;color:#111827;outline:none"
>
<option>Aadhar Card</option>
<option>PAN Card</option>
<option>Passport</option>
<option>Driving License</option>
</select>
</div>
<div style="display:flex;flex-direction:column;gap:6px">
<p style="margin:0;font-size:11px;font-weight:700;color:#6B7280;letter-spacing:0.01em;text-transform:none">
Upload document<span style="color:#FF5E13;margin-left:4px">*</span>
</p>
<input
type="text"
value=""
placeholder={`Upload ${profileDocumentType().toLowerCase()} (PDF/JPG/PNG)`}
style="width:100%;height:36px;border:1px solid #E5E7EB;border-radius:8px;background:white;padding:0 10px;font-size:12px;color:#111827;outline:none"
/>
</div>
</div>
</Show>
<div style="display:flex;justify-content:flex-end;gap:8px;margin-top:12px;padding-top:12px;border-top:1px solid #E5E7EB">
<button type="button" onClick={() => setProfileFormData({})} style="height:34px;border-radius:8px;border:1px solid #D1D5DB;background:white;color:#374151;padding:0 14px;font-size:12px;font-weight:700">Cancel</button>
<button
type="button"
onClick={() => {
if (!hasLive()) return;
const data = profileFormData();
setProfileSaving(true);
const fn = data['First Name'] || '';
const ln = data['Last Name'] || '';
const display = data['Business Name'] || data['Company Name'] || data['Contact Person Name'] || [fn, ln].filter(Boolean).join(' ');
fetch(`${GW}/api/${livePrefix()}/profile/me`, {
method: 'PATCH',
credentials: 'include',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({
display_name: display || undefined,
full_name: display || undefined,
first_name: fn || undefined,
last_name: ln || undefined,
business_name: data['Business Name'] || undefined,
company_name: data['Company Name'] || undefined,
contact_person: data['Contact Person Name'] || undefined,
bio: data['Bio'] || undefined,
location: data['City'] || undefined,
area: data['Area'] || undefined,
state: data['State'] || undefined,
pin_code: data['PIN Code'] || undefined,
phone: data['Mobile Number'] || undefined,
email: data['Email Address'] || undefined,
}),
}).then((r) => {
setProfileSaving(false);
setProfileSaveStatus(r.ok ? 'saved' : 'error');
setTimeout(() => setProfileSaveStatus('idle'), 2500);
}).catch(() => { setProfileSaving(false); setProfileSaveStatus('error'); });
}}
style={`height:34px;border-radius:8px;border:none;background:${profileSaveStatus() === 'error' ? '#DC2626' : profileSaveStatus() === 'saved' ? '#16A34A' : '#03004E'};color:white;padding:0 14px;font-size:12px;font-weight:700`}
>
{profileSaving() ? 'Saving…' : profileSaveStatus() === 'saved' ? 'Saved ✓' : profileSaveStatus() === 'error' ? 'Error — Retry' : 'Save Changes'}
</button>
</div>
</div>
<div style="border:1px solid #E5E7EB;background:white;border-radius:16px;padding:14px;box-shadow:0 1px 4px rgba(0,0,0,0.06)">
<p style="margin:0;font-size:11px;letter-spacing:0.04em;text-transform:uppercase;color:#6B7280">What To Do Next</p>
<p style="margin:8px 0 0;font-size:18px;font-weight:800;color:#111827;line-height:1.3">Complete profile and portfolio to unlock leads</p>
<p style="margin:8px 0 0;font-size:12px;color:#6B7280;line-height:1.5">Finish My Profile and My Portfolio, then submit both for admin approval. Leads will unlock only after approval.</p>
<div style="display:grid;gap:8px;margin-top:10px">
<div style="display:flex;align-items:center;justify-content:space-between;gap:8px;padding:8px;border:1px solid #E5E7EB;border-radius:8px;background:#F9FAFB">
<span style="font-size:11px;font-weight:700;color:#374151">My Profile</span>
<span style="font-size:11px;font-weight:800;color:#111827">{approvalTone(profileApprovalState()).label}</span>
</div>
<Show when={isProfessionalRole()}>
<div style="display:flex;align-items:center;justify-content:space-between;gap:8px;padding:8px;border:1px solid #E5E7EB;border-radius:8px;background:#F9FAFB">
<span style="font-size:11px;font-weight:700;color:#374151">My Portfolio</span>
<span style="font-size:11px;font-weight:800;color:#111827">{approvalTone(portfolioApprovalState()).label}</span>
</div>
</Show>
</div>
<div style="display:grid;gap:8px;margin-top:10px">
<button type="button" onClick={() => { props.onSidebarSelect('My Profile'); props.onTabSelect('basic information'); }} style="height:32px;border:1px solid #D1D5DB;border-radius:8px;background:white;color:#374151;padding:0 12px;font-size:12px;font-weight:700">Fill My Profile</button>
<Show when={isProfessionalRole()}>
<button type="button" onClick={() => { props.onSidebarSelect('My Portfolio'); props.onTabSelect('about'); }} style="height:32px;border:1px solid #D1D5DB;border-radius:8px;background:white;color:#374151;padding:0 12px;font-size:12px;font-weight:700">Fill My Portfolio</button>
</Show>
<button type="button" onClick={() => props.onSidebarSelect('Verification')} style="height:32px;border:none;border-radius:8px;background:#03004E;color:white;padding:0 12px;font-size:12px;font-weight:700">Submit For Approval</button>
</div>
</div>
</div>
);
}
if (customerKey() === 'leads') {
if (!bothApprovalsApproved()) {
return (
<div style="display:grid;gap:12px">
<div style="border:1px solid #E5E7EB;background:white;border-radius:14px;padding:14px">
<p style="margin:0;font-size:22px;font-weight:800;color:#111827">Leads Locked</p>
<p style="margin:8px 0 0;font-size:13px;color:#6B7280;line-height:1.55">
Complete verification before accessing leads. You must submit profile and portfolio, then get admin approval.
</p>
<div style="display:grid;grid-template-columns:1fr 1fr;gap:8px;margin-top:10px">
<div style="border:1px solid #E5E7EB;border-radius:10px;background:#F9FAFB;padding:10px">
<p style="margin:0;font-size:11px;color:#6B7280;text-transform:uppercase;letter-spacing:0.05em">Profile Verification</p>
<p style="margin:6px 0 0;font-size:13px;font-weight:800;color:#111827">{approvalTone(profileApprovalState()).label}</p>
</div>
<div style="border:1px solid #E5E7EB;border-radius:10px;background:#F9FAFB;padding:10px">
<p style="margin:0;font-size:11px;color:#6B7280;text-transform:uppercase;letter-spacing:0.05em">Portfolio Verification</p>
<p style="margin:6px 0 0;font-size:13px;font-weight:800;color:#111827">{approvalTone(portfolioApprovalState()).label}</p>
</div>
</div>
<div style="display:flex;gap:8px;flex-wrap:wrap;margin-top:10px">
<button type="button" onClick={() => props.onSidebarSelect('Verification')} style="height:32px;border-radius:8px;border:none;background:#03004E;color:white;padding:0 12px;font-size:12px;font-weight:700">Open Verification</button>
<button type="button" onClick={() => { props.onSidebarSelect('My Profile'); props.onTabSelect('basic information'); }} style="height:32px;border-radius:8px;border:1px solid #E5E7EB;background:white;color:#374151;padding:0 12px;font-size:12px;font-weight:700">Complete Profile</button>
<button type="button" onClick={() => { props.onSidebarSelect('My Portfolio'); props.onTabSelect('about'); }} style="height:32px;border-radius:8px;border:1px solid #E5E7EB;background:white;color:#374151;padding:0 12px;font-size:12px;font-weight:700">Complete Portfolio</button>
</div>
</div>
</div>
);
}
const requestStatusPill = (status: string) => {
if (status === 'request_sent') return { bg: '#DBEAFE', c: '#1D4ED8', text: 'Request Sent' };
if (status === 'approved') return { bg: '#DCFCE7', c: '#15803D', text: 'Approved' };
if (status === 'contact_unlocked') return { bg: '#ECFDF3', c: '#047857', text: 'Contact Unlocked' };
if (status === 'rejected') return { bg: '#FEE2E2', c: '#B91C1C', text: 'Rejected' };
if (status === 'expired_refunded') return { bg: '#E0E7FF', c: '#4338CA', text: 'Expired · Refunded' };
if (status === 'cancelled_by_professional') return { bg: '#FFF1EB', c: '#C2410C', text: 'Cancelled by Professional' };
return { bg: '#F3F4F6', c: '#6B7280', text: titleCase(status) };
};
const leadTabs = () => (activeLeadDetailId() ? [...LEAD_MARKETPLACE_TABS, 'View Details'] : LEAD_MARKETPLACE_TABS);
const selectedLead = () => leadCards().find((item) => item.id === activeLeadDetailId()) || null;
if (resolvedTabKey() === 'requested contacts') {
return (
<div style="display:flex;flex-direction:column;gap:10px">
<div style="display:grid;grid-template-columns:1fr 1fr 1fr;gap:10px">
<div style="border:1px solid #E5E7EB;background:white;border-radius:14px;padding:12px">
<p style="margin:0;font-size:11px;letter-spacing:0.06em;text-transform:uppercase;color:#9CA3AF">Total Spent</p>
<p style="margin:8px 0 0;font-size:30px;line-height:1;font-weight:800;color:#111827">{250 - leadCredits()}</p>
<p style="margin:6px 0 0;font-size:12px;color:#6B7280">Tracecoins used this month</p>
</div>
<div style="border:1px solid #D7DBFF;background:#03004E;border-radius:14px;padding:12px;color:white">
<p style="margin:0;font-size:11px;letter-spacing:0.06em;text-transform:uppercase;color:#D7DBFF">Active Requests</p>
<p style="margin:8px 0 0;font-size:30px;line-height:1;font-weight:800">{leadCards().filter((card) => card.status === 'requested').length}</p>
<div style="height:4px;border-radius:999px;background:rgba(255,255,255,0.2);overflow:hidden;margin-top:8px">
<div style={`height:100%;width:${Math.min(100, leadCards().filter((card) => card.status === 'requested').length * 10)}%;background:#FF5E13`} />
</div>
</div>
<div style="border:1px solid #E5E7EB;background:white;border-radius:14px;padding:12px">
<p style="margin:0;font-size:11px;letter-spacing:0.06em;text-transform:uppercase;color:#9CA3AF">Locked Credits</p>
<p style="margin:8px 0 0;font-size:30px;line-height:1;font-weight:800;color:#111827">{lockedLeadCredits()}</p>
<p style="margin:6px 0 0;font-size:12px;color:#6B7280">Held until service seeker decision (auto-refund in 1 day)</p>
</div>
</div>
<div style="border:1px solid #E5E7EB;background:white;border-radius:16px;overflow:hidden;box-shadow:0 1px 4px rgba(0,0,0,0.06)">
<div style="padding:10px 12px;border-bottom:1px solid #E5E7EB;display:flex;justify-content:space-between;align-items:center">
<p style="margin:0;font-size:18px;font-weight:800;color:#111827">Requested Contacts</p>
<div style="display:flex;gap:8px">
<div style="position:relative">
<button type="button" onClick={() => { setRequestedSortOpen((prev) => !prev); setRequestedFilterOpen(false); }} style="display:inline-flex;height:32px;align-items:center;gap:6px;border-radius:8px;border:1px solid #E5E7EB;background:white;padding:0 10px;font-size:12px;color:#374151;font-weight:600">
<svg width="13" height="13" viewBox="0 0 24 24" fill="none" stroke="#FF5E13" stroke-width="2"><path d="M7 4v13"/><path d="m3 13 4 4 4-4"/><path d="M17 20V7"/><path d="m21 11-4-4-4 4"/></svg>
Sort
</button>
<Show when={requestedSortOpen()}>
<div style="position:absolute;right:0;top:36px;z-index:20;min-width:170px;border:1px solid #E5E7EB;border-radius:10px;background:white;padding:6px;box-shadow:0 8px 24px rgba(0,0,0,0.12)">
{['Newest First', 'Oldest First'].map((item) => (
<button type="button" onClick={() => { setRequestedSortFilter(item); setRequestedSortOpen(false); }} style={`display:block;width:100%;text-align:left;border:none;background:${requestedSortFilter() === item ? '#FFF1EB' : 'transparent'};color:${requestedSortFilter() === item ? '#FF5E13' : '#374151'};padding:8px 10px;border-radius:8px;font-size:12px;font-weight:600;cursor:pointer`}>
{item}
</button>
))}
</div>
</Show>
</div>
<div style="position:relative">
<button type="button" onClick={() => { setRequestedFilterOpen((prev) => !prev); setRequestedSortOpen(false); }} style="display:inline-flex;height:32px;align-items:center;gap:6px;border-radius:8px;border:1px solid #E5E7EB;background:white;padding:0 10px;font-size:12px;color:#374151;font-weight:600">
<svg width="13" height="13" viewBox="0 0 24 24" fill="none" stroke="#FF5E13" stroke-width="2"><path d="M3 5h18M6 12h12M10 19h4"/></svg>
Filter
</button>
<Show when={requestedFilterOpen()}>
<div style="position:absolute;right:0;top:36px;z-index:20;min-width:220px;border:1px solid #E5E7EB;border-radius:10px;background:white;padding:6px;box-shadow:0 8px 24px rgba(0,0,0,0.12)">
{['All Status', 'Request Sent', 'Contact Unlocked', 'Rejected', 'Expired Refunded', 'Cancelled By Professional'].map((item) => (
<button type="button" onClick={() => { setRequestedStatusFilter(item); setRequestedFilterOpen(false); }} style={`display:block;width:100%;text-align:left;border:none;background:${requestedStatusFilter() === item ? '#FFF1EB' : 'transparent'};color:${requestedStatusFilter() === item ? '#FF5E13' : '#374151'};padding:8px 10px;border-radius:8px;font-size:12px;font-weight:600;cursor:pointer`}>
{item}
</button>
))}
</div>
</Show>
</div>
<button type="button" style="height:32px;border-radius:8px;border:none;background:#03004E;padding:0 10px;font-size:12px;font-weight:700;color:white">Export</button>
</div>
</div>
<div style="padding:10px 12px;border-bottom:1px solid #E5E7EB;display:flex;align-items:center;gap:8px;flex-wrap:wrap">
<div style="height:32px;min-width:220px;flex:1;border:1px solid #E5E7EB;border-radius:8px;background:#F9FAFB;padding:0 10px;display:flex;align-items:center;gap:8px;font-size:12px;color:#9CA3AF">
<Search size={14} />
<input value={requestedSearch()} onInput={(e) => setRequestedSearch(e.currentTarget.value)} placeholder="Search by lead ID / title / area..." style="border:none;background:transparent;outline:none;width:100%;font-size:12px;color:#111827" />
</div>
<span style="height:24px;padding:0 10px;border-radius:999px;border:1px solid #E5E7EB;background:#F9FAFB;display:inline-flex;align-items:center;font-size:11px;font-weight:700;color:#6B7280">Sort: {requestedSortFilter()}</span>
<span style="height:24px;padding:0 10px;border-radius:999px;border:1px solid #E5E7EB;background:#F9FAFB;display:inline-flex;align-items:center;font-size:11px;font-weight:700;color:#6B7280">Status: {requestedStatusFilter()}</span>
</div>
<div style="max-height:360px;overflow:auto">
<table style="width:100%;border-collapse:collapse">
<thead style="background:#03004E;color:white">
<tr>
{['Lead ID', 'Lead Title', 'Request Date', 'Request Status', 'Cost', 'Decision Date', 'Action'].map((h) => (
<th style="padding:10px;text-align:left;font-size:10px;letter-spacing:0.06em;text-transform:uppercase">{h}</th>
))}
</tr>
</thead>
<tbody>
<For each={pagedRequestedRows()}>
{(row) => {
const badge = requestStatusPill(row.status);
return (
<tr style="border-top:1px solid #F3F4F6">
<td style="padding:10px;font-size:12px;color:#4B5563;font-weight:700">#{row.id}</td>
<td style="padding:10px">
<p style="margin:0;font-size:14px;font-weight:700;color:#111827">{row.title}</p>
<p style="margin:2px 0 0;font-size:12px;color:#9CA3AF">{row.city}</p>
</td>
<td style="padding:10px;font-size:12px;color:#374151">{row.requestDate}</td>
<td style="padding:10px"><span style={`height:22px;padding:0 10px;border-radius:999px;display:inline-flex;align-items:center;font-size:11px;font-weight:700;background:${badge.bg};color:${badge.c}`}>{badge.text}</span></td>
<td style="padding:10px;font-size:14px;font-weight:700;color:#111827">{leadCostPerContact}</td>
<td style="padding:10px;font-size:12px;color:#6B7280">{row.decisionDate}</td>
<td style="padding:10px">
<div style="display:flex;gap:6px;flex-wrap:wrap">
<Show when={row.status === 'request_sent'}>
<button type="button" onClick={() => approveLeadContact(row.id)} style="height:28px;border-radius:8px;border:none;background:#03004E;padding:0 10px;font-size:11px;font-weight:700;color:white">Approve (Demo)</button>
<button type="button" onClick={() => cancelLeadRequest(row.id)} style="height:28px;border-radius:8px;border:1px solid #FECACA;background:#FEF2F2;padding:0 10px;font-size:11px;font-weight:700;color:#B91C1C">Cancel Request</button>
<button type="button" onClick={() => refundPendingLead(row.id)} style="height:28px;border-radius:8px;border:1px solid #E5E7EB;background:white;padding:0 10px;font-size:11px;font-weight:700;color:#374151">Refund After 1 Day</button>
</Show>
<Show when={row.status === 'contact_unlocked'}>
<button type="button" style="height:28px;border-radius:8px;border:none;background:#FF5E13;padding:0 10px;font-size:11px;font-weight:700;color:white">View Data</button>
</Show>
</div>
</td>
</tr>
);
}}
</For>
</tbody>
</table>
</div>
<div style="padding:10px 12px;border-top:1px solid #E5E7EB;display:flex;justify-content:space-between;align-items:center">
<p style="margin:0;font-size:12px;color:#6B7280">
Showing {pagedRequestedRows().length ? (requestedPage() - 1) * requestedPerPage + 1 : 0} to {(requestedPage() - 1) * requestedPerPage + pagedRequestedRows().length} of {filteredRequestedRows().length} requests
</p>
<div style="display:flex;gap:6px">
<For each={Array.from({ length: totalRequestedPages() }, (_, i) => i + 1)}>
{(pageNo) => (
<button
type="button"
onClick={() => setRequestedPage(pageNo)}
style={`width:30px;height:30px;border-radius:8px;border:1px solid #E5E7EB;background:${requestedPage() === pageNo ? '#FF5E13' : 'white'};color:${requestedPage() === pageNo ? 'white' : '#6B7280'};font-size:12px;font-weight:700`}
>
{pageNo}
</button>
)}
</For>
</div>
</div>
</div>
</div>
);
}
if (leadMarketplaceTab() === 'View Details' && selectedLead()) {
const lead = selectedLead()!;
const spec = leadDetailsSpec(lead);
return (
<div style="display:flex;flex-direction:column;gap:10px">
<div style="border:1px solid #E5E7EB;background:white;border-radius:16px;padding:14px;box-shadow:0 1px 4px rgba(0,0,0,0.06)">
<div style="border:1px solid #E5E7EB;border-radius:12px;background:linear-gradient(135deg,#FFF8F4 0%, #FFFFFF 70%);padding:14px">
<div style="display:flex;align-items:flex-start;justify-content:space-between;gap:10px;flex-wrap:wrap">
<div>
<p style="margin:0;font-size:11px;letter-spacing:0.06em;text-transform:uppercase;color:#6B7280">View Details</p>
<p style="margin:4px 0 0;font-size:26px;font-weight:800;color:#111827;line-height:1.2">{lead.title}</p>
<p style="margin:6px 0 0;font-size:12px;color:#6B7280">{lead.category} • {lead.location} • {lead.area}</p>
</div>
<span style="height:24px;padding:0 10px;border-radius:999px;background:#FFF1EB;color:#C2410C;display:inline-flex;align-items:center;font-size:11px;font-weight:700">{lead.match}</span>
</div>
<div style="margin-top:10px;display:flex;align-items:center;gap:8px;flex-wrap:wrap">
<span style="height:24px;padding:0 8px;border-radius:999px;border:1px solid #E5E7EB;background:white;font-size:11px;color:#374151;display:inline-flex;align-items:center;gap:4px"><Calendar size={11} style="color:#6B7280" /><strong style="font-weight:700;color:#6B7280">Time Frame:</strong>{spec.timeframe}</span>
<span style="height:24px;padding:0 8px;border-radius:999px;border:1px solid #E5E7EB;background:white;font-size:11px;color:#374151;display:inline-flex;align-items:center;gap:4px"><Calendar size={11} style="color:#6B7280" /><strong style="font-weight:700;color:#6B7280">Date:</strong>{lead.dateRequired}</span>
<span style="height:24px;padding:0 8px;border-radius:999px;border:1px solid #E5E7EB;background:white;font-size:11px;color:#374151;display:inline-flex;align-items:center;gap:4px"><Coins size={11} style="color:#6B7280" /><strong style="font-weight:700;color:#6B7280">Budget:</strong>{lead.budget}</span>
<span style="height:24px;padding:0 8px;border-radius:999px;border:1px solid #E5E7EB;background:white;font-size:11px;color:#374151;display:inline-flex;align-items:center;gap:4px"><MapPin size={11} style="color:#6B7280" /><strong style="font-weight:700;color:#6B7280">Area:</strong>{String(lead.area || 'Chennai')}</span>
</div>
</div>
<div style="margin-top:10px;display:grid;grid-template-columns:repeat(4,minmax(0,1fr));gap:8px">
<div style="border:1px solid #E5E7EB;border-radius:10px;background:white;padding:10px;display:flex;align-items:flex-start;gap:8px">
<span style="width:24px;height:24px;border-radius:999px;background:#FFF1EB;display:inline-flex;align-items:center;justify-content:center;flex-shrink:0"><Calendar size={13} style="color:#FF5E13" /></span>
<div><p style="margin:0;font-size:11px;color:#6B7280;font-weight:700">Schedule</p><p style="margin:3px 0 0;font-size:13px;font-weight:800;color:#111827;line-height:1.35">{spec.timeframe}</p></div>
</div>
<div style="border:1px solid #E5E7EB;border-radius:10px;background:white;padding:10px;display:flex;align-items:flex-start;gap:8px">
<span style="width:24px;height:24px;border-radius:999px;background:#FFF1EB;display:inline-flex;align-items:center;justify-content:center;flex-shrink:0"><Calendar size={13} style="color:#FF5E13" /></span>
<div><p style="margin:0;font-size:11px;color:#6B7280;font-weight:700">Date Required</p><p style="margin:3px 0 0;font-size:13px;font-weight:800;color:#111827">{lead.dateRequired}</p></div>
</div>
<div style="border:1px solid #E5E7EB;border-radius:10px;background:white;padding:10px;display:flex;align-items:flex-start;gap:8px">
<span style="width:24px;height:24px;border-radius:999px;background:#FFF1EB;display:inline-flex;align-items:center;justify-content:center;flex-shrink:0"><MapPin size={13} style="color:#FF5E13" /></span>
<div><p style="margin:0;font-size:11px;color:#6B7280;font-weight:700">Area</p><p style="margin:3px 0 0;font-size:13px;font-weight:800;color:#111827">{String(lead.area || 'Chennai')}</p></div>
</div>
<div style="border:1px solid #E5E7EB;border-radius:10px;background:white;padding:10px;display:flex;align-items:flex-start;gap:8px">
<span style="width:24px;height:24px;border-radius:999px;background:#FFF1EB;display:inline-flex;align-items:center;justify-content:center;flex-shrink:0"><TrendingUp size={13} style="color:#FF5E13" /></span>
<div style="min-width:0;flex:1">
<p style="margin:0;font-size:11px;color:#6B7280;font-weight:700">Win Probability</p>
<p style={`margin:3px 0 0;font-size:13px;font-weight:800;color:${leadProbabilityColor(leadProbability(lead))}`}>{leadProbability(lead)}%</p>
<div style="margin-top:5px;height:5px;border-radius:999px;background:#E5E7EB;overflow:hidden">
<div style={`height:100%;width:${leadProbability(lead)}%;background:${leadProbabilityColor(leadProbability(lead))}`} />
</div>
</div>
</div>
</div>
<div style="margin-top:10px;display:grid;grid-template-columns:2fr 1fr;gap:10px">
<div style="display:grid;gap:10px">
<div style="border:1px solid #E5E7EB;border-radius:12px;background:white;padding:12px">
<p style="margin:0;font-size:11px;letter-spacing:0.05em;text-transform:uppercase;color:#6B7280;font-weight:700">Work Scope</p>
<p style="margin:8px 0 0;font-size:14px;color:#1F2937;line-height:1.65">{spec.scope}</p>
</div>
<div style="border:1px solid #E5E7EB;border-radius:12px;background:#F9FAFB;padding:12px">
<p style="margin:0 0 8px;font-size:11px;letter-spacing:0.05em;text-transform:uppercase;color:#6B7280;font-weight:700">Lead Specific Highlights</p>
<div style="display:grid;gap:6px">
<For each={spec.highlights}>{(item) => <div style="display:flex;align-items:flex-start;gap:6px"><span style="margin-top:3px;width:6px;height:6px;border-radius:999px;background:#FF5E13;flex-shrink:0" /><span style="font-size:14px;color:#1F2937;line-height:1.55">{item}</span></div>}</For>
</div>
</div>
</div>
<div style="border:1px solid #E5E7EB;border-radius:12px;background:white;padding:12px;display:grid;gap:8px;align-content:start">
<div style="display:flex;justify-content:space-between;gap:8px;padding-bottom:6px;border-bottom:1px solid #F3F4F6"><span style="font-size:12px;color:#6B7280">Lead ID</span><span style="font-size:12px;color:#111827;font-weight:700">{lead.id}</span></div>
<div style="display:flex;justify-content:space-between;gap:8px;padding-bottom:6px;border-bottom:1px solid #F3F4F6">
<span style="font-size:12px;color:#6B7280">Unlock Cost</span>
<span style="height:22px;padding:0 8px;border-radius:999px;border:1px solid #FFD8C2;background:#FFF1EB;display:inline-flex;align-items:center;gap:6px"><span style="width:14px;height:14px;border-radius:999px;background:#FF5E13;color:white;display:inline-flex;align-items:center;justify-content:center;font-size:9px;font-weight:800;line-height:1">NG</span><span style="font-size:12px;color:#C2410C;font-weight:800">{lead.cost} Tracecoin</span></span>
</div>
<div style="display:flex;justify-content:space-between;gap:8px;padding-bottom:6px;border-bottom:1px solid #F3F4F6"><span style="font-size:12px;color:#6B7280">Contacted</span><span style="font-size:12px;color:#111827;font-weight:700">{lead.contactCount}/{lead.maxContacts}</span></div>
<div style="display:flex;gap:8px;margin-top:6px;flex-wrap:wrap">
<button type="button" onClick={() => { setLeadMarketplaceTab('All Leads'); setActiveLeadDetailId(''); }} style="height:32px;border-radius:8px;border:1px solid #E5E7EB;background:white;padding:0 12px;font-size:12px;font-weight:700;color:#374151">Back to Leads</button>
<button type="button" onClick={() => openLeadContactConfirm(lead.id)} disabled={usableLeadCredits() < leadCostPerContact || lead.contactCount >= lead.maxContacts} style={`height:32px;border-radius:8px;border:none;padding:0 12px;font-size:12px;font-weight:700;color:white;background:${usableLeadCredits() < leadCostPerContact || lead.contactCount >= lead.maxContacts ? '#9CA3AF' : '#03004E'}`}>Request Contact</button>
</div>
</div>
</div>
</div>
</div>
);
}
return (
<div style="display:flex;flex-direction:column;gap:10px">
<div style="border:1px solid #E5E7EB;background:white;border-radius:16px;overflow:hidden;box-shadow:0 1px 4px rgba(0,0,0,0.06)">
<div style="padding:10px 12px;border-bottom:1px solid #E5E7EB;display:flex;align-items:center;gap:8px;flex-wrap:wrap">
<For each={leadTabs()}>
{(item) => (
<button
type="button"
onClick={() => { setLeadMarketplaceTab(item); if (item !== 'View Details') setActiveLeadDetailId(''); }}
style={`height:30px;padding:0 8px;border:none;border-bottom:${leadMarketplaceTab() === item ? '2px solid #FF5E13' : '2px solid transparent'};background:none;font-size:12px;font-weight:700;color:${leadMarketplaceTab() === item ? '#FF5E13' : '#6B7280'}`}
>
{item}
</button>
)}
</For>
</div>
<div style="padding:10px 12px;border-bottom:1px solid #E5E7EB;display:flex;align-items:center;gap:8px;flex-wrap:wrap">
<div style="height:34px;min-width:220px;flex:1;border:1px solid #E5E7EB;border-radius:8px;background:#F9FAFB;padding:0 10px;display:flex;align-items:center;gap:8px;font-size:12px;color:#9CA3AF">
<Search size={14} />
<input value={leadSearch()} onInput={(e) => setLeadSearch(e.currentTarget.value)} placeholder="Search by role, area, keyword..." style="border:none;background:transparent;outline:none;width:100%;font-size:12px;color:#111827" />
</div>
<div style="position:relative">
<button
type="button"
onClick={() => { setLeadFiltersOpen((prev) => !prev); setLeadSortOpen(false); }}
style="display:inline-flex;height:34px;align-items:center;gap:6px;border-radius:8px;border:1px solid #E5E7EB;background:white;padding:0 12px;font-size:12px;color:#374151;font-weight:600"
>
<svg width="13" height="13" viewBox="0 0 24 24" fill="none" stroke="#FF5E13" stroke-width="2"><path d="M3 5h18M6 12h12M10 19h4"/></svg>
Filters
</button>
<Show when={leadFiltersOpen()}>
<div style="position:absolute;right:0;top:38px;z-index:20;min-width:280px;border:1px solid #E5E7EB;border-radius:12px;background:white;box-shadow:0 8px 24px rgba(0,0,0,0.12);padding:10px;display:grid;gap:8px">
<div>
<p style="margin:0 0 4px;font-size:10px;letter-spacing:0.05em;text-transform:uppercase;color:#9CA3AF;font-weight:700">Area</p>
<select value={leadAreaFilter()} onChange={(e) => setLeadAreaFilter(e.currentTarget.value)} style="height:34px;width:100%;border-radius:8px;border:1px solid #E5E7EB;background:white;padding:0 10px;font-size:12px;color:#374151">
<option>All Areas</option>
<option>ECR</option>
<option>Nungambakkam</option>
<option>Sholinganallur</option>
<option>Mylapore</option>
<option>T Nagar</option>
<option>Guindy</option>
</select>
</div>
<div>
<p style="margin:0 0 4px;font-size:10px;letter-spacing:0.05em;text-transform:uppercase;color:#9CA3AF;font-weight:700">Budget</p>
<select value={leadBudgetFilter()} onChange={(e) => setLeadBudgetFilter(e.currentTarget.value)} style="height:34px;width:100%;border-radius:8px;border:1px solid #E5E7EB;background:white;padding:0 10px;font-size:12px;color:#374151">
<option>All Budgets</option>
<option>Under 1L</option>
<option>1L - 2L</option>
<option>Above 2L</option>
</select>
</div>
<div>
<p style="margin:0 0 4px;font-size:10px;letter-spacing:0.05em;text-transform:uppercase;color:#9CA3AF;font-weight:700">Date</p>
<select value={leadDateFilter()} onChange={(e) => setLeadDateFilter(e.currentTarget.value)} style="height:34px;width:100%;border-radius:8px;border:1px solid #E5E7EB;background:white;padding:0 10px;font-size:12px;color:#374151">
<option>Any Date</option>
<option>Within 7 Days</option>
<option>This Month</option>
</select>
</div>
<div style="display:flex;justify-content:flex-end;gap:8px;padding-top:4px">
<button type="button" onClick={() => { setLeadAreaFilter('All Areas'); setLeadBudgetFilter('All Budgets'); setLeadDateFilter('Any Date'); }} style="height:30px;border-radius:8px;border:1px solid #E5E7EB;background:white;padding:0 10px;font-size:11px;color:#374151;font-weight:700">Reset</button>
<button type="button" onClick={() => setLeadFiltersOpen(false)} style="height:30px;border-radius:8px;border:none;background:#03004E;color:white;padding:0 10px;font-size:11px;font-weight:700">Apply</button>
</div>
</div>
</Show>
</div>
<div style="position:relative">
<button
type="button"
onClick={() => { setLeadSortOpen((prev) => !prev); setLeadFiltersOpen(false); }}
style="display:inline-flex;height:34px;align-items:center;gap:6px;border-radius:8px;border:1px solid #E5E7EB;background:white;padding:0 12px;font-size:12px;color:#374151;font-weight:600"
>
<svg width="13" height="13" viewBox="0 0 24 24" fill="none" stroke="#FF5E13" stroke-width="2"><path d="M7 4v13"/><path d="m3 13 4 4 4-4"/><path d="M17 20V7"/><path d="m21 11-4-4-4 4"/></svg>
Sort
</button>
<Show when={leadSortOpen()}>
<div style="position:absolute;right:0;top:38px;z-index:20;min-width:190px;border:1px solid #E5E7EB;border-radius:10px;background:white;padding:6px;box-shadow:0 8px 24px rgba(0,0,0,0.12)">
{['Newest First', 'Budget High-Low', 'Budget Low-High'].map((item) => (
<button type="button" onClick={() => { setLeadSortFilter(item); setLeadSortOpen(false); }} style={`display:block;width:100%;text-align:left;border:none;background:${leadSortFilter() === item ? '#FFF1EB' : 'transparent'};color:${leadSortFilter() === item ? '#FF5E13' : '#374151'};padding:8px 10px;border-radius:8px;font-size:12px;font-weight:600;cursor:pointer`}>
{item}
</button>
))}
</div>
</Show>
</div>
<span style="height:24px;padding:0 10px;border-radius:999px;border:1px solid #E5E7EB;background:#F9FAFB;display:inline-flex;align-items:center;font-size:11px;font-weight:700;color:#6B7280">Sort: {leadSortFilter()}</span>
</div>
<div style="padding:12px;display:grid;gap:10px;background:#FAFBFF;max-height:520px;overflow:auto">
<For each={pagedLeadCards()}>
{(lead) => (
<div style={`border:1px solid ${lead.status === 'requested' ? '#FFE2D3' : lead.status === 'unlocked' ? '#CFF5E9' : lead.status === 'closed' ? '#F3F4F6' : '#E5E7EB'};border-radius:14px;background:white;padding:14px 16px`}>
<div style="display:grid;grid-template-columns:minmax(0,1fr) auto;gap:12px;align-items:start">
<div style="min-width:0">
<div style="display:flex;align-items:center;gap:8px;flex-wrap:wrap">
<span style={`height:20px;padding:0 8px;border-radius:999px;font-size:10px;font-weight:700;display:inline-flex;align-items:center;background:${lead.status === 'unlocked' ? '#ECFDF3' : lead.status === 'requested' ? '#FFF1EB' : lead.status === 'closed' ? '#F3F4F6' : '#EEF2FF'};color:${lead.status === 'unlocked' ? '#047857' : lead.status === 'requested' ? '#C2410C' : lead.status === 'closed' ? '#6B7280' : '#3730A3'}`}>
{lead.status === 'unlocked' ? 'Contact Unlocked' : lead.status === 'requested' ? 'Request Sent' : lead.status === 'closed' ? 'Lead Closed' : 'Open Lead'}
</span>
<span style="font-size:12px;color:#6B7280">{lead.category} {lead.location}</span>
</div>
<p style="margin:6px 0 0;font-size:20px;line-height:1.3;font-weight:800;color:#111827">{lead.title}</p>
<div style="margin-top:10px;display:flex;align-items:center;gap:12px;white-space:nowrap;overflow-x:auto;padding-bottom:2px">
<span style="font-size:12px;color:#111827;font-weight:700">Area: {lead.area}</span>
<span style="font-size:12px;color:#D1D5DB">|</span>
<span style="font-size:12px;color:#111827;font-weight:700">Date: {lead.dateRequired}</span>
<span style="font-size:12px;color:#D1D5DB">|</span>
<span style="height:24px;display:inline-flex;align-items:center;gap:6px;padding:0 8px;border:1px solid #FFE2D3;border-radius:999px;background:#FFF7F2">
<span style="width:14px;height:14px;border-radius:999px;background:#FF5E13;color:white;display:inline-flex;align-items:center;justify-content:center;font-size:10px;font-weight:800;line-height:1">T</span>
<span style="font-size:12px;color:#C2410C;font-weight:800">{lead.cost}</span>
</span>
<span style="font-size:12px;color:#D1D5DB">|</span>
<span style="display:inline-flex;align-items:center;gap:6px">
<svg width="62" height="34" viewBox="0 0 120 70" aria-label="Lead probability gauge">
<path d="M 10 60 A 50 50 0 0 1 110 60" fill="none" stroke="#E5E7EB" stroke-width="8" stroke-linecap="round" />
<path d="M 10 60 A 50 50 0 0 1 110 60" fill="none" stroke={leadProbabilityColor(leadProbability(lead))} stroke-width="8" stroke-linecap="round" stroke-dasharray={leadGaugeDash(leadProbability(lead))} />
<line x1="60" y1="60" x2={leadGaugeNeedlePoint(leadProbability(lead)).x} y2={leadGaugeNeedlePoint(leadProbability(lead)).y} stroke="#111827" stroke-width="3" stroke-linecap="round" />
<circle cx="60" cy="60" r="4.5" fill="#111827" />
</svg>
<span style={`font-size:12px;font-weight:800;color:${leadProbabilityColor(leadProbability(lead))}`}>{leadProbability(lead)}%</span>
</span>
<span style="font-size:12px;color:#D1D5DB">|</span>
<span style="height:24px;display:inline-flex;align-items:center;gap:6px;padding:0 8px;border:1px solid #E5E7EB;border-radius:999px;background:#F9FAFB">
<svg width="12" height="12" viewBox="0 0 24 24" fill="none" stroke="#6B7280" stroke-width="2"><path d="M16 21v-2a4 4 0 0 0-4-4H5a4 4 0 0 0-4 4v2"/><circle cx="8.5" cy="7" r="4"/><path d="M20 8v6"/><path d="M23 11h-6"/></svg>
<span style="font-size:11px;color:#111827;font-weight:700">{lead.contactCount}/{lead.maxContacts}</span>
<span style="font-size:10px;color:#6B7280">contacted</span>
</span>
<span style="font-size:12px;color:#6B7280;font-weight:700">{Math.max(0, lead.maxContacts - lead.contactCount)} slots left</span>
</div>
</div>
<div style="display:flex;flex-direction:column;align-items:flex-end;gap:8px">
<p style="margin:0;font-size:11px;letter-spacing:0.06em;text-transform:uppercase;color:#FF5E13;font-weight:700">{lead.match}</p>
<p style="margin:0;font-size:24px;line-height:1;font-weight:800;color:#111827">{lead.budget}</p>
<p style="margin:0;font-size:11px;color:#9CA3AF">Est. Budget</p>
<div style="display:flex;gap:6px;flex-wrap:wrap;justify-content:flex-end;padding-top:2px">
<button type="button" onClick={() => openLeadDetailsInNewTab(lead.id)} style="height:30px;border-radius:8px;border:1px solid #E5E7EB;background:white;padding:0 10px;font-size:12px;font-weight:700;color:#374151">View Details</button>
<Show when={lead.status === 'open'}>
<button
type="button"
onClick={() => openLeadContactConfirm(lead.id)}
disabled={usableLeadCredits() < leadCostPerContact || lead.contactCount >= lead.maxContacts}
style={`height:30px;border-radius:8px;border:none;padding:0 10px;font-size:12px;font-weight:700;color:white;background:${usableLeadCredits() < leadCostPerContact || lead.contactCount >= lead.maxContacts ? '#9CA3AF' : '#03004E'};cursor:${usableLeadCredits() < leadCostPerContact || lead.contactCount >= lead.maxContacts ? 'not-allowed' : 'pointer'}`}
>
Request Contact
</button>
</Show>
<Show when={lead.status === 'requested'}>
<button type="button" onClick={() => cancelLeadRequest(lead.id)} style="height:30px;border-radius:8px;border:1px solid #FECACA;background:#FEF2F2;padding:0 10px;font-size:12px;font-weight:700;color:#B91C1C">Cancel Request</button>
</Show>
</div>
</div>
</div>
</div>
)}
</For>
</div>
<div style="padding:10px 12px;border-top:1px solid #E5E7EB;display:flex;justify-content:space-between;align-items:center">
<p style="margin:0;font-size:12px;color:#6B7280">
Showing {pagedLeadCards().length ? (leadPage() - 1) * leadsPerPage + 1 : 0} to {(leadPage() - 1) * leadsPerPage + pagedLeadCards().length} of {filteredLeadCards().length} leads
</p>
<div style="display:flex;gap:6px">
<For each={Array.from({ length: totalLeadPages() }, (_, i) => i + 1)}>
{(pageNo) => (
<button
type="button"
onClick={() => setLeadPage(pageNo)}
style={`width:30px;height:30px;border-radius:8px;border:1px solid #E5E7EB;background:${leadPage() === pageNo ? '#FF5E13' : 'white'};color:${leadPage() === pageNo ? 'white' : '#6B7280'};font-size:12px;font-weight:700`}
>
{pageNo}
</button>
)}
</For>
</div>
</div>
</div>
<Show when={leadContactConfirmId()}>
<div style="position:fixed;inset:0;background:rgba(15,23,42,0.45);display:flex;align-items:center;justify-content:center;z-index:60;padding:16px">
<div style="width:min(460px,100%);border:1px solid #E5E7EB;background:white;border-radius:14px;box-shadow:0 10px 30px rgba(0,0,0,0.18);padding:16px">
<div style="display:flex;align-items:center;gap:8px">
<span style="width:22px;height:22px;border-radius:999px;background:#FFF1EB;color:#FF5E13;display:inline-flex;align-items:center;justify-content:center;font-size:13px;font-weight:800">T</span>
<p style="margin:0;font-size:16px;font-weight:800;color:#111827">Confirm Contact Unlock</p>
</div>
<p style="margin:10px 0 0;font-size:13px;color:#374151;line-height:1.5">You are about to spend <strong>25 Tracecoins</strong> to request and view this service seeker contact when approved. Do you want to continue?</p>
<div style="display:flex;justify-content:flex-end;gap:8px;margin-top:14px">
<button type="button" onClick={() => setLeadContactConfirmId('')} style="height:34px;border-radius:8px;border:1px solid #D1D5DB;background:white;color:#374151;padding:0 14px;font-size:12px;font-weight:700">Cancel</button>
<button type="button" onClick={confirmLeadContactRequest} style="height:34px;border-radius:8px;border:none;background:#03004E;color:white;padding:0 14px;font-size:12px;font-weight:700">Yes, Request Contact</button>
</div>
</div>
</div>
</Show>
</div>
);
}
if (customerKey() === 'jobs') {
if (isJobSeekerRole()) {
const selectedJob = () => jobBoardJobs().find((job) => job.id === jobSeekerSelectedId()) || jobBoardJobs()[0];
const selectedAppliedTab = normalizeTabKey(tab) === 'applied';
const selectedOpenJobCards = () => {
const tabKey = normalizeTabKey(tab);
const rows = jobBoardJobs();
if (tabKey === 'recommended') return rows.slice(0, 3);
if (tabKey === 'saved') return rows.slice(1);
if (tabKey === 'expiring soon') return rows.filter((_, idx) => idx % 2 === 0);
return rows;
};
const stepWidth = () => `${Math.round((jobSeekerApplyStep() / 4) * 100)}%`;
if (selectedAppliedTab) {
return (
<div style="display:flex;flex-direction:column;gap:10px">
<div style="display:flex;justify-content:space-between;align-items:center">
<div style="display:grid;grid-template-columns:repeat(4,minmax(0,1fr));gap:8px;flex:1">
{[
{ label: 'Total Applications', value: '24', tone: 'dark' },
{ label: 'Under Review', value: '08', tone: 'blue' },
{ label: 'Shortlisted', value: '03', tone: 'orange' },
{ label: 'Interviews', value: '02', tone: 'green' },
].map((card) => (
<div style={`border:1px solid #E5E7EB;border-radius:12px;padding:10px;background:${card.tone === 'dark' ? '#03004E' : 'white'};color:${card.tone === 'dark' ? 'white' : '#111827'};box-shadow:0 1px 3px rgba(0,0,0,0.05)`}>
<p style={`margin:0;font-size:11px;letter-spacing:0.05em;text-transform:uppercase;color:${card.tone === 'dark' ? '#D7DBFF' : '#9CA3AF'}`}>{card.label}</p>
<p style={`margin:4px 0 0;font-size:32px;line-height:1;font-weight:800;color:${card.tone === 'orange' ? '#C2410C' : card.tone === 'green' ? '#16A34A' : card.tone === 'blue' ? '#2563EB' : card.tone === 'dark' ? 'white' : '#111827'}`}>{card.value}</p>
</div>
))}
</div>
<div style="display:flex;gap:8px;margin-left:10px">
<button type="button" style="height:34px;border-radius:8px;border:1px solid #E5E7EB;background:white;padding:0 10px;font-size:12px;font-weight:700;color:#374151">Filters</button>
<button type="button" style="height:34px;border-radius:8px;border:1px solid #E5E7EB;background:white;padding:0 10px;font-size:12px;font-weight:700;color:#374151">Most Recent</button>
</div>
</div>
<div style="border:1px solid #E5E7EB;border-radius:14px;background:white;overflow:hidden;box-shadow:0 1px 4px rgba(0,0,0,0.06)">
<div style="padding:10px 12px;border-bottom:1px solid #E5E7EB;display:flex;justify-content:space-between;align-items:center">
<p style="margin:0;font-size:14px;font-weight:800;color:#111827;letter-spacing:0.05em;text-transform:uppercase">Latest Updates</p>
<span style="font-size:11px;color:#9CA3AF">Track your application status and recruiter responses</span>
</div>
<div style="display:grid;gap:8px;padding:10px">
<For each={JOB_SEEKER_APPLIED_ROWS}>
{(row) => {
const tone = row.status === 'Shortlisted'
? { bg: '#FFF1EB', c: '#C2410C' }
: row.status === 'Under Review'
? { bg: '#E8F0FF', c: '#2563EB' }
: row.status === 'Not Selected'
? { bg: '#FEE2E2', c: '#B91C1C' }
: { bg: '#F3F4F6', c: '#374151' };
return (
<div style={`border:1px solid #E5E7EB;border-left:4px solid ${tone.c};border-radius:10px;background:#FCFCFD;padding:10px;display:grid;grid-template-columns:1fr auto auto;gap:10px;align-items:center`}>
<div>
<p style="margin:0;font-size:10px;letter-spacing:0.05em;text-transform:uppercase;color:#6B7280">{row.id} Applied On Oct 12, 2023</p>
<p style="margin:4px 0 0;font-size:28px;line-height:1.2;font-weight:800;color:#111827">{row.title}</p>
<p style="margin:4px 0 0;font-size:13px;color:#4B5563">{row.company} {row.location}</p>
</div>
<div style="text-align:right">
<span style={`height:24px;padding:0 10px;border-radius:999px;background:${tone.bg};color:${tone.c};font-size:11px;font-weight:700;display:inline-flex;align-items:center`}>{row.status}</span>
<p style="margin:6px 0 0;font-size:12px;color:#6B7280">{row.note}</p>
</div>
<button type="button" style="height:32px;border-radius:8px;border:1px solid #E5E7EB;background:white;padding:0 10px;font-size:12px;font-weight:700;color:#374151">View Details</button>
</div>
);
}}
</For>
</div>
</div>
<div style="border:1px solid #FFD8C2;border-radius:14px;background:#FFF8F4;padding:14px;display:flex;justify-content:space-between;align-items:center;gap:10px">
<div>
<p style="margin:0;font-size:30px;line-height:1.2;font-weight:800;color:#111827">Boost your response rate</p>
<p style="margin:6px 0 0;font-size:13px;color:#6B7280">Recruiters are more likely to respond to profiles with updated resume and portfolio links.</p>
</div>
<button type="button" onClick={() => setJobSeekerScreen('apply')} style="height:34px;border:none;border-radius:9px;background:#03004E;color:white;padding:0 12px;font-size:12px;font-weight:700;white-space:nowrap">Optimize Profile</button>
</div>
</div>
);
}
if (jobSeekerScreen() === 'apply') {
return (
<div style="border:1px solid #E5E7EB;border-radius:16px;background:white;box-shadow:0 1px 4px rgba(0,0,0,0.06);overflow:hidden">
<div style="display:grid;grid-template-columns:280px 1fr;min-height:520px">
<aside style="background:#03004E;color:white;padding:14px;display:flex;flex-direction:column;justify-content:space-between">
<div>
<p style="margin:0;font-size:36px;line-height:1.1;font-weight:800">Nxtgauge</p>
<p style="margin:12px 0 0;font-size:10px;letter-spacing:0.08em;text-transform:uppercase;color:#A7B2FF">Applying For</p>
<p style="margin:4px 0 0;font-size:42px;line-height:1.1;font-weight:800">{selectedJob().title}</p>
<p style="margin:8px 0 0;font-size:13px;color:#D7DBFF">Full-time Remote {selectedJob().salary}</p>
<div style="margin-top:14px;border:1px solid rgba(255,255,255,0.18);border-radius:10px;padding:10px;background:rgba(255,255,255,0.04)">
<p style="margin:0;font-size:12px;font-weight:700">{selectedJob().company}</p>
<p style="margin:3px 0 0;font-size:11px;color:#D7DBFF">{selectedJob().location}</p>
</div>
</div>
<div>
<p style="margin:0;font-size:12px;color:#D7DBFF">Step {jobSeekerApplyStep()} of 4</p>
<div style="height:4px;border-radius:999px;background:rgba(255,255,255,0.2);margin-top:6px;overflow:hidden"><div style={`height:100%;background:#FF5E13;width:${stepWidth()}`} /></div>
</div>
</aside>
<div style="padding:14px;display:flex;flex-direction:column;gap:12px">
<div style="display:flex;align-items:center;gap:8px;border-bottom:1px solid #E5E7EB;padding-bottom:10px">
{[
{ key: 1, label: 'Profile' },
{ key: 2, label: 'Materials' },
{ key: 3, label: 'Details' },
{ key: 4, label: 'Review' },
].map((step, idx, arr) => (
<>
<button
type="button"
onClick={() => setJobSeekerApplyStep(step.key)}
style={`height:28px;padding:0 10px;border-radius:999px;border:1px solid ${jobSeekerApplyStep() === step.key ? '#FF5E13' : '#E5E7EB'};background:${jobSeekerApplyStep() === step.key ? '#FFF1EB' : '#F9FAFB'};font-size:11px;font-weight:700;color:${jobSeekerApplyStep() === step.key ? '#C2410C' : '#6B7280'}`}
>
{step.key}. {step.label}
</button>
<Show when={idx < arr.length - 1}><span style="color:#D1D5DB"></span></Show>
</>
))}
</div>
<p style="margin:0;font-size:38px;line-height:1.1;font-weight:800;color:#111827">Resume & Portfolio</p>
<p style="margin:0;font-size:14px;color:#6B7280">Showcase your best work and professional journey to the hiring team.</p>
<div>
<p style="margin:0 0 6px;font-size:11px;letter-spacing:0.06em;text-transform:uppercase;color:#6B7280">Resume (PDF)</p>
<div style="min-height:120px;border:1px dashed #F4B6A0;border-radius:12px;background:#FFFCFB;display:flex;flex-direction:column;align-items:center;justify-content:center;gap:8px">
<FileText size={24} style="color:#C2410C" />
<p style="margin:0;font-size:14px;color:#111827">Drop your resume here or <span style="color:#C2410C;font-weight:700">browse</span></p>
<p style="margin:0;font-size:12px;color:#6B7280">Maximum file size: 5MB</p>
</div>
</div>
<div style="display:grid;grid-template-columns:1fr 1fr;gap:10px">
<div style="grid-column:1 / -1">
<p style="margin:0 0 6px;font-size:11px;letter-spacing:0.06em;text-transform:uppercase;color:#6B7280">Portfolio Link</p>
<div style="height:40px;border:1px solid #E5E7EB;border-radius:10px;background:#F9FAFB;padding:0 10px;display:flex;align-items:center;font-size:13px;color:#6B7280">https://yourportfolio.com</div>
</div>
<div style="height:54px;border:1px solid #E5E7EB;border-radius:10px;background:#F9FAFB;padding:0 10px;display:flex;align-items:center;justify-content:space-between">
<div><p style="margin:0;font-size:12px;font-weight:700;color:#111827">Cover Letter</p><p style="margin:2px 0 0;font-size:11px;color:#6B7280">Optional doc</p></div>
<button type="button" style="width:20px;height:20px;border-radius:999px;border:none;background:#FFF1EB;color:#C2410C;font-size:14px;font-weight:700;line-height:1">+</button>
</div>
<div style="height:54px;border:1px solid #E5E7EB;border-radius:10px;background:#F9FAFB;padding:0 10px;display:flex;align-items:center;justify-content:space-between">
<div><p style="margin:0;font-size:12px;font-weight:700;color:#111827">GitHub Profile</p><p style="margin:2px 0 0;font-size:11px;color:#6B7280">Optional link</p></div>
<button type="button" style="width:20px;height:20px;border-radius:999px;border:none;background:#FFF1EB;color:#C2410C;font-size:14px;font-weight:700;line-height:1">+</button>
</div>
</div>
<div style="display:flex;align-items:center;justify-content:space-between;border-top:1px solid #E5E7EB;padding-top:10px">
<button type="button" onClick={() => setJobSeekerScreen('detail')} style="height:34px;border-radius:8px;border:1px solid #E5E7EB;background:white;padding:0 12px;font-size:12px;font-weight:700;color:#374151">Back</button>
<div style="display:flex;gap:8px">
<button type="button" style="height:34px;border-radius:8px;border:1px solid #E5E7EB;background:white;padding:0 12px;font-size:12px;font-weight:700;color:#374151">Save Draft</button>
<button type="button" onClick={() => setJobSeekerApplyStep((prev) => Math.min(4, prev + 1))} style="height:34px;border-radius:8px;border:none;background:#C2410C;padding:0 14px;font-size:12px;font-weight:700;color:white">Continue</button>
</div>
</div>
</div>
</div>
</div>
);
}
if (jobSeekerScreen() === 'detail') {
return (
<div style="display:grid;grid-template-columns:1.1fr 1fr;gap:10px;align-items:start">
<div style="border:1px solid #E5E7EB;border-radius:14px;background:white;padding:12px;display:flex;flex-direction:column;gap:8px;box-shadow:0 1px 4px rgba(0,0,0,0.06);max-height:78vh;overflow:auto">
<p style="margin:0;font-size:24px;line-height:1.15;font-weight:800;color:#111827">Available Positions</p>
<p style="margin:0;font-size:13px;color:#6B7280">Found 128 high-match opportunities for your profile.</p>
<For each={selectedOpenJobCards()}>
{(job) => (
<button
type="button"
onClick={() => setJobSeekerSelectedId(job.id)}
style={`text-align:left;border:1px solid ${job.id === selectedJob().id ? '#FF5E13' : '#E5E7EB'};border-radius:12px;background:${job.id === selectedJob().id ? '#FFFCFA' : '#FCFCFD'};padding:10px;display:grid;gap:6px`}
>
<div style="display:flex;justify-content:space-between;align-items:center">
<p style="margin:0;font-size:18px;line-height:1.25;font-weight:800;color:#111827">{job.title}</p>
<span style="height:20px;padding:0 8px;border-radius:999px;background:#FFF1EB;color:#C2410C;font-size:10px;font-weight:700;display:inline-flex;align-items:center">Top Match</span>
</div>
<p style="margin:0;font-size:13px;color:#4B5563">{job.company} {job.location}</p>
<p style="margin:0;font-size:13px;color:#6B7280;line-height:1.45">We are looking for a visionary professional to lead core product experiences and collaborate across teams.</p>
<div style="display:flex;justify-content:space-between;align-items:center;gap:8px">
<div style="display:flex;flex-wrap:wrap;gap:6px">
<For each={job.tags.slice(0, 3)}>
{(tag) => <span style="height:22px;padding:0 8px;border-radius:7px;border:1px solid #E5E7EB;background:#F3F4F6;font-size:10px;font-weight:700;color:#4B5563;display:inline-flex;align-items:center">{tag}</span>}
</For>
</div>
<p style="margin:0;font-size:20px;line-height:1.1;font-weight:800;color:#111827">{job.salary}</p>
</div>
</button>
)}
</For>
</div>
<div style="border:1px solid #E5E7EB;border-radius:14px;background:white;box-shadow:0 1px 4px rgba(0,0,0,0.06);overflow:hidden;max-height:78vh;display:flex;flex-direction:column">
<div style="height:140px;background:linear-gradient(180deg,#d8e3eb 0%,#f8fafc 100%);position:relative">
<button type="button" onClick={() => setJobSeekerScreen('list')} style="position:absolute;right:10px;top:10px;width:28px;height:28px;border-radius:999px;border:1px solid rgba(255,255,255,0.7);background:rgba(17,24,39,0.35);color:white;display:flex;align-items:center;justify-content:center">×</button>
</div>
<div style="padding:12px;display:grid;gap:10px;overflow:auto">
<div>
<p style="margin:0;font-size:26px;line-height:1.2;font-weight:800;color:#111827">{selectedJob().title}</p>
<p style="margin:6px 0 0;font-size:13px;font-weight:700;color:#C2410C">{selectedJob().company} {selectedJob().location}</p>
</div>
<div style="display:grid;grid-template-columns:repeat(2,minmax(0,1fr));gap:8px">
{[
['Location', selectedJob().location],
['Experience', selectedJob().exp],
['Employment', selectedJob().type],
['Compensation', selectedJob().salary],
['Match', selectedJob().match],
['Posted', selectedJob().posted],
].map(([label, val]) => (
<div style="border:1px solid #E5E7EB;border-radius:10px;background:#F9FAFB;padding:10px">
<p style="margin:0;font-size:10px;letter-spacing:0.06em;text-transform:uppercase;color:#9CA3AF">{label}</p>
<p style="margin:5px 0 0;font-size:13px;font-weight:700;color:#111827">{val}</p>
</div>
))}
</div>
<div>
<p style="margin:0;font-size:11px;letter-spacing:0.09em;text-transform:uppercase;color:#6B7280">Skills</p>
<div style="display:flex;flex-wrap:wrap;gap:6px;margin-top:7px">
<For each={selectedJob().tags}>
{(tag) => <span style="height:24px;padding:0 9px;border-radius:8px;border:1px solid #E5E7EB;background:#F3F4F6;font-size:10px;font-weight:700;color:#374151;display:inline-flex;align-items:center">{tag}</span>}
</For>
</div>
</div>
<div>
<p style="margin:0;font-size:11px;letter-spacing:0.09em;text-transform:uppercase;color:#6B7280">The Opportunity</p>
<p style="margin:6px 0 0;font-size:14px;line-height:1.6;color:#374151">As a senior role at Nxtgauge, you will shape product experiences used by thousands of professionals and build systems that scale with ambition.</p>
</div>
<div>
<p style="margin:0;font-size:11px;letter-spacing:0.09em;text-transform:uppercase;color:#6B7280">Core Requirements</p>
<div style="display:grid;gap:6px;margin-top:6px">
{[
'Mastery of product and design system governance.',
'Strong track record of shipping complex B2B products.',
'Deep understanding of accessibility and UX research.',
].map((item) => <p style="margin:0;font-size:13px;color:#374151;line-height:1.45"> {item}</p>)}
</div>
</div>
<div style="border:1px solid #191970;border-radius:12px;background:#03004E;color:white;padding:12px">
<p style="margin:0;font-size:10px;letter-spacing:0.08em;text-transform:uppercase;color:#CDD4FF">Total Compensation</p>
<p style="margin:6px 0 0;font-size:24px;line-height:1.2;font-weight:800">{selectedJob().salary} <span style="font-size:12px;font-weight:600">/ year</span></p>
<div style="display:flex;gap:8px;margin-top:10px">
<button type="button" onClick={() => { setJobSeekerApplyStep(2); setJobSeekerScreen('apply'); }} style="height:36px;flex:1;border:none;border-radius:9px;background:#FF5E13;color:white;padding:0 12px;font-size:12px;font-weight:700">Apply Now</button>
<button type="button" style="width:36px;height:36px;border-radius:9px;border:1px solid rgba(255,255,255,0.3);background:transparent;color:white;display:flex;align-items:center;justify-content:center"><Bookmark size={14} /></button>
</div>
<p style="margin:8px 0 0;font-size:10px;letter-spacing:0.08em;text-transform:uppercase;color:#AAB4FF;text-align:center">Application update: {selectedJob().posted}</p>
</div>
</div>
</div>
</div>
);
}
return (
<div style="display:flex;flex-direction:column;gap:10px">
<div style="display:grid;grid-template-columns:repeat(5,minmax(0,1fr));gap:8px">
{[
{ label: 'Matching Jobs', value: '124', accent: '#B91C1C', hint: '' },
{ label: 'Saved Jobs', value: '12', accent: '#4338CA', hint: '' },
{ label: 'Applied', value: '48', accent: '#6B7280', hint: '' },
{ label: 'Shortlisted', value: '06', accent: '#0EA5E9', hint: '' },
{ label: 'New Today', value: '18', accent: 'white', hint: 'dark' },
].map((card) => (
<div style={`border:1px solid #E5E7EB;border-radius:12px;padding:10px;background:${card.hint === 'dark' ? '#03004E' : 'white'};box-shadow:0 1px 3px rgba(0,0,0,0.05)`}>
<p style={`margin:0;font-size:10px;letter-spacing:0.06em;text-transform:uppercase;color:${card.hint === 'dark' ? '#D7DBFF' : '#6B7280'}`}>{card.label}</p>
<p style={`margin:5px 0 0;font-size:34px;line-height:1;font-weight:800;color:${card.hint === 'dark' ? 'white' : card.accent}`}>{card.value}</p>
</div>
))}
</div>
<div style="border:1px solid #E5E7EB;border-radius:12px;background:white;padding:10px;display:flex;align-items:center;gap:8px;flex-wrap:wrap;box-shadow:0 1px 3px rgba(0,0,0,0.05)">
<button type="button" style="height:32px;border-radius:8px;border:1px solid #E5E7EB;background:#F9FAFB;padding:0 10px;font-size:12px;font-weight:700;color:#374151">Filters</button>
{['Role: All Roles', 'Industry: Tech', 'Location: Remote', 'Salary: $100k+', 'Experience: Senior'].map((item) => (
<button type="button" style="height:32px;border-radius:8px;border:1px solid #E5E7EB;background:white;padding:0 10px;font-size:12px;color:#374151">{item}</button>
))}
<button type="button" style="height:32px;border-radius:8px;border:none;background:#EEF2FF;padding:0 10px;font-size:12px;font-weight:700;color:#3730A3">Reset</button>
</div>
<div style="display:grid;gap:8px">
<For each={selectedOpenJobCards()}>
{(job) => (
<div style="border:1px solid #E5E7EB;border-radius:12px;background:white;padding:10px;display:grid;grid-template-columns:1fr auto;gap:10px;box-shadow:0 1px 3px rgba(0,0,0,0.05)">
<div>
<p style="margin:0;font-size:20px;line-height:1.25;font-weight:800;color:#111827">{job.title}</p>
<p style="margin:5px 0 0;font-size:13px;color:#4B5563">{job.company} {job.location}</p>
<div style="display:flex;flex-wrap:wrap;gap:6px;margin-top:8px">
{[job.salary, job.exp, job.type].map((pill, idx) => (
<span style={`height:22px;padding:0 8px;border-radius:8px;border:1px solid #E5E7EB;background:${idx === 2 ? '#FFF8F4' : '#F9FAFB'};font-size:10px;font-weight:700;color:${idx === 2 ? '#C2410C' : '#374151'};display:inline-flex;align-items:center`}>{pill}</span>
))}
</div>
<div style="display:flex;flex-wrap:wrap;gap:6px;margin-top:8px">
<For each={job.tags}>{(tag) => <span style="height:22px;padding:0 8px;border-radius:7px;border:1px solid #E5E7EB;background:#F3F4F6;font-size:10px;font-weight:700;color:#4B5563;display:inline-flex;align-items:center">{tag}</span>}</For>
</div>
</div>
<div style="display:flex;flex-direction:column;align-items:flex-end;justify-content:space-between;gap:8px;min-width:200px">
<div style="text-align:right">
<span style="height:20px;padding:0 8px;border-radius:999px;background:#FFF1EB;color:#C2410C;font-size:10px;font-weight:700;display:inline-flex;align-items:center">{job.match}</span>
<p style="margin:8px 0 0;font-size:12px;color:#9CA3AF">{job.posted}</p>
</div>
<div style="display:flex;align-items:center;gap:6px">
<button
type="button"
onClick={() => {
setJobSeekerSelectedId(job.id);
setJobSeekerScreen('detail');
}}
style="height:32px;border-radius:8px;border:1px solid #E5E7EB;background:white;padding:0 10px;font-size:12px;font-weight:700;color:#374151"
>
View Details
</button>
<button
type="button"
onClick={() => {
setJobSeekerSelectedId(job.id);
setJobSeekerApplyStep(2);
setJobSeekerScreen('apply');
}}
style="height:32px;border-radius:8px;border:none;background:#03004E;padding:0 12px;font-size:12px;font-weight:700;color:white"
>
Apply Now
</button>
</div>
</div>
</div>
)}
</For>
</div>
<div style="display:flex;justify-content:center">
<button type="button" style="height:34px;border-radius:10px;border:1px solid #FFD8C2;background:#FFFCFA;padding:0 16px;font-size:12px;font-weight:700;color:#C2410C">Show More Opportunities</button>
</div>
</div>
);
}
const progressWidth = () => `${Math.round((jobPostStep() / COMPANY_JOB_STEPS.length) * 100)}%`;
const latestSubmission = () => companyJobSubmissions()[0] || null;
const latestStatusLabel = () => {
const status = latestSubmission()?.status;
if (status === 'VERIFICATION_PENDING') return 'In Verification Management';
if (status === 'VERIFIED' || status === 'APPROVAL_PENDING') return 'In Approval Management';
if (status === 'APPROVED_LIVE') return 'Approved and Live';
return 'Submitted';
};
const goNextJobStep = () => {
if (jobPostStep() >= COMPANY_JOB_STEPS.length) {
setJobPostView('review');
return;
}
setJobPostStep((prev) => Math.min(COMPANY_JOB_STEPS.length, prev + 1));
};
const goPrevJobStep = () => setJobPostStep((prev) => Math.max(1, prev - 1));
if (jobPostView() === 'success') {
return (
<div style="display:grid;grid-template-columns:2fr 1fr;gap:10px">
<div style="border:1px solid #E5E7EB;background:white;border-radius:16px;padding:20px;box-shadow:0 1px 4px rgba(0,0,0,0.06)">
<div style="display:flex;flex-direction:column;align-items:center;text-align:center;padding:16px 0 8px">
<div style="width:150px;height:150px;border-radius:999px;border:1px solid #F3E2DA;background:#FFF9F5;display:flex;align-items:center;justify-content:center">
<div style="width:56px;height:56px;border-radius:999px;background:#C2410C;color:white;display:flex;align-items:center;justify-content:center;font-size:28px;font-weight:800"></div>
</div>
<p style="margin:14px 0 0;font-size:52px;line-height:1.05;font-weight:800;color:#0D0D2A">Job Submitted Successfully!</p>
<p style="margin:10px 0 0;font-size:14px;color:#4B5563;max-width:640px;line-height:1.5">Your listing for <strong>{companyJobDraft().title}</strong> has been sent to Verification Management first, then Approval Management. Job seekers can see it only after final approval.</p>
<div style="margin-top:24px;padding-top:20px;border-top:1px solid #E5E7EB;display:flex;justify-content:flex-end;gap:12px">
<button type="button" style="height:40px;padding:0 20px;border-radius:10px;border:1px solid #E5E7EB;background:white;font-size:13px;font-weight:600;color:#374151;cursor:pointer">Discard Changes</button>
<Show
when={!verificationPending()}
fallback={
<button type="button" disabled style="height:40px;padding:0 24px;border-radius:10px;background:#F3F4F6;color:#9CA3AF;border:none;font-size:13px;font-weight:600;cursor:not-allowed">Application Submitted</button>
}
>
<button
type="button"
onClick={() => setVerificationPending(true)}
style="height:40px;padding:0 24px;border-radius:10px;background:#FF5E13;color:white;border:none;font-size:13px;font-weight:600;cursor:pointer;box-shadow:0 2px 4px rgba(255,94,19,0.2)"
>
Submit for Verification
</button>
</Show>
</div>
<p style="margin:8px 0 0;font-size:12px;font-weight:700;color:#C2410C">{latestStatusLabel()}</p>
<div style="display:flex;gap:8px;margin-top:14px">
<button type="button" style="height:36px;border-radius:10px;border:1px solid #E5E7EB;background:white;padding:0 14px;font-size:12px;font-weight:700;color:#374151">View Job Details</button>
<button type="button" onClick={() => { setJobPostView('form'); setJobPostStep(1); }} style="height:36px;border-radius:10px;border:none;background:#C2410C;padding:0 14px;font-size:12px;font-weight:700;color:white">Post Another Job</button>
</div>
</div>
<div style="margin-top:14px;border:1px solid #E5E7EB;border-radius:14px;background:#FCFCFD;padding:14px">
<div style="display:flex;justify-content:space-between;align-items:center">
<p style="margin:0;font-size:30px;font-weight:800;color:#111827">Approval Tracking</p>
<span style="height:24px;padding:0 10px;border-radius:999px;background:#FFF1EB;color:#C2410C;font-size:11px;font-weight:700;display:inline-flex;align-items:center">REF: #JX-9902</span>
</div>
<div style="display:grid;gap:10px;margin-top:12px">
{[
{ title: 'Job Submitted', desc: 'Form submitted by company.', done: true },
{ title: 'Verification Management', desc: 'Job content and policy checks run here first.', done: !!latestSubmission() && latestSubmission()!.status !== 'VERIFICATION_PENDING' },
{ title: 'Approval Management', desc: 'Post-verification approval gate for publishing.', done: !!latestSubmission() && latestSubmission()!.status === 'APPROVED_LIVE' },
{ title: 'Published To Job Seekers', desc: 'Visible in Jobs list and View Details only after approval.', done: !!latestSubmission() && latestSubmission()!.status === 'APPROVED_LIVE' },
].map((row, idx) => (
<div style="display:flex;align-items:flex-start;gap:10px">
<span style={`width:18px;height:18px;border-radius:999px;display:inline-flex;align-items:center;justify-content:center;font-size:10px;font-weight:800;flex-shrink:0;background:${row.done ? '#FFF1EB' : '#F3F4F6'};color:${row.done ? '#C2410C' : '#9CA3AF'}`}>{row.done ? '✓' : idx + 1}</span>
<div>
<p style={`margin:0;font-size:14px;font-weight:700;color:${row.done ? '#111827' : '#94A3B8'}`}>{row.title}</p>
<p style={`margin:2px 0 0;font-size:12px;line-height:1.45;color:${row.done ? '#6B7280' : '#B8C1D1'}`}>{row.desc}</p>
</div>
</div>
))}
</div>
</div>
</div>
<div style="display:flex;flex-direction:column;gap:10px">
<div style="border:1px solid #E5E7EB;background:#03004E;border-radius:16px;padding:14px;color:white;box-shadow:0 1px 4px rgba(0,0,0,0.06)">
<p style="margin:0;font-size:30px;line-height:1.15;font-weight:800">Did you know?</p>
<p style="margin:8px 0 0;font-size:13px;line-height:1.55;color:#D5D8FF">Jobs with high-quality company descriptions receive <strong>40% more applications</strong>. Take a moment to update your profile.</p>
<button type="button" style="margin-top:10px;height:30px;border:none;border-radius:8px;background:#C2410C;padding:0 10px;font-size:12px;font-weight:700;color:white">Enhance Profile</button>
</div>
<div style="border:1px solid #E5E7EB;background:white;border-radius:16px;padding:14px;box-shadow:0 1px 4px rgba(0,0,0,0.06)">
<p style="margin:0;font-size:18px;font-weight:800;color:#111827">Back to Dashboard</p>
<p style="margin:6px 0 0;font-size:12px;color:#6B7280">Monitor all your active listings and candidate inflow.</p>
<button type="button" style="margin-top:10px;height:34px;width:100%;border:1px solid #E5E7EB;border-radius:10px;background:#F9FAFB;padding:0 12px;font-size:12px;font-weight:700;color:#374151">Go to Dashboard</button>
</div>
<div style="border:1px solid #E5E7EB;background:white;border-radius:12px;padding:10px;display:flex;align-items:center;justify-content:space-between">
<div>
<p style="margin:0;font-size:13px;font-weight:700;color:#111827">Need help?</p>
<p style="margin:2px 0 0;font-size:11px;color:#6B7280">Chat with a hiring specialist</p>
</div>
<button type="button" style="height:28px;border:none;border-radius:8px;background:#FFF1EB;color:#C2410C;padding:0 10px;font-size:11px;font-weight:700">Connect</button>
</div>
</div>
</div>
);
}
if (jobPostView() === 'review') {
return (
<div style="display:grid;grid-template-columns:2fr 1fr;gap:10px">
<div style="display:flex;flex-direction:column;gap:10px">
<div style="border:1px solid #E5E7EB;background:white;border-radius:16px;padding:14px;box-shadow:0 1px 4px rgba(0,0,0,0.06)">
<p style="margin:0;font-size:11px;letter-spacing:0.05em;text-transform:uppercase;color:#9CA3AF">Jobs &gt; Review & Checkout</p>
<p style="margin:6px 0 0;font-size:50px;line-height:1.05;font-weight:800;color:#0D0D2A">Review & Checkout</p>
<p style="margin:8px 0 0;font-size:14px;color:#6B7280">Step 5 of 5: Finalize your job posting details and approve payment.</p>
</div>
<div style="border:1px solid #E5E7EB;background:white;border-radius:16px;padding:14px;box-shadow:0 1px 4px rgba(0,0,0,0.06)">
<div style="display:flex;justify-content:space-between;align-items:center">
<p style="margin:0;font-size:35px;font-weight:800;color:#111827">Job Basics</p>
<button type="button" style="height:28px;border:none;border-radius:8px;background:#FFF1EB;color:#C2410C;padding:0 10px;font-size:11px;font-weight:700">Edit</button>
</div>
<div style="display:grid;grid-template-columns:1fr 1fr;gap:8px;margin-top:10px">
{[
['Job Title', companyJobDraft().title],
['Employment Type', companyJobDraft().type],
['Company Department', companyJobDraft().department],
['Openings', companyJobDraft().openings],
].map(([label, value]) => (
<div style="border:1px solid #E5E7EB;border-radius:10px;background:#F9FAFB;padding:10px">
<p style="margin:0;font-size:10px;letter-spacing:0.06em;text-transform:uppercase;color:#9CA3AF">{label}</p>
<p style="margin:6px 0 0;font-size:20px;font-weight:800;color:#111827;line-height:1.2">{value}</p>
</div>
))}
</div>
</div>
<div style="border:1px solid #E5E7EB;background:white;border-radius:16px;padding:14px;box-shadow:0 1px 4px rgba(0,0,0,0.06)">
<div style="display:flex;justify-content:space-between;align-items:center">
<p style="margin:0;font-size:35px;font-weight:800;color:#111827">Role & Requirements</p>
<button type="button" style="height:28px;border:none;border-radius:8px;background:#FFF1EB;color:#C2410C;padding:0 10px;font-size:11px;font-weight:700">Edit</button>
</div>
<p style="margin:10px 0 0;font-size:11px;letter-spacing:0.05em;text-transform:uppercase;color:#9CA3AF">Technical Skills</p>
<div style="display:flex;flex-wrap:wrap;gap:6px;margin-top:6px">
<For each={companyJobDraft().tags}>
{(skill) => <span style="height:24px;padding:0 8px;border-radius:8px;border:1px solid #E5E7EB;background:#F3F4F6;font-size:11px;font-weight:700;color:#374151;display:inline-flex;align-items:center">{skill}</span>}
</For>
</div>
<p style="margin:10px 0 0;font-size:11px;letter-spacing:0.05em;text-transform:uppercase;color:#9CA3AF">Required Experience</p>
<p style="margin:4px 0 0;font-size:16px;font-weight:700;color:#111827">{companyJobDraft().exp}</p>
</div>
<div style="border:1px solid #E5E7EB;background:white;border-radius:16px;padding:14px;box-shadow:0 1px 4px rgba(0,0,0,0.06)">
<div style="display:flex;justify-content:space-between;align-items:center">
<p style="margin:0;font-size:35px;font-weight:800;color:#111827">Compensation & Location</p>
<button type="button" style="height:28px;border:none;border-radius:8px;background:#FFF1EB;color:#C2410C;padding:0 10px;font-size:11px;font-weight:700">Edit</button>
</div>
<div style="display:grid;grid-template-columns:1fr 1fr;gap:8px;margin-top:10px">
<div style="border:1px solid #E5E7EB;border-radius:10px;background:#F9FAFB;padding:10px">
<p style="margin:0;font-size:10px;letter-spacing:0.06em;text-transform:uppercase;color:#9CA3AF">Salary Range</p>
<p style="margin:6px 0 0;font-size:20px;font-weight:800;color:#111827">{companyJobDraft().salary} / year</p>
</div>
<div style="border:1px solid #E5E7EB;border-radius:10px;background:#F9FAFB;padding:10px">
<p style="margin:0;font-size:10px;letter-spacing:0.06em;text-transform:uppercase;color:#9CA3AF">Location</p>
<p style="margin:6px 0 0;font-size:20px;font-weight:800;color:#111827">{companyJobDraft().location}</p>
</div>
</div>
</div>
</div>
<div style="display:flex;flex-direction:column;gap:10px">
<div style="border:1px solid #E5E7EB;background:white;border-radius:16px;overflow:hidden;box-shadow:0 1px 4px rgba(0,0,0,0.06)">
<div style="background:#03004E;padding:12px;color:white">
<p style="margin:0;font-size:30px;font-weight:800;line-height:1.1">Pricing & Approval</p>
<p style="margin:6px 0 0;font-size:13px;color:#D7DBFF">Review costs and confirm submission</p>
</div>
<div style="padding:12px">
<div style="border:1px solid #E5E7EB;border-radius:10px;background:#F9FAFB;padding:10px;display:flex;justify-content:space-between;align-items:center">
<span style="font-size:14px;color:#374151">Post Status</span>
<span style="font-size:14px;font-weight:700;color:#C2410C">This is a Paid Job Post</span>
</div>
<div style="display:grid;gap:8px;margin-top:10px">
<div style="display:flex;justify-content:space-between;font-size:14px;color:#374151"><span>Credits Required</span><strong style="color:#111827">500 Tracecoins</strong></div>
<div style="display:flex;justify-content:space-between;font-size:14px;color:#374151"><span>Current Balance</span><strong style="color:#111827">1200 Tracecoins</strong></div>
<div style="height:1px;background:#E5E7EB" />
<div style="display:flex;justify-content:space-between;font-size:16px;color:#111827;font-weight:800"><span>Remaining Balance</span><span style="color:#16A34A">700 Tracecoins</span></div>
</div>
<div style="margin-top:10px;border:1px solid #FFD8C2;border-radius:10px;background:#FFF8F4;padding:10px">
<p style="margin:0;font-size:13px;font-weight:700;color:#111827">Approval Required: Yes</p>
<p style="margin:4px 0 0;font-size:12px;color:#6B7280;line-height:1.45">Your post will be reviewed by our moderation team within 24 hours.</p>
</div>
<button type="button" onClick={() => { submitCompanyJobForReview(); setJobPostView('success'); }} style="margin-top:10px;height:38px;width:100%;border:none;border-radius:10px;background:#03004E;color:white;font-size:12px;font-weight:700">Pay 500 Tracecoins & Submit</button>
</div>
</div>
<div style="border:1px solid #FFD8C2;background:linear-gradient(135deg,#3A1E00 0%,#C2410C 90%);border-radius:16px;padding:14px;color:white;box-shadow:0 1px 4px rgba(0,0,0,0.06)">
<p style="margin:0;font-size:11px;letter-spacing:0.06em;text-transform:uppercase;color:#FFE7DA">Pro Tip</p>
<p style="margin:8px 0 0;font-size:16px;line-height:1.4;font-weight:700">Boost your visibility by 40% with Sponsored Highlights.</p>
<button type="button" style="margin-top:10px;height:30px;border:1px solid rgba(255,255,255,0.35);border-radius:8px;background:transparent;color:white;padding:0 10px;font-size:12px;font-weight:700">Learn More</button>
</div>
<div style="display:flex;justify-content:space-between;gap:8px">
<button type="button" onClick={() => { setJobPostView('form'); setJobPostStep(4); }} style="height:34px;flex:1;border-radius:8px;border:1px solid #E5E7EB;background:white;color:#374151;padding:0 12px;font-size:12px;font-weight:700">Back</button>
<button type="button" onClick={() => { submitCompanyJobForReview(); setJobPostView('success'); }} style="height:34px;flex:1;border-radius:8px;border:none;background:#C2410C;color:white;padding:0 12px;font-size:12px;font-weight:700">Confirm & Submit</button>
</div>
</div>
</div>
);
}
return (
<div style="display:flex;flex-direction:column;gap:10px">
<div style="display:grid;grid-template-columns:2fr 3fr;gap:10px">
<div style="border:1px solid #E5E7EB;background:white;border-radius:16px;padding:14px;box-shadow:0 1px 4px rgba(0,0,0,0.06)">
<p style="margin:0;font-size:11px;letter-spacing:0.06em;text-transform:uppercase;color:#9CA3AF">Billing Policy</p>
<p style="margin:6px 0 0;font-size:34px;line-height:1.1;font-weight:800;color:#111827">Job Posting Rule</p>
<span style="margin-top:8px;height:22px;padding:0 8px;border-radius:999px;background:#ECFDF3;color:#15803D;font-size:11px;font-weight:700;display:inline-flex;align-items:center">Free First Job</span>
<div style="display:grid;grid-template-columns:1fr 1fr;gap:8px;margin-top:10px">
<div style="border:1px solid #E5E7EB;border-radius:10px;background:#F9FAFB;padding:10px"><p style="margin:0;font-size:11px;color:#6B7280">Cost Required</p><p style="margin:6px 0 0;font-size:20px;font-weight:800;color:#111827">0 Tracecoins</p></div>
<div style="border:1px solid #E5E7EB;border-radius:10px;background:#F9FAFB;padding:10px"><p style="margin:0;font-size:11px;color:#6B7280">Usage Count</p><p style="margin:6px 0 0;font-size:20px;font-weight:800;color:#111827">1 Job Posted</p></div>
</div>
</div>
<div style="border:1px solid #E5E7EB;background:white;border-radius:16px;padding:14px;box-shadow:0 1px 4px rgba(0,0,0,0.06)">
<p style="margin:0;font-size:11px;letter-spacing:0.06em;text-transform:uppercase;color:#9CA3AF">Lifecycle</p>
<p style="margin:6px 0 0;font-size:34px;line-height:1.1;font-weight:800;color:#111827">Approval Flow Status</p>
<div style="display:flex;align-items:center;gap:6px;margin-top:12px">
<For each={['Draft', 'Submitted', 'Under Review', 'Approved', 'Live']}>
{(step, idx) => (
<>
<span style={`width:26px;height:26px;border-radius:999px;display:inline-flex;align-items:center;justify-content:center;font-size:11px;font-weight:800;background:${idx() + 1 <= jobPostStep() ? '#C2410C' : '#E5E7EB'};color:${idx() + 1 <= jobPostStep() ? 'white' : '#9CA3AF'}`}>{idx() + 1}</span>
<Show when={idx() < 4}>
<span style={`height:2px;flex:1;min-width:18px;background:${idx() + 1 < jobPostStep() ? '#C2410C' : '#E5E7EB'}`} />
</Show>
</>
)}
</For>
</div>
</div>
</div>
<div style="display:grid;grid-template-columns:2fr 1fr;gap:10px">
<div style="border:1px solid #E5E7EB;background:white;border-radius:16px;padding:14px;box-shadow:0 1px 4px rgba(0,0,0,0.06)">
<p style="margin:0;font-size:36px;font-weight:800;color:#111827">Step {jobPostStep()}: {COMPANY_JOB_STEPS[jobPostStep() - 1]}</p>
<div style="height:6px;border-radius:999px;background:#E5E7EB;overflow:hidden;margin-top:10px"><div style={`height:100%;background:#C2410C;width:${progressWidth()}`} /></div>
<div style="display:grid;grid-template-columns:1fr 1fr;gap:10px;margin-top:12px">
<div style="grid-column:1 / -1;display:flex;flex-direction:column;gap:6px"><p style="margin:0;font-size:11px;letter-spacing:0.05em;text-transform:uppercase;color:#6B7280">Job Title</p><div style="height:38px;border:1px solid #E5E7EB;border-radius:10px;background:#F9FAFB;padding:0 10px;display:flex;align-items:center;font-size:12px;color:#374151">{companyJobDraft().title}</div></div>
<div style="display:flex;flex-direction:column;gap:6px"><p style="margin:0;font-size:11px;letter-spacing:0.05em;text-transform:uppercase;color:#6B7280">Department</p><div style="height:38px;border:1px solid #E5E7EB;border-radius:10px;background:#F9FAFB;padding:0 10px;display:flex;align-items:center;justify-content:space-between;font-size:12px;color:#374151"><span>{companyJobDraft().department}</span><span></span></div></div>
<div style="display:flex;flex-direction:column;gap:6px"><p style="margin:0;font-size:11px;letter-spacing:0.05em;text-transform:uppercase;color:#6B7280">Job Category</p><div style="height:38px;border:1px solid #E5E7EB;border-radius:10px;background:#F9FAFB;padding:0 10px;display:flex;align-items:center;justify-content:space-between;font-size:12px;color:#374151"><span>Select Category</span><span></span></div></div>
<div style="grid-column:1 / -1;display:flex;flex-direction:column;gap:6px"><p style="margin:0;font-size:11px;letter-spacing:0.05em;text-transform:uppercase;color:#6B7280">Employment Type</p><div style="display:flex;gap:6px"><span style="height:28px;padding:0 10px;border-radius:999px;border:1px solid #E5E7EB;background:#FFF1EB;color:#C2410C;display:inline-flex;align-items:center;font-size:11px;font-weight:700">{companyJobDraft().type}</span></div></div>
<div style="display:flex;flex-direction:column;gap:6px"><p style="margin:0;font-size:11px;letter-spacing:0.05em;text-transform:uppercase;color:#6B7280">Seniority</p><div style="height:38px;border:1px solid #E5E7EB;border-radius:10px;background:#F9FAFB;padding:0 10px;display:flex;align-items:center;justify-content:space-between;font-size:12px;color:#374151"><span>Entry Level</span><span></span></div></div>
<div style="display:flex;flex-direction:column;gap:6px"><p style="margin:0;font-size:11px;letter-spacing:0.05em;text-transform:uppercase;color:#6B7280">Openings</p><div style="height:38px;border:1px solid #E5E7EB;border-radius:10px;background:#F9FAFB;padding:0 10px;display:flex;align-items:center;font-size:12px;color:#374151">{companyJobDraft().openings}</div></div>
<div style="grid-column:1 / -1;display:flex;flex-direction:column;gap:6px"><p style="margin:0;font-size:11px;letter-spacing:0.05em;text-transform:uppercase;color:#6B7280">Description</p><div style="min-height:120px;border:1px solid #E5E7EB;border-radius:10px;background:#F9FAFB;padding:10px;font-size:12px;color:#9CA3AF">Detailed job responsibilities, skills, and expectations...</div></div>
</div>
<div style="display:flex;justify-content:space-between;align-items:center;gap:8px;margin-top:12px;border-top:1px solid #E5E7EB;padding-top:10px">
<button type="button" onClick={goPrevJobStep} disabled={jobPostStep() === 1} style={`height:34px;border-radius:8px;border:1px solid #E5E7EB;background:white;padding:0 12px;font-size:12px;font-weight:700;color:${jobPostStep() === 1 ? '#9CA3AF' : '#374151'};cursor:${jobPostStep() === 1 ? 'not-allowed' : 'pointer'}`}>Back</button>
<div style="display:flex;gap:8px">
<button type="button" style="height:34px;border-radius:8px;border:1px solid #E5E7EB;background:white;padding:0 12px;font-size:12px;font-weight:700;color:#374151">Save as Draft</button>
<button type="button" onClick={jobPostStep() === COMPANY_JOB_STEPS.length ? () => setJobPostView('review') : goNextJobStep} style="height:34px;border-radius:8px;border:none;background:#03004E;padding:0 12px;font-size:12px;font-weight:700;color:white">
{jobPostStep() === COMPANY_JOB_STEPS.length ? 'Go To Review' : 'Next: Role & Requirements'}
</button>
</div>
</div>
</div>
<div style="display:flex;flex-direction:column;gap:10px">
<div style="border:1px solid #D9D8F9;background:#4F4B8A;border-radius:16px;padding:14px;color:white;box-shadow:0 1px 4px rgba(0,0,0,0.06)">
<div style="display:flex;justify-content:space-between;align-items:center">
<p style="margin:0;font-size:11px;letter-spacing:0.06em;text-transform:uppercase;color:#DADAFB">Live Preview</p>
<span style="height:20px;padding:0 8px;border-radius:999px;border:1px solid rgba(255,255,255,0.3);font-size:10px;font-weight:700;display:inline-flex;align-items:center">FREE POST</span>
</div>
<p style="margin:10px 0 0;font-size:10px;color:#DADAFB;letter-spacing:0.06em;text-transform:uppercase">Position</p>
<p style="margin:4px 0 0;font-size:26px;line-height:1.15;font-weight:800;color:white">Product Design Manager</p>
<div style="display:grid;gap:8px;margin-top:12px">
{[
['Location', 'Remote / San Francisco'],
['Type', 'Full-Time Role'],
['Posting Fee', '$0.00 (Tracecoins: 0)'],
].map(([label, val]) => (
<div style="display:flex;justify-content:space-between;align-items:center;gap:8px">
<span style="font-size:11px;color:#D7DBFF">{label}</span>
<span style="font-size:12px;font-weight:700;color:white;text-align:right">{val}</span>
</div>
))}
</div>
</div>
<div style="border:1px solid #E5E7EB;background:#FFF8F4;border-radius:14px;padding:12px;box-shadow:0 1px 3px rgba(0,0,0,0.05)">
<p style="margin:0;font-size:12px;font-weight:700;color:#111827">Need help writing?</p>
<p style="margin:6px 0 0;font-size:12px;color:#6B7280;line-height:1.45">Try our AI Generator to create a compelling description in seconds.</p>
<button type="button" style="margin-top:8px;height:30px;border:none;border-radius:8px;background:#FFF1EB;color:#C2410C;padding:0 10px;font-size:11px;font-weight:700">Launch AI Assistant</button>
</div>
</div>
</div>
</div>
);
}
if (customerKey() === 'my requirements') {
const statusMeta = (value: string) => {
const key = String(value || '').toLowerCase();
if (key === 'approved') return { bg: '#DCFCE7', c: '#15803D', label: 'Approved' };
if (key === 'active') return { bg: '#DBEAFE', c: '#1D4ED8', label: 'Active' };
if (key === 'under review') return { bg: '#FFEDD5', c: '#C2410C', label: 'Under Review' };
if (key === 'draft') return { bg: '#F3F4F6', c: '#4B5563', label: 'Draft' };
if (key === 'closed') return { bg: '#E5E7EB', c: '#4B5563', label: 'Closed' };
if (key === 'rejected') return { bg: '#FEE2E2', c: '#B91C1C', label: 'Rejected' };
return { bg: '#F3F4F6', c: '#6B7280', label: titleCase(value) };
};
const filteredRows = requirementRows().filter((row) => {
if (tab === 'open') return row.status === 'active' || row.status === 'under review';
if (tab === 'closed') return row.status === 'closed';
if (tab === 'drafts') return row.status === 'draft';
return true;
});
const selectedRoleDetails = () => REQUIREMENT_ROLE_DETAILS[selectedRequirementRole()] || REQUIREMENT_ROLE_DETAILS.PHOTOGRAPHER;
const steps = ['Choose Type', 'Basic Details', 'Role-Specific', 'Budget & Location', 'Attachments', 'Review'];
const canGoBack = () => requirementsStep() > 1;
const nextLabel = () => (requirementsStep() === steps.length ? 'Submit For Approval' : 'Next');
const goNextStep = () => {
if (requirementsStep() >= steps.length) {
submitRequirementForReview();
return;
}
setRequirementsStep((prev) => Math.min(steps.length, prev + 1));
};
const goBackStep = () => setRequirementsStep((prev) => Math.max(1, prev - 1));
if (requirementsView() === 'new') {
return (
<div style="display:flex;flex-direction:column;gap:12px">
<div style="border:1px solid #E5E7EB;background:white;border-radius:16px;padding:16px;box-shadow:0 1px 4px rgba(0,0,0,0.06)">
<div style="display:flex;align-items:center;justify-content:space-between">
<p style="margin:0;font-size:20px;font-weight:800;color:#111827">Post New Requirement</p>
<button
type="button"
onClick={() => { setRequirementsView('list'); setRequirementsStep(1); }}
style="height:30px;border-radius:8px;border:1px solid #E5E7EB;background:white;color:#374151;padding:0 10px;font-size:12px;font-weight:700"
>
Back To List
</button>
</div>
<div style="display:flex;align-items:flex-start;justify-content:space-between;gap:8px;margin-top:12px">
<For each={steps}>
{(step, idx) => (
<div style="flex:1;display:flex;align-items:center;gap:8px">
<div style="display:flex;flex-direction:column;align-items:center;gap:6px;min-width:58px">
<span style={`width:32px;height:32px;border-radius:999px;display:inline-flex;align-items:center;justify-content:center;font-size:12px;font-weight:800;border:1px solid ${requirementsStep() > idx() + 1 ? '#FF5E13' : '#D1D5DB'};background:${requirementsStep() === idx() + 1 ? '#FF5E13' : requirementsStep() > idx() + 1 ? '#FFF1EB' : '#F9FAFB'};color:${requirementsStep() === idx() + 1 ? 'white' : requirementsStep() > idx() + 1 ? '#FF5E13' : '#6B7280'}`}>{idx() + 1}</span>
<p style={`margin:0;font-size:10px;font-weight:700;text-transform:uppercase;letter-spacing:0.04em;color:${requirementsStep() === idx() + 1 ? '#111827' : '#9CA3AF'}`}>{step}</p>
</div>
<Show when={idx() < steps.length - 1}>
<div style={`height:2px;flex:1;background:${requirementsStep() > idx() + 1 ? '#FF5E13' : '#E5E7EB'};margin-top:16px`} />
</Show>
</div>
)}
</For>
</div>
</div>
<Show when={requirementsStep() === 1}>
<div style="border:1px solid #E5E7EB;background:white;border-radius:16px;padding:16px;box-shadow:0 1px 4px rgba(0,0,0,0.06)">
<p style="margin:0;font-size:30px;font-weight:800;color:#111827;text-align:center">What kind of professional are you looking for?</p>
<p style="margin:8px 0 0;font-size:14px;color:#6B7280;text-align:center">Select a category to start your requirement. This helps us match you with the right experts.</p>
<div style="display:grid;grid-template-columns:repeat(3,minmax(0,1fr));gap:10px;margin-top:14px">
<For each={REQUIREMENT_ROLE_OPTIONS}>
{(role) => {
const active = selectedRequirementRole() === role.key;
return (
<button
type="button"
onClick={() => setSelectedRequirementRole(role.key)}
style={`text-align:left;border:1px solid ${active ? '#FF5E13' : '#E5E7EB'};border-radius:12px;background:white;padding:12px;min-height:154px;display:flex;flex-direction:column;gap:10px`}
>
<div style="display:flex;align-items:center;justify-content:space-between">
<span style="width:40px;height:40px;border-radius:10px;background:#F3F4F6;border:1px solid #E5E7EB;display:flex;align-items:center;justify-content:center">
<img src={role.icon} alt="" style={`width:18px;height:18px;object-fit:contain;filter:${ORANGE_ICON_FILTER}`} />
</span>
<Show when={active}>
<span style="display:inline-flex;align-items:center;justify-content:center;width:20px;height:20px;border-radius:999px;background:#FFF1EB;border:1px solid #FFD8C2;color:#FF5E13;font-size:12px;font-weight:800"></span>
</Show>
</div>
<p style="margin:0;font-size:16px;font-weight:800;color:#111827">{role.title}</p>
<p style="margin:0;font-size:12px;line-height:1.5;color:#6B7280">{role.desc}</p>
<span style={`margin-top:auto;height:30px;border-radius:8px;display:inline-flex;align-items:center;justify-content:center;font-size:12px;font-weight:700;${active ? 'background:#FF5E13;color:white' : 'border:1px solid #E5E7EB;color:#FF5E13'}`}>{active ? 'Continue' : 'Select'}</span>
</button>
);
}}
</For>
</div>
</div>
</Show>
<Show when={requirementsStep() === 2}>
<div style="border:1px solid #E5E7EB;background:white;border-radius:16px;padding:16px;box-shadow:0 1px 4px rgba(0,0,0,0.06)">
<p style="margin:0;font-size:30px;font-weight:800;color:#111827">Basic Requirement Details</p>
<p style="margin:8px 0 0;font-size:13px;color:#6B7280">Add core details before role-specific inputs.</p>
<div style="display:grid;grid-template-columns:1fr 1fr;gap:10px;margin-top:12px">
{['Requirement Title', 'Priority', 'Requirement Description', 'Expected Start Date', 'Service City', 'Contact Number'].map((field) => (
<div style="display:flex;flex-direction:column;gap:6px">
<p style="margin:0;font-size:11px;font-weight:700;letter-spacing:0.05em;text-transform:uppercase;color:#6B7280">{field}</p>
<div style="height:38px;border:1px solid #E5E7EB;border-radius:10px;background:#F9FAFB;padding:0 10px;display:flex;align-items:center;font-size:12px;color:#9CA3AF">
Enter {field.toLowerCase()}
</div>
</div>
))}
</div>
</div>
</Show>
<Show when={requirementsStep() === 3}>
<div style="border:1px solid #E5E7EB;background:white;border-radius:16px;padding:16px;box-shadow:0 1px 4px rgba(0,0,0,0.06)">
<p style="margin:0;font-size:30px;font-weight:800;color:#111827">{selectedRoleDetails().title}</p>
<p style="margin:8px 0 0;font-size:13px;color:#6B7280">{selectedRoleDetails().subtitle}</p>
<div style="display:grid;grid-template-columns:1fr 1fr;gap:10px;margin-top:12px">
<For each={selectedRoleDetails().fields}>
{(field, idx) => (
<div style={`display:flex;flex-direction:column;gap:6px;${field.includes('Venue') ? 'grid-column:1 / -1' : ''}`}>
<p style="margin:0;font-size:11px;font-weight:700;letter-spacing:0.05em;text-transform:uppercase;color:#6B7280">{field}</p>
<div style="height:38px;border:1px solid #E5E7EB;border-radius:10px;background:#F9FAFB;padding:0 10px;display:flex;align-items:center;justify-content:space-between;font-size:12px;color:#9CA3AF">
<span>{idx() % 2 === 0 ? 'Select' : 'Enter'} {field.toLowerCase()}</span>
<Show when={idx() % 2 === 0}><span></span></Show>
</div>
</div>
)}
</For>
</div>
<div style="display:grid;grid-template-columns:repeat(3,minmax(0,1fr));gap:10px;margin-top:12px">
<For each={selectedRoleDetails().toggles}>
{(toggleLabel) => (
<div style="display:flex;align-items:center;gap:10px;padding:10px;border:1px solid #E5E7EB;border-radius:10px;background:#FBFBFB">
<span style="width:32px;height:18px;border-radius:999px;background:#E5E7EB;position:relative;display:inline-flex;align-items:center;padding:2px">
<span style="width:14px;height:14px;border-radius:999px;background:white" />
</span>
<span style="font-size:12px;font-weight:700;color:#374151">{toggleLabel}</span>
</div>
)}
</For>
</div>
<div style="margin-top:12px">
<p style="margin:0;font-size:11px;font-weight:700;letter-spacing:0.05em;text-transform:uppercase;color:#6B7280">Style Needed</p>
<div style="display:grid;grid-template-columns:repeat(4,minmax(0,1fr));gap:8px;margin-top:8px">
<For each={selectedRoleDetails().styles}>
{(styleLabel) => (
<div style="height:34px;border:1px solid #E5E7EB;border-radius:8px;background:#F9FAFB;padding:0 10px;display:flex;align-items:center;gap:8px;font-size:12px;color:#374151">
<span style="width:14px;height:14px;border-radius:4px;border:1px solid #F5B197;background:white" />
{styleLabel}
</div>
)}
</For>
</div>
</div>
</div>
</Show>
<Show when={requirementsStep() === 4}>
<div style="border:1px solid #E5E7EB;background:white;border-radius:16px;padding:16px;box-shadow:0 1px 4px rgba(0,0,0,0.06)">
<p style="margin:0;font-size:30px;font-weight:800;color:#111827">Budget & Location</p>
<p style="margin:8px 0 0;font-size:13px;color:#6B7280">Set your target budget and service location to get relevant quotes.</p>
<div style="display:grid;grid-template-columns:1fr 1fr;gap:10px;margin-top:12px">
{['Budget Range', 'Urgency Level', 'Service Location', 'Mode (Remote / On-site)', 'Preferred Start Date', 'Flexible Budget'].map((field, idx) => (
<div style="display:flex;flex-direction:column;gap:6px">
<p style="margin:0;font-size:11px;font-weight:700;letter-spacing:0.05em;text-transform:uppercase;color:#6B7280">{field}</p>
<div style="height:38px;border:1px solid #E5E7EB;border-radius:10px;background:#F9FAFB;padding:0 10px;display:flex;align-items:center;justify-content:space-between;font-size:12px;color:#9CA3AF">
<span>{idx % 2 === 0 ? 'Enter' : 'Select'} {field.toLowerCase()}</span>
<Show when={idx % 2 === 1}><span></span></Show>
</div>
</div>
))}
</div>
</div>
</Show>
<Show when={requirementsStep() === 5}>
<div style="border:1px solid #E5E7EB;background:white;border-radius:16px;padding:16px;box-shadow:0 1px 4px rgba(0,0,0,0.06)">
<p style="margin:0;font-size:30px;font-weight:800;color:#111827">Attachments</p>
<p style="margin:8px 0 0;font-size:13px;color:#6B7280">Upload reference files to help professionals understand your requirement better.</p>
<div style="display:grid;grid-template-columns:repeat(4,minmax(0,1fr));gap:10px;margin-top:12px">
{[1, 2, 3, 4].map((slot) => (
<div style="height:110px;border:1px dashed #F5B197;border-radius:12px;background:#FFF9F6;display:flex;flex-direction:column;align-items:center;justify-content:center;gap:6px">
<span style="width:26px;height:26px;border-radius:999px;background:white;border:1px solid #FFD8C2;display:flex;align-items:center;justify-content:center;color:#FF5E13;font-weight:800">+</span>
<p style="margin:0;font-size:11px;color:#6B7280">Attachment {slot}</p>
</div>
))}
</div>
</div>
</Show>
<Show when={requirementsStep() === 6}>
<div style="display:grid;grid-template-columns:2fr 1fr;gap:10px">
<div style="border:1px solid #E5E7EB;background:white;border-radius:16px;padding:16px;box-shadow:0 1px 4px rgba(0,0,0,0.06)">
<p style="margin:0;font-size:30px;font-weight:800;color:#111827">Review & Submit</p>
<p style="margin:8px 0 0;font-size:13px;color:#6B7280">Final check before submitting for approval.</p>
<div style="margin-top:12px;display:grid;gap:8px">
{[
['Category', titleCase(selectedRequirementRole().replace(/_/g, ' ').toLowerCase())],
['Requirement Title', 'Luxury Wedding Shoot'],
['Budget Range', '₹1,50,000 - ₹2,00,000'],
['Service Location', 'Chennai, TN'],
['Expected Start Date', 'Nov 12, 2023'],
].map(([label, value]) => (
<div style="display:flex;justify-content:space-between;align-items:center;padding:10px;border:1px solid #E5E7EB;border-radius:10px;background:#F9FAFB">
<span style="font-size:12px;color:#6B7280">{label}</span>
<span style="font-size:12px;font-weight:700;color:#111827">{value}</span>
</div>
))}
</div>
</div>
<div style="border:1px solid #E5E7EB;background:linear-gradient(180deg,#FFFFFF 0%,#FFF6F0 100%);border-radius:16px;padding:16px;box-shadow:0 1px 4px rgba(0,0,0,0.06)">
<p style="margin:0;font-size:11px;letter-spacing:0.06em;text-transform:uppercase;color:#6B7280">Approval Flow</p>
<p style="margin:8px 0 0;font-size:20px;font-weight:800;color:#111827">Submit For Approval</p>
<p style="margin:8px 0 0;font-size:12px;color:#6B7280;line-height:1.5">Once submitted, your requirement goes to Verification Management first, then Approval Management. Only after both stages it goes live in professional leads.</p>
<div style="display:grid;gap:8px;margin-top:12px">
{['Draft Created', 'Verification Management', 'Approval Management', 'Live In Leads'].map((state, idx) => (
<div style="display:flex;align-items:center;gap:8px">
<span style={`width:18px;height:18px;border-radius:999px;display:inline-flex;align-items:center;justify-content:center;font-size:10px;font-weight:800;background:${idx < 2 ? '#ECFDF3' : '#F3F4F6'};color:${idx < 2 ? '#059669' : '#9CA3AF'}`}>{idx < 2 ? '✓' : idx + 1}</span>
<span style={`font-size:12px;color:${idx < 2 ? '#111827' : '#6B7280'}`}>{state}</span>
</div>
))}
</div>
</div>
</div>
</Show>
<div style="display:flex;align-items:center;justify-content:space-between;border-top:1px solid #E5E7EB;padding-top:12px">
<button
type="button"
onClick={goBackStep}
disabled={!canGoBack()}
style={`height:34px;border-radius:8px;padding:0 12px;font-size:12px;font-weight:700;border:1px solid #E5E7EB;background:white;color:${canGoBack() ? '#374151' : '#9CA3AF'};cursor:${canGoBack() ? 'pointer' : 'not-allowed'}`}
>
Back
</button>
<div style="display:flex;gap:8px">
<button type="button" style="height:34px;border-radius:8px;border:1px solid #E5E7EB;background:white;color:#374151;padding:0 12px;font-size:12px;font-weight:700">Save As Draft</button>
<button type="button" onClick={goNextStep} style="height:34px;border-radius:8px;border:none;background:#03004E;color:white;padding:0 12px;font-size:12px;font-weight:700">{nextLabel()}</button>
</div>
</div>
</div>
);
}
if (requirementsView() === 'detail') {
const selectedRow = requirementRows().find((row) => row.id === selectedRequirementId()) || requirementRows()[0];
const status = statusMeta(selectedRow.status);
return (
<div style="display:grid;grid-template-columns:2fr 1fr;gap:10px">
<div style="display:flex;flex-direction:column;gap:10px">
<div style="border:1px solid #E5E7EB;background:white;border-radius:16px;padding:14px;box-shadow:0 1px 4px rgba(0,0,0,0.06)">
<div style="display:flex;align-items:center;justify-content:space-between;gap:8px">
<div>
<p style="margin:0;font-size:11px;letter-spacing:0.05em;text-transform:uppercase;color:#9CA3AF">Requirements &gt; Details</p>
<p style="margin:6px 0 0;font-size:38px;line-height:1.05;font-weight:800;color:#111827">{selectedRow.title}</p>
<div style="display:flex;align-items:center;gap:8px;margin-top:8px">
<span style={`height:22px;padding:0 10px;border-radius:999px;display:inline-flex;align-items:center;font-size:11px;font-weight:700;background:${status.bg};color:${status.c}`}>{status.label}</span>
<span style="height:22px;padding:0 10px;border-radius:999px;display:inline-flex;align-items:center;font-size:11px;font-weight:700;background:#DBEAFE;color:#1D4ED8">Active</span>
<span style="font-size:12px;color:#6B7280">Created on {selectedRow.submission}</span>
</div>
</div>
<div style="display:flex;gap:8px">
<button type="button" style="height:32px;border-radius:8px;border:1px solid #E5E7EB;background:white;padding:0 12px;font-size:12px;font-weight:700;color:#374151">Duplicate</button>
<button type="button" style="height:32px;border-radius:8px;border:none;background:#FF5E13;padding:0 12px;font-size:12px;font-weight:700;color:white">Boost Requirement</button>
</div>
</div>
<div style="display:grid;grid-template-columns:repeat(3,minmax(0,1fr));gap:8px;margin-top:10px">
{[
['Proposed Budget', selectedRow.budget],
['Location & Urgency', selectedRow.location],
['Project Scope', '3 Day Event'],
].map(([label, val]) => (
<div style="border:1px solid #E5E7EB;border-radius:12px;background:#F9FAFB;padding:10px">
<p style="margin:0;font-size:11px;letter-spacing:0.05em;text-transform:uppercase;color:#9CA3AF">{label}</p>
<p style="margin:7px 0 0;font-size:16px;font-weight:800;color:#111827">{val}</p>
</div>
))}
</div>
</div>
<div style="border:1px solid #E5E7EB;background:white;border-radius:16px;padding:14px;box-shadow:0 1px 4px rgba(0,0,0,0.06)">
<p style="margin:0;font-size:34px;font-weight:800;color:#111827">Requirement Overview</p>
<p style="margin:10px 0 0;font-size:13px;line-height:1.7;color:#374151">We are looking for a premium {selectedRow.category.toLowerCase().replace(/_/g, ' ')} team for a high-end project with a focus on quality, timeline ownership, and clear communication throughout execution.</p>
<div style="margin-top:10px;padding:10px;border:1px solid #FEC7AA;border-radius:10px;background:#FFF9F5">
<p style="margin:0;font-size:11px;letter-spacing:0.05em;text-transform:uppercase;color:#C2410C;font-weight:700">Special Instructions</p>
<ul style="margin:8px 0 0;padding-left:18px;color:#374151;font-size:13px;line-height:1.6">
<li>Mandatory experience in similar premium projects.</li>
<li>Must be available for milestone reviews every 72 hours.</li>
<li>NDA required before onboarding and deliverable sharing.</li>
</ul>
</div>
</div>
<div style="border:1px solid #E5E7EB;background:white;border-radius:16px;padding:14px;box-shadow:0 1px 4px rgba(0,0,0,0.06)">
<p style="margin:0;font-size:30px;font-weight:800;color:#111827">Reference Material</p>
<div style="display:grid;grid-template-columns:repeat(4,minmax(0,1fr));gap:8px;margin-top:10px">
{[1, 2, 3, 4].map((slot) => (
<div style="height:84px;border:1px dashed #F5B197;border-radius:10px;background:#FFF9F6;display:flex;align-items:center;justify-content:center;color:#9CA3AF;font-size:12px;font-weight:700">
{slot === 4 ? 'Add More' : `Asset ${slot}`}
</div>
))}
</div>
</div>
</div>
<div style="display:flex;flex-direction:column;gap:10px">
<div style="border:1px solid #D9D8F9;background:#4F4B8A;border-radius:16px;padding:14px;color:white;box-shadow:0 1px 4px rgba(0,0,0,0.08)">
<p style="margin:0;font-size:11px;letter-spacing:0.06em;text-transform:uppercase;color:#DADAFB">Response Metrics</p>
<p style="margin:8px 0 0;font-size:54px;line-height:1;font-weight:800">08</p>
<p style="margin:4px 0 0;font-size:12px;color:#E5E7FF">Total Responses</p>
<div style="height:1px;background:rgba(255,255,255,0.2);margin:10px 0" />
<div style="display:flex;justify-content:space-between;font-size:12px">
<span>03 Shortlisted</span>
<span>03 Rejected</span>
</div>
</div>
<div style="border:1px solid #E5E7EB;background:white;border-radius:16px;padding:14px;box-shadow:0 1px 4px rgba(0,0,0,0.06)">
<p style="margin:0;font-size:11px;letter-spacing:0.06em;text-transform:uppercase;color:#9CA3AF">Approval History</p>
<div style="display:grid;gap:10px;margin-top:10px">
{['Requirement Approved', 'Under Review', 'Submitted', 'Draft Created'].map((step) => (
<div style="display:flex;align-items:center;gap:8px">
<span style="width:18px;height:18px;border-radius:999px;background:#ECFDF3;color:#059669;display:inline-flex;align-items:center;justify-content:center;font-size:10px;font-weight:800"></span>
<div>
<p style="margin:0;font-size:12px;font-weight:700;color:#111827">{step}</p>
<p style="margin:1px 0 0;font-size:11px;color:#9CA3AF">Oct 24, 2023</p>
</div>
</div>
))}
</div>
</div>
<div style="border:1px solid #E5E7EB;background:white;border-radius:16px;padding:12px;display:grid;gap:8px;box-shadow:0 1px 4px rgba(0,0,0,0.06)">
{['Extend Expiry', 'Edit Details', 'Close Requirement'].map((action, idx) => (
<button type="button" style={`height:36px;border-radius:8px;border:1px solid ${idx === 2 ? '#FECACA' : '#E5E7EB'};background:${idx === 2 ? '#FEF2F2' : '#F9FAFB'};color:${idx === 2 ? '#B91C1C' : '#374151'};font-size:12px;font-weight:700`}>{action}</button>
))}
</div>
</div>
</div>
);
}
const totalCount = requirementRows().length;
const draftCount = requirementRows().filter((item) => item.status === 'draft').length;
const submittedCount = requirementRows().filter((item) => item.status === 'under review').length;
const approvedCount = requirementRows().filter((item) => item.status === 'approved').length;
const activeCount = requirementRows().filter((item) => item.status === 'active').length;
const rejectedCount = requirementRows().filter((item) => item.status === 'rejected').length;
return (
<div style="display:flex;flex-direction:column;gap:10px">
<div style="border:1px solid #E5E7EB;background:white;border-radius:16px;padding:14px;box-shadow:0 1px 4px rgba(0,0,0,0.06)">
<div style="display:flex;align-items:center;justify-content:space-between;gap:10px">
<div>
<p style="margin:0;font-size:47px;line-height:1.05;font-weight:800;color:#111827">My Requirements</p>
<p style="margin:6px 0 0;font-size:14px;color:#6B7280">Create, submit for approval, and manage your requirements with precision and clarity.</p>
</div>
<button
type="button"
onClick={() => { setRequirementsView('new'); setRequirementsStep(1); }}
style="height:38px;border-radius:10px;border:none;background:#03004E;color:white;padding:0 14px;font-size:12px;font-weight:700"
>
+ Post New Requirement
</button>
</div>
<div style="display:grid;grid-template-columns:2fr repeat(5,minmax(0,1fr));gap:8px;margin-top:10px">
{[
['Total Requirements', String(totalCount)],
['Drafts', String(draftCount)],
['Submitted', String(submittedCount)],
['Approved', String(approvedCount)],
['Active', String(activeCount)],
['Rejected', String(rejectedCount)],
].map(([label, value], idx) => (
<div style={`border:1px solid #E5E7EB;border-radius:10px;background:${idx === 0 ? '#FFF8F4' : '#F9FAFB'};padding:10px;${idx === 0 ? 'border-left:3px solid #FF5E13' : ''}`}>
<p style="margin:0;font-size:10px;letter-spacing:0.06em;text-transform:uppercase;color:#9CA3AF;font-weight:700">{label}</p>
<p style={`margin:6px 0 0;font-size:${idx === 0 ? '42px' : '36px'};line-height:1;font-weight:800;color:#111827`}>{value}</p>
</div>
))}
</div>
</div>
<div style="border:1px solid #E5E7EB;background:white;border-radius:16px;overflow:hidden;box-shadow:0 1px 4px rgba(0,0,0,0.06)">
<div style="padding:10px 12px;border-bottom:1px solid #E5E7EB;display:flex;align-items:center;gap:8px">
{['All Requirements', 'Drafts', 'Submitted', 'Approved', 'Active', 'Rejected', 'Closed', 'Expired'].map((label, idx) => (
<button type="button" style={`height:30px;padding:0 8px;border:none;border-bottom:${idx === 0 ? '2px solid #FF5E13' : '2px solid transparent'};background:none;font-size:12px;font-weight:700;color:${idx === 0 ? '#FF5E13' : '#6B7280'}`}>{label}</button>
))}
</div>
<div style="padding:10px 12px;border-bottom:1px solid #E5E7EB;display:flex;align-items:center;gap:8px">
<div style="height:34px;flex:1;border:1px solid #E5E7EB;border-radius:8px;background:#F9FAFB;padding:0 10px;display:flex;align-items:center;gap:8px;font-size:12px;color:#9CA3AF"><Search size={14} /> Search ID or Title...</div>
{['Sort By: Newest', 'Category: All', 'Status: All'].map((item) => (
<button type="button" style="height:34px;border-radius:8px;border:1px solid #E5E7EB;background:white;padding:0 10px;font-size:12px;color:#374151">{item}</button>
))}
<button type="button" style="height:34px;border-radius:8px;border:1px solid #E5E7EB;background:white;padding:0 10px;font-size:12px;color:#374151">Export</button>
</div>
<div style="overflow-x:auto">
<table style="width:100%;border-collapse:collapse;min-width:920px">
<thead style="background:#F9FAFB">
<tr>
{['ID', 'Requirement Details', 'Category', 'Budget & Location', 'Submission', 'Status', 'Actions'].map((head) => (
<th style="padding:10px;text-align:left;font-size:10px;letter-spacing:0.06em;text-transform:uppercase;color:#9CA3AF">{head}</th>
))}
</tr>
</thead>
<tbody>
<For each={filteredRows}>
{(row) => {
const status = statusMeta(row.status);
return (
<tr style="border-top:1px solid #F3F4F6">
<td style="padding:10px;font-size:12px;color:#9CA3AF;font-weight:700">{row.id}</td>
<td style="padding:10px">
<p style="margin:0;font-size:14px;font-weight:700;color:#111827">{row.title}</p>
<p style="margin:3px 0 0;font-size:12px;color:#6B7280">{row.summary}</p>
</td>
<td style="padding:10px"><span style="height:20px;padding:0 8px;border-radius:999px;border:1px solid #E5E7EB;background:#F3F4F6;display:inline-flex;align-items:center;font-size:10px;font-weight:700;color:#4B5563">{row.category}</span></td>
<td style="padding:10px">
<p style="margin:0;font-size:13px;font-weight:700;color:#111827">{row.budget}</p>
<p style="margin:2px 0 0;font-size:11px;color:#9CA3AF">{row.location}</p>
</td>
<td style="padding:10px;font-size:12px;color:#6B7280">{row.submission}</td>
<td style="padding:10px">
<span style={`height:22px;padding:0 10px;border-radius:999px;display:inline-flex;align-items:center;font-size:11px;font-weight:700;background:${status.bg};color:${status.c}`}>{status.label}</span>
<p style="margin:6px 0 0;font-size:11px;color:#1D4ED8;font-weight:700">{row.responseTag}</p>
</td>
<td style="padding:10px">
<div style="display:flex;gap:6px">
<button
type="button"
onClick={() => { setSelectedRequirementId(row.id); setRequirementsView('detail'); }}
style="height:28px;border-radius:8px;border:1px solid #E5E7EB;background:white;padding:0 10px;font-size:11px;font-weight:700;color:#374151"
>
View
</button>
<button type="button" style="height:28px;border-radius:8px;border:none;background:#03004E;padding:0 10px;font-size:11px;font-weight:700;color:white">Edit</button>
</div>
</td>
</tr>
);
}}
</For>
</tbody>
</table>
</div>
<div style="padding:10px 12px;border-top:1px solid #E5E7EB;display:flex;justify-content:space-between;align-items:center">
<p style="margin:0;font-size:12px;color:#9CA3AF">Showing 1 to {Math.min(filteredRows.length, 6)} of {filteredRows.length} requirements</p>
<div style="display:flex;gap:6px">
{[1, 2, 3].map((p) => <button type="button" style={`width:28px;height:28px;border-radius:8px;border:1px solid #E5E7EB;background:${p === 1 ? '#FF5E13' : 'white'};color:${p === 1 ? 'white' : '#6B7280'};font-size:12px;font-weight:700`}>{p}</button>)}
</div>
</div>
</div>
</div>
);
}
if (customerKey() === 'received responses' || customerKey() === 'my responses') {
if (normalizeRoleKey(props.roleKey || '') !== 'CUSTOMER') {
const leadStatusPill = (status: string) => {
if (status === 'request_sent') return { c: '#374151', text: 'Request Sent' };
if (status === 'contact_unlocked') return { c: '#374151', text: 'Contact Unlocked' };
if (status === 'approved') return { c: '#374151', text: 'Approved' };
if (status === 'rejected') return { c: '#374151', text: 'Rejected' };
if (status === 'expired_refunded') return { c: '#374151', text: 'Expired · Refunded' };
if (status === 'cancelled_by_professional') return { c: '#374151', text: 'Cancelled' };
return { c: '#374151', text: titleCase(status) };
};
const responseSelectedLead = () => leadCards().find((item) => item.id === activeResponseLeadId()) || null;
const list = filteredRequestedRows().filter((row) => {
if (resolvedTabKey() === 'pending leads') return row.status === 'request_sent';
return true;
});
const totalPages = Math.max(1, Math.ceil(list.length / requestedPerPage));
const currentPage = Math.min(requestedPage(), totalPages);
const pagedList = list.slice((currentPage - 1) * requestedPerPage, (currentPage - 1) * requestedPerPage + requestedPerPage);
if (responsesDetailMode() && responseSelectedLead()) {
const lead = responseSelectedLead()!;
const spec = leadDetailsSpec(lead);
return (
<div style="display:flex;flex-direction:column;gap:10px">
<div style="border:1px solid #E5E7EB;background:white;border-radius:16px;padding:14px;box-shadow:0 1px 4px rgba(0,0,0,0.06)">
<div style="border:1px solid #E5E7EB;border-radius:12px;background:#FFFFFF;padding:14px">
<div style="display:flex;align-items:flex-start;justify-content:space-between;gap:10px;flex-wrap:wrap">
<div>
<p style="margin:0;font-size:11px;letter-spacing:0.06em;text-transform:uppercase;color:#6B7280">My Responses View Details</p>
<p style="margin:4px 0 0;font-size:24px;font-weight:800;color:#111827">{lead.title}</p>
<p style="margin:4px 0 0;font-size:12px;color:#6B7280">{lead.category} {lead.location} {lead.area}</p>
</div>
<button type="button" onClick={() => { setResponsesDetailMode(false); setActiveResponseLeadId(''); }} style="height:32px;border-radius:8px;border:1px solid #E5E7EB;background:white;padding:0 12px;font-size:12px;font-weight:700;color:#374151">Back to My Responses</button>
</div>
<div style="margin-top:10px;display:grid;grid-template-columns:repeat(4,minmax(0,1fr));gap:8px">
<div style="border:1px solid #E5E7EB;border-radius:10px;background:#F9FAFB;padding:10px;display:flex;gap:8px">
<span style="width:24px;height:24px;border-radius:999px;background:#FFF1EB;display:inline-flex;align-items:center;justify-content:center;flex-shrink:0"><Calendar size={13} style="color:#FF5E13" /></span>
<div><p style="margin:0;font-size:11px;color:#6B7280;font-weight:700">Schedule</p><p style="margin:3px 0 0;font-size:13px;font-weight:800;color:#111827">{spec.timeframe}</p></div>
</div>
<div style="border:1px solid #E5E7EB;border-radius:10px;background:#F9FAFB;padding:10px;display:flex;gap:8px">
<span style="width:24px;height:24px;border-radius:999px;background:#FFF1EB;display:inline-flex;align-items:center;justify-content:center;flex-shrink:0"><Calendar size={13} style="color:#FF5E13" /></span>
<div><p style="margin:0;font-size:11px;color:#6B7280;font-weight:700">Date Required</p><p style="margin:3px 0 0;font-size:13px;font-weight:800;color:#111827">{lead.dateRequired}</p></div>
</div>
<div style="border:1px solid #E5E7EB;border-radius:10px;background:#F9FAFB;padding:10px;display:flex;gap:8px">
<span style="width:24px;height:24px;border-radius:999px;background:#FFF1EB;display:inline-flex;align-items:center;justify-content:center;flex-shrink:0"><MapPin size={13} style="color:#FF5E13" /></span>
<div><p style="margin:0;font-size:11px;color:#6B7280;font-weight:700">Area</p><p style="margin:3px 0 0;font-size:13px;font-weight:800;color:#111827">{String(lead.area || 'Chennai')}</p></div>
</div>
<div style="border:1px solid #E5E7EB;border-radius:10px;background:#F9FAFB;padding:10px;display:flex;gap:8px">
<span style="width:24px;height:24px;border-radius:999px;background:#FFF1EB;display:inline-flex;align-items:center;justify-content:center;flex-shrink:0"><Coins size={13} style="color:#FF5E13" /></span>
<div><p style="margin:0;font-size:11px;color:#6B7280;font-weight:700">Unlock Cost</p><p style="margin:3px 0 0;font-size:13px;font-weight:800;color:#C2410C">{lead.cost} Tracecoin</p></div>
</div>
</div>
</div>
<div style="margin-top:10px;display:grid;grid-template-columns:2fr 1fr;gap:10px">
<div style="display:grid;gap:10px">
<div style="border:1px solid #E5E7EB;border-radius:12px;background:white;padding:12px">
<p style="margin:0;font-size:11px;letter-spacing:0.05em;text-transform:uppercase;color:#6B7280;font-weight:700">Work Scope</p>
<p style="margin:8px 0 0;font-size:14px;color:#1F2937;line-height:1.65">{spec.scope}</p>
</div>
<div style="border:1px solid #E5E7EB;border-radius:12px;background:#F9FAFB;padding:12px">
<p style="margin:0 0 8px;font-size:11px;letter-spacing:0.05em;text-transform:uppercase;color:#6B7280;font-weight:700">Lead Specific Highlights</p>
<div style="display:grid;gap:6px"><For each={spec.highlights}>{(item) => <div style="display:flex;align-items:flex-start;gap:6px"><span style="margin-top:3px;width:6px;height:6px;border-radius:999px;background:#FF5E13;flex-shrink:0" /><span style="font-size:14px;color:#1F2937;line-height:1.55">{item}</span></div>}</For></div>
</div>
</div>
<div style="border:1px solid #E5E7EB;border-radius:12px;background:white;padding:12px;display:grid;gap:8px;align-content:start">
<div style="display:flex;justify-content:space-between;gap:8px;padding-bottom:6px;border-bottom:1px solid #F3F4F6"><span style="font-size:12px;color:#6B7280">Lead ID</span><span style="font-size:12px;color:#111827;font-weight:700">{lead.id}</span></div>
<div style="display:flex;justify-content:space-between;gap:8px;padding-bottom:6px;border-bottom:1px solid #F3F4F6"><span style="font-size:12px;color:#6B7280">Category</span><span style="font-size:12px;color:#111827;font-weight:700">{lead.category}</span></div>
<div style="display:flex;justify-content:space-between;gap:8px;padding-bottom:6px;border-bottom:1px solid #F3F4F6"><span style="font-size:12px;color:#6B7280">Location</span><span style="font-size:12px;color:#111827;font-weight:700">{lead.location}</span></div>
<div style="display:flex;justify-content:space-between;gap:8px;padding-bottom:6px;border-bottom:1px solid #F3F4F6"><span style="font-size:12px;color:#6B7280">Status</span><span style="font-size:12px;color:#111827;font-weight:700">From My Responses</span></div>
</div>
</div>
</div>
</div>
);
}
return (
<div style="display:flex;flex-direction:column;gap:10px">
<div style="border:1px solid #E5E7EB;background:white;border-radius:16px;overflow:hidden;box-shadow:0 1px 4px rgba(0,0,0,0.06)">
<div style="padding:10px 12px;border-bottom:1px solid #E5E7EB;display:flex;justify-content:space-between;align-items:center;gap:10px">
<p style="margin:0;font-size:18px;font-weight:800;color:#111827">My Responses</p>
<div style="display:flex;gap:8px">
<div style="position:relative">
<button type="button" onClick={() => { setRequestedSortOpen((prev) => !prev); setRequestedFilterOpen(false); }} style="display:inline-flex;height:32px;align-items:center;gap:6px;border-radius:8px;border:1px solid #E5E7EB;background:white;padding:0 10px;font-size:12px;color:#374151;font-weight:600">
<svg width="13" height="13" viewBox="0 0 24 24" fill="none" stroke="#FF5E13" stroke-width="2"><path d="M7 4v13"/><path d="m3 13 4 4 4-4"/><path d="M17 20V7"/><path d="m21 11-4-4-4 4"/></svg>
Sort
</button>
<Show when={requestedSortOpen()}>
<div style="position:absolute;right:0;top:36px;z-index:20;min-width:170px;border:1px solid #E5E7EB;border-radius:10px;background:white;padding:6px;box-shadow:0 8px 24px rgba(0,0,0,0.12)">
{['Newest First', 'Oldest First'].map((item) => (
<button type="button" onClick={() => { setRequestedSortFilter(item); setRequestedSortOpen(false); }} style={`display:block;width:100%;text-align:left;border:none;background:${requestedSortFilter() === item ? '#FFF1EB' : 'transparent'};color:${requestedSortFilter() === item ? '#FF5E13' : '#374151'};padding:8px 10px;border-radius:8px;font-size:12px;font-weight:600;cursor:pointer`}>
{item}
</button>
))}
</div>
</Show>
</div>
<div style="position:relative">
<button type="button" onClick={() => { setRequestedFilterOpen((prev) => !prev); setRequestedSortOpen(false); }} style="display:inline-flex;height:32px;align-items:center;gap:6px;border-radius:8px;border:1px solid #E5E7EB;background:white;padding:0 10px;font-size:12px;color:#374151;font-weight:600">
<svg width="13" height="13" viewBox="0 0 24 24" fill="none" stroke="#FF5E13" stroke-width="2"><path d="M3 5h18M6 12h12M10 19h4"/></svg>
Filter
</button>
<Show when={requestedFilterOpen()}>
<div style="position:absolute;right:0;top:36px;z-index:20;min-width:220px;border:1px solid #E5E7EB;border-radius:10px;background:white;padding:6px;box-shadow:0 8px 24px rgba(0,0,0,0.12)">
{['All Status', 'Request Sent', 'Contact Unlocked', 'Rejected', 'Expired Refunded', 'Cancelled By Professional'].map((item) => (
<button type="button" onClick={() => { setRequestedStatusFilter(item); setRequestedFilterOpen(false); }} style={`display:block;width:100%;text-align:left;border:none;background:${requestedStatusFilter() === item ? '#FFF1EB' : 'transparent'};color:${requestedStatusFilter() === item ? '#FF5E13' : '#374151'};padding:8px 10px;border-radius:8px;font-size:12px;font-weight:600;cursor:pointer`}>
{item}
</button>
))}
</div>
</Show>
</div>
<button type="button" style="display:inline-flex;height:32px;align-items:center;gap:6px;border-radius:8px;border:none;background:#03004E;padding:0 10px;font-size:12px;font-weight:700;color:white"><svg width="13" height="13" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><path d="M21 15v4a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2v-4"/><polyline points="7 10 12 15 17 10"/><line x1="12" y1="15" x2="12" y2="3"/></svg>Export</button>
</div>
</div>
<div style="padding:10px 12px;border-bottom:1px solid #E5E7EB;display:flex;align-items:center;gap:8px;flex-wrap:wrap">
<div style="height:32px;min-width:220px;flex:1;border:1px solid #E5E7EB;border-radius:8px;background:#F9FAFB;padding:0 10px;display:flex;align-items:center;gap:8px;font-size:12px;color:#9CA3AF">
<Search size={14} />
<input value={requestedSearch()} onInput={(e) => setRequestedSearch(e.currentTarget.value)} placeholder="Search by lead ID / title / area..." style="border:none;background:transparent;outline:none;width:100%;font-size:12px;color:#111827" />
</div>
<span style="height:24px;padding:0 10px;border-radius:999px;border:1px solid #E5E7EB;background:#F9FAFB;display:inline-flex;align-items:center;font-size:11px;font-weight:700;color:#6B7280">Sort: {requestedSortFilter()}</span>
<span style="height:24px;padding:0 10px;border-radius:999px;border:1px solid #E5E7EB;background:#F9FAFB;display:inline-flex;align-items:center;font-size:11px;font-weight:700;color:#6B7280">Status: {requestedStatusFilter()}</span>
</div>
<div style="max-height:420px;overflow:auto">
<table style="width:100%;border-collapse:collapse">
<thead style="background:#03004E;color:white">
<tr>
{['Lead ID', 'Lead Title', 'Request Date', 'Status', 'Cost', 'Decision Date', 'Action'].map((h) => (
<th style="padding:10px;text-align:left;font-size:10px;letter-spacing:0.06em;text-transform:uppercase">{h}</th>
))}
</tr>
</thead>
<tbody>
<For each={pagedList}>
{(row) => {
const badge = leadStatusPill(row.status);
return (
<tr style="border-top:1px solid #F3F4F6">
<td style="padding:10px;font-size:12px;color:#4B5563;font-weight:700">#{row.id}</td>
<td style="padding:10px">
<p style="margin:0;font-size:14px;font-weight:700;color:#111827">{row.title}</p>
<p style="margin:2px 0 0;font-size:12px;color:#9CA3AF">{row.city}</p>
</td>
<td style="padding:10px;font-size:12px;color:#374151">{row.requestDate}</td>
<td style="padding:10px">
<span style={`display:inline-flex;align-items:center;font-size:12px;font-weight:600;color:${badge.c}`}>{badge.text}</span>
</td>
<td style="padding:10px;font-size:14px;font-weight:700;color:#111827">{leadCostPerContact}</td>
<td style="padding:10px;font-size:12px;color:#6B7280">{row.decisionDate}</td>
<td style="padding:10px">
<div style="display:flex;gap:6px;flex-wrap:wrap">
<button
type="button"
title="View Details"
aria-label="View Details"
onClick={() => openResponseLeadDetails(row.id)}
style="width:30px;height:30px;border-radius:8px;border:1px solid #E5E7EB;background:white;display:inline-flex;align-items:center;justify-content:center;color:#374151"
>
<Eye size={14} />
</button>
<Show when={row.status === 'request_sent'}>
<button
type="button"
title="Cancel Request"
aria-label="Cancel Request"
onClick={() => cancelLeadRequest(row.id)}
style="width:30px;height:30px;border-radius:8px;border:1px solid #E5E7EB;background:white;display:inline-flex;align-items:center;justify-content:center;color:#374151"
>
<X size={14} />
</button>
</Show>
</div>
</td>
</tr>
);
}}
</For>
</tbody>
</table>
</div>
<div style="padding:10px 12px;border-top:1px solid #E5E7EB;display:flex;justify-content:space-between;align-items:center">
<p style="margin:0;font-size:12px;color:#6B7280">
Showing {pagedList.length ? (currentPage - 1) * requestedPerPage + 1 : 0} to {(currentPage - 1) * requestedPerPage + pagedList.length} of {list.length} responses
</p>
<div style="display:flex;gap:6px">
<For each={Array.from({ length: totalPages }, (_, i) => i + 1)}>
{(pageNo) => (
<button
type="button"
onClick={() => setRequestedPage(pageNo)}
style={`width:30px;height:30px;border-radius:8px;border:1px solid #E5E7EB;background:${currentPage === pageNo ? '#FF5E13' : 'white'};color:${currentPage === pageNo ? 'white' : '#6B7280'};font-size:12px;font-weight:700`}
>
{pageNo}
</button>
)}
</For>
</div>
</div>
</div>
</div>
);
}
const list = RESPONSE_ROWS.filter((r) => {
if (tab === 'new') return r.status === 'new';
if (tab === 'shortlisted') return r.status === 'shortlisted';
if (tab === 'rejected') return r.status === 'rejected';
return true;
});
return (
<div style="border:1px solid #E5E7EB;background:white;border-radius:16px;padding:14px;box-shadow:0 1px 4px rgba(0,0,0,0.06);display:grid;gap:8px">
<div style="display:flex;align-items:center;justify-content:space-between;gap:8px;padding:10px;border:1px solid #FFE2D3;border-radius:10px;background:#FFF8F4">
<p style="margin:0;font-size:12px;color:#9A3412;line-height:1.4">Before accepting contact request, review portfolio, experience, and quoted charges.</p>
<button
type="button"
onClick={openPortfolioPreviewInline}
style="height:30px;border-radius:8px;border:none;background:#03004E;color:white;padding:0 10px;font-size:12px;font-weight:700;white-space:nowrap"
>
Preview Portfolio
</button>
</div>
<For each={list}>{(row) => {
const chip = statusChip(row.status);
return (
<div style="display:grid;grid-template-columns:2fr 1fr 1fr;gap:8px;padding:10px;border:1px solid #E5E7EB;border-radius:8px;background:#fff">
<div>
<p style="margin:0;font-size:13px;font-weight:700;color:#111827">{row.name}</p>
<p style="margin:2px 0 0;font-size:12px;color:#6B7280">Applied for active requirement</p>
<p style="margin:6px 0 0;font-size:11px;font-weight:700;color:#FF5E13">Experience: 7+ years</p>
</div>
<div style="font-size:18px;font-weight:800;color:#111827">{row.quote}</div>
<div style="display:flex;align-items:center;justify-content:flex-end;gap:8px;flex-wrap:wrap">
<span style={`display:inline-flex;align-items:center;justify-content:center;height:22px;border-radius:999px;padding:0 8px;background:${chip.bg};color:${chip.c};font-size:11px;font-weight:700;text-transform:uppercase`}>{row.status}</span>
<button
type="button"
onClick={openPortfolioPreviewInline}
style="height:30px;border-radius:8px;border:1px solid #E5E7EB;background:white;color:#374151;padding:0 10px;font-size:12px;font-weight:700"
>
View Portfolio
</button>
<button type="button" style="height:30px;border-radius:8px;border:none;background:#0D0D2A;color:white;padding:0 10px;font-size:12px;font-weight:700">View Profile</button>
<button type="button" style="height:30px;border-radius:8px;border:none;background:#FF5E13;color:white;padding:0 10px;font-size:12px;font-weight:700">Accept Contact</button>
</div>
</div>
);
}}</For>
</div>
);
}
if (customerKey() === 'shortlisted responses') {
const list = RESPONSE_ROWS.filter((r) => {
if (tab === 'interview scheduled') return r.status === 'shortlisted';
if (tab === 'finalized') return r.status === 'shortlisted';
return r.status === 'shortlisted';
});
return (
<div style="display:grid;grid-template-columns:repeat(3,minmax(0,1fr));gap:10px">
<For each={list}>{(p) => (
<div style="border:1px solid #E5E7EB;background:white;border-radius:16px;padding:12px;box-shadow:0 1px 4px rgba(0,0,0,0.06)">
<p style="margin:0;font-size:13px;font-weight:700;color:#111827">{p.name}</p>
<p style="margin:4px 0 0;font-size:12px;color:#6B7280">High-potential response for active requirement</p>
<p style="margin:8px 0 0;font-size:12px;font-weight:700;color:#FF5E13">Quoted {p.quote}</p>
<div style="display:flex;gap:8px;margin-top:10px">
<button type="button" style="height:30px;border-radius:8px;border:1px solid #E5E7EB;background:#F9FAFB;color:#374151;padding:0 10px;font-size:12px;font-weight:700">View Details</button>
<button type="button" style="height:30px;border-radius:8px;border:none;background:#FF5E13;color:white;padding:0 10px;font-size:12px;font-weight:700">Finalize</button>
</div>
</div>
)}</For>
</div>
);
}
if (customerKey() === 'credits') {
const invoiceRows = [
['#INV-2023-089', 'Oct 14, 2023', 'Enterprise Pack', '₹499.00', 'Paid'],
['#INV-2023-074', 'Sep 14, 2023', 'Enterprise Pack', '₹499.00', 'Paid'],
['#INV-2023-052', 'Aug 14, 2023', 'Growth Starter', '₹135.00', 'Paid'],
['#INV-2023-031', 'Jul 14, 2023', 'Growth Starter', '₹135.00', 'Failed'],
] as const;
const renderCheckout = () => {
const pkg = checkoutPackage();
if (!pkg) return null;
const step = paymentStep();
return (
<div style="display:flex;flex-direction:column;gap:14px">
<div style="display:flex;align-items:center;gap:10px">
<button
type="button"
onClick={() => { setCheckoutPackage(null); setPaymentStep('idle'); }}
style="height:32px;border:1px solid #E5E7EB;background:white;border-radius:8px;padding:0 12px;font-size:12px;font-weight:700;color:#374151;cursor:pointer"
>
Back to Packages
</button>
<h3 style="margin:0;font-size:18px;font-weight:800;color:#111827">Finalize Purchase</h3>
</div>
<div style="display:grid;grid-template-columns:1fr 1.2fr;gap:14px">
<div style="border:1px solid #E5E7EB;background:white;border-radius:16px;padding:16px;box-shadow:0 1px 4px rgba(0,0,0,0.06)">
<p style="margin:0;font-size:11px;text-transform:uppercase;letter-spacing:0.05em;color:#6B7280;font-weight:700">Selected Package</p>
<h2 style="margin:8px 0;font-size:24px;font-weight:800;color:#111827">{pkg.display_name || pkg.name}</h2>
<div style="display:flex;align-items:center;gap:10px;margin-top:12px">
<div style="width:40px;height:40px;border-radius:12px;background:#FFF1EB;border:1px solid #FFD8C2;display:flex;align-items:center;justify-content:center">
<img src="/sidebar-icons/credits.svg" alt="" style={`width:20px;height:20px;object-fit:contain;filter:${ORANGE_ICON_FILTER}`} />
</div>
<div>
<p style="margin:0;font-size:18px;font-weight:800;color:#111827">{(Number(pkg.credits) + Number(pkg.bonus_credits || 0)).toLocaleString()} TC</p>
<p style="margin:0;font-size:12px;color:#6B7280">Total Tracecoins</p>
</div>
</div>
<div style="margin-top:16px;padding-top:16px;border-top:1px solid #F3F4F6">
<div style="display:flex;justify-content:space-between;margin-bottom:8px">
<span style="font-size:13px;color:#6B7280">Base Credits</span>
<span style="font-size:13px;font-weight:700;color:#111827">{Number(pkg.credits).toLocaleString()}</span>
</div>
<Show when={pkg.bonus_credits > 0}>
<div style="display:flex;justify-content:space-between;margin-bottom:8px">
<span style="font-size:13px;color:#6B7280">Bonus Credits</span>
<span style="font-size:13px;font-weight:700;color:#FF5E13">+{Number(pkg.bonus_credits).toLocaleString()}</span>
</div>
</Show>
{/* Coupon section */}
<Show when={!appliedCoupon()}>
<div style="margin-top:12px;padding:10px;border:1px dashed #D1D5DB;border-radius:10px;background:#F9FAFB">
<p style="margin:0 0 6px;font-size:11px;font-weight:700;color:#374151">Coupon Code</p>
<div style="display:flex;gap:6px">
<input
type="text"
placeholder="Enter code"
value={couponCode()}
onInput={(e) => setCouponCode(e.currentTarget.value)}
style="flex:1;height:32px;border:1px solid #E5E7EB;border-radius:6px;padding:0 8px;font-size:12px"
disabled={couponLoading()}
/>
<button
type="button"
onClick={() => void applyCoupon()}
disabled={couponLoading()}
style="height:32px;border:none;border-radius:6px;background:#03004E;color:white;padding:0 12px;font-size:12px;font-weight:700;cursor:pointer;opacity:${couponLoading() ? 0.6 : 1}"
>
{couponLoading() ? 'Applying...' : 'Apply'}
</button>
</div>
<Show when={couponError()}>
<p style="margin:4px 0 0;font-size:11px;color:#DC2626">{couponError()}</p>
</Show>
</div>
</Show>
<Show when={appliedCoupon()}>
<div style="margin-top:8px;display:flex;justify-content:space-between;font-size:13px;color:#15803D">
<span>Coupon ({appliedCoupon()!.code})</span>
<span>-{appliedCoupon()!.discount_type === 'PERCENT' ? `${appliedCoupon()!.discount_value}%` : `${appliedCoupon()!.discount_value}`}</span>
</div>
</Show>
<div style="display:flex;justify-content:space-between;margin-top:12px;padding-top:12px;border-top:2px solid #F3F4F6">
<span style="font-size:15px;font-weight:800;color:#111827">Total Amount</span>
<span style="font-size:18px;font-weight:800;color:#FF5E13">{((appliedCoupon() ? appliedCoupon().final_price_inr : Number(pkg.price_paise)) / 100).toLocaleString('en-IN')}</span>
</div>
</div>
</div>
<div style="border:1px solid #D9D8F9;background:#F5F5FF;border-radius:16px;padding:20px;display:flex;flex-direction:column;align-items:center;justify-content:center;text-align:center;box-shadow:0 1px 4px rgba(0,0,0,0.06)">
<Show when={step === 'idle'}>
<div style="width:56px;height:56px;border-radius:999px;background:white;display:flex;align-items:center;justify-content:center;margin-bottom:14px;box-shadow:0 2px 8px rgba(3,0,78,0.1)">
<img src="/sidebar-icons/security.svg" alt="" style={`width:28px;height:28px;object-fit:contain;filter:${BLUE_ICON_FILTER}`} />
</div>
<h3 style="margin:0;font-size:18px;font-weight:800;color:#03004E">Secure Gateway</h3>
<p style="margin:8px 0 20px;font-size:13px;color:#4F4B8A;max-width:240px">Continue to our secure partner gateway to complete the transaction.</p>
<button
type="button"
onClick={() => startPayment(pkg)}
style="height:44px;width:100%;max-width:220px;border:none;border-radius:10px;background:#03004E;color:white;font-size:14px;font-weight:700;cursor:pointer;box-shadow:0 4px 12px rgba(3,0,78,0.2)"
>
Confirm & Pay Now
</button>
</Show>
<Show when={step === 'processing' || step === 'verifying'}>
<div style="width:48px;height:48px;border:4px solid #E5E7EB;border-top-color:#FF5E13;border-radius:50%;animation:spin 1s linear infinite;margin-bottom:16px" />
<style>{`@keyframes spin { to { transform: rotate(360deg); } }`}</style>
<h3 style="margin:0;font-size:18px;font-weight:800;color:#111827">{step === 'processing' ? 'Creating Order...' : 'Verifying Payment...'}</h3>
<p style="margin:6px 0 0;font-size:13px;color:#6B7280">Please do not refresh or close the page.</p>
</Show>
<Show when={step === 'success'}>
<div style="width:64px;height:64px;border-radius:999px;background:#DCFCE7;display:flex;align-items:center;justify-content:center;margin-bottom:16px;border:2px solid #22C55E">
<span style="font-size:32px;color:#15803D"></span>
</div>
<h3 style="margin:0;font-size:22px;font-weight:800;color:#15803D">Payment Successful!</h3>
<p style="margin:8px 0 20px;font-size:13px;color:#15803D;max-width:280px">Your account has been credited with {(Number(pkg.credits) + Number(pkg.bonus_credits || 0)).toLocaleString()} Tracecoins.</p>
<div style="background:white;border:1px solid #DCFCE7;border-radius:12px;padding:12px;width:100%;max-width:260px;margin-bottom:20px">
<p style="margin:0;font-size:11px;color:#6B7280;text-transform:uppercase">New Balance</p>
<p style="margin:4px 0 0;font-size:20px;font-weight:800;color:#111827">{leadCredits().toLocaleString()} TC</p>
</div>
<button
type="button"
onClick={() => { setCheckoutPackage(null); setPaymentStep('idle'); }}
style="height:40px;width:100%;max-width:220px;border:none;border-radius:10px;background:#03004E;color:white;font-size:13px;font-weight:700;cursor:pointer"
>
Back to Dashboard
</button>
</Show>
<Show when={step === 'error'}>
<div style="width:64px;height:64px;border-radius:999px;background:#FEE2E2;display:flex;align-items:center;justify-content:center;margin-bottom:16px;border:2px solid #EF4444">
<span style="font-size:32px;color:#B91C1C"></span>
</div>
<h3 style="margin:0;font-size:20px;font-weight:800;color:#B91C1C">Payment Failed</h3>
<p style="margin:8px 0 20px;font-size:13px;color:#6B7280;max-width:240px">Something went wrong with the mock gateway. Please try again.</p>
<button
type="button"
onClick={() => setPaymentStep('idle')}
style="height:40px;width:100%;max-width:220px;border:none;border-radius:10px;background:#B91C1C;color:white;font-size:13px;font-weight:700;cursor:pointer"
>
Try Again
</button>
</Show>
</div>
</div>
</div>
);
};
const renderCreditManagement = () => {
let amtInput: HTMLInputElement | undefined;
let rsnInput: HTMLInputElement | undefined;
return (
<div style="display:flex;flex-direction:column;gap:14px">
<div style="display:flex;align-items:center;gap:10px">
<button
type="button"
onClick={() => setCreditManageView(false)}
style="height:32px;border:1px solid #E5E7EB;background:white;border-radius:8px;padding:0 12px;font-size:12px;font-weight:700;color:#374151;cursor:pointer"
>
Back
</button>
<h3 style="margin:0;font-size:18px;font-weight:800;color:#111827">Credit Management Console</h3>
</div>
<div style="border:1px solid #E5E7EB;background:white;border-radius:16px;padding:20px;box-shadow:0 1px 4px rgba(0,0,0,0.06)">
<div style="display:flex;align-items:center;gap:12px;margin-bottom:20px;padding-bottom:16px;border-bottom:1px solid #F3F4F6">
<div style="width:48px;height:48px;border-radius:999px;background:#FFF1EB;display:flex;align-items:center;justify-content:center">
<img src="/sidebar-icons/credits.svg" alt="" style={`width:24px;height:24px;object-fit:contain;filter:${ORANGE_ICON_FILTER}`} />
</div>
<div>
<p style="margin:0;font-size:12px;color:#6B7280;text-transform:uppercase;font-weight:700">Current User Balance</p>
<p style="margin:2px 0 0;font-size:24px;font-weight:800;color:#111827">{leadCredits().toLocaleString()} TC</p>
</div>
</div>
<div style="display:grid;grid-template-columns:1fr 2fr 1fr;gap:12px;align-items:flex-end">
<div>
<label style="display:block;font-size:11px;font-weight:700;color:#6B7280;margin-bottom:6px;text-transform:uppercase">Amount (coins)</label>
<input
ref={amtInput}
type="number"
placeholder="e.g. 500 or -200"
style="width:100%;height:38px;border-radius:8px;border:1px solid #E5E7EB;padding:0 12px;font-size:13px;outline:none"
/>
</div>
<div>
<label style="display:block;font-size:11px;font-weight:700;color:#6B7280;margin-bottom:6px;text-transform:uppercase">Reason for adjustment</label>
<input
ref={rsnInput}
type="text"
placeholder="e.g. Compensation for downtime, Referral bonus..."
style="width:100%;height:38px;border-radius:8px;border:1px solid #E5E7EB;padding:0 12px;font-size:13px;outline:none"
/>
</div>
<button
type="button"
onClick={() => {
const amount = Number(amtInput?.value || 0);
const reason = rsnInput?.value || 'Administrative Adjustment';
if (amount === 0) return;
adjustCreditsManually(amount, reason);
if (amtInput) amtInput.value = '';
if (rsnInput) rsnInput.value = '';
}}
style="height:38px;border:none;border-radius:8px;background:#03004E;color:white;font-size:13px;font-weight:700;cursor:pointer"
>
Apply Adjustment
</button>
</div>
<p style="margin:10px 0 0;font-size:11px;color:#6B7280">Note: Adjustments are final and logged in transaction history for auditing.</p>
</div>
</div>
);
};
if (checkoutPackage()) return renderCheckout();
if (creditManageView()) return renderCreditManagement();
if (tab === 'buy credits') {
return (
<div style="display:flex;flex-direction:column;gap:10px">
<div style="border:1px solid #E5E7EB;background:white;border-radius:16px;padding:14px;box-shadow:0 1px 4px rgba(0,0,0,0.06);display:flex;align-items:center;justify-content:space-between;gap:12px">
<div>
<p style="margin:0;font-size:11px;text-transform:uppercase;letter-spacing:0.06em;color:#6B7280">Current Balance</p>
<p style="margin:6px 0 0;font-size:22px;font-weight:800;color:#111827;line-height:1">{leadCredits().toLocaleString()} <span style="font-size:14px;color:#64748B;font-weight:700">Tracecoins</span></p>
<p style="margin:6px 0 0;font-size:12px;color:#FF5E13;font-weight:700">+12% from last month</p>
</div>
<div style="display:flex;gap:10px">
<button
type="button"
onClick={() => setCreditManageView(true)}
style="height:36px;border:1px solid #E5E7EB;background:white;border-radius:8px;color:#374151;padding:0 14px;font-size:12px;font-weight:700;white-space:nowrap"
>
Manage Credits
</button>
<button type="button" style="height:36px;border:none;border-radius:8px;background:#03004E;color:white;padding:0 14px;font-size:12px;font-weight:700;white-space:nowrap">Buy Credits</button>
</div>
</div>
<div style="display:grid;grid-template-columns:repeat(4,minmax(0,1fr));gap:10px">
<For each={pricingPackagesResource() || []}>
{(pkg) => (
<div style={`border:${pkg.is_popular ? '2px solid #FF5E13' : '1px solid #E5E7EB'};background:white;border-radius:12px;padding:14px;box-shadow:0 1px 3px rgba(0,0,0,0.05);position:relative`}>
<Show when={pkg.is_popular}>
<span style="position:absolute;top:-10px;right:10px;height:20px;padding:0 10px;border-radius:999px;background:#FF5E13;color:white;font-size:10px;font-weight:700;display:inline-flex;align-items:center">MOST POPULAR</span>
</Show>
<div style="width:28px;height:28px;border-radius:999px;background:#FFF1EB;border:1px solid #FFD8C2;display:flex;align-items:center;justify-content:center">
<img src="/sidebar-icons/credits.svg" alt="" style={`width:14px;height:14px;object-fit:contain;filter:${ORANGE_ICON_FILTER}`} />
</div>
<p style="margin:0;font-size:10px;letter-spacing:0.06em;text-transform:uppercase;font-weight:700;color:#9CA3AF">{pkg.name}</p>
<p style="margin:3px 0 0;font-size:20px;font-weight:800;color:#111827;line-height:1.1">{pkg.display_name || pkg.name}</p>
<p style="margin:8px 0 0;font-size:20px;font-weight:800;color:#111827;line-height:1">{Number(pkg.credits).toLocaleString()}</p>
<p style="margin:0;font-size:12px;color:#6B7280">Credits</p>
<Show when={pkg.bonus_credits > 0}>
<p style="margin:8px 0 0;font-size:10px;font-weight:700;color:#FF5E13;background:#FFF1EB;border:1px solid #FFD8C2;height:20px;padding:0 8px;border-radius:999px;display:inline-flex;align-items:center">+{pkg.bonus_credits} bonus</p>
</Show>
<p style="margin:10px 0 0;font-size:20px;font-weight:800;color:#111827;line-height:1">{(Number(pkg.price_paise) / 100).toLocaleString('en-IN')}</p>
<button
type="button"
onClick={() => setCheckoutPackage(pkg)}
style={`margin-top:10px;height:32px;width:100%;border:none;border-radius:8px;background:${pkg.is_popular ? '#FF5E13' : '#03004E'};color:white;font-size:12px;font-weight:700;cursor:pointer`}
>
Buy Package
</button>
</div>
)}
</For>
</div>
<div style="display:grid;grid-template-columns:2fr 1fr;gap:10px">
<div style="border:1px solid #E5E7EB;background:white;border-radius:12px;padding:12px;box-shadow:0 1px 3px rgba(0,0,0,0.05)">
<p style="margin:0;font-size:13px;font-weight:700;color:#111827">Why buy larger packs?</p>
<div style="display:grid;grid-template-columns:repeat(3,minmax(0,1fr));gap:8px;margin-top:8px">
<div style="border:1px solid #E5E7EB;border-radius:10px;padding:10px;background:#F9FAFB">
<p style="margin:0;font-size:12px;font-weight:700;color:#111827">More savings</p>
<p style="margin:4px 0 0;font-size:11px;color:#6B7280">Higher packs include bonus credits.</p>
</div>
<div style="border:1px solid #E5E7EB;border-radius:10px;padding:10px;background:#F9FAFB">
<p style="margin:0;font-size:12px;font-weight:700;color:#111827">No expiry</p>
<p style="margin:4px 0 0;font-size:11px;color:#6B7280">Credits stay active with your account.</p>
</div>
<div style="border:1px solid #E5E7EB;border-radius:10px;padding:10px;background:#F9FAFB">
<p style="margin:0;font-size:12px;font-weight:700;color:#111827">Instant activation</p>
<p style="margin:4px 0 0;font-size:11px;color:#6B7280">Credits reflect right after purchase.</p>
</div>
</div>
</div>
<div style="border:1px solid #E5E7EB;background:#03004E;border-radius:12px;padding:12px;color:white;box-shadow:0 1px 3px rgba(0,0,0,0.05)">
<p style="margin:0;font-size:11px;letter-spacing:0.06em;text-transform:uppercase;color:#C7D2FE">Recommended</p>
<p style="margin:6px 0 0;font-size:20px;font-weight:800;line-height:1.2">Start with Standard</p>
<p style="margin:6px 0 0;font-size:12px;color:#D7DBFF;line-height:1.45">Best value for active buying and response unlocks.</p>
<button type="button" style="margin-top:10px;height:32px;border:none;border-radius:8px;background:#FF5E13;color:white;padding:0 12px;font-size:12px;font-weight:700;width:100%">Buy Standard</button>
</div>
</div>
</div>
);
}
if (tab === 'transactions') {
return (
<div style="display:flex;flex-direction:column;gap:10px">
<div style="border:1px solid #E5E7EB;background:white;border-radius:16px;box-shadow:0 1px 4px rgba(0,0,0,0.06);overflow:hidden">
<div style="padding:12px;display:flex;align-items:center;justify-content:space-between;border-bottom:1px solid #E5E7EB;background:#F9FAFB">
<div style="display:flex;align-items:center;gap:8px;flex:1">
<input value="" readOnly placeholder="Search transactions..." style="height:34px;min-width:220px;flex:1;border-radius:8px;border:1px solid #E5E7EB;background:white;padding:0 12px;font-size:12px;color:#6B7280;outline:none" />
<button type="button" style="display:inline-flex;height:34px;align-items:center;gap:6px;border-radius:8px;border:1px solid #E5E7EB;background:white;padding:0 12px;font-size:12px;font-weight:500;color:#374151">
<svg width="13" height="13" viewBox="0 0 24 24" fill="none" stroke="#FF5E13" stroke-width="2"><path d="M7 4v13"/><path d="m3 13 4 4 4-4"/><path d="M17 20V7"/><path d="m21 11-4-4-4 4"/></svg>
Sort
</button>
<button type="button" style="display:inline-flex;height:34px;align-items:center;gap:6px;border-radius:8px;border:1px solid #E5E7EB;background:white;padding:0 12px;font-size:12px;font-weight:500;color:#374151">
<svg width="13" height="13" viewBox="0 0 24 24" fill="none" stroke="#FF5E13" stroke-width="2"><path d="M3 5h18M6 12h12M10 19h4"/></svg>
Filters
</button>
</div>
<button type="button" style="display:inline-flex;align-items:center;gap:6px;height:34px;border:none;border-radius:8px;background:#0D0D2A;color:white;padding:0 12px;font-size:12px;font-weight:600">
<svg width="13" height="13" viewBox="0 0 24 24" fill="none" stroke="#FF5E13" stroke-width="2"><path d="M21 15v4a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2v-4"/><polyline points="7 10 12 15 17 10"/><line x1="12" y1="15" x2="12" y2="3"/></svg>
Export
</button>
</div>
<table style="width:100%;border-collapse:collapse">
<thead style="background:#03004E;color:white">
<tr>
{['Invoice No', 'Package', 'Credits', 'Amount Paid', 'Status', 'Date', 'Actions'].map((h) => (
<th style="padding:10px;font-size:11px;text-align:left;letter-spacing:0.04em;text-transform:uppercase">{h}</th>
))}
</tr>
</thead>
<tbody>
{txRows().map((row) => (
<tr style="border-bottom:1px solid #E5E7EB">
<td style="padding:10px;font-size:12px;font-weight:700;color:#374151">{row[0]}</td>
<td style="padding:10px;font-size:12px;color:#111827">{row[1]}</td>
<td style="padding:10px;font-size:12px;color:#111827">{row[2]}</td>
<td style="padding:10px;font-size:12px;font-weight:700;color:#111827">{row[3]}</td>
<td style="padding:10px;font-size:12px">
<span style={`display:inline-flex;height:22px;padding:0 10px;border-radius:999px;font-size:11px;font-weight:700;align-items:center;background:${row[4] === 'Completed' ? '#FFF3EE' : row[4] === 'Pending' ? '#EEF2FF' : '#F3F4F6'};color:${row[4] === 'Completed' ? '#FF5E13' : row[4] === 'Pending' ? '#03004E' : '#6B7280'}`}>{row[4]}</span>
</td>
<td style="padding:10px;font-size:12px;color:#64748B">{row[5]}</td>
<td style="padding:10px">
<button type="button" style="display:inline-flex;align-items:center;gap:6px;height:28px;border-radius:8px;border:1px solid #E5E7EB;background:white;padding:0 10px;font-size:11px;font-weight:700;color:#374151">
<svg width="12" height="12" viewBox="0 0 24 24" fill="none" stroke="#FF5E13" stroke-width="2"><path d="M21 15v4a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2v-4"/><polyline points="7 10 12 15 17 10"/><line x1="12" y1="15" x2="12" y2="3"/></svg>
Download
</button>
</td>
</tr>
))}
</tbody>
</table>
<div style="padding:10px;display:flex;align-items:center;justify-content:space-between">
<p style="margin:0;font-size:12px;color:#64748B">Showing 1 to 4 of 42 transactions</p>
<div style="display:flex;gap:6px">
{[1, 2, 3].map((p) => (
<button type="button" style={`width:30px;height:30px;border-radius:8px;border:1px solid #E5E7EB;background:${p === 1 ? '#FF5E13' : '#fff'};color:${p === 1 ? 'white' : '#6B7280'};font-size:12px;font-weight:700`}>{p}</button>
))}
</div>
</div>
</div>
</div>
);
}
if (tab === 'usage history') {
return (
<div style="display:flex;flex-direction:column;gap:10px">
<div style="display:grid;grid-template-columns:repeat(3,minmax(0,1fr));gap:10px">
<div style="border:1px solid #E5E7EB;background:white;border-radius:12px;padding:12px;box-shadow:0 1px 3px rgba(0,0,0,0.05)"><div style="display:flex;align-items:center;gap:6px"><img src="/sidebar-icons/report.svg" alt="" style={`width:14px;height:14px;object-fit:contain;filter:${ORANGE_ICON_FILTER}`} /><p style="margin:0;font-size:11px;color:#6B7280;text-transform:uppercase">Total Used (30d)</p></div><p style="margin:8px 0 0;font-size:20px;font-weight:800;color:#111827">12,480</p></div>
<div style="border:1px solid #E5E7EB;background:white;border-radius:12px;padding:12px;box-shadow:0 1px 3px rgba(0,0,0,0.05)"><div style="display:flex;align-items:center;gap:6px"><img src="/sidebar-icons/leads.svg" alt="" style={`width:14px;height:14px;object-fit:contain;filter:${ORANGE_ICON_FILTER}`} /><p style="margin:0;font-size:11px;color:#6B7280;text-transform:uppercase">Most Used Action</p></div><p style="margin:8px 0 0;font-size:18px;font-weight:800;color:#111827">Boost Profile</p></div>
<div style="border:1px solid #E5E7EB;background:white;border-radius:12px;padding:12px;box-shadow:0 1px 3px rgba(0,0,0,0.05)"><div style="display:flex;align-items:center;gap:6px"><img src="/sidebar-icons/credits.svg" alt="" style={`width:14px;height:14px;object-fit:contain;filter:${ORANGE_ICON_FILTER}`} /><p style="margin:0;font-size:11px;color:#6B7280;text-transform:uppercase">Current Balance</p></div><p style="margin:8px 0 0;font-size:20px;font-weight:800;color:#111827">12,450 TC</p></div>
</div>
<div style="border:1px solid #E5E7EB;background:white;border-radius:16px;box-shadow:0 1px 4px rgba(0,0,0,0.06);overflow:hidden">
<div style="padding:12px;display:flex;align-items:center;justify-content:space-between;border-bottom:1px solid #E5E7EB;background:#F9FAFB">
<div style="display:flex;align-items:center;gap:8px;flex:1">
<input value="" readOnly placeholder="Search usage history..." style="height:34px;min-width:220px;flex:1;border-radius:8px;border:1px solid #E5E7EB;background:white;padding:0 12px;font-size:12px;color:#6B7280;outline:none" />
<button type="button" style="display:inline-flex;height:34px;align-items:center;gap:6px;border-radius:8px;border:1px solid #E5E7EB;background:white;padding:0 12px;font-size:12px;font-weight:500;color:#374151">
<svg width="13" height="13" viewBox="0 0 24 24" fill="none" stroke="#FF5E13" stroke-width="2"><path d="M7 4v13"/><path d="m3 13 4 4 4-4"/><path d="M17 20V7"/><path d="m21 11-4-4-4 4"/></svg>
Sort
</button>
<button type="button" style="display:inline-flex;height:34px;align-items:center;gap:6px;border-radius:8px;border:1px solid #E5E7EB;background:white;padding:0 12px;font-size:12px;font-weight:500;color:#374151">
<svg width="13" height="13" viewBox="0 0 24 24" fill="none" stroke="#FF5E13" stroke-width="2"><path d="M3 5h18M6 12h12M10 19h4"/></svg>
Filters
</button>
</div>
<button type="button" style="display:inline-flex;align-items:center;gap:6px;height:34px;border:none;border-radius:8px;background:#0D0D2A;color:white;padding:0 12px;font-size:12px;font-weight:600">
<svg width="13" height="13" viewBox="0 0 24 24" fill="none" stroke="#FF5E13" stroke-width="2"><path d="M21 15v4a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2v-4"/><polyline points="7 10 12 15 17 10"/><line x1="12" y1="15" x2="12" y2="3"/></svg>
Export
</button>
</div>
<table style="width:100%;border-collapse:collapse">
<thead style="background:#03004E;color:white">
<tr>
{['Usage ID', 'Action Type', 'Credits Used', 'Related ID', 'Date', 'Remarks'].map((h) => (
<th style="padding:10px;font-size:11px;text-align:left;letter-spacing:0.04em;text-transform:uppercase">{h}</th>
))}
</tr>
</thead>
<tbody>
{[
['#US-99421', 'Unlock Contact', '-5.00', 'CONT-2034', 'Oct 24, 2023', 'Direct profile unlock for enterprise tier'],
['#US-99418', 'Boost Profile', '-25.00', 'PROF-8812', 'Oct 23, 2023', 'Featured placement (7 days)'],
['#US-99405', 'Batch Export', '-150.00', 'EXP-0044', 'Oct 22, 2023', 'Export of 300 leads'],
['#US-99388', 'Unlock Contact', '-5.00', 'CONT-1992', 'Oct 21, 2023', 'Individual unlock action'],
].map((row) => (
<tr style="border-bottom:1px solid #E5E7EB">
<td style="padding:10px;font-size:12px;font-weight:700;color:#64748B">{row[0]}</td>
<td style="padding:10px;font-size:12px;color:#111827">{row[1]}</td>
<td style="padding:10px;font-size:12px;font-weight:700;color:#03004E">{row[2]}</td>
<td style="padding:10px;font-size:12px;color:#64748B">{row[3]}</td>
<td style="padding:10px;font-size:12px;color:#64748B">{row[4]}</td>
<td style="padding:10px;font-size:12px;color:#64748B">{row[5]}</td>
</tr>
))}
</tbody>
</table>
<div style="padding:10px;display:flex;justify-content:space-between;align-items:center;border-top:1px solid #E5E7EB">
<p style="margin:0;font-size:12px;color:#64748B">Showing 1 to 4 of 142 results</p>
<div style="display:flex;gap:6px">
{[1, 2, 3].map((p) => (
<button type="button" style={`width:30px;height:30px;border-radius:8px;border:1px solid #E5E7EB;background:${p === 1 ? '#03004E' : '#fff'};color:${p === 1 ? 'white' : '#6B7280'};font-size:12px;font-weight:700`}>{p}</button>
))}
</div>
</div>
</div>
</div>
);
}
if (tab === 'invoices') {
return (
<div style="border:1px solid #E5E7EB;background:white;border-radius:16px;box-shadow:0 1px 4px rgba(0,0,0,0.06);overflow:hidden">
<div style="padding:12px;display:flex;align-items:center;justify-content:space-between;border-bottom:1px solid #E5E7EB;background:#F9FAFB">
<div style="display:flex;align-items:center;gap:8px;flex:1">
<input value="" readOnly placeholder="Search invoices..." style="height:34px;min-width:220px;flex:1;border-radius:8px;border:1px solid #E5E7EB;background:white;padding:0 12px;font-size:12px;color:#6B7280;outline:none" />
<button type="button" style="display:inline-flex;height:34px;align-items:center;gap:6px;border-radius:8px;border:1px solid #E5E7EB;background:white;padding:0 12px;font-size:12px;font-weight:500;color:#374151">
<svg width="13" height="13" viewBox="0 0 24 24" fill="none" stroke="#FF5E13" stroke-width="2"><path d="M7 4v13"/><path d="m3 13 4 4 4-4"/><path d="M17 20V7"/><path d="m21 11-4-4-4 4"/></svg>
Sort
</button>
<button type="button" style="display:inline-flex;height:34px;align-items:center;gap:6px;border-radius:8px;border:1px solid #E5E7EB;background:white;padding:0 12px;font-size:12px;font-weight:500;color:#374151">
<svg width="13" height="13" viewBox="0 0 24 24" fill="none" stroke="#FF5E13" stroke-width="2"><path d="M3 5h18M6 12h12M10 19h4"/></svg>
Filters
</button>
</div>
<button type="button" style="display:inline-flex;align-items:center;gap:6px;height:34px;border:none;border-radius:8px;background:#0D0D2A;color:white;padding:0 12px;font-size:12px;font-weight:600">
<svg width="13" height="13" viewBox="0 0 24 24" fill="none" stroke="#FF5E13" stroke-width="2"><path d="M21 15v4a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2v-4"/><polyline points="7 10 12 15 17 10"/><line x1="12" y1="15" x2="12" y2="3"/></svg>
Export
</button>
</div>
<table style="width:100%;border-collapse:collapse">
<thead style="background:#03004E;color:white">
<tr>
{['Invoice Number', 'Billing Date', 'Package', 'Total', 'Status'].map((h) => (
<th style="padding:10px;font-size:11px;text-align:left;letter-spacing:0.04em;text-transform:uppercase">{h}</th>
))}
</tr>
</thead>
<tbody>
{invoiceRows.map((row) => (
<tr style="border-bottom:1px solid #E5E7EB">
<td style="padding:10px;font-size:12px;font-weight:700;color:#111827">{row[0]}</td>
<td style="padding:10px;font-size:12px;color:#6B7280">{row[1]}</td>
<td style="padding:10px;font-size:12px;color:#111827">{row[2]}</td>
<td style="padding:10px;font-size:12px;font-weight:700;color:#111827">{row[3]}</td>
<td style="padding:10px;font-size:12px">
<span style={`display:inline-flex;height:22px;padding:0 10px;border-radius:999px;font-size:11px;font-weight:700;align-items:center;background:${row[4] === 'Paid' ? '#FFF3EE' : '#F3F4F6'};color:${row[4] === 'Paid' ? '#FF5E13' : '#6B7280'}`}>{row[4]}</span>
</td>
</tr>
))}
</tbody>
</table>
<div style="padding:10px;display:flex;justify-content:space-between;align-items:center;border-top:1px solid #E5E7EB">
<p style="margin:0;font-size:12px;color:#64748B">Showing 1 to 4 of 24 invoices</p>
<div style="display:flex;gap:6px">
{[1, 2, 3].map((p) => (
<button type="button" style={`width:30px;height:30px;border-radius:8px;border:1px solid #E5E7EB;background:${p === 1 ? '#03004E' : '#fff'};color:${p === 1 ? 'white' : '#6B7280'};font-size:12px;font-weight:700`}>{p}</button>
))}
</div>
</div>
</div>
);
}
return (
<div style="display:flex;flex-direction:column;gap:10px">
<div style="display:grid;grid-template-columns:repeat(3,minmax(0,1fr));gap:10px">
<div style="border:1px solid #E5E7EB;background:white;border-radius:12px;padding:12px;box-shadow:0 1px 3px rgba(0,0,0,0.05)">
<div style="display:flex;align-items:center;gap:6px">
<img src="/sidebar-icons/credits.svg" alt="" style={`width:14px;height:14px;object-fit:contain;filter:${ORANGE_ICON_FILTER}`} />
<p style="margin:0;font-size:11px;color:#6B7280;text-transform:uppercase">Current Balance</p>
</div>
<p style="margin:8px 0 0;font-size:26px;font-weight:800;color:#111827">12,450 TC</p>
<p style="margin:4px 0 0;font-size:12px;color:#FF5E13;font-weight:700">+12% from last month</p>
</div>
<div style="border:1px solid #E5E7EB;background:white;border-radius:12px;padding:12px;box-shadow:0 1px 3px rgba(0,0,0,0.05)">
<div style="display:flex;align-items:center;gap:6px">
<img src="/sidebar-icons/report.svg" alt="" style={`width:14px;height:14px;object-fit:contain;filter:${ORANGE_ICON_FILTER}`} />
<p style="margin:0;font-size:11px;color:#6B7280;text-transform:uppercase">Monthly Usage</p>
</div>
<p style="margin:8px 0 0;font-size:26px;font-weight:800;color:#111827">1,248</p>
<p style="margin:4px 0 0;font-size:12px;color:#6B7280">Avg 42/day</p>
</div>
<div style="border:1px solid #E5E7EB;background:white;border-radius:12px;padding:12px;box-shadow:0 1px 3px rgba(0,0,0,0.05)">
<div style="display:flex;align-items:center;gap:6px">
<img src="/sidebar-icons/approval.svg" alt="" style={`width:14px;height:14px;object-fit:contain;filter:${ORANGE_ICON_FILTER}`} />
<p style="margin:0;font-size:11px;color:#6B7280;text-transform:uppercase">Pending Invoices</p>
</div>
<p style="margin:8px 0 0;font-size:26px;font-weight:800;color:#111827">1</p>
<button type="button" style="margin-top:8px;height:30px;border:none;border-radius:8px;background:#03004E;color:white;padding:0 10px;font-size:12px;font-weight:700">Buy Credits</button>
</div>
</div>
<div style="border:1px solid #E5E7EB;background:white;border-radius:16px;box-shadow:0 1px 4px rgba(0,0,0,0.06);overflow:hidden">
<div style="padding:10px 12px;background:#F9FAFB;border-bottom:1px solid #E5E7EB;display:flex;justify-content:space-between;align-items:center">
<p style="margin:0;font-size:14px;font-weight:700;color:#111827">Recent Transactions</p>
<button type="button" style="height:30px;border:none;border-radius:8px;background:#03004E;color:white;padding:0 10px;font-size:12px;font-weight:700">Buy Credits</button>
</div>
<div style="padding:12px;display:flex;align-items:center;justify-content:space-between;border-bottom:1px solid #E5E7EB;background:#F9FAFB">
<div style="display:flex;align-items:center;gap:8px;flex:1">
<input value="" readOnly placeholder="Search transactions..." style="height:34px;min-width:220px;flex:1;border-radius:8px;border:1px solid #E5E7EB;background:white;padding:0 12px;font-size:12px;color:#6B7280;outline:none" />
<button type="button" style="display:inline-flex;height:34px;align-items:center;gap:6px;border-radius:8px;border:1px solid #E5E7EB;background:white;padding:0 12px;font-size:12px;font-weight:500;color:#374151">
<svg width="13" height="13" viewBox="0 0 24 24" fill="none" stroke="#FF5E13" stroke-width="2"><path d="M7 4v13"/><path d="m3 13 4 4 4-4"/><path d="M17 20V7"/><path d="m21 11-4-4-4 4"/></svg>
Sort
</button>
<button type="button" style="display:inline-flex;height:34px;align-items:center;gap:6px;border-radius:8px;border:1px solid #E5E7EB;background:white;padding:0 12px;font-size:12px;font-weight:500;color:#374151">
<svg width="13" height="13" viewBox="0 0 24 24" fill="none" stroke="#FF5E13" stroke-width="2"><path d="M3 5h18M6 12h12M10 19h4"/></svg>
Filters
</button>
</div>
<button type="button" style="display:inline-flex;align-items:center;gap:6px;height:34px;border:none;border-radius:8px;background:#0D0D2A;color:white;padding:0 12px;font-size:12px;font-weight:600">
<svg width="13" height="13" viewBox="0 0 24 24" fill="none" stroke="#FF5E13" stroke-width="2"><path d="M21 15v4a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2v-4"/><polyline points="7 10 12 15 17 10"/><line x1="12" y1="15" x2="12" y2="3"/></svg>
Export
</button>
</div>
<table style="width:100%;border-collapse:collapse">
<thead style="background:#03004E;color:white">
<tr>
{['Transaction ID', 'Package', 'Credits', 'Amount Paid', 'Status', 'Date'].map((h) => (
<th style="padding:10px;font-size:11px;text-align:left;letter-spacing:0.04em;text-transform:uppercase">{h}</th>
))}
</tr>
</thead>
<tbody>
{txRows().slice(0, 3).map((row: any) => (
<tr style="border-bottom:1px solid #E5E7EB">
<td style="padding:10px;font-size:12px;font-weight:700;color:#374151">{row[0]}</td>
<td style="padding:10px;font-size:12px;color:#111827">{row[1]}</td>
<td style="padding:10px;font-size:12px;color:#111827">{row[2]}</td>
<td style="padding:10px;font-size:12px;font-weight:700;color:#111827">{row[3]}</td>
<td style="padding:10px;font-size:12px">
<span style={`display:inline-flex;height:22px;padding:0 10px;border-radius:999px;font-size:11px;font-weight:700;align-items:center;background:${row[4] === 'Completed' ? '#FFF3EE' : row[4] === 'Pending' ? '#EEF2FF' : '#F3F4F6'};color:${row[4] === 'Completed' ? '#FF5E13' : row[4] === 'Pending' ? '#03004E' : '#6B7280'}`}>{row[4]}</span>
</td>
<td style="padding:10px;font-size:12px;color:#64748B">{row[5]}</td>
</tr>
))}
</tbody>
</table>
</div>
</div>
);
}
if (customerKey() === 'explore nxtgauge') {
return (
<div style="display:flex;flex-direction:column;gap:12px">
<div style="border:1px solid #E5E7EB;border-radius:16px;background:white;box-shadow:0 1px 4px rgba(0,0,0,0.06);padding:20px">
<p style="margin:0;font-size:34px;line-height:1.12;font-weight:800;color:#111827;max-width:760px">Explore opportunities on Nxtgauge</p>
<p style="margin:10px 0 0;font-size:14px;line-height:1.5;max-width:760px;color:#6B7280">Discover services, connect with verified users, and expand into additional roles using the same dashboard workflow.</p>
</div>
<div style="display:grid;grid-template-columns:repeat(3,minmax(0,1fr));gap:10px">
{[
{ key: 'COMPANY', title: 'Company', icon: '/sidebar-icons/users.svg', subtitle: 'Hire talent, post jobs, and manage applications in one path.' },
{ key: 'JOB_SEEKER', title: 'Job Seeker', icon: '/sidebar-icons/company.svg', subtitle: 'Explore opportunities and apply to roles with your profile.' },
{ key: 'SERVICE_SEEKER', title: 'Service Seeker', icon: '/sidebar-icons/candidate.svg', subtitle: 'Post requirements and connect with verified professionals.' },
].map((roleCard) => {
const activeRoles = userRoles();
const isRegistered = activeRoles.some((ar) => normalizeRoleKey(ar.role_key || ar.key) === normalizeRoleKey(roleCard.key));
const isCurrentRole = normalizeRoleKey(props.roleKey || '') === normalizeRoleKey(roleCard.key);
return (
<div style="border:1px solid #E5E7EB;background:white;border-radius:16px;padding:14px;box-shadow:0 1px 4px rgba(0,0,0,0.06);display:flex;flex-direction:column;gap:8px">
<img src={roleCard.icon} alt="" style={`width:20px;height:20px;object-fit:contain;filter:${ORANGE_ICON_FILTER}`} />
<p style="margin:0;font-size:18px;font-weight:800;color:#111827">{roleCard.title}</p>
<p style="margin:0;font-size:12px;line-height:1.5;color:#6B7280">{roleCard.subtitle}</p>
<button
type="button"
onClick={() => isCurrentRole ? null : isRegistered ? switchRole(roleCard.key) : registerRole(roleCard.key)}
style={`margin-top:auto;height:32px;border-radius:8px;border:none;background:${isCurrentRole ? '#E5E7EB' : '#03004E'};color:${isCurrentRole ? '#4B5563' : 'white'};padding:0 10px;font-size:12px;font-weight:700`}
>
{isCurrentRole ? 'Current Role' : isRegistered ? 'Switch' : `Register as ${roleCard.title}`}
</button>
</div>
);
})}
</div>
<div style="border:1px solid #E5E7EB;background:white;border-radius:16px;padding:14px;box-shadow:0 1px 4px rgba(0,0,0,0.06)">
<p style="margin:0;font-size:30px;font-weight:800;color:#111827">Professional Services</p>
<p style="margin:6px 0 0;font-size:13px;color:#6B7280">Choose from 10 professional roles, including UGC Content Creator.</p>
<div style="display:grid;grid-template-columns:repeat(5,minmax(0,1fr));gap:10px;margin-top:12px">
<For each={exploreRoleCards()}>
{(role) => (
<div style="border:1px solid #E5E7EB;border-radius:12px;background:white;padding:12px;display:flex;flex-direction:column;min-height:174px">
<div style="display:flex;align-items:center;justify-content:space-between">
<Show
when={role.iconAsset}
fallback={<role.Icon size={18} color="#FF5E13" strokeWidth={2.2} />}
>
<img src={role.iconAsset} alt="" style={`width:18px;height:18px;object-fit:contain;filter:${ORANGE_ICON_FILTER}`} />
</Show>
<span style={`display:inline-flex;align-items:center;height:20px;padding:0 8px;border-radius:999px;font-size:10px;font-weight:700;border:1px solid ${role.status === 'Registered' ? '#D1D5DB' : '#FFD8C2'};background:${role.status === 'Registered' ? '#F3F4F6' : '#FFF1EB'};color:${role.status === 'Registered' ? '#4B5563' : '#FF5E13'}`}>{role.status}</span>
</div>
<p style="margin:10px 0 0;font-size:18px;font-weight:800;color:#111827;line-height:1.2">{role.title}</p>
<p style="margin:6px 0 0;font-size:12px;line-height:1.45;color:#6B7280">{role.subtitle}</p>
<button
type="button"
onClick={() => role.action === 'Active' ? null : role.action === 'Switch' ? switchRole(role.key) : registerRole(role.key)}
style={`margin-top:auto;height:32px;border-radius:8px;border:none;background:${role.action === 'Register' ? '#03004E' : '#E5E7EB'};color:${role.action === 'Register' ? 'white' : '#4B5563'};padding:0 10px;font-size:12px;font-weight:700`}
>
{role.action}
</button>
</div>
)}
</For>
</div>
</div>
<div style="border:1px solid #E5E7EB;background:linear-gradient(180deg,#FFFFFF 0%,#FFFAF7 100%);border-radius:20px;padding:18px;box-shadow:0 8px 20px rgba(15,23,42,0.06)">
<p style="margin:0;font-size:12px;letter-spacing:0.08em;text-transform:uppercase;font-weight:700;color:#FF5E13;text-align:center">Growth Advantage</p>
<p style="margin:4px 0 0;font-size:32px;font-weight:800;color:#111827;text-align:center;line-height:1.1">Why Add More Services?</p>
<p style="margin:8px auto 0;font-size:13px;line-height:1.5;color:#6B7280;text-align:center;max-width:760px">A multi-service profile helps you acquire more opportunities, improve trust, and scale consistently on a single platform.</p>
<div style="margin-top:14px;display:grid;grid-template-columns:repeat(4,minmax(0,1fr));gap:12px">
<div style="border:1px solid #E5E7EB;background:white;border-radius:14px;padding:14px;box-shadow:0 2px 6px rgba(0,0,0,0.05)">
<div style="width:34px;height:34px;border-radius:999px;background:#FFF3EE;display:flex;align-items:center;justify-content:center"><img src="/sidebar-icons/leads.svg" alt="" style={`width:16px;height:16px;object-fit:contain;filter:${ORANGE_ICON_FILTER}`} /></div>
<p style="margin:10px 0 0;font-size:15px;font-weight:800;color:#111827">Reach More Buyers</p>
<p style="margin:6px 0 0;font-size:12px;color:#6B7280;line-height:1.45">Get discovered by customers across multiple demand categories.</p>
</div>
<div style="border:1px solid #E5E7EB;background:white;border-radius:14px;padding:14px;box-shadow:0 2px 6px rgba(0,0,0,0.05)">
<div style="width:34px;height:34px;border-radius:999px;background:#FFF3EE;display:flex;align-items:center;justify-content:center"><img src="/sidebar-icons/pricing.svg" alt="" style={`width:16px;height:16px;object-fit:contain;filter:${ORANGE_ICON_FILTER}`} /></div>
<p style="margin:10px 0 0;font-size:15px;font-weight:800;color:#111827">Increase Revenue Paths</p>
<p style="margin:6px 0 0;font-size:12px;color:#6B7280;line-height:1.45">Offer additional services and create new income streams.</p>
</div>
<div style="border:1px solid #E5E7EB;background:white;border-radius:14px;padding:14px;box-shadow:0 2px 6px rgba(0,0,0,0.05)">
<div style="width:34px;height:34px;border-radius:999px;background:#FFF3EE;display:flex;align-items:center;justify-content:center"><img src="/sidebar-icons/approval.svg" alt="" style={`width:16px;height:16px;object-fit:contain;filter:${ORANGE_ICON_FILTER}`} /></div>
<p style="margin:10px 0 0;font-size:15px;font-weight:800;color:#111827">Strengthen Credibility</p>
<p style="margin:6px 0 0;font-size:12px;color:#6B7280;line-height:1.45">Verified multi-service profiles build confidence and improve conversion.</p>
</div>
<div style="border:1px solid #E5E7EB;background:white;border-radius:14px;padding:14px;box-shadow:0 2px 6px rgba(0,0,0,0.05)">
<div style="width:34px;height:34px;border-radius:999px;background:#FFF3EE;display:flex;align-items:center;justify-content:center"><img src="/sidebar-icons/report.svg" alt="" style={`width:16px;height:16px;object-fit:contain;filter:${ORANGE_ICON_FILTER}`} /></div>
<p style="margin:10px 0 0;font-size:15px;font-weight:800;color:#111827">Scale Faster</p>
<p style="margin:6px 0 0;font-size:12px;color:#6B7280;line-height:1.45">Grow your business from one unified account and workflow.</p>
</div>
</div>
</div>
</div>
);
}
if (customerKey() === 'verification') {
if (tab === 'approval status') {
return (
<div style="display:flex;flex-direction:column;gap:12px">
<div style="border:1px solid #E5E7EB;background:white;border-radius:14px;padding:14px">
<p style="margin:0;font-size:22px;font-weight:800;color:#111827">Verification Center</p>
<p style="margin:6px 0 0;font-size:13px;color:#6B7280;line-height:1.5">Complete just 2 steps. Submit profile and portfolio. We review and unlock access.</p>
<div style="display:grid;grid-template-columns:repeat(3,minmax(0,1fr));gap:8px;margin-top:10px">
<div style="border:1px solid #E5E7EB;border-radius:10px;background:#F9FAFB;padding:10px">
<p style="margin:0;font-size:10px;letter-spacing:0.06em;text-transform:uppercase;color:#9CA3AF">Step 1</p>
<p style="margin:8px 0 0;font-size:13px;font-weight:800;color:#111827">Profile: {approvalTone(profileApprovalState()).label}</p>
</div>
<div style="border:1px solid #E5E7EB;border-radius:10px;background:#F9FAFB;padding:10px">
<p style="margin:0;font-size:10px;letter-spacing:0.06em;text-transform:uppercase;color:#9CA3AF">Step 2</p>
<p style="margin:8px 0 0;font-size:13px;font-weight:800;color:#111827">Portfolio: {approvalTone(portfolioApprovalState()).label}</p>
</div>
<div style="border:1px solid #E5E7EB;border-radius:10px;background:#F9FAFB;padding:10px">
<p style="margin:0;font-size:10px;letter-spacing:0.06em;text-transform:uppercase;color:#9CA3AF">Final Access</p>
<p style="margin:8px 0 0;font-size:13px;font-weight:800;color:#111827">{bothApprovalsApproved() ? 'Unlocked' : 'Blocked'}</p>
</div>
</div>
</div>
<div style="display:grid;grid-template-columns:1fr 1fr;gap:12px">
<div style="border:1px solid #E5E7EB;background:white;border-radius:14px;padding:14px">
<p style="margin:0;font-size:18px;font-weight:800;color:#111827">Profile Verification</p>
<p style="margin:6px 0 0;font-size:12px;color:#6B7280">Finish your basic information and required documents, then submit.</p>
<div style="display:flex;gap:8px;justify-content:flex-end;margin-top:10px">
<button type="button" onClick={() => { props.onSidebarSelect('My Profile'); props.onTabSelect('basic information'); }} style="height:32px;border-radius:8px;border:1px solid #E5E7EB;background:white;padding:0 12px;font-size:12px;font-weight:700;color:#374151">Open My Profile</button>
<button type="button" onClick={submitProfileForApproval} disabled={profileApprovalState() === 'IN_REVIEW'} style={`height:32px;border-radius:8px;border:none;background:${profileApprovalState() === 'IN_REVIEW' ? '#9CA3AF' : '#03004E'};color:white;padding:0 12px;font-size:12px;font-weight:700`}>Submit Profile</button>
</div>
</div>
<div style="border:1px solid #E5E7EB;background:white;border-radius:14px;padding:14px">
<p style="margin:0;font-size:18px;font-weight:800;color:#111827">Portfolio Verification</p>
<p style="margin:6px 0 0;font-size:12px;color:#6B7280">Add portfolio details and submit separately for review.</p>
<div style="display:flex;gap:8px;justify-content:flex-end;margin-top:10px">
<button type="button" onClick={() => { props.onSidebarSelect('My Portfolio'); props.onTabSelect('about'); }} style="height:32px;border-radius:8px;border:1px solid #E5E7EB;background:white;padding:0 12px;font-size:12px;font-weight:700;color:#374151">Open My Portfolio</button>
<button type="button" onClick={submitPortfolioForApproval} disabled={portfolioApprovalState() === 'IN_REVIEW'} style={`height:32px;border-radius:8px;border:none;background:${portfolioApprovalState() === 'IN_REVIEW' ? '#9CA3AF' : '#03004E'};color:white;padding:0 12px;font-size:12px;font-weight:700`}>Submit Portfolio</button>
</div>
</div>
</div>
<div style="border:1px solid #E5E7EB;background:#F9FAFB;border-radius:12px;padding:12px">
<p style="margin:0;font-size:12px;color:#374151;line-height:1.5"><strong>Admin Flow:</strong> once submitted, both items appear in Verification Management. Admin can request documents or approve. Leads remain blocked until both are approved.</p>
</div>
</div>
);
}
if (tab === 'submitted details') {
return (
<div style="display:grid;grid-template-columns:1fr 1.5fr;gap:12px">
<div style="border:1px solid #E5E7EB;background:white;border-radius:14px;padding:14px">
<p style="margin:0;font-size:20px;font-weight:800;color:#111827">Application Snapshot</p>
<div style="display:grid;gap:8px;margin-top:10px">
{[
['Service', 'Social Media Manager'],
['Submission ID', 'VRF-2023-1042'],
['Submitted On', 'Oct 22, 2023'],
['Status', 'Under Review'],
].map(([k, v]) => (
<div style="padding:10px;border:1px solid #E5E7EB;border-radius:10px;background:#F9FAFB;font-size:12px;color:#374151"><strong>{k}:</strong> {v}</div>
))}
</div>
</div>
<div style="border:1px solid #E5E7EB;background:white;border-radius:14px;padding:14px">
<p style="margin:0;font-size:20px;font-weight:800;color:#111827">Submitted Details</p>
<div style="margin-top:10px;padding:10px;border:1px solid #E5E7EB;border-radius:10px;background:#F9FAFB;font-size:12px;line-height:1.6;color:#374151">
Strategic Social Media Manager with 6+ years of experience in content strategy, campaign planning, and audience growth for D2C brands.
</div>
<div style="display:flex;gap:6px;flex-wrap:wrap;margin-top:10px">
{['Sprout Social', 'Figma', 'Google Analytics'].map((tool) => <span style="height:24px;padding:0 10px;border-radius:999px;border:1px solid #E5E7EB;background:white;display:inline-flex;align-items:center;font-size:11px;color:#374151">{tool}</span>)}
</div>
</div>
</div>
);
}
if (tab === 'documents') {
return (
<div style="display:grid;gap:12px">
<div style="display:grid;grid-template-columns:repeat(3,minmax(0,1fr));gap:10px">
<div style="border:1px solid #E5E7EB;background:white;border-radius:12px;padding:12px">
<p style="margin:0;font-size:10px;letter-spacing:0.06em;text-transform:uppercase;color:#9CA3AF">Total Documents</p>
<p style="margin:6px 0 0;font-size:22px;font-weight:800;color:#111827">3</p>
</div>
<div style="border:1px solid #E5E7EB;background:white;border-radius:12px;padding:12px">
<p style="margin:0;font-size:10px;letter-spacing:0.06em;text-transform:uppercase;color:#9CA3AF">Approved</p>
<p style="margin:6px 0 0;font-size:22px;font-weight:800;color:#03004E">2</p>
</div>
<div style="border:1px solid #FFE2D3;background:#FFF8F4;border-radius:12px;padding:12px">
<p style="margin:0;font-size:10px;letter-spacing:0.06em;text-transform:uppercase;color:#9CA3AF">Needs Action</p>
<p style="margin:6px 0 0;font-size:22px;font-weight:800;color:#FF5E13">1</p>
</div>
</div>
<div style="border:1px solid #FFE2D3;border-radius:12px;background:#FFF8F4;padding:12px;display:flex;align-items:center;justify-content:space-between;gap:10px">
<div>
<p style="margin:0;font-size:12px;font-weight:800;color:#111827">Admin requested missing documents</p>
<p style="margin:4px 0 0;font-size:12px;color:#374151">Required Missing Documents: Address Proof (clear PDF/JPG/PNG).</p>
</div>
<button type="button" style="height:32px;border:none;border-radius:8px;background:#03004E;color:white;padding:0 12px;font-size:12px;font-weight:700;white-space:nowrap">Upload Missing</button>
</div>
<div style="border:1px solid #E5E7EB;background:white;border-radius:14px;overflow:hidden">
<div style="padding:12px;display:flex;align-items:center;justify-content:space-between;border-bottom:1px solid #E5E7EB">
<p style="margin:0;font-size:18px;font-weight:800;color:#111827">Documents</p>
<button type="button" style="height:32px;border:none;border-radius:8px;background:#03004E;color:white;padding:0 12px;font-size:12px;font-weight:700">Upload New</button>
</div>
<table style="width:100%;border-collapse:collapse">
<thead>
<tr style="border-bottom:1px solid #E5E7EB;background:#F9FAFB">
<th style="text-align:left;padding:10px;font-size:11px;color:#6B7280">Document</th>
<th style="text-align:left;padding:10px;font-size:11px;color:#6B7280">File</th>
<th style="text-align:left;padding:10px;font-size:11px;color:#6B7280">Status</th>
<th style="text-align:right;padding:10px;font-size:11px;color:#6B7280">Action</th>
</tr>
</thead>
<tbody>
{[
['Identity Proof', 'id_scan_v2.pdf', 'Approved'],
['Address Proof', 'electricity_bill.jpg', 'Rejected'],
['Tax Document', 'itr_form16_2023.pdf', 'Approved'],
].map(([doc, file, state]) => (
<tr style="border-top:1px solid #F3F4F6">
<td style="padding:10px;font-size:12px;color:#111827;font-weight:600">{doc}</td>
<td style="padding:10px;font-size:12px;color:#374151">{file}</td>
<td style="padding:10px">
<span style={`height:22px;padding:0 8px;border-radius:999px;border:1px solid ${state === 'Rejected' ? '#FFD8C2' : '#DDEBFF'};background:${state === 'Rejected' ? '#FFF1EB' : '#EEF4FF'};display:inline-flex;align-items:center;font-size:10px;font-weight:700;color:${state === 'Rejected' ? '#FF5E13' : '#03004E'}`}>{state}</span>
</td>
<td style="padding:10px;text-align:right">
<Show
when={state === 'Rejected'}
fallback={<span style="font-size:11px;color:#9CA3AF">No action</span>}
>
<button type="button" style="height:28px;border:1px solid #D1D5DB;border-radius:8px;background:white;color:#374151;padding:0 10px;font-size:11px;font-weight:700">Re-upload</button>
</Show>
</td>
</tr>
))}
</tbody>
</table>
</div>
<div style="border:1px solid #FFE2D3;border-radius:12px;background:#FFF8F4;padding:10px;font-size:12px;color:#374151">
Address Proof needs re-upload to continue verification.
</div>
</div>
);
}
if (tab === 're-upload') {
return (
<div style="display:grid;grid-template-columns:1fr 1.5fr;gap:12px">
<div style="border:1px solid #FFE2D3;background:#FFF8F4;border-radius:14px;padding:12px">
<p style="margin:0;font-size:12px;color:#FF5E13;font-weight:700">1 item needs correction</p>
<p style="margin:8px 0 0;font-size:12px;color:#374151">Identity proof is unclear. Please upload a clear copy.</p>
</div>
<div style="border:1px solid #E5E7EB;background:white;border-radius:14px;padding:12px">
<p style="margin:0;font-size:20px;font-weight:800;color:#111827">Upload Correction</p>
<input type="file" style="margin-top:10px;font-size:12px;color:#374151" />
<div style="display:flex;justify-content:flex-end;gap:8px;margin-top:10px">
<button type="button" style="height:32px;border-radius:8px;border:1px solid #D1D5DB;background:white;color:#374151;padding:0 12px;font-size:12px;font-weight:700">Cancel</button>
<button type="button" style="height:32px;border-radius:8px;border:none;background:#03004E;color:white;padding:0 12px;font-size:12px;font-weight:700">Submit</button>
</div>
</div>
</div>
);
}
if (tab === 'activity') {
return (
<div style="border:1px solid #E5E7EB;background:white;border-radius:14px;overflow:hidden">
<div style="padding:12px;border-bottom:1px solid #E5E7EB">
<p style="margin:0;font-size:20px;font-weight:800;color:#111827">Verification Updates</p>
</div>
<div style="padding:12px;display:grid;gap:8px">
{[
['Today, 11:30 AM', 'Correction submitted', 'Completed'],
['Today, 10:45 AM', 'Re-upload requested', 'Action Needed'],
['Yesterday, 04:20 PM', 'Reviewer note added', 'Updated'],
['Yesterday, 11:10 AM', 'Verification started', 'Started'],
].map(([time, title, state]) => (
<div style="display:flex;align-items:center;justify-content:space-between;gap:10px;padding:10px;border:1px solid #E5E7EB;border-radius:10px;background:#F9FAFB">
<div>
<p style="margin:0;font-size:12px;font-weight:700;color:#111827">{title}</p>
<p style="margin:2px 0 0;font-size:11px;color:#6B7280">{time}</p>
</div>
<span style={`height:22px;padding:0 8px;border-radius:999px;border:1px solid ${state === 'Action Needed' ? '#FFD8C2' : '#DDEBFF'};background:${state === 'Action Needed' ? '#FFF1EB' : '#EEF4FF'};display:inline-flex;align-items:center;font-size:10px;font-weight:700;color:${state === 'Action Needed' ? '#FF5E13' : '#03004E'}`}>{state}</span>
</div>
))}
</div>
</div>
);
}
return (
<div style="border:1px solid #E5E7EB;background:white;border-radius:14px;padding:14px">
<p style="margin:0;font-size:14px;font-weight:700;color:#111827">Details will appear here</p>
<p style="margin:6px 0 0;font-size:12px;color:#6B7280">This section is being prepared. Please use the available tabs above.</p>
</div>
);
}
if (customerKey() === 'help center' || customerKey() === 'support') {
const active = helpCenterTab();
return (
<div style="display:flex;flex-direction:column;gap:12px">
<div style="border:1px solid #E5E7EB;background:white;border-radius:16px;padding:14px;box-shadow:0 1px 4px rgba(0,0,0,0.06)">
<p style="margin:0;font-size:28px;line-height:1.1;font-weight:800;color:#111827">Help Center</p>
<p style="margin:8px 0 0;font-size:14px;color:#6B7280">Get help, manage tickets, and contact support.</p>
<div style="display:flex;align-items:center;gap:24px;margin-top:12px;border-bottom:1px solid #E5E7EB;padding-bottom:8px">
<button type="button" onClick={() => setHelpCenterTab('help_center')} style={`border:none;background:none;padding:0 0 8px;font-size:13px;font-weight:${active === 'help_center' ? 700 : 500};color:${active === 'help_center' ? '#FF5E13' : '#6B7280'};border-bottom:${active === 'help_center' ? '2px solid #FF5E13' : '2px solid transparent'};cursor:pointer`}>Help Center</button>
<button type="button" onClick={() => setHelpCenterTab('my_tickets')} style={`border:none;background:none;padding:0 0 8px;font-size:13px;font-weight:${active === 'my_tickets' ? 700 : 500};color:${active === 'my_tickets' ? '#FF5E13' : '#6B7280'};border-bottom:${active === 'my_tickets' ? '2px solid #FF5E13' : '2px solid transparent'};cursor:pointer`}>My Tickets</button>
<button type="button" onClick={() => setHelpCenterTab('create_ticket')} style={`border:none;background:none;padding:0 0 8px;font-size:13px;font-weight:${active === 'create_ticket' ? 700 : 500};color:${active === 'create_ticket' ? '#FF5E13' : '#6B7280'};border-bottom:${active === 'create_ticket' ? '2px solid #FF5E13' : '2px solid transparent'};cursor:pointer`}>Create Ticket</button>
</div>
</div>
<Show when={active === 'help_center'}>
<div style="border:1px solid #E5E7EB;background:white;border-radius:16px;padding:14px;box-shadow:0 1px 4px rgba(0,0,0,0.06)">
<p style="margin:0;font-size:11px;letter-spacing:0.08em;text-transform:uppercase;font-weight:700;color:#FF5E13">Knowledge Base</p>
<div style="display:grid;grid-template-columns:1fr 1.8fr auto;gap:8px;margin-top:10px">
<select
value={kbCategory()}
onChange={(e) => setKbCategory(e.currentTarget.value)}
style="height:36px;border:1px solid #D1D5DB;border-radius:10px;background:white;padding:0 12px;display:flex;align-items:center;font-size:12px;color:#111827;outline:none"
>
<option value="All">All Categories</option>
<For each={kbCategoriesWithCounts()}>{cat => <option value={cat.title}>{cat.title}</option>}</For>
</select>
<div style="position:relative">
<input
type="text"
placeholder="Search help articles, tickets, and guides"
value={kbSearch()}
onInput={(e) => setKbSearch(e.currentTarget.value)}
style="width:100%;height:36px;border:1px solid #D1D5DB;border-radius:10px;background:white;padding:0 12px;font-size:12px;color:#111827;outline:none"
/>
</div>
<button type="button" style="height:36px;width:44px;border:none;border-radius:10px;background:#0D0D2A;color:white;font-size:16px;font-weight:700"></button>
</div>
<div style="display:flex;align-items:center;gap:8px;margin-top:10px">
<span style="font-size:11px;color:#6B7280">Popular:</span>
{['Verification', 'Credits', 'Lead Response'].map((tag) => <button onClick={() => setKbSearch(tag)} style="height:22px;padding:0 10px;border-radius:999px;border:1px solid #E5E7EB;background:#F9FAFB;color:#374151;display:inline-flex;align-items:center;font-size:11px;cursor:pointer">{tag}</button>)}
</div>
</div>
<div style="display:flex;justify-content:space-between;align-items:center">
<p style="margin:0;font-size:24px;font-weight:800;color:#111827;line-height:1.1">Browse by Category</p>
</div>
<div style="display:grid;grid-template-columns:repeat(4,minmax(0,1fr));gap:10px">
<For each={kbCategoriesWithCounts()}>{(cat) => (
<button
onClick={() => setKbCategory(cat.title)}
style="text-align:left;border:1px solid #E5E7EB;background:white;border-radius:14px;padding:12px;box-shadow:0 1px 4px rgba(0,0,0,0.05);cursor:pointer;transition:all 0.2s"
onMouseEnter={(e) => e.currentTarget.style.borderColor = '#FF5E13'}
onMouseLeave={(e) => e.currentTarget.style.borderColor = '#E5E7EB'}
>
<div style={`width:32px;height:32px;border-radius:8px;background:#FFF3EE;display:flex;align-items:center;justify-content:center;margin-bottom:10px`}>
<BookOpen size={16} color="#FF5E13" />
</div>
<p style="margin:0;font-size:14px;font-weight:800;color:#111827">{cat.title}</p>
<p style="margin:6px 0 0;font-size:11px;line-height:1.45;color:#6B7280">{cat.description}</p>
<div style="display:flex;justify-content:space-between;align-items:center;margin-top:10px">
<span style="font-size:10px;font-weight:700;color:#9CA3AF;text-transform:uppercase">{cat.articles} Articles</span>
</div>
</button>
)}</For>
</div>
<div style="display:grid;grid-template-columns:100%;gap:12px">
<div>
<div style="display:flex;justify-content:space-between;align-items:end;margin-bottom:10px">
<p style="margin:0;font-size:24px;font-weight:800;color:#111827;line-height:1.1">
{kbSearch() || kbCategory() !== 'All' ? 'Search Results' : 'Important Articles'}
</p>
<Show when={kbSearch() || kbCategory() !== 'All'}>
<button onClick={() => { setKbSearch(''); setKbCategory('All'); }} style="font-size:11px;color:#FF5E13;font-weight:700;background:none;border:none;cursor:pointer">Clear Filters</button>
</Show>
</div>
<div style="display:grid;grid-template-columns:repeat(2, 1fr);gap:8px">
<For each={filteredKbArticles()}>{(a) => (
<button type="button" style="height:auto;min-height:44px;border:1px solid #E5E7EB;background:white;border-radius:10px;padding:12px;display:flex;justify-content:space-between;align-items:center;cursor:pointer;transition:all 0.15s">
<div style="display:flex;flex-direction:column;gap:2px;text-align:left">
<span style="font-[10px];font-weight:700;color:#FF5E13;text-transform:uppercase;letter-spacing:0.02em">{a.category}</span>
<span style="font-size:13px;font-weight:700;color:#111827">{a.title}</span>
<span style="font-size:11px;color:#6B7280;line-clamp:1;overflow:hidden;text-overflow:ellipsis;display:-webkit-box;-webkit-line-clamp:1;-webkit-box-orient:vertical">{a.content}</span>
</div>
<span style="font-size:14px;color:#9CA3AF"></span>
</button>
)}</For>
<Show when={filteredKbArticles().length === 0}>
<div style="grid-column:1/-1;padding:40px;text-align:center;background:#F9FAFB;border-radius:12px;border:1px dashed #D1D5DB">
<AlertCircle size={24} color="#9CA3AF" style="margin-bottom:8px" />
<p style="margin:0;font-size:13px;color:#6B7280">No articles found matching your criteria.</p>
</div>
</Show>
</div>
</div>
</div>
<div>
<div style="display:flex;justify-content:space-between;align-items:center;margin-bottom:10px">
<p style="margin:0;font-size:24px;font-weight:800;color:#111827;line-height:1.1">Quick Guides</p>
<span style="font-size:11px;color:#9CA3AF">Actionable tutorials to master Nxtgauge</span>
</div>
<div style="display:grid;grid-template-columns:repeat(3,minmax(0,1fr));gap:10px">
{[
{ title: 'The First 24 Hours: Setup', text: 'Complete your core profile requirements and find your first lead fast.', read: '8 min read', level: 'Beginner', Icon: Rocket },
{ title: 'Maximizing Lead Conversions', text: 'Use response quality and timing strategies to improve closure rate.', read: '15 min read', level: 'Growth', Icon: TrendingUp },
{ title: 'Data Privacy & Security', text: 'Understand how we protect your business and financial information.', read: '5 min read', level: 'Essential', Icon: ShieldCheck },
].map((g) => (
<div style="border:1px solid #E5E7EB;background:white;border-radius:12px;overflow:hidden;box-shadow:0 1px 4px rgba(0,0,0,0.05)">
<div style="padding:10px;display:flex;align-items:center;justify-content:space-between;border-bottom:1px solid #F3F4F6;background:#FAFAFA">
<span style="height:22px;padding:0 8px;border-radius:999px;background:#FFF1EB;color:#FF5E13;font-size:10px;font-weight:700;display:inline-flex;align-items:center;border:1px solid #FFD8C2">{g.level}</span>
<span style="width:24px;height:24px;border-radius:999px;background:#FFF1EB;display:inline-flex;align-items:center;justify-content:center;color:#FF5E13;border:1px solid #FFD8C2">
<g.Icon size={13} />
</span>
</div>
<div style="padding:12px">
<p style="margin:0;font-size:14px;font-weight:800;color:#111827">{g.title}</p>
<p style="margin:6px 0 0;font-size:12px;line-height:1.45;color:#6B7280">{g.text}</p>
<div style="margin-top:10px;display:flex;align-items:center;justify-content:space-between">
<p style="margin:0;font-size:11px;color:#9CA3AF;text-transform:uppercase">{g.read}</p>
<button type="button" style="height:26px;border-radius:7px;border:1px solid #D1D5DB;background:white;color:#374151;padding:0 9px;font-size:11px;font-weight:700">Read</button>
</div>
</div>
</div>
))}
</div>
</div>
</Show>
<Show when={active === 'my_tickets'}>
<div style="border:1px solid #E5E7EB;background:white;border-radius:16px;padding:16px;box-shadow:0 1px 4px rgba(0,0,0,0.06)">
<div style="display:flex;justify-content:space-between;align-items:center;gap:10px">
<p style="margin:0;font-size:24px;line-height:1.1;font-weight:800;color:#111827">My Tickets</p>
<div style="display:flex;gap:8px">
<span style="height:26px;padding:0 10px;border-radius:999px;border:1px solid #E5E7EB;background:#F9FAFB;display:inline-flex;align-items:center;font-size:11px;color:#374151;font-weight:600">Open: {ticketSummary().openCount}</span>
<span style="height:26px;padding:0 10px;border-radius:999px;border:1px solid #E5E7EB;background:#F9FAFB;display:inline-flex;align-items:center;font-size:11px;color:#374151;font-weight:600">Resolved: {ticketSummary().resolvedCount}</span>
</div>
</div>
<div style="margin-top:10px;border:1px solid #E5E7EB;border-radius:10px;background:#F9FAFB;padding:10px 12px">
<p style="margin:0;font-size:12px;color:#6B7280">You have {ticketSummary().total} tickets. {ticketSummary().openCount} still need action and {ticketSummary().resolvedCount} are resolved.</p>
</div>
<div style="margin-top:14px;display:flex;align-items:center;gap:18px;border-bottom:1px solid #E5E7EB;padding-bottom:8px">
<button type="button" onClick={() => setMyTicketsTab('all_tickets')} style={`border:none;background:none;padding:0 0 8px;font-size:13px;font-weight:${myTicketsTab() === 'all_tickets' ? 700 : 500};color:${myTicketsTab() === 'all_tickets' ? '#FF5E13' : '#6B7280'};border-bottom:${myTicketsTab() === 'all_tickets' ? '2px solid #FF5E13' : '2px solid transparent'};cursor:pointer`}>All Tickets</button>
<button type="button" onClick={() => setMyTicketsTab('view_ticket')} style={`border:none;background:none;padding:0 0 8px;font-size:13px;font-weight:${myTicketsTab() === 'view_ticket' ? 700 : 500};color:${myTicketsTab() === 'view_ticket' ? '#FF5E13' : '#6B7280'};border-bottom:${myTicketsTab() === 'view_ticket' ? '2px solid #FF5E13' : '2px solid transparent'};cursor:pointer`}>View Ticket</button>
</div>
<Show when={myTicketsTab() === 'all_tickets'}>
<div style="margin-top:12px;border:1px solid #E5E7EB;border-radius:12px;overflow:hidden;background:white">
<table style="width:100%;border-collapse:collapse">
<thead>
<tr style="background:#F9FAFB;border-bottom:1px solid #E5E7EB">
<th style="text-align:left;padding:10px;font-size:11px;color:#6B7280;text-transform:uppercase;letter-spacing:0.04em">Ticket ID</th>
<th style="text-align:left;padding:10px;font-size:11px;color:#6B7280;text-transform:uppercase;letter-spacing:0.04em">Request</th>
<th style="text-align:left;padding:10px;font-size:11px;color:#6B7280;text-transform:uppercase;letter-spacing:0.04em">State</th>
<th style="text-align:left;padding:10px;font-size:11px;color:#6B7280;text-transform:uppercase;letter-spacing:0.04em">Priority</th>
<th style="text-align:left;padding:10px;font-size:11px;color:#6B7280;text-transform:uppercase;letter-spacing:0.04em">Last Message</th>
<th style="text-align:left;padding:10px;font-size:11px;color:#6B7280;text-transform:uppercase;letter-spacing:0.04em">Updated</th>
<th style="text-align:left;padding:10px;font-size:11px;color:#6B7280;text-transform:uppercase;letter-spacing:0.04em">Action</th>
</tr>
</thead>
<tbody>
{HELP_TICKET_ROWS.map((t) => (
<tr onClick={() => openTicketDetails(t.id)} style={`border-top:1px solid #F3F4F6;background:${activeTicketId() === t.id ? '#F5F7FF' : 'white'};cursor:pointer`}>
<td style="padding:10px;font-size:12px;font-weight:700;color:#374151">{t.id}</td>
<td style="padding:10px;font-size:12px;color:#111827;font-weight:600">{t.title}</td>
<td style="padding:10px">
<span style={`height:22px;padding:0 8px;border-radius:999px;border:1px solid ${t.status === 'Resolved' ? '#D1FAE5' : '#E5E7EB'};background:${t.status === 'Resolved' ? '#ECFDF3' : '#F9FAFB'};display:inline-flex;align-items:center;font-size:10px;font-weight:700;color:${t.status === 'Resolved' ? '#059669' : '#374151'}`}>{t.status}</span>
</td>
<td style="padding:10px;font-size:12px;color:#6B7280">{t.priority}</td>
<td style="padding:10px;font-size:12px;color:#6B7280">{t.lastMessage}</td>
<td style="padding:10px;font-size:12px;color:#6B7280">{t.updated}</td>
<td style="padding:10px">
<button
type="button"
onClick={() => {
openTicketDetails(t.id);
}}
style="height:28px;border-radius:8px;border:none;background:#0D0D2A;color:white;font-size:11px;font-weight:700;padding:0 10px;cursor:pointer"
>
Open
</button>
</td>
</tr>
))}
</tbody>
</table>
</div>
</Show>
<Show when={myTicketsTab() === 'view_ticket'}>
<div style="margin-top:12px;border:1px solid #E5E7EB;border-radius:12px;background:white;padding:14px">
<div style="display:flex;justify-content:space-between;align-items:center;gap:8px">
<div>
<p style="margin:0;font-size:16px;font-weight:800;color:#111827">Ticket Communication</p>
<p style="margin:3px 0 0;font-size:12px;color:#6B7280">{activeTicketId()} Share updates and attachments with support team.</p>
</div>
<span style="height:24px;padding:0 10px;border-radius:999px;border:1px solid #E5E7EB;background:#F9FAFB;display:inline-flex;align-items:center;font-size:11px;color:#374151">Message Support</span>
</div>
<div style="margin-top:12px;display:grid;grid-template-columns:1.4fr 1fr;gap:10px">
<div style="display:grid;gap:8px">
<div style="border:1px solid #E5E7EB;border-radius:8px;background:#F9FAFB;padding:10px">
<p style="margin:0;font-size:11px;color:#6B7280">User message</p>
<p style="margin:4px 0 0;font-size:12px;color:#111827">{selectedTicketDetails().userMessage}</p>
</div>
<div style="border:1px solid #E5E7EB;border-radius:8px;background:#F9FAFB;padding:10px">
<p style="margin:0;font-size:11px;color:#6B7280">Admin reply</p>
<p style="margin:4px 0 0;font-size:12px;color:#111827">{selectedTicketDetails().adminMessage}</p>
</div>
<div style="margin-top:2px;border:1px solid #E5E7EB;border-radius:10px;background:white;padding:10px">
<p style="margin:0 0 8px;font-size:12px;font-weight:700;color:#111827">Reply to Support</p>
<textarea
value={ticketMessage()}
onInput={(event) => setTicketMessage(event.currentTarget.value)}
placeholder="Write your message to support team..."
style="min-height:82px;width:100%;border:1px solid #E5E7EB;border-radius:10px;background:#F9FAFB;padding:10px;font-size:12px;color:#111827;resize:vertical"
/>
<div style="display:flex;justify-content:flex-end;margin-top:8px">
<button type="button" style="height:36px;border-radius:10px;border:none;background:#0D0D2A;color:white;padding:0 14px;font-size:12px;font-weight:700">Send Message</button>
</div>
</div>
</div>
<div style="display:grid;gap:8px">
<div style="border:1px solid #E5E7EB;border-radius:10px;background:#F9FAFB;padding:10px">
<p style="margin:0;font-size:11px;font-weight:700;color:#6B7280;text-transform:uppercase">Requested Documents</p>
<div style="display:grid;gap:6px;margin-top:8px">
<For each={selectedTicketDetails().requestedDocuments.length ? selectedTicketDetails().requestedDocuments : ['No documents requested']}>{(d) => (
<div style="display:flex;justify-content:space-between;align-items:center;background:white;border:1px solid #E5E7EB;border-radius:8px;padding:6px 8px">
<span style="font-size:12px;color:#111827">{d}</span>
<span style="font-size:10px;font-weight:700;color:#6B7280">{selectedTicketDetails().requestedDocuments.length ? 'Pending' : 'NA'}</span>
</div>
)}</For>
</div>
</div>
<div style="border:1px solid #E5E7EB;border-radius:10px;background:#F9FAFB;padding:10px">
<p style="margin:0;font-size:11px;font-weight:700;color:#6B7280;text-transform:uppercase">Received From User</p>
<div style="display:grid;gap:6px;margin-top:8px">
<For each={selectedTicketDetails().receivedFiles.length ? selectedTicketDetails().receivedFiles : [{ file: 'No files received yet', state: 'Pending Upload' as const }]}>{(f) => (
<div style="display:flex;justify-content:space-between;align-items:center;background:white;border:1px solid #E5E7EB;border-radius:8px;padding:6px 8px">
<span style="font-size:12px;color:#111827">{f.file}</span>
<span style={`font-size:10px;font-weight:700;color:${f.state === 'Received' ? '#059669' : '#6B7280'}`}>{f.state}</span>
</div>
)}</For>
</div>
</div>
<div style="border:1px dashed #D1D5DB;border-radius:10px;background:#F9FAFB;padding:10px">
<p style="margin:0 0 8px;font-size:11px;font-weight:700;color:#6B7280;text-transform:uppercase">Upload Requested Files</p>
<input
type="file"
multiple
onChange={(event) => {
const files = event.currentTarget.files;
setViewTicketFiles(files ? Array.from(files).map((file) => file.name) : []);
}}
style="width:100%;font-size:12px;color:#374151"
/>
<Show when={viewTicketFiles().length}>
<div style="display:flex;flex-wrap:wrap;gap:6px;margin-top:8px">
<For each={viewTicketFiles()}>{(name) => <span style="height:24px;padding:0 8px;border-radius:999px;background:white;border:1px solid #E5E7EB;display:inline-flex;align-items:center;font-size:11px;color:#374151">{name}</span>}</For>
</div>
</Show>
</div>
</div>
</div>
</div>
</Show>
</div>
</Show>
<Show when={active === 'create_ticket'}>
<div style="border:1px solid #E5E7EB;background:white;border-radius:16px;padding:16px;box-shadow:0 1px 4px rgba(0,0,0,0.06)">
<p style="margin:0;font-size:24px;line-height:1.1;font-weight:800;color:#111827">Create Ticket</p>
<p style="margin:6px 0 0;font-size:13px;color:#6B7280">Create a support request with complete details for faster resolution.</p>
<div style="margin-top:12px;display:grid;grid-template-columns:1fr 1fr;gap:12px">
<div><p style="margin:0 0 6px;font-size:11px;font-weight:700;color:#6B7280;text-transform:uppercase">Subject</p><input type="text" placeholder="Enter ticket subject" style="height:38px;width:100%;border:1px solid #D1D5DB;border-radius:10px;background:white;padding:0 10px;font-size:12px;color:#111827" /></div>
<div><p style="margin:0 0 6px;font-size:11px;font-weight:700;color:#6B7280;text-transform:uppercase">Category</p><select style="height:38px;width:100%;border:1px solid #D1D5DB;border-radius:10px;background:white;padding:0 10px;font-size:12px;color:#111827"><option>Profile & Verification</option><option>Billing & Credits</option><option>Requirements</option><option>Other</option></select></div>
<div><p style="margin:0 0 6px;font-size:11px;font-weight:700;color:#6B7280;text-transform:uppercase">Priority</p><select style="height:38px;width:100%;border:1px solid #D1D5DB;border-radius:10px;background:white;padding:0 10px;font-size:12px;color:#111827"><option>Medium</option><option>High</option><option>Low</option></select></div>
<div><p style="margin:0 0 6px;font-size:11px;font-weight:700;color:#6B7280;text-transform:uppercase">Order / Requirement ID</p><input type="text" placeholder="Optional reference" style="height:38px;width:100%;border:1px solid #D1D5DB;border-radius:10px;background:white;padding:0 10px;font-size:12px;color:#111827" /></div>
<div style="grid-column:1 / -1"><p style="margin:0 0 6px;font-size:11px;font-weight:700;color:#6B7280;text-transform:uppercase">Description</p><textarea placeholder="Describe the issue in detail..." style="min-height:120px;width:100%;border:1px solid #D1D5DB;border-radius:10px;background:white;padding:10px;font-size:12px;color:#111827;resize:vertical" /></div>
<div style="grid-column:1 / -1">
<p style="margin:0 0 6px;font-size:11px;font-weight:700;color:#6B7280;text-transform:uppercase">Attachment</p>
<div style="border:1px dashed #D1D5DB;border-radius:10px;background:#F9FAFB;padding:12px">
<input
type="file"
multiple
onChange={(event) => {
const files = event.currentTarget.files;
setCreateTicketFiles(files ? Array.from(files).map((file) => file.name) : []);
}}
style="width:100%;font-size:12px;color:#374151"
/>
<Show when={createTicketFiles().length}>
<div style="display:flex;flex-wrap:wrap;gap:6px;margin-top:8px">
<For each={createTicketFiles()}>{(name) => <span style="height:24px;padding:0 8px;border-radius:999px;background:white;border:1px solid #E5E7EB;display:inline-flex;align-items:center;font-size:11px;color:#374151">{name}</span>}</For>
</div>
</Show>
</div>
</div>
</div>
<div style="display:flex;justify-content:flex-end;gap:8px;margin-top:12px">
<button type="button" style="height:36px;border-radius:8px;border:1px solid #D1D5DB;background:white;color:#374151;padding:0 12px;font-size:12px;font-weight:700">Cancel</button>
<button type="button" style="height:36px;border-radius:8px;border:none;background:#0D0D2A;color:white;padding:0 14px;font-size:12px;font-weight:700">Submit Ticket</button>
</div>
</div>
</Show>
</div>
);
}
if (customerKey() === 'settings') {
if (tab === 'privacy') {
return <div style="border:1px solid #E5E7EB;background:white;border-radius:16px;padding:14px;box-shadow:0 1px 4px rgba(0,0,0,0.06)">{['Profile visibility', 'Data sharing consent', 'Session management'].map((s) => <div style="display:flex;justify-content:space-between;align-items:center;padding:8px;border:1px solid #E5E7EB;border-radius:8px;background:#F9FAFB;margin-top:8px"><span style="font-size:12px;color:#374151">{s}</span><span style="font-size:12px;color:#6B7280">Enabled</span></div>)}</div>;
}
if (tab === 'notifications') {
return <div style="border:1px solid #E5E7EB;background:white;border-radius:16px;padding:14px;box-shadow:0 1px 4px rgba(0,0,0,0.06)">{['Response alerts', 'Billing alerts', 'Security alerts'].map((s) => <div style="display:flex;justify-content:space-between;align-items:center;padding:8px;border:1px solid #E5E7EB;border-radius:8px;background:#F9FAFB;margin-top:8px"><span style="font-size:12px;color:#374151">{s}</span><span style="font-size:12px;color:#6B7280">On</span></div>)}</div>;
}
return <div style="border:1px solid #E5E7EB;background:white;border-radius:16px;padding:14px;box-shadow:0 1px 4px rgba(0,0,0,0.06)">{['Change Password', 'Two-factor authentication', 'Login activity'].map((s) => <div style="padding:8px;border:1px solid #E5E7EB;border-radius:8px;background:#F9FAFB;font-size:12px;color:#374151;margin-top:8px">{s}</div>)}</div>;
}
if (customerKey() === 'switch role' || customerKey() === 'switch services' || customerKey() === 'switch service') {
const activeRoles = userRoles();
return (
<div style="display:flex;flex-direction:column;gap:12px">
<div style="border:1px solid #E5E7EB;border-radius:16px;background:white;box-shadow:0 1px 4px rgba(0,0,0,0.06);padding:20px">
<p style="margin:0;font-size:30px;line-height:1.1;font-weight:800;color:#111827">Switch Services</p>
<p style="margin:6px 0 0;font-size:14px;color:#6B7280">Switch between your active roles to manage different business operations.</p>
</div>
<div style="display:grid;grid-template-columns:repeat(3,minmax(0,1fr));gap:12px">
<For each={activeRoles}>
{(role) => {
const roleKey = String(role.role_key || role.key).toUpperCase();
const isCurrent = normalizeRoleKey(props.roleKey || '') === normalizeRoleKey(roleKey);
return (
<div style={`border:1px solid ${isCurrent ? '#FFD8C2' : '#E5E7EB'};background:${isCurrent ? '#FFF9F7' : 'white'};border-radius:16px;padding:16px;box-shadow:0 1px 4px rgba(0,0,0,0.06);display:flex;flex-direction:column;gap:8px`}>
<div style="display:flex;align-items:center;justify-content:space-between">
<div style="width:32px;height:32px;border-radius:8px;background:#FFF3EE;display:flex;align-items:center;justify-content:center">
{(() => {
const Icon = roleIcon(roleKey);
return <Icon size={16} color="#FF5E13" />;
})()}
</div>
<span style={`height:20px;padding:0 8px;border-radius:999px;font-size:10px;font-weight:700;background:${role.status === 'APPROVED' ? '#ECFDF5' : '#FEF3C7'};color:${role.status === 'APPROVED' ? '#059669' : '#D97706'}`}>{role.status}</span>
</div>
<p style="margin:4px 0 0;font-size:16px;font-weight:800;color:#111827">{titleCase(roleKey.replace(/_/g, ' '))}</p>
<button
type="button"
disabled={isCurrent}
onClick={() => switchRole(roleKey)}
style={`margin-top:auto;height:32px;border-radius:8px;border:none;background:${isCurrent ? '#E5E7EB' : '#03004E'};color:${isCurrent ? '#4B5563' : 'white'};padding:0 10px;font-size:12px;font-weight:700;cursor:${isCurrent ? 'default' : 'pointer'}`}
>
{isCurrent ? 'Active' : 'Switch Role'}
</button>
</div>
);
}}
</For>
<Show when={activeRoles.length === 0}>
<div style="grid-column:1 / -1;padding:20px;text-align:center;color:#6B7280;background:#F9FAFB;border-radius:12px;border:1px dashed #D1D5DB">
No other active roles found. Explore Nxtgauge to add more.
</div>
</Show>
</div>
</div>
);
}
if (customerKey() === 'logout') {
if (tab === 'cancel') {
return <div style="max-width:420px;margin:0 auto;border:1px solid #E5E7EB;background:white;border-radius:16px;padding:14px;text-align:center;box-shadow:0 1px 4px rgba(0,0,0,0.06)"><p style="margin:0;font-size:18px;font-weight:800;color:#111827">Logout Cancelled</p><p style="margin:6px 0 0;font-size:13px;color:#6B7280">Your session is still active.</p></div>;
}
return (
<div style="max-width:420px;margin:0 auto;border:1px solid #E5E7EB;background:white;border-radius:16px;padding:14px;text-align:center;box-shadow:0 1px 4px rgba(0,0,0,0.06)">
<p style="margin:0;font-size:18px;font-weight:800;color:#111827">Confirm Logout</p>
<p style="margin:6px 0 0;font-size:13px;color:#6B7280">Are you sure you want to logout from the service seeker portal?</p>
<div style="display:flex;justify-content:center;gap:8px;margin-top:12px"><button type="button" style="height:32px;border-radius:8px;border:1px solid #E5E7EB;background:white;padding:0 12px;font-size:12px;font-weight:700;color:#374151">Cancel</button><button type="button" style="height:32px;border-radius:8px;border:none;background:#FF5E13;color:white;padding:0 12px;font-size:12px;font-weight:700">Logout</button></div>
</div>
);
}
if (customerKey() === 'my portfolio') {
if (!isProfessionalRoleKey(props.roleKey || '')) {
return (
<div style="border:1px solid #E5E7EB;background:white;border-radius:16px;padding:16px;box-shadow:0 1px 4px rgba(0,0,0,0.06)">
<p style="margin:0;font-size:16px;font-weight:800;color:#111827">My Portfolio is only for professionals</p>
<p style="margin:6px 0 0;font-size:13px;color:#6B7280">Switch to a professional role to manage and preview portfolio content.</p>
</div>
);
}
return renderPortfolioContent();
}
return (
<div style="border:1px solid #E5E7EB;background:white;border-radius:16px;padding:14px;box-shadow:0 1px 4px rgba(0,0,0,0.06)">
<p style="margin:0;font-size:13px;font-weight:700;color:#111827">Screen Preview</p>
<p style="margin:6px 0 0;font-size:13px;color:#6B7280">Service seeker screen is selected and interactive.</p>
</div>
);
};
const portfolioTabIcon = (tabLabel: string) => {
const key = normalizeTabKey(tabLabel);
if (key === 'overview') return LayoutGrid;
if (key === 'about') return UserCircle2;
if (key.includes('services')) return Coins;
if (key.includes('portfolio')) return Image;
if (key.includes('experience')) return BriefcaseBusiness;
if (key === 'testimonials') return Star;
if (key === 'faqs') return HelpCircle;
return FileText;
};
return (
<div style="border:1px solid #E5E7EB;border-radius:12px;overflow:hidden;background:#fff">
<Show when={!props.hidePreviewHeader}>
<div style="padding:10px 14px;border-bottom:1px solid #E5E7EB;background:#F9FAFB;display:flex;justify-content:space-between;align-items:center">
<p style="margin:0;font-size:12px;font-weight:700;color:#374151">Actual End-User Dashboard UI Preview</p>
<div style="display:flex;align-items:center;gap:8px">
<Show when={props.onOpenFullscreen}>
<button
type="button"
onClick={() => props.onOpenFullscreen?.()}
title="Open Full Screen Preview"
aria-label="Open Full Screen Preview"
style="height:28px;width:28px;border-radius:7px;border:1px solid #E5E7EB;background:white;display:inline-flex;align-items:center;justify-content:center;cursor:pointer;color:#374151"
>
<svg width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2">
<polyline points="15 3 21 3 21 9"></polyline>
<polyline points="9 21 3 21 3 15"></polyline>
<line x1="21" y1="3" x2="14" y2="10"></line>
<line x1="3" y1="21" x2="10" y2="14"></line>
</svg>
</button>
</Show>
<StatusBadge status={props.status} />
</div>
</div>
</Show>
<div style="display:grid;grid-template-columns:220px 1fr;min-height:680px;background:#F3F4F6">
<aside style="display:flex;flex-direction:column;border-right:1px solid #E5E7EB;background:#fff">
<div style="height:64px;display:flex;align-items:center;border-bottom:1px solid #E5E7EB;padding:0 14px">
<img src="/nxtgauge-logo.png" alt="Nxtgauge" style="height:40px;object-fit:contain;max-width:170px" />
</div>
<div style="padding:10px 8px;display:flex;flex-direction:column;gap:4px;overflow:auto">
<For each={previewSidebarItems()}>
{(item) => (
<button
type="button"
onClick={() => props.onSidebarSelect(item)}
style={`display:flex;align-items:center;gap:9px;text-align:left;height:34px;border-radius:8px;padding:0 10px;border:none;cursor:pointer;font-size:12px;font-weight:600;background:${props.activeSidebar === item ? '#FFF3EE' : 'transparent'};color:${props.activeSidebar === item ? '#FF5E13' : '#6B7280'}`}
>
{(() => {
const Icon = sidebarIcon(item);
return <Icon size={14} style={`color:${props.activeSidebar === item ? '#FF5E13' : '#9CA3AF'};flex-shrink:0`} strokeWidth={props.activeSidebar === item ? 2.5 : 2} />;
})()}
{titleCase(item)}
</button>
)}
</For>
</div>
</aside>
<main style="display:flex;flex-direction:column">
<header style="height:64px;background:#fff;border-bottom:1px solid #E5E7EB;display:flex;align-items:center;justify-content:space-between;padding:0 16px">
<div style="display:flex;align-items:center;gap:10px">
<div style="position:relative">
<Search size={16} style="position:absolute;left:10px;top:50%;transform:translateY(-50%);color:#9CA3AF" />
<input value="" readOnly placeholder="Search resources..." style="height:36px;width:280px;border-radius:10px;border:1px solid #E5E7EB;background:#F9FAFB;padding:0 12px 0 32px;font-size:12px;color:#6B7280" />
</div>
</div>
<div style="display:flex;align-items:center;gap:8px">
<button type="button" style="width:34px;height:34px;border-radius:8px;border:1px solid #E5E7EB;background:white;display:flex;align-items:center;justify-content:center;cursor:pointer;color:#6B7280"><Bell size={16} /></button>
<button type="button" style="width:34px;height:34px;border-radius:8px;border:1px solid #E5E7EB;background:white;display:flex;align-items:center;justify-content:center;cursor:pointer;color:#6B7280"><Settings size={16} /></button>
<button type="button" style="width:34px;height:34px;border-radius:8px;border:1px solid #E5E7EB;background:white;display:flex;align-items:center;justify-content:center;cursor:pointer;color:#6B7280"><User size={16} /></button>
</div>
</header>
<Show when={!isVerified()}>
<div style="margin:14px 14px 0;background:#FFF7ED;border:1px solid #FFEDD5;border-radius:12px;padding:12px 16px;display:flex;align-items:center;justify-content:space-between;gap:12px;box-shadow:0 1px 2px rgba(0,0,0,0.05)">
<div style="display:flex;align-items:center;gap:12px">
<div style="width:36px;height:36px;border-radius:10px;background:#FFEDD5;display:flex;align-items:center;justify-content:center;color:#EA580C">
<AlertCircle size={20} />
</div>
<div>
<p style="margin:0;font-size:14px;font-weight:700;color:#9A3412">Account Verification Required</p>
<p style="margin:2px 0 0;font-size:12px;color:#C2410C">{verificationPending() ? 'Your profile is currently under review by our team. This usually takes 24-48 hours.' : 'Complete your profile details to unlock all platform features and start receiving leads.'}</p>
</div>
</div>
<button
type="button"
onClick={() => props.onSidebarSelect('My Profile')}
style="height:32px;padding:0 14px;border-radius:8px;background:#EA580C;color:white;border:none;font-size:12px;font-weight:600;cursor:pointer;display:flex;align-items:center;white-space:nowrap"
>
{verificationPending() ? 'View Verification Status' : 'Complete Profile'}
</button>
</div>
</Show>
<div style="padding:14px">
<Show when={!(isCustomerExternalMode() && (customerKey() === 'help center' || customerKey() === 'support'))}>
<div style="border:1px solid #E5E7EB;background:white;border-radius:16px;padding:14px;box-shadow:0 1px 4px rgba(0,0,0,0.06)">
<p style="margin:0;font-size:12px;color:#6B7280">Current View</p>
<p style="margin:4px 0 0;font-size:24px;font-weight:800;color:#111827">{isCustomerExternalMode() ? customerView().title : titleCase(props.activeSidebar)}</p>
<p style="margin:4px 0 0;font-size:13px;color:#6B7280">{isCustomerExternalMode() ? customerView().subtitle : 'Interactive preview for configured dashboard.'}</p>
<Show when={previewTabs().length > 0 && customerKey() !== 'my portfolio'}>
<Show
when={customerKey() === 'my portfolio'}
fallback={
<div style="margin-top:12px;display:flex;align-items:center;gap:20px;border-bottom:1px solid #E5E7EB">
<For each={previewTabs()}>
{(item) => (
(() => {
const itemKey = normalizeTabKey(item);
const isLockedTestimonialsTab = customerKey() === 'my portfolio' && itemKey === 'testimonials' && !portfolioTestimonialsUnlocked();
return (
<button
type="button"
disabled={isLockedTestimonialsTab}
onClick={() => {
if (isLockedTestimonialsTab) return;
props.onTabSelect(item);
}}
title={isLockedTestimonialsTab ? 'Unlock after 3 completed jobs and 2 customer feedback entries' : ''}
style={`padding-bottom:10px;font-size:13px;font-weight:500;background:none;border:none;cursor:${isLockedTestimonialsTab ? 'not-allowed' : 'pointer'};opacity:${isLockedTestimonialsTab ? 0.5 : 1};${resolvedTabKey() === itemKey ? 'color:#FF5E13;border-bottom:2px solid #FF5E13;margin-bottom:-1px' : 'color:#6B7280'}`}
>
{titleCase(item)} {isLockedTestimonialsTab ? '• Locked' : ''}
</button>
);
})()
)}
</For>
</div>
}
>
<div style="margin-top:12px;display:flex;align-items:center;gap:8px;overflow-x:auto;padding-bottom:2px">
<For each={previewTabs()}>
{(item) => {
const itemKey = normalizeTabKey(item);
const isLockedTestimonialsTab = itemKey === 'testimonials' && !portfolioTestimonialsUnlocked();
const isActive = resolvedTabKey() === itemKey;
const Icon = portfolioTabIcon(item);
return (
<button
type="button"
disabled={isLockedTestimonialsTab}
onClick={() => {
if (isLockedTestimonialsTab) return;
props.onTabSelect(item);
}}
title={isLockedTestimonialsTab ? 'Unlock after 3 completed jobs and 2 customer feedback entries' : ''}
style={`min-width:148px;height:40px;border-radius:10px;border:1px solid ${isActive ? '#FFD8C2' : '#E5E7EB'};background:${isActive ? '#FFF8F4' : 'white'};padding:0 10px;display:flex;align-items:center;gap:8px;cursor:${isLockedTestimonialsTab ? 'not-allowed' : 'pointer'};opacity:${isLockedTestimonialsTab ? 0.5 : 1};flex-shrink:0`}
>
<span style={`width:22px;height:22px;border-radius:7px;display:inline-flex;align-items:center;justify-content:center;flex-shrink:0;border:1px solid ${isActive ? '#FFD8C2' : '#E5E7EB'};background:${isActive ? '#FFF1EB' : '#F9FAFB'}`}>
<Icon size={12} style={`color:${isActive ? '#C2410C' : '#6B7280'}`} />
</span>
<span style={`font-size:12px;font-weight:700;line-height:1.2;white-space:nowrap;overflow:hidden;text-overflow:ellipsis;color:${isActive ? '#111827' : '#374151'}`}>
{titleCase(item)}{isLockedTestimonialsTab ? ' · Locked' : ''}
</span>
</button>
);
}}
</For>
</div>
</Show>
</Show>
</div>
</Show>
<Show when={isCustomerExternalMode()} fallback={<div style="margin-top:10px;border:1px solid #E5E7EB;background:white;border-radius:16px;padding:14px;box-shadow:0 1px 4px rgba(0,0,0,0.06)"><p style="margin:0;font-size:13px;color:#374151">Default preview mode.</p></div>}>
<div style="margin-top:10px">{renderCustomerContent()}</div>
</Show>
<Show when={!isCustomerExternalMode()}>
<div style="border:1px solid #E5E7EB;background:white;border-radius:16px;padding:14px;margin-top:10px;box-shadow:0 1px 4px rgba(0,0,0,0.06)">
<p style="margin:0;font-size:11px;letter-spacing:0.05em;text-transform:uppercase;color:#6B7280">Fields View</p>
<div style="display:grid;grid-template-columns:repeat(2,minmax(0,1fr));gap:8px;margin-top:8px">
<For each={previewFields()}>
{(f) => <div style="padding:8px;border:1px solid #E5E7EB;border-radius:8px;background:#F9FAFB;font-size:12px;color:#374151">{titleCase(f)}</div>}
</For>
</div>
</div>
</Show>
</div>
</main>
</div>
</div>
);
}