diff --git a/App.tsx b/App.tsx index 21bfb55..9a9772b 100644 --- a/App.tsx +++ b/App.tsx @@ -46,12 +46,8 @@ const RequireOnboarding: React.FC<{ children: React.ReactNode }> = ({ children } if (!org) return
Loading organization...
; - // Get the user's relationship to this organization const userOrgRelation = organizations.find(o => o.orgId === selectedOrgId); const isOrgOwner = userOrgRelation?.role === 'owner'; - - // SINGLE SOURCE OF TRUTH: Organization onboarding completion is the authoritative source - // User organization records are updated to reflect this, but org.onboardingCompleted is primary const onboardingCompleted = org.onboardingCompleted === true; console.log('RequireOnboarding check:', { diff --git a/components/figma/EnhancedFigmaQuestion.tsx b/components/figma/EnhancedFigmaQuestion.tsx index 16a0c95..07b1c03 100644 --- a/components/figma/EnhancedFigmaQuestion.tsx +++ b/components/figma/EnhancedFigmaQuestion.tsx @@ -1,4 +1,6 @@ -import React, { ReactNode } from 'react'; +import React, { ReactNode, useState } from 'react'; +import ImageUpload from '../ui/ImageUpload'; +import { StoredImage, uploadCompanyLogo } from '../../services/imageStorageService'; interface FigmaQuestionProps { question: string; @@ -15,6 +17,10 @@ interface FigmaQuestionProps { totalSteps?: number; stepTitle?: string; className?: string; + // Image upload props + orgId?: string; + onImageUploaded?: (image: StoredImage) => void; + currentImage?: StoredImage | null; } export const EnhancedFigmaQuestion: React.FC = ({ @@ -31,8 +37,44 @@ export const EnhancedFigmaQuestion: React.FC = ({ currentStep = 1, totalSteps = 8, stepTitle = "Company Overview & Mission.", - className = "" + className = "", + // Image upload props + orgId, + onImageUploaded, + currentImage }) => { + const [uploadingImage, setUploadingImage] = useState(false); + const [uploadError, setUploadError] = useState(''); + + const handleImageSelected = async (file: File) => { + if (!orgId) { + setUploadError('Organization ID is required'); + return; + } + + setUploadingImage(true); + setUploadError(''); + + try { + const uploadedImage = await uploadCompanyLogo(file, orgId); + if (onImageUploaded) { + onImageUploaded(uploadedImage); + } + } catch (error) { + console.error('Failed to upload image:', error); + setUploadError(error instanceof Error ? error.message : 'Failed to upload image'); + } finally { + setUploadingImage(false); + } + }; + + const handleImageRemove = () => { + // For now, just call the callback with null + // You could also implement deleteCompanyLogo here if needed + if (onImageUploaded) { + onImageUploaded(null as any); + } + }; // Generate the progress indicator dots const renderProgressDots = () => { const dots = []; @@ -62,12 +104,52 @@ export const EnhancedFigmaQuestion: React.FC = ({ }; return ( -
+
-
- {question} -
+ {currentStep === 1 ? +
+
+
Company Logo
+
+
+ +
+
+
+
+ + + +
+
+ {currentImage ? 'Change image' : 'Upload image'} +
+
+
+ {uploadError && ( +
{uploadError}
+ )} + {currentImage && ( +
+ {Math.round(currentImage.compressedSize / 1024)}KB • {currentImage.width}×{currentImage.height}px +
+ )} +
+
+
+ : +
+ {question} +
+ } {children}
@@ -78,11 +160,11 @@ export const EnhancedFigmaQuestion: React.FC = ({ data-show-icon-right="false" data-show-text="true" data-size="Big" - className={`h-12 px-8 py-3.5 bg-Neutrals-NeutralSlate100 rounded-[999px] flex justify-center items-center gap-1 overflow-hidden ${backDisabled ? 'opacity-50 cursor-not-allowed' : 'cursor-pointer hover:bg-Neutrals-NeutralSlate200'}`} + className={`h-12 px-8 py-3.5 bg-[--Neutrals-NeutralSlate100] rounded-[999px] flex justify-center items-center gap-1 overflow-hidden ${backDisabled ? 'opacity-50 cursor-not-allowed' : 'cursor-pointer hover:bg-[--Neutrals-NeutralSlate200]'}`} onClick={!backDisabled ? onBack : undefined} >
-
+
{backText}
@@ -94,11 +176,11 @@ export const EnhancedFigmaQuestion: React.FC = ({ data-show-icon-right="false" data-show-text="true" data-size="Big" - className={`flex-1 h-12 px-4 py-3.5 bg-Brand-Orange rounded-[999px] outline outline-2 outline-offset-[-2px] outline-blue-400 flex justify-center items-center gap-1 overflow-hidden ${nextDisabled ? 'opacity-50 cursor-not-allowed' : 'cursor-pointer hover:opacity-90'}`} + className={`flex-1 h-12 px-4 py-3.5 bg-[--Brand-Orange] rounded-[999px] outline outline-2 outline-offset-[-2px] outline-blue-400 flex justify-center items-center gap-1 overflow-hidden ${nextDisabled ? 'opacity-50 cursor-not-allowed' : 'cursor-pointer hover:opacity-90'}`} onClick={!nextDisabled ? onNext : undefined} >
-
+
{nextText}
@@ -107,25 +189,25 @@ export const EnhancedFigmaQuestion: React.FC = ({
{/* Step indicator - top left */} -
-
+
+
{currentStep} of {totalSteps}
{/* Skip button - top right */} -
-
+
+
Skip
{/* Progress indicator and title - top center */}
-
+
{renderProgressDots()}
-
+
{stepTitle}
@@ -148,21 +230,21 @@ export const FigmaQuestionCard: React.FC = ({ className = "" }) => { return ( -
+
Q
-
+
{question}
{description && ( -
+
{description}
)}
-
+
A
@@ -191,8 +273,8 @@ export const EnhancedFigmaInput: React.FC = ({ rows = 4, className = "" }) => { - const baseClasses = "self-stretch min-h-40 p-5 relative bg-Neutrals-NeutralSlate100 rounded-xl inline-flex justify-start items-start gap-2.5 overflow-hidden"; - const inputClasses = "flex-1 justify-start text-Neutrals-NeutralSlate500 text-base font-normal font-['Inter'] leading-normal bg-transparent border-none outline-none resize-none"; + const baseClasses = "self-stretch min-h-40 p-5 relative bg-[--Neutrals-NeutralSlate100] rounded-xl inline-flex justify-start items-start gap-2.5 overflow-hidden"; + const inputClasses = "flex-1 justify-start text-[--Neutrals-NeutralSlate500] text-base font-normal font-['Inter'] leading-normal bg-transparent border-none outline-none resize-none"; if (multiline) { return ( @@ -205,8 +287,8 @@ export const EnhancedFigmaInput: React.FC = ({ className={inputClasses} />
-
-
+
+
); diff --git a/components/figma/FigmaInput.tsx b/components/figma/FigmaInput.tsx index b156002..9c85b14 100644 --- a/components/figma/FigmaInput.tsx +++ b/components/figma/FigmaInput.tsx @@ -31,17 +31,17 @@ export const FigmaInput: React.FC = ({
{showLabel && label && (
-
+
{label}
{required && ( -
*
+
*
)}
)}
-
+
{icon && (
{icon} @@ -52,7 +52,7 @@ export const FigmaInput: React.FC = ({ value={value} onChange={onChange} placeholder={placeholder} - className="flex-1 justify-start text-Neutrals-NeutralSlate950 text-sm font-normal font-['Inter'] leading-tight bg-transparent border-none outline-none placeholder:text-Neutrals-NeutralSlate500" + className="flex-1 justify-start text-[--Neutrals-NeutralSlate950] text-sm font-normal font-['Inter'] leading-tight bg-transparent border-none outline-none placeholder:text-Neutrals-NeutralSlate500" />
@@ -60,9 +60,9 @@ export const FigmaInput: React.FC = ({ {buttonText && ( @@ -95,23 +95,23 @@ export const FigmaSelect: React.FC = ({
{label && (
-
+
{label}
{required && ( -
*
+
*
)}
)}
-
+
+ +
+ {currentImage ? ( + <> + Uploaded + {!loading && onImageRemove && ( + + )} + + ) : ( +
+ {loading ? ( +
+ ) : ( + + + + + )} +
+ )} + + {dragOver && ( +
+ Drop image +
+ )} +
+ + {error && ( +
+ {error} +
+ )} +
+ ); +}; + +export default ImageUpload; \ No newline at end of file diff --git a/contexts/OrgContext.tsx b/contexts/OrgContext.tsx index 5b57052..74ee029 100644 --- a/contexts/OrgContext.tsx +++ b/contexts/OrgContext.tsx @@ -82,70 +82,6 @@ export const OrgProvider: React.FC<{ children: React.ReactNode; selectedOrgId: s useEffect(() => { console.log('OrgContext effect running, orgId:', orgId, 'isFirebaseConfigured:', isFirebaseConfigured); if (!orgId) return; // Wait for orgId to be available - if (!isFirebaseConfigured) { - // Demo mode data - use persistent localStorage with proper initialization - console.log('Setting up demo org data with persistence'); - - // Get or create persistent demo org - let demoOrg = demoStorage.getOrganization(orgId); - if (!demoOrg) { - demoOrg = { - orgId: orgId, - name: 'Demo Company', - onboardingCompleted: false - }; - demoStorage.saveOrganization(demoOrg); - - // Initialize with empty employee list for clean start - // (Removed automatic seeding of 6 default employees per user feedback) - - // Don't automatically create sample submissions - let users create real data - // through the proper questionnaire flow - - // Note: Sample employee reports removed - real reports generated via AI after questionnaire submission - - // Don't save sample company report - let users generate real AI-powered reports - } - - // Load persistent demo data - setOrg({ orgId, name: demoOrg.name, onboardingCompleted: demoOrg.onboardingCompleted }); - - // Convert employees to expected format - const demoEmployees = demoStorage.getEmployeesByOrg(orgId); - const convertedEmployees: Employee[] = demoEmployees.map(emp => ({ - id: emp.id, - name: emp.name, - email: emp.email, - initials: emp.name ? emp.name.split(' ').map(n => n[0]).join('').toUpperCase() : emp.email.substring(0, 2).toUpperCase(), - department: emp.department, - role: emp.role, - isOwner: emp.id === user?.uid - })); - setEmployees(convertedEmployees); - - // Load any existing submissions from localStorage - const orgSubmissions = demoStorage.getSubmissionsByOrg(orgId); - const convertedSubmissions: Record = {}; - Object.entries(orgSubmissions).forEach(([employeeId, demoSub]) => { - convertedSubmissions[employeeId] = { - employeeId, - answers: Object.entries(demoSub.answers).map(([question, answer]) => ({ - question, - answer - })) - }; - }); - setSubmissions(convertedSubmissions); - - // Load any existing AI-generated reports from localStorage - const orgReports = demoStorage.getEmployeeReportsByOrg(orgId); - setReports(orgReports); - - // Load any existing company reports from localStorage - const companyReports = demoStorage.getCompanyReportsByOrg(orgId); - setFullCompanyReports(companyReports); - return; - } console.log('Setting up Firebase org data'); const orgRef = doc(db, 'orgs', orgId); getDoc(orgRef).then(async (snap) => { @@ -183,89 +119,27 @@ export const OrgProvider: React.FC<{ children: React.ReactNode; selectedOrgId: s }, [orgId]); const upsertOrg = async (data: Partial) => { - if (!isFirebaseConfigured) { - const updatedOrg = { ...(org || { orgId, name: 'Demo Company' }), ...data } as OrgData; - setOrg(updatedOrg); - - // If onboarding was completed, update localStorage for persistence and notify other contexts - if (data.onboardingCompleted) { - const demoOrgData = { - orgId: updatedOrg.orgId, - name: updatedOrg.name, - onboardingCompleted: updatedOrg.onboardingCompleted || false, - ...updatedOrg // Include all additional fields - }; - demoStorage.saveOrganization(demoOrgData); - - console.log('OrgContext: Onboarding completed, dispatching update event', { - orgId: updatedOrg.orgId, - onboardingCompleted: true - }); - - // Signal to UserOrganizationsContext and other components about completion - window.dispatchEvent(new CustomEvent('organizationUpdated', { - detail: { orgId: updatedOrg.orgId, onboardingCompleted: true } - })); - } - - // Organization already exists, no need to sync with server during onboarding - // We'll update Firestore directly in the Firebase mode below - } else { - // Firebase mode - save to Firestore - const orgRef = doc(db, 'orgs', orgId); - await setDoc(orgRef, data, { merge: true }); - - // Update local state - const updatedOrg = { ...(org || { orgId, name: 'Your Company' }), ...data } as OrgData; - setOrg(updatedOrg); - - // If onboarding was completed, notify other contexts - if (data.onboardingCompleted) { - console.log('OrgContext (Firebase): Onboarding completed, dispatching update event', { - orgId: updatedOrg.orgId, - onboardingCompleted: true - }); - - window.dispatchEvent(new CustomEvent('organizationUpdated', { - detail: { orgId: updatedOrg.orgId, onboardingCompleted: true } - })); - } - } - }; - - const updateOrg = async (data: Partial) => { - if (!isFirebaseConfigured) { - const updatedOrg = { ...(org || { orgId, name: 'Demo Company' }), ...data } as OrgData; - setOrg(updatedOrg); - - // Also sync with server for multi-tenant persistence - try { - const response = await fetch(`${API_URL}/api/organizations/${orgId}`, { - method: 'PUT', - headers: { 'Content-Type': 'application/json' }, - body: JSON.stringify(data) - }); - - if (!response.ok) { - console.warn('Failed to sync organization data with server'); - } - } catch (error) { - console.warn('Failed to sync organization data:', error); - } - - return; - } const orgRef = doc(db, 'orgs', orgId); await setDoc(orgRef, data, { merge: true }); + + // Update local state + const updatedOrg = { ...(org || { orgId, name: 'Your Company' }), ...data } as OrgData; + setOrg(updatedOrg); + + // If onboarding was completed, notify other contexts + if (data.onboardingCompleted) { + console.log('OrgContext (Firebase): Onboarding completed, dispatching update event', { + orgId: updatedOrg.orgId, + onboardingCompleted: true + }); + + window.dispatchEvent(new CustomEvent('organizationUpdated', { + detail: { orgId: updatedOrg.orgId, onboardingCompleted: true } + })); + } }; const saveReport = async (employeeId: string, report: Report) => { - if (!isFirebaseConfigured) { - setReports(prev => ({ ...prev, [employeeId]: report })); - // Persist to localStorage - demoStorage.saveEmployeeReport(orgId, employeeId, report); - return; - } const ref = doc(db, 'orgs', orgId, 'reports', employeeId); await setDoc(ref, report, { merge: true }); }; @@ -446,7 +320,7 @@ export const OrgProvider: React.FC<{ children: React.ReactNode; selectedOrgId: s // Exclude owners from employee counts - they are company wiki contributors, not employees const actualEmployees = employees.filter(emp => !emp.isOwner); const totalEmployees = actualEmployees.length; - + // Only count submissions from non-owner employees const employeeSubmissions = Object.fromEntries( Object.entries(submissions).filter(([employeeId]) => { @@ -539,7 +413,7 @@ export const OrgProvider: React.FC<{ children: React.ReactNode; selectedOrgId: s const payload = await res.json(); console.log('API success response:', payload); - + // Ensure the report has all required fields to prevent undefined errors const data: CompanyReport = { id: Date.now().toString(), @@ -565,7 +439,7 @@ export const OrgProvider: React.FC<{ children: React.ReactNode; selectedOrgId: s // Override with API data if available ...(payload.report || payload) }; - + await saveFullCompanyReport(data); return data; } catch (e) { diff --git a/contexts/UserOrganizationsContext.tsx b/contexts/UserOrganizationsContext.tsx index 9bb510d..cb08db9 100644 --- a/contexts/UserOrganizationsContext.tsx +++ b/contexts/UserOrganizationsContext.tsx @@ -41,26 +41,14 @@ export const UserOrganizationsProvider: React.FC<{ children: React.ReactNode }> } try { - if (!isFirebaseConfigured) { - // Demo mode - fetch from server API - const response = await fetch(`${API_URL}/api/user/${user.uid}/organizations`); - if (response.ok) { - const data = await response.json(); - setOrganizations(data.organizations || []); - } else { - console.error('Failed to load organizations:', response.status); - setOrganizations([]); - } + // Firebase mode - fetch from Cloud Functions + const response = await fetch(`${API_URL}/getUserOrganizations?userId=${user.uid}`); + if (response.ok) { + const data = await response.json(); + setOrganizations(data.organizations || []); } else { - // Firebase mode - fetch from Cloud Functions - const response = await fetch(`${API_URL}/getUserOrganizations?userId=${user.uid}`); - if (response.ok) { - const data = await response.json(); - setOrganizations(data.organizations || []); - } else { - console.error('Failed to load organizations:', response.status); - setOrganizations([]); - } + console.error('Failed to load organizations:', response.status); + setOrganizations([]); } } catch (error) { console.error('Failed to load organizations:', error); @@ -134,53 +122,29 @@ export const UserOrganizationsProvider: React.FC<{ children: React.ReactNode }> let newOrg: UserOrganization; let requiresSubscription = false; - if (!isFirebaseConfigured) { - // Demo mode - use server API - const response = await fetch(`${API_URL}/api/organizations`, { - method: 'POST', - headers: { 'Content-Type': 'application/json' }, - body: JSON.stringify({ name, userId: user.uid }) - }); + // Firebase mode - use Cloud Function + const response = await fetch(`${API_URL}/createOrganization`, { + method: 'POST', + headers: { 'Content-Type': 'application/json' }, + body: JSON.stringify({ name, userId: user.uid }) + }); - if (!response.ok) { - throw new Error(`Failed to create organization: ${response.status}`); - } - - const data = await response.json(); - newOrg = { - orgId: data.orgId, - name: data.name, - role: data.role, - onboardingCompleted: data.onboardingCompleted, - joinedAt: data.joinedAt - }; - - setOrganizations(prev => [...prev, newOrg]); - } else { - // Firebase mode - use Cloud Function - const response = await fetch(`${API_URL}/createOrganization`, { - method: 'POST', - headers: { 'Content-Type': 'application/json' }, - body: JSON.stringify({ name, userId: user.uid }) - }); - - if (!response.ok) { - throw new Error(`Failed to create organization: ${response.status}`); - } - - const data = await response.json(); - newOrg = { - orgId: data.orgId, - name: data.name, - role: data.role, - onboardingCompleted: data.onboardingCompleted, - joinedAt: data.joinedAt - }; - - requiresSubscription = data.requiresSubscription || false; - setOrganizations(prev => [...prev, newOrg]); + if (!response.ok) { + throw new Error(`Failed to create organization: ${response.status}`); } + const data = await response.json(); + newOrg = { + orgId: data.orgId, + name: data.name, + role: data.role, + onboardingCompleted: data.onboardingCompleted, + joinedAt: data.joinedAt + }; + + requiresSubscription = data.requiresSubscription || false; + setOrganizations(prev => [...prev, newOrg]); + return { orgId: newOrg.orgId, requiresSubscription }; } catch (error) { console.error('Failed to create organization:', error); @@ -192,70 +156,70 @@ export const UserOrganizationsProvider: React.FC<{ children: React.ReactNode }> if (!user) throw new Error('User not authenticated'); try { - if (!isFirebaseConfigured) { - // Demo mode - use server API to get and consume invite - const inviteStatusRes = await fetch(`/api/invitations/${inviteCode}`); - if (!inviteStatusRes.ok) { - throw new Error('Invalid or expired invite code'); - } + // if (!isFirebaseConfigured) { + // // Demo mode - use server API to get and consume invite + // const inviteStatusRes = await fetch(`/api/invitations/${inviteCode}`); + // if (!inviteStatusRes.ok) { + // throw new Error('Invalid or expired invite code'); + // } - const inviteData = await inviteStatusRes.json(); - if (inviteData.used) { - throw new Error('Invite code has already been used'); - } + // const inviteData = await inviteStatusRes.json(); + // if (inviteData.used) { + // throw new Error('Invite code has already been used'); + // } - // Consume the invite - const consumeRes = await fetch(`/api/invitations/${inviteCode}/consume`, { - method: 'POST' - }); - if (!consumeRes.ok) { - throw new Error('Failed to consume invite'); - } + // // Consume the invite + // const consumeRes = await fetch(`/api/invitations/${inviteCode}/consume`, { + // method: 'POST' + // }); + // if (!consumeRes.ok) { + // throw new Error('Failed to consume invite'); + // } - const consumedData = await consumeRes.json(); - const orgId = consumedData.orgId; + // const consumedData = await consumeRes.json(); + // const orgId = consumedData.orgId; - // Get organization data (this might be from localStorage for demo mode) - const orgData = demoStorage.getOrganization(orgId); - if (!orgData) { - throw new Error('Organization not found'); - } + // // Get organization data (this might be from localStorage for demo mode) + // const orgData = demoStorage.getOrganization(orgId); + // if (!orgData) { + // throw new Error('Organization not found'); + // } - const userOrg: UserOrganization = { - orgId: orgId, - name: orgData.name, - role: 'employee', - onboardingCompleted: orgData.onboardingCompleted || false, - joinedAt: Date.now() - }; + // const userOrg: UserOrganization = { + // orgId: orgId, + // name: orgData.name, + // role: 'employee', + // onboardingCompleted: orgData.onboardingCompleted || false, + // joinedAt: Date.now() + // }; - setOrganizations(prev => [...prev, userOrg]); - return orgId; - } else { - // Firebase mode - use Cloud Function - const response = await fetch(`${API_URL}/joinOrganization`, { - method: 'POST', - headers: { 'Content-Type': 'application/json' }, - body: JSON.stringify({ userId: user.uid, inviteCode }) - }); + // setOrganizations(prev => [...prev, userOrg]); + // return orgId; + // } else { + // Firebase mode - use Cloud Function + const response = await fetch(`${API_URL}/joinOrganization`, { + method: 'POST', + headers: { 'Content-Type': 'application/json' }, + body: JSON.stringify({ userId: user.uid, inviteCode }) + }); - if (!response.ok) { - const errorData = await response.json(); - throw new Error(errorData.error || 'Failed to join organization'); - } - - const data = await response.json(); - const userOrg: UserOrganization = { - orgId: data.orgId, - name: data.name, - role: data.role, - onboardingCompleted: data.onboardingCompleted, - joinedAt: data.joinedAt - }; - - setOrganizations(prev => [...prev, userOrg]); - return data.orgId; + if (!response.ok) { + const errorData = await response.json(); + throw new Error(errorData.error || 'Failed to join organization'); } + + const data = await response.json(); + const userOrg: UserOrganization = { + orgId: data.orgId, + name: data.name, + role: data.role, + onboardingCompleted: data.onboardingCompleted, + joinedAt: data.joinedAt + }; + + setOrganizations(prev => [...prev, userOrg]); + return data.orgId; + // } } catch (error) { console.error('Failed to join organization:', error); throw error; diff --git a/pages/EmployeeQuestionnaire.tsx b/pages/EmployeeQuestionnaire.tsx index 2788bd9..7e0a95a 100644 --- a/pages/EmployeeQuestionnaire.tsx +++ b/pages/EmployeeQuestionnaire.tsx @@ -25,6 +25,113 @@ const AuditlyIcon: React.FC = () => ( ); +// Progress Bar Component for Section Headers +const SectionProgressBar: React.FC<{ currentSection: number; totalSections: number }> = ({ currentSection, totalSections }) => { + return ( +
+ {Array.from({ length: 7 }, (_, index) => { + const isActive = index === 0; // First step is always active for section start + return ( +
+ {index === 0 ? ( +
+ ) : ( + + + + )} +
+ ); + })} +
+ ); +}; + +// Yes/No Choice Component +const YesNoChoice: React.FC<{ + question: string; + value?: string; + onChange: (value: string) => void; + onBack?: () => void; + onNext: () => void; + onSkip?: () => void; + currentStep?: number; + totalSteps?: number; +}> = ({ question, value, onChange, onBack, onNext, onSkip, currentStep, totalSteps }) => { + return ( +
+
+
+
{question}
+
+
onChange('No')} + className={`w-20 h-20 relative rounded-[999px] overflow-hidden cursor-pointer transition-colors ${value === 'No' ? 'bg-Neutrals-NeutralSlate800' : 'bg-Neutrals-NeutralSlate100 hover:bg-Neutrals-NeutralSlate200'}`} + > +
+ No +
+
+
onChange('Yes')} + className={`w-20 h-20 relative rounded-[999px] overflow-hidden cursor-pointer transition-colors ${value === 'Yes' ? 'bg-Neutrals-NeutralSlate800' : 'bg-Neutrals-NeutralSlate100 hover:bg-Neutrals-NeutralSlate200'}`} + > +
+ Yes +
+
+
+
+
+ {onBack && ( + + )} + +
+
+ + {/* Skip button */} + {onSkip && ( +
+
Skip
+
+ )} + + {/* Progress indicators */} + {currentStep && totalSteps && ( + <> +
+
{currentStep} of {totalSteps}
+
+
+ +
Leadership & Organizational Structure
+
+ + )} +
+ ); +}; + + // Section Intro Component - From EmployeeFormsController const SectionIntro: React.FC<{ sectionNumber: string; @@ -175,17 +282,1214 @@ const EmployeeFormStep1: React.FC<{ onNext: (data: any) => void }> = ({ onNext } ); }; + +// Step 2: Personal Information +const EmployeeFormStep2: React.FC<{ onNext: (data: any) => void; onBack: () => void }> = ({ onNext, onBack }) => { + const [formData, setFormData] = useState({ + email: '', + name: '', + company: '' + }); + + return ( +
+
+
+
Personal Information
+
+
+
+
Email
+
*
+
+
+
+ setFormData({ ...formData, email: e.target.value })} + className="flex-1 bg-transparent text-Neutrals-NeutralSlate950 text-sm font-normal font-['Inter'] leading-tight placeholder:text-Neutrals-NeutralSlate500 outline-none" + placeholder="Email@gmail.com" + /> +
+
+
+
+
+
Your Name
+
*
+
+
+
+ setFormData({ ...formData, name: e.target.value })} + className="flex-1 bg-transparent text-Neutrals-NeutralSlate950 text-sm font-normal font-['Inter'] leading-tight placeholder:text-Neutrals-NeutralSlate500 outline-none" + placeholder="John Doe" + /> +
+
+
+
+
+
What is the name of your Company and department?
+
*
+
+
+
+ setFormData({ ...formData, company: e.target.value })} + className="flex-1 bg-transparent text-Neutrals-NeutralSlate950 text-sm font-normal font-['Inter'] leading-tight placeholder:text-Neutrals-NeutralSlate500 outline-none" + placeholder="Doe Enterprises" + /> +
+
+
+
+
+
+ + +
+
+
+ ); +}; + +// Step 3: Current Title and Department (Text Area) +const EmployeeFormStep3: React.FC<{ onNext: (data: any) => void; onBack: () => void }> = ({ onNext, onBack }) => { + const [answer, setAnswer] = useState(''); + + return ( +
+ +
+ onNext({ titleAndDepartment: answer })} + onSkip={() => onNext({ titleAndDepartment: '' })} + nextDisabled={!answer.trim()} + currentStep={1} + totalSteps={7} + /> +
+
+ ); +}; + +// Step 4: Daily Tasks (Text Area) +const EmployeeFormStep4: React.FC<{ onNext: (data: any) => void; onBack: () => void }> = ({ onNext, onBack }) => { + const [answer, setAnswer] = useState(''); + + return ( +
+ +
+ onNext({ dailyTasks: answer })} + onSkip={() => onNext({ dailyTasks: '' })} + nextDisabled={!answer.trim()} + currentStep={2} + totalSteps={7} + /> +
+
+ ); +}; + +// Step 5: Role Understanding Rating +const EmployeeFormStep5: React.FC<{ onNext: (data: any) => void; onBack: () => void }> = ({ onNext, onBack }) => { + const [rating, setRating] = useState(); + + return ( +
+ +
+ onNext({ roleUnderstanding: rating })} + onSkip={() => onNext({ roleUnderstanding: null })} + nextDisabled={!rating} + currentStep={3} + totalSteps={7} + /> +
+
+ ); +}; + +// Step 6: Work Satisfaction +const EmployeeFormStep6: React.FC<{ onNext: (data: any) => void; onBack: () => void }> = ({ onNext, onBack }) => { + const [rating, setRating] = useState(); + + return ( +
+ +
+ onNext({ workSatisfaction: rating })} + onSkip={() => onNext({ workSatisfaction: null })} + nextDisabled={!rating} + currentStep={4} + totalSteps={7} + /> +
+
+ ); +}; + +// Step 7: Communication Rating +const EmployeeFormStep7: React.FC<{ onNext: (data: any) => void; onBack: () => void }> = ({ onNext, onBack }) => { + const [rating, setRating] = useState(); + + return ( +
+ +
+ onNext({ communicationRating: rating })} + onSkip={() => onNext({ communicationRating: null })} + nextDisabled={!rating} + currentStep={5} + totalSteps={7} + /> +
+
+ ); +}; + +// Step 8: Work Style Preference +const EmployeeFormStep8: React.FC<{ onNext: (data: any) => void; onBack: () => void }> = ({ onNext, onBack }) => { + const [selectedOption, setSelectedOption] = useState(''); + + const options = [ + 'I prefer working independently', + 'I enjoy collaborative teamwork', + 'I like a mix of both', + 'I prefer clear instructions and structure', + 'I thrive with autonomy and flexibility' + ]; + + return ( +
+
+
+

