Files
auditly/pages/HelpAndSettings.tsx

336 lines
16 KiB
TypeScript

import React, { useState } from 'react';
import { useTheme } from '../contexts/ThemeContext';
import { useAuth } from '../contexts/AuthContext';
import { useOrg } from '../contexts/OrgContext';
import { Card, Button } from '../components/UiKit';
import { Theme } from '../types';
const HelpAndSettings: React.FC = () => {
const { theme, setTheme } = useTheme();
const { user, signOutUser } = useAuth();
const { org, upsertOrg, issueInviteViaApi } = useOrg();
const [activeTab, setActiveTab] = useState<'settings' | 'help'>('settings');
const [inviteForm, setInviteForm] = useState({ name: '', email: '', role: '', department: '' });
const [isInviting, setIsInviting] = useState(false);
const [inviteResult, setInviteResult] = useState<string | null>(null);
const handleLogout = async () => {
try {
await signOutUser();
} catch (error) {
console.error('Logout error:', error);
}
};
const handleRestartOnboarding = async () => {
try {
await upsertOrg({ onboardingCompleted: false });
// The RequireOnboarding component will redirect automatically
} catch (error) {
console.error('Failed to restart onboarding:', error);
}
};
const handleInviteEmployee = async () => {
if (!inviteForm.name.trim() || !inviteForm.email.trim() || isInviting) return;
setIsInviting(true);
setInviteResult(null);
try {
const result = await issueInviteViaApi({
name: inviteForm.name.trim(),
email: inviteForm.email.trim(),
role: inviteForm.role.trim() || undefined,
department: inviteForm.department.trim() || undefined
});
setInviteResult(`Invitation sent! Share this link: ${result.inviteLink}`);
setInviteForm({ name: '', email: '', role: '', department: '' });
} catch (error) {
console.error('Failed to send invitation:', error);
setInviteResult('Failed to send invitation. Please try again.');
} finally {
setIsInviting(false);
}
};
const renderSettings = () => (
<div className="space-y-6">
<Card>
<h3 className="text-lg font-semibold text-[--text-primary] mb-4">Appearance</h3>
<div className="space-y-3">
<div>
<label className="block text-sm font-medium text-[--text-primary] mb-2">
Theme
</label>
<div className="flex space-x-2">
<Button
variant={theme === Theme.Light ? 'primary' : 'secondary'}
size="sm"
onClick={() => setTheme(Theme.Light)}
>
Light
</Button>
<Button
variant={theme === Theme.Dark ? 'primary' : 'secondary'}
size="sm"
onClick={() => setTheme(Theme.Dark)}
>
Dark
</Button>
<Button
variant={theme === Theme.System ? 'primary' : 'secondary'}
size="sm"
onClick={() => setTheme(Theme.System)}
>
System
</Button>
</div>
</div>
</div>
</Card>
<Card>
<h3 className="text-lg font-semibold text-[--text-primary] mb-4">Organization</h3>
<div className="space-y-3">
<div>
<span className="text-sm text-[--text-secondary]">Company:</span>
<div className="font-medium text-[--text-primary]">{org?.name}</div>
</div>
<div>
<span className="text-sm text-[--text-secondary]">Onboarding:</span>
<div className="font-medium text-[--text-primary]">
{org?.onboardingCompleted ? 'Completed' : 'Incomplete'}
</div>
</div>
<div className="pt-4">
<Button variant="secondary" onClick={handleRestartOnboarding}>
Restart Onboarding
</Button>
<p className="text-xs text-[--text-secondary] mt-2">
This will reset your company profile and require you to complete the setup process again.
</p>
</div>
</div>
</Card>
<Card>
<h3 className="text-lg font-semibold text-[--text-primary] mb-4">Invite Employee</h3>
<div className="space-y-4">
<div className="grid grid-cols-1 md:grid-cols-2 gap-4">
<div>
<label className="block text-sm font-medium text-[--text-primary] mb-1">
Name *
</label>
<input
type="text"
value={inviteForm.name}
onChange={(e) => setInviteForm(prev => ({ ...prev, name: e.target.value }))}
className="w-full px-3 py-2 border border-gray-300 rounded-md focus:outline-none focus:ring-2 focus:ring-blue-500"
placeholder="John Doe"
/>
</div>
<div>
<label className="block text-sm font-medium text-[--text-primary] mb-1">
Email *
</label>
<input
type="email"
value={inviteForm.email}
onChange={(e) => setInviteForm(prev => ({ ...prev, email: e.target.value }))}
className="w-full px-3 py-2 border border-gray-300 rounded-md focus:outline-none focus:ring-2 focus:ring-blue-500"
placeholder="john.doe@company.com"
/>
</div>
<div>
<label className="block text-sm font-medium text-[--text-primary] mb-1">
Role
</label>
<input
type="text"
value={inviteForm.role}
onChange={(e) => setInviteForm(prev => ({ ...prev, role: e.target.value }))}
className="w-full px-3 py-2 border border-gray-300 rounded-md focus:outline-none focus:ring-2 focus:ring-blue-500"
placeholder="Senior Developer"
/>
</div>
<div>
<label className="block text-sm font-medium text-[--text-primary] mb-1">
Department
</label>
<input
type="text"
value={inviteForm.department}
onChange={(e) => setInviteForm(prev => ({ ...prev, department: e.target.value }))}
className="w-full px-3 py-2 border border-gray-300 rounded-md focus:outline-none focus:ring-2 focus:ring-blue-500"
placeholder="Engineering"
/>
</div>
</div>
<Button
onClick={handleInviteEmployee}
disabled={!inviteForm.name.trim() || !inviteForm.email.trim() || isInviting}
className="w-full"
>
{isInviting ? 'Sending Invitation...' : 'Send Invitation'}
</Button>
{inviteResult && (
<div className={`p-3 rounded-md text-sm ${inviteResult.includes('Failed')
? 'bg-red-50 text-red-800 border border-red-200'
: 'bg-green-50 text-green-800 border border-green-200'
}`}>
{inviteResult}
</div>
)}
<p className="text-xs text-[--text-secondary]">
The invited employee will receive an email with instructions to join your organization.
</p>
</div>
</Card>
<Card>
<h3 className="text-lg font-semibold text-[--text-primary] mb-4">Account</h3>
<div className="space-y-3">
<div>
<span className="text-sm text-[--text-secondary]">Email:</span>
<div className="font-medium text-[--text-primary]">{user?.email}</div>
</div>
<div>
<span className="text-sm text-[--text-secondary]">User ID:</span>
<div className="font-medium text-[--text-primary] font-mono text-xs">{user?.uid}</div>
</div>
<div className="pt-4">
<Button variant="secondary" onClick={handleLogout}>
Sign Out
</Button>
</div>
</div>
</Card>
<Card>
<h3 className="text-lg font-semibold text-[--text-primary] mb-4">Data & Privacy</h3>
<div className="space-y-3">
<Button variant="secondary" className="w-full justify-start">
Export My Data
</Button>
<Button variant="secondary" className="w-full justify-start">
Privacy Settings
</Button>
<Button variant="secondary" className="w-full justify-start text-red-600">
Delete Account
</Button>
</div>
</Card>
</div>
);
const renderHelp = () => (
<div className="space-y-6">
<Card>
<h3 className="text-lg font-semibold text-[--text-primary] mb-4">Getting Started</h3>
<div className="space-y-4">
<div>
<h4 className="font-medium text-[--text-primary] mb-2">1. Set up your organization</h4>
<p className="text-[--text-secondary] text-sm">
Complete the onboarding process to configure your company information and preferences.
</p>
</div>
<div>
<h4 className="font-medium text-[--text-primary] mb-2">2. Add employees</h4>
<p className="text-[--text-secondary] text-sm">
Invite team members and add their basic information to start generating reports.
</p>
</div>
<div>
<h4 className="font-medium text-[--text-primary] mb-2">3. Generate reports</h4>
<p className="text-[--text-secondary] text-sm">
Use AI-powered reports to gain insights into employee performance and organizational health.
</p>
</div>
</div>
</Card>
<Card>
<h3 className="text-lg font-semibold text-[--text-primary] mb-4">Frequently Asked Questions</h3>
<div className="space-y-4">
<div>
<h4 className="font-medium text-[--text-primary] mb-2">How do I add new employees?</h4>
<p className="text-[--text-secondary] text-sm">
Go to the Reports page and use the "Add Employee" button to invite new team members.
</p>
</div>
<div>
<h4 className="font-medium text-[--text-primary] mb-2">How are reports generated?</h4>
<p className="text-[--text-secondary] text-sm">
Reports use AI to analyze employee data and provide insights on performance, strengths, and development opportunities.
</p>
</div>
<div>
<h4 className="font-medium text-[--text-primary] mb-2">Is my data secure?</h4>
<p className="text-[--text-secondary] text-sm">
Yes, all data is encrypted and stored securely. We follow industry best practices for data protection.
</p>
</div>
</div>
</Card>
<Card>
<h3 className="text-lg font-semibold text-[--text-primary] mb-4">Contact Support</h3>
<div className="space-y-3">
<Button variant="secondary" className="w-full justify-start">
📧 Email Support
</Button>
<Button variant="secondary" className="w-full justify-start">
💬 Live Chat
</Button>
<Button variant="secondary" className="w-full justify-start">
📚 Documentation
</Button>
</div>
</Card>
</div>
);
return (
<div className="p-6 max-w-4xl mx-auto">
<div className="mb-6">
<h1 className="text-3xl font-bold text-[--text-primary]">Help & Settings</h1>
<p className="text-[--text-secondary] mt-1">
Manage your account and get help
</p>
</div>
<div className="mb-6">
<div className="flex space-x-4 border-b border-[--border-color]">
<button
onClick={() => setActiveTab('settings')}
className={`px-4 py-2 font-medium border-b-2 transition-colors ${activeTab === 'settings'
? 'border-blue-500 text-blue-500'
: 'border-transparent text-[--text-secondary] hover:text-[--text-primary]'
}`}
>
Settings
</button>
<button
onClick={() => setActiveTab('help')}
className={`px-4 py-2 font-medium border-b-2 transition-colors ${activeTab === 'help'
? 'border-blue-500 text-blue-500'
: 'border-transparent text-[--text-secondary] hover:text-[--text-primary]'
}`}
>
Help
</button>
</div>
</div>
{activeTab === 'settings' ? renderSettings() : renderHelp()}
</div>
);
};
export default HelpAndSettings;