238 lines
8.8 KiB
JavaScript
238 lines
8.8 KiB
JavaScript
import { useState, useEffect } from 'react';
|
|
import axios from 'axios';
|
|
|
|
export default function PrivacySettingsModal({ isOpen, onClose }) {
|
|
const [organizations, setOrganizations] = useState([]);
|
|
const [currentOrgIndex, setCurrentOrgIndex] = useState(0);
|
|
const [loading, setLoading] = useState(true);
|
|
const [saving, setSaving] = useState(false);
|
|
const [settings, setSettings] = useState({
|
|
share_career_exploration: false,
|
|
share_interest_inventory: false,
|
|
share_career_profiles: false,
|
|
share_college_profiles: false,
|
|
share_financial_profile: false,
|
|
share_roadmap: false
|
|
});
|
|
|
|
useEffect(() => {
|
|
if (isOpen) {
|
|
fetchOrganizations();
|
|
}
|
|
}, [isOpen]);
|
|
|
|
const fetchOrganizations = async () => {
|
|
try {
|
|
setLoading(true);
|
|
const { data } = await axios.get('/api/privacy-settings', {
|
|
withCredentials: true
|
|
});
|
|
if (data.organizations && data.organizations.length > 0) {
|
|
setOrganizations(data.organizations);
|
|
// Load settings for first org
|
|
setSettings(data.organizations[0].settings || {
|
|
share_career_exploration: false,
|
|
share_interest_inventory: false,
|
|
share_career_profiles: false,
|
|
share_college_profiles: false,
|
|
share_financial_profile: false,
|
|
share_roadmap: false
|
|
});
|
|
}
|
|
} catch (err) {
|
|
console.error('[PrivacySettingsModal] Error loading organizations:', err);
|
|
} finally {
|
|
setLoading(false);
|
|
}
|
|
};
|
|
|
|
const handleSave = async () => {
|
|
if (organizations.length === 0) return;
|
|
|
|
try {
|
|
setSaving(true);
|
|
const currentOrg = organizations[currentOrgIndex];
|
|
|
|
await axios.post('/api/privacy-settings', {
|
|
organization_id: currentOrg.organization_id,
|
|
...settings
|
|
}, {
|
|
withCredentials: true
|
|
});
|
|
|
|
// If there are more organizations, move to next
|
|
if (currentOrgIndex < organizations.length - 1) {
|
|
const nextIndex = currentOrgIndex + 1;
|
|
setCurrentOrgIndex(nextIndex);
|
|
setSettings(organizations[nextIndex].settings || {
|
|
share_career_exploration: false,
|
|
share_interest_inventory: false,
|
|
share_career_profiles: false,
|
|
share_college_profiles: false,
|
|
share_financial_profile: false,
|
|
share_roadmap: false
|
|
});
|
|
} else {
|
|
// All done, close modal
|
|
onClose();
|
|
}
|
|
} catch (err) {
|
|
console.error('[PrivacySettingsModal] Error saving settings:', err);
|
|
alert('Failed to save privacy settings. Please try again.');
|
|
} finally {
|
|
setSaving(false);
|
|
}
|
|
};
|
|
|
|
const toggleSetting = (key) => {
|
|
setSettings(prev => ({
|
|
...prev,
|
|
[key]: !prev[key]
|
|
}));
|
|
};
|
|
|
|
if (!isOpen) return null;
|
|
|
|
if (loading) {
|
|
return (
|
|
<div className="fixed inset-0 bg-black bg-opacity-50 flex items-center justify-center z-50">
|
|
<div className="bg-white rounded-lg p-8 max-w-2xl w-full mx-4">
|
|
<div className="text-center">
|
|
<div className="animate-spin rounded-full h-12 w-12 border-b-2 border-aptiva mx-auto"></div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
);
|
|
}
|
|
|
|
if (organizations.length === 0) {
|
|
return null; // No organizations, don't show modal
|
|
}
|
|
|
|
const currentOrg = organizations[currentOrgIndex];
|
|
const isLastOrg = currentOrgIndex === organizations.length - 1;
|
|
|
|
return (
|
|
<div className="fixed inset-0 bg-black bg-opacity-50 flex items-center justify-center z-50 p-4">
|
|
<div className="bg-white rounded-lg shadow-xl max-w-2xl w-full max-h-[90vh] overflow-y-auto">
|
|
<div className="p-6">
|
|
<h2 className="text-2xl font-bold text-gray-900 mb-2">
|
|
Privacy Settings
|
|
</h2>
|
|
<p className="text-sm text-gray-600 mb-6">
|
|
You've enrolled in <strong>{currentOrg.organization_name}</strong>. Please choose what information you'd like to share with them.
|
|
{organizations.length > 1 && ` (${currentOrgIndex + 1} of ${organizations.length})`}
|
|
</p>
|
|
|
|
<div className="bg-blue-50 border border-blue-200 rounded-lg p-4 mb-6">
|
|
<p className="text-sm text-blue-900">
|
|
<strong>Your privacy is important to us.</strong> All settings default to private. When you enable sharing, your organization's counselors can view your activity data (tied to your name and email) to provide personalized guidance. You can change these settings anytime from Profile → Privacy Settings.
|
|
</p>
|
|
</div>
|
|
|
|
<div className="space-y-4">
|
|
<PrivacyToggleItem
|
|
label="Career Exploration Data"
|
|
description="Share information about careers you've researched and viewed"
|
|
checked={settings.share_career_exploration}
|
|
onChange={() => toggleSetting('share_career_exploration')}
|
|
/>
|
|
|
|
<PrivacyToggleItem
|
|
label="Interest Inventory Results"
|
|
description="Share your RIASEC interest inventory results and career recommendations"
|
|
checked={settings.share_interest_inventory}
|
|
onChange={() => toggleSetting('share_interest_inventory')}
|
|
/>
|
|
|
|
<PrivacyToggleItem
|
|
label="Career Profiles"
|
|
description="Share your saved career profiles and career planning details"
|
|
checked={settings.share_career_profiles}
|
|
onChange={() => toggleSetting('share_career_profiles')}
|
|
/>
|
|
|
|
<PrivacyToggleItem
|
|
label="College Profiles"
|
|
description="Share your saved college profiles and college planning details"
|
|
checked={settings.share_college_profiles}
|
|
onChange={() => toggleSetting('share_college_profiles')}
|
|
/>
|
|
|
|
<PrivacyInfoItem
|
|
label="Financial Profile"
|
|
description="Your financial information is never shared with organizations to protect your privacy. Note: Expected salary projections from College Profiles may be visible to counselors as they are considered career planning data, not personal financial information."
|
|
/>
|
|
|
|
<PrivacyToggleItem
|
|
label="Career Roadmap"
|
|
description="Share your career roadmap and milestones"
|
|
checked={settings.share_roadmap}
|
|
onChange={() => toggleSetting('share_roadmap')}
|
|
/>
|
|
</div>
|
|
|
|
<div className="mt-8 flex justify-end gap-3">
|
|
<button
|
|
onClick={handleSave}
|
|
disabled={saving}
|
|
className="px-6 py-2 bg-aptiva text-white rounded-md hover:bg-aptiva-dark transition-colors disabled:opacity-50 disabled:cursor-not-allowed"
|
|
>
|
|
{saving ? 'Saving...' : (isLastOrg ? 'Save & Continue' : 'Next Organization')}
|
|
</button>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
);
|
|
}
|
|
|
|
function PrivacyToggleItem({ label, description, checked, onChange }) {
|
|
return (
|
|
<div className="flex items-start justify-between p-4 border border-gray-200 rounded-lg hover:bg-gray-50 transition-colors">
|
|
<div className="flex-1">
|
|
<label className="text-sm font-medium text-gray-900 cursor-pointer" onClick={onChange}>
|
|
{label}
|
|
</label>
|
|
<p className="text-sm text-gray-500 mt-1">{description}</p>
|
|
</div>
|
|
<div className="ml-4 flex-shrink-0">
|
|
<button
|
|
type="button"
|
|
className={`relative inline-flex h-6 w-11 flex-shrink-0 cursor-pointer rounded-full border-2 border-transparent transition-colors duration-200 ease-in-out focus:outline-none focus:ring-2 focus:ring-aptiva focus:ring-offset-2 ${
|
|
checked ? 'bg-aptiva' : 'bg-gray-200'
|
|
}`}
|
|
role="switch"
|
|
aria-checked={checked}
|
|
onClick={onChange}
|
|
>
|
|
<span
|
|
aria-hidden="true"
|
|
className={`pointer-events-none inline-block h-5 w-5 transform rounded-full bg-white shadow ring-0 transition duration-200 ease-in-out ${
|
|
checked ? 'translate-x-5' : 'translate-x-0'
|
|
}`}
|
|
/>
|
|
</button>
|
|
</div>
|
|
</div>
|
|
);
|
|
}
|
|
|
|
function PrivacyInfoItem({ label, description }) {
|
|
return (
|
|
<div className="flex items-start justify-between p-4 border border-blue-200 rounded-lg bg-blue-50">
|
|
<div className="flex-1">
|
|
<div className="text-sm font-medium text-gray-900">
|
|
{label}
|
|
</div>
|
|
<p className="text-sm text-gray-700 mt-1">{description}</p>
|
|
</div>
|
|
<div className="ml-4 flex-shrink-0">
|
|
<svg className="h-6 w-6 text-blue-600" fill="none" viewBox="0 0 24 24" stroke="currentColor">
|
|
<path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} d="M12 15v2m-6 4h12a2 2 0 002-2v-6a2 2 0 00-2-2H6a2 2 0 00-2 2v6a2 2 0 002 2zm10-10V7a4 4 0 00-8 0v4h8z" />
|
|
</svg>
|
|
</div>
|
|
</div>
|
|
);
|
|
}
|