update onboarding colors and add image upload

This commit is contained in:
Ra
2025-08-20 11:20:28 -07:00
parent 875280cdac
commit 9332a48542
12 changed files with 2078 additions and 426 deletions

View File

@@ -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<string, Submission> = {};
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<OrgData>) => {
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<OrgData>) => {
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) {

View File

@@ -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;