+ What work style best describes you? +

+
+ {options.map((option, index) => ( + + ))} +
+
+
+
+ onNext({ workStyle: selectedOption })} + onSkip={() => onNext({ workStyle: '' })} + nextDisabled={!selectedOption} + currentStep={6} + totalSteps={7} + /> +
+
+ ); +}; + +// Step 9: Section Intro - Output & Accountability +const EmployeeFormStep9: React.FC<{ onNext: () => void }> = ({ onNext }) => { + return ( + + ); +}; + +// Step 10: Weekly Output Rating +const EmployeeFormStep10: React.FC<{ onNext: (data: any) => void; onBack: () => void }> = ({ onNext, onBack }) => { + const [rating, setRating] = useState(); + + return ( +
+ +
+ onNext({ weeklyOutput: rating })} + onSkip={() => onNext({ weeklyOutput: null })} + nextDisabled={!rating} + currentStep={1} + totalSteps={7} + /> +
+
+ ); +}; + +// Step 11: Top Deliverables +const EmployeeFormStep11: React.FC<{ onNext: (data: any) => void; onBack: () => void }> = ({ onNext, onBack }) => { + const [answer, setAnswer] = useState(''); + + return ( +
+ +
+ onNext({ topDeliverables: answer })} + onSkip={() => onNext({ topDeliverables: '' })} + nextDisabled={!answer.trim()} + currentStep={2} + totalSteps={7} + /> +
+
+ ); +}; + +// Step 12: Measurable Results +const EmployeeFormStep12: React.FC<{ onNext: (data: any) => void; onBack: () => void }> = ({ onNext, onBack }) => { + const [answer, setAnswer] = useState(''); + + return ( +
+ +
+ onNext({ measurableResults: answer })} + onSkip={() => onNext({ measurableResults: '' })} + nextDisabled={!answer.trim()} + currentStep={3} + totalSteps={7} + /> +
+
+ ); +}; + +// Step 13: Weekly KPIs +const EmployeeFormStep13: React.FC<{ onNext: (data: any) => void; onBack: () => void }> = ({ onNext, onBack }) => { + const [answer, setAnswer] = useState(''); + + return ( + onNext({ hasKPIs: answer })} + onSkip={() => onNext({ hasKPIs: '' })} + currentStep={4} + totalSteps={7} + /> + ); +}; + +// Step 14: KPI Details +const EmployeeFormStep14: React.FC<{ onNext: (data: any) => void; onBack: () => void }> = ({ onNext, onBack }) => { + const [answer, setAnswer] = useState(''); + + return ( +
+ +
+ onNext({ kpiDetails: answer })} + onSkip={() => onNext({ kpiDetails: '' })} + currentStep={5} + totalSteps={7} + /> +
+
+ ); +}; + +// Step 15: Reporting Structure +const EmployeeFormStep15: React.FC<{ onNext: (data: any) => void; onBack: () => void }> = ({ onNext, onBack }) => { + const [answer, setAnswer] = useState(''); + + return ( +
+ +
+ onNext({ reportingStructure: answer })} + onSkip={() => onNext({ reportingStructure: '' })} + currentStep={6} + totalSteps={7} + /> +
+
+ ); +}; + +// Step 16: Section Intro - Team & Collaboration +const EmployeeFormStep16: React.FC<{ onNext: () => void }> = ({ onNext }) => { + return ( + + ); +}; + +// Step 17: Work Closest With +const EmployeeFormStep17: React.FC<{ onNext: (data: any) => void; onBack: () => void }> = ({ onNext, onBack }) => { + const [answer, setAnswer] = useState(''); + + return ( +
+ +
+ onNext({ workClosestWith: answer })} + onSkip={() => onNext({ workClosestWith: '' })} + currentStep={1} + totalSteps={7} + /> +
+
+ ); +}; + +// Step 18: Collaboration Issues +const EmployeeFormStep18: React.FC<{ onNext: (data: any) => void; onBack: () => void }> = ({ onNext, onBack }) => { + const [answer, setAnswer] = useState(''); + + return ( +
+ +
+ onNext({ collaborationIssues: answer })} + onSkip={() => onNext({ collaborationIssues: '' })} + currentStep={2} + totalSteps={7} + /> +
+
+ ); +}; + +// Step 19: Team Communication Rating +const EmployeeFormStep19: React.FC<{ onNext: (data: any) => void; onBack: () => void }> = ({ onNext, onBack }) => { + const [rating, setRating] = useState(); + + return ( +
+ +
+ onNext({ teamCommunication: rating })} + onSkip={() => onNext({ teamCommunication: null })} + nextDisabled={!rating} + currentStep={3} + totalSteps={7} + /> +
+
+ ); +}; + +// Step 20: Team Support +const EmployeeFormStep20: React.FC<{ onNext: (data: any) => void; onBack: () => void }> = ({ onNext, onBack }) => { + const [answer, setAnswer] = useState(''); + + return ( +
+ +
+ onNext({ teamSupport: answer })} + onSkip={() => onNext({ teamSupport: '' })} + currentStep={4} + totalSteps={7} + /> +
+
+ ); +}; + +// Step 21: Tools and Resources Section +const EmployeeFormStep21: React.FC<{ onNext: () => void }> = ({ onNext }) => { + return ( + + ); +}; + +// Step 22: Current Tools +const EmployeeFormStep22: React.FC<{ onNext: (data: any) => void; onBack: () => void }> = ({ onNext, onBack }) => { + const [answer, setAnswer] = useState(''); + + return ( +
+ +
+ onNext({ currentTools: answer })} + onSkip={() => onNext({ currentTools: '' })} + currentStep={1} + totalSteps={7} + /> +
+
+ ); +}; + +// Step 23: Tool Effectiveness +const EmployeeFormStep23: React.FC<{ onNext: (data: any) => void; onBack: () => void }> = ({ onNext, onBack }) => { + const [rating, setRating] = useState(); + + return ( +
+ +
+ onNext({ toolEffectiveness: rating })} + onSkip={() => onNext({ toolEffectiveness: null })} + nextDisabled={!rating} + currentStep={2} + totalSteps={7} + /> +
+
+ ); +}; + +// Step 24: Missing Tools +const EmployeeFormStep24: React.FC<{ onNext: (data: any) => void; onBack: () => void }> = ({ onNext, onBack }) => { + const [answer, setAnswer] = useState(''); + + return ( +
+ +
+ onNext({ missingTools: answer })} + onSkip={() => onNext({ missingTools: '' })} + currentStep={3} + totalSteps={7} + /> +
+
+ ); +}; + +// Step 25: Skills & Development Section +const EmployeeFormStep25: React.FC<{ onNext: () => void }> = ({ onNext }) => { + return ( + + ); +}; + +// Step 26: Key Skills +const EmployeeFormStep26: React.FC<{ onNext: (data: any) => void; onBack: () => void }> = ({ onNext, onBack }) => { + const [answer, setAnswer] = useState(''); + + return ( +
+ +
+ onNext({ keySkills: answer })} + onSkip={() => onNext({ keySkills: '' })} + currentStep={1} + totalSteps={7} + /> +
+
+ ); +}; + +// Step 27: Skill Development +const EmployeeFormStep27: React.FC<{ onNext: (data: any) => void; onBack: () => void }> = ({ onNext, onBack }) => { + const [answer, setAnswer] = useState(''); + + return ( +
+ +
+ onNext({ skillDevelopment: answer })} + onSkip={() => onNext({ skillDevelopment: '' })} + currentStep={2} + totalSteps={7} + /> +
+
+ ); +}; + +// Step 28: Training Opportunities +const EmployeeFormStep28: React.FC<{ onNext: (data: any) => void; onBack: () => void }> = ({ onNext, onBack }) => { + const [answer, setAnswer] = useState(''); + + return ( + onNext({ awareOfTraining: answer })} + onSkip={() => onNext({ awareOfTraining: '' })} + currentStep={3} + totalSteps={7} + /> + ); +}; + +// Step 29: Career Goals +const EmployeeFormStep29: React.FC<{ onNext: (data: any) => void; onBack: () => void }> = ({ onNext, onBack }) => { + const [answer, setAnswer] = useState(''); + + return ( +
+ +
+ onNext({ careerGoals: answer })} + onSkip={() => onNext({ careerGoals: '' })} + currentStep={4} + totalSteps={7} + /> +
+
+ ); +}; + +// Step 30: Feedback & Improvement Section +const EmployeeFormStep30: React.FC<{ onNext: () => void }> = ({ onNext }) => { + return ( + + ); +}; + +// Step 31: Company Improvements +const EmployeeFormStep31: React.FC<{ onNext: (data: any) => void; onBack: () => void }> = ({ onNext, onBack }) => { + const [answer, setAnswer] = useState(''); + + return ( +
+ +
+ onNext({ companyImprovements: answer })} + onSkip={() => onNext({ companyImprovements: '' })} + currentStep={1} + totalSteps={7} + /> +
+
+ ); +}; + +// Step 32: Job Satisfaction +const EmployeeFormStep32: React.FC<{ onNext: (data: any) => void; onBack: () => void }> = ({ onNext, onBack }) => { + const [rating, setRating] = useState(); + + return ( +
+ +
+ onNext({ jobSatisfaction: rating })} + onSkip={() => onNext({ jobSatisfaction: null })} + nextDisabled={!rating} + currentStep={2} + totalSteps={7} + /> +
+
+ ); +}; + +// Step 33: Additional Feedback +const EmployeeFormStep33: React.FC<{ onNext: (data: any) => void; onBack: () => void }> = ({ onNext, onBack }) => { + const [answer, setAnswer] = useState(''); + + return ( +
+ +
+ onNext({ additionalFeedback: answer })} + onSkip={() => onNext({ additionalFeedback: '' })} + currentStep={3} + totalSteps={7} + /> +
+
+ ); +}; + +// Step 35: Leadership & Organizational Structure - Magic Wand Question +const EmployeeFormStep35: React.FC<{ onNext: () => void; onBack: () => void }> = ({ onNext, onBack }) => { + const [answer, setAnswer] = React.useState(''); + + return ( +
+
+
+
If you had a magic wand, what would you change about how we operate?
+
+