\n\n \n'
: 'Enter your advertisement text here...'}
rows="8"
required
style={{
fontFamily: formData.adType === 'code' ? 'monospace' : 'inherit',
fontSize: formData.adType === 'code' ? '0.85rem' : '1rem'
}}
/>
{formData.adType === 'code' && (
{previewCode ? 'Hide Preview' : 'Preview Code'}
{previewCode && formData.content && (
)}
)}
{formData.adType === 'code' && (
⚠️ Security Warning:
Only add code from trusted sources (Google AdSense, etc.)
Malicious code can compromise user security
Test thoroughly before activating
Code executes in user's browser with full permissions
)}
setFormData(prev => ({ ...prev, isActive: e.target.checked }))}
style={{ accentColor: 'var(--accent-primary)', width: '18px', height: '18px' }}
/>
Active (show this ad to users)
{editingAd ? 'Update Ad' : 'Create Ad'}
{
setShowCreateForm(false);
setEditingAd(null);
setFormData({ title: '', content: '', adType: 'text', dismissDelay: 5, isActive: true });
setPreviewCode(false);
}}
>
Cancel
)}
);
}
return (
{message.text && (
{message.text}
)}
{!showCreateForm ? (
📢 Advertisement List
setShowCreateForm(true)}
>
+ Create New Ad
{ads.length === 0 ? (
📢
No ads yet
Create your first advertisement to show before/after quizzes!
) : (
{ads.map(ad => (
{ad.title}
{ad.isActive ? 'Active' : 'Inactive'}
Impressions: {ad.impressions || 0}
{ad.content}
startEdit(ad)}
style={{ padding: '0.5rem 1rem', fontSize: '0.85rem' }}
>
Edit
toggleAdStatus(ad.id, ad.isActive)}
style={{ padding: '0.5rem 1rem', fontSize: '0.85rem' }}
>
{ad.isActive ? 'Deactivate' : 'Activate'}
deleteAd(ad.id)}
style={{ padding: '0.5rem 1rem', fontSize: '0.85rem' }}
>
Delete
))}
)}
) : (
)}
);
}
// Global Ad Settings Component
function GlobalAdSettings({ settings, onUpdate }) {
const { db } = useFirebase();
const [formData, setFormData] = useState(settings);
const [message, setMessage] = useState({ type: '', text: '' });
useEffect(() => {
setFormData(settings);
}, [settings]);
const saveSettings = async (e) => {
e.preventDefault();
try {
await db.collection('settings').doc('ads').set(formData);
setMessage({ type: 'success', text: 'Settings saved successfully!' });
onUpdate();
setTimeout(() => setMessage({ type: '', text: '' }), 3000);
} catch (error) {
setMessage({ type: 'error', text: `Error: ${error.message}` });
}
};
return (
⚙️ Global Ad Settings
{message.text && (
{message.text}
)}
Save Global Settings
);
}
// Ad Targeting Component
function AdTargeting() {
const { db } = useFirebase();
const [targetType, setTargetType] = useState('quiz'); // quiz, group, user
const [quizzes, setQuizzes] = useState([]);
const [groups, setGroups] = useState([]);
const [users, setUsers] = useState([]);
const [loading, setLoading] = useState(true);
const [message, setMessage] = useState({ type: '', text: '' });
useEffect(() => {
loadTargetingData();
}, []);
const loadTargetingData = async () => {
try {
const [quizzesSnap, groupsSnap, usersSnap] = await Promise.all([
db.collection('quizzes').get(),
db.collection('groups').get(),
db.collection('users').get()
]);
setQuizzes(quizzesSnap.docs.map(doc => ({ id: doc.id, ...doc.data() })));
setGroups(groupsSnap.docs.map(doc => ({ id: doc.id, ...doc.data() })));
setUsers(usersSnap.docs.map(doc => ({ id: doc.id, ...doc.data() })));
} catch (error) {
console.error('Error loading targeting data:', error);
}
setLoading(false);
};
const toggleAds = async (collection, docId, currentState) => {
try {
await db.collection(collection).doc(docId).update({
adsDisabled: !currentState
});
setMessage({ type: 'success', text: 'Ad settings updated!' });
loadTargetingData();
setTimeout(() => setMessage({ type: '', text: '' }), 3000);
} catch (error) {
setMessage({ type: 'error', text: `Error: ${error.message}` });
}
};
if (loading) {
return Loading targeting options...
;
}
return (
🎯 Ad Targeting Rules
{message.text && (
{message.text}
)}
💡 How It Works:
Control ads at granular levels. Disable ads for specific quizzes, groups, or individual users.
These settings override global settings. Priority: User > Group > Quiz > Global.
setTargetType('quiz')}
>
By Quiz ({quizzes.length})
setTargetType('group')}
>
By Group ({groups.length})
setTargetType('user')}
>
By User ({users.length})
{targetType === 'quiz' && (
Quiz-Level Ad Control
{quizzes.length === 0 ? (
No quizzes available
) : (
{quizzes.map(quiz => (
{quiz.title}
{quiz.questionCount} questions
toggleAds('quizzes', quiz.id, quiz.adsDisabled)}
style={{ padding: '0.5rem 1rem', fontSize: '0.85rem' }}
>
{quiz.adsDisabled ? '✓ Ads Disabled' : 'Disable Ads'}
))}
)}
)}
{targetType === 'group' && (
Group-Level Ad Control
{groups.length === 0 ? (
No groups available
) : (
{groups.map(group => (
{group.name}
{group.members?.length || 0} members
toggleAds('groups', group.id, group.adsDisabled)}
style={{ padding: '0.5rem 1rem', fontSize: '0.85rem' }}
>
{group.adsDisabled ? '✓ Ads Disabled' : 'Disable Ads'}
))}
)}
)}
{targetType === 'user' && (
User-Level Ad Control
{users.length === 0 ? (
No users available
) : (
{users.map(user => (
{user.displayName}
{user.email}
toggleAds('users', user.id, user.adsDisabled)}
style={{ padding: '0.5rem 1rem', fontSize: '0.85rem' }}
>
{user.adsDisabled ? '✓ Ads Disabled' : 'Disable Ads'}
))}
)}
)}
);
}
function UserManagement() {
const { db } = useFirebase();
const [users, setUsers] = useState([]);
const [loading, setLoading] = useState(true);
const [newUserEmail, setNewUserEmail] = useState('');
const [newUserRole, setNewUserRole] = useState('user');
const [message, setMessage] = useState({ type: '', text: '' });
const roles = [
{ value: 'admin', label: 'Admin', description: 'Full access to admin panel' },
{ value: 'quiz_creator', label: 'Quiz Creator', description: 'Can create questions and quizzes' },
{ value: 'teacher', label: 'Teacher', description: 'Can view student performance' },
{ value: 'user', label: 'User', description: 'Standard user access' }
];
useEffect(() => {
loadUsers();
}, []);
const loadUsers = async () => {
try {
const snapshot = await db.collection('users').orderBy('email').get();
const usersData = snapshot.docs.map(doc => ({
id: doc.id,
...doc.data()
}));
setUsers(usersData);
} catch (error) {
console.error('Error loading users:', error);
}
setLoading(false);
};
const updateUserRole = async (userId, newRole) => {
try {
await db.collection('users').doc(userId).update({
role: newRole,
updatedAt: firebase.firestore.FieldValue.serverTimestamp()
});
setMessage({ type: 'success', text: 'User role updated successfully!' });
loadUsers();
} catch (error) {
setMessage({ type: 'error', text: `Error: ${error.message}` });
}
};
const getRoleColor = (role) => {
switch(role) {
case 'admin': return '#ff3366';
case 'quiz_creator': return '#0099ff';
case 'teacher': return '#9333ea';
case 'user': return '#00ff88';
default: return '#a0aec0';
}
};
const getRoleBadgeStyle = (role) => ({
backgroundColor: `${getRoleColor(role)}20`,
color: getRoleColor(role),
padding: '0.5rem 1rem',
borderRadius: '6px',
fontSize: '0.75rem',
fontWeight: '700',
textTransform: 'uppercase'
});
if (loading) {
return Loading users...
;
}
return (
User Management
{message.text && (
{message.text}
)}
{/* Role Descriptions */}
Role Descriptions
{roles.map(role => (
{role.label}
{role.description}
))}
{/* Users List */}
User
Email
Current Role
Change Role
{users.map(user => (
{user.displayName || 'No name'}
{user.email}
{user.role || 'user'}
updateUserRole(user.id, e.target.value)}
style={{ maxWidth: '200px' }}
>
{roles.map(role => (
{role.label}
))}
))}
{users.length === 0 && (
No users found. Users will appear here after they sign up.
)}
💡 How to Add New Users
New users must first create an account through the mobile app or authentication system
Once they've signed up, they'll appear in this list with the default "user" role
You can then update their role using the dropdown menu
Role changes take effect immediately
);
}
// Main App Component
function App() {
return (
);
}
function MainApp() {
const { user, userRole, loading, isAdmin } = useAuth();
if (loading) {
return (
);
}
// Not logged in - show login screen
if (!user) {
return ;
}
// Logged in but not admin - show unauthorized
if (!isAdmin) {
return ;
}
// Admin user - show dashboard
return ;
}
ReactDOM.render( , document.getElementById('root'));