diff --git a/backend/package.json b/backend/package.json
index cb0135e..a0fa40d 100644
--- a/backend/package.json
+++ b/backend/package.json
@@ -1,6 +1,6 @@
{
"name": "rosterchirp-backend",
- "version": "0.12.46",
+ "version": "0.12.47",
"description": "RosterChirp backend server",
"main": "src/index.js",
"scripts": {
diff --git a/build.sh b/build.sh
index b705232..c756507 100644
--- a/build.sh
+++ b/build.sh
@@ -13,7 +13,7 @@
# ─────────────────────────────────────────────────────────────
set -euo pipefail
-VERSION="${1:-0.12.46}"
+VERSION="${1:-0.12.47}"
ACTION="${2:-}"
REGISTRY="${REGISTRY:-}"
IMAGE_NAME="rosterchirp"
diff --git a/frontend/package.json b/frontend/package.json
index 9779498..bc2f70c 100644
--- a/frontend/package.json
+++ b/frontend/package.json
@@ -1,6 +1,6 @@
{
"name": "rosterchirp-frontend",
- "version": "0.12.46",
+ "version": "0.12.47",
"private": true,
"scripts": {
"dev": "vite",
diff --git a/frontend/src/components/AddChildAliasModal.jsx b/frontend/src/components/AddChildAliasModal.jsx
new file mode 100644
index 0000000..543e595
--- /dev/null
+++ b/frontend/src/components/AddChildAliasModal.jsx
@@ -0,0 +1,195 @@
+import { useState, useEffect } from 'react';
+import { useToast } from '../contexts/ToastContext.jsx';
+import { api } from '../utils/api.js';
+
+export default function AddChildAliasModal({ onClose }) {
+ const toast = useToast();
+ const [aliases, setAliases] = useState([]);
+ const [editingAlias, setEditingAlias] = useState(null); // null = new entry
+ const [form, setForm] = useState({ firstName: '', lastName: '', dob: '', phone: '', email: '' });
+ const [avatarFile, setAvatarFile] = useState(null);
+ const [saving, setSaving] = useState(false);
+
+ useEffect(() => {
+ api.getAliases().then(({ aliases }) => setAliases(aliases || [])).catch(() => {});
+ }, []);
+
+ const set = k => e => setForm(p => ({ ...p, [k]: e.target.value }));
+
+ const resetForm = () => {
+ setEditingAlias(null);
+ setForm({ firstName: '', lastName: '', dob: '', phone: '', email: '' });
+ setAvatarFile(null);
+ };
+
+ const handleSelectAlias = (a) => {
+ if (editingAlias?.id === a.id) { resetForm(); return; }
+ setEditingAlias(a);
+ setForm({
+ firstName: a.first_name || '',
+ lastName: a.last_name || '',
+ dob: a.date_of_birth ? a.date_of_birth.slice(0, 10) : '',
+ phone: a.phone || '',
+ email: a.email || '',
+ });
+ setAvatarFile(null);
+ };
+
+ const handleSave = async () => {
+ if (!form.firstName.trim() || !form.lastName.trim())
+ return toast('First and last name required', 'error');
+ setSaving(true);
+ try {
+ if (editingAlias) {
+ await api.updateAlias(editingAlias.id, {
+ firstName: form.firstName.trim(),
+ lastName: form.lastName.trim(),
+ dateOfBirth: form.dob || null,
+ phone: form.phone || null,
+ email: form.email || null,
+ });
+ if (avatarFile) await api.uploadAliasAvatar(editingAlias.id, avatarFile);
+ toast('Child alias updated', 'success');
+ } else {
+ const { alias } = await api.createAlias({
+ firstName: form.firstName.trim(),
+ lastName: form.lastName.trim(),
+ dateOfBirth: form.dob || null,
+ phone: form.phone || null,
+ email: form.email || null,
+ });
+ if (avatarFile) await api.uploadAliasAvatar(alias.id, avatarFile);
+ toast('Child alias added', 'success');
+ }
+ const { aliases: fresh } = await api.getAliases();
+ setAliases(fresh || []);
+ resetForm();
+ } catch (e) {
+ toast(e.message, 'error');
+ } finally {
+ setSaving(false);
+ }
+ };
+
+ const handleDelete = async (e, aliasId) => {
+ e.stopPropagation();
+ try {
+ await api.deleteAlias(aliasId);
+ setAliases(prev => prev.filter(a => a.id !== aliasId));
+ if (editingAlias?.id === aliasId) resetForm();
+ toast('Child alias removed', 'success');
+ } catch (err) { toast(err.message, 'error'); }
+ };
+
+ const lbl = (text, required) => (
+
+ );
+
+ return (
+
e.target === e.currentTarget && onClose()}>
+
+
+ {/* Header */}
+
+
Add Child Alias
+
+
+
+ {/* Existing aliases list */}
+ {aliases.length > 0 && (
+
+
+ Your Children — click to edit
+
+
+ {aliases.map((a, i) => (
+
handleSelectAlias(a)}
+ style={{
+ display: 'flex', alignItems: 'center', gap: 10,
+ padding: '9px 12px', cursor: 'pointer',
+ borderBottom: i < aliases.length - 1 ? '1px solid var(--border)' : 'none',
+ background: editingAlias?.id === a.id ? 'var(--primary-light)' : 'transparent',
+ }}
+ >
+
+ {a.first_name} {a.last_name}
+
+ {a.date_of_birth && (
+
+ {a.date_of_birth.slice(0, 10)}
+
+ )}
+
+
+ ))}
+
+
+ )}
+
+ {/* Form section label */}
+
+ {editingAlias
+ ? `Editing: ${editingAlias.first_name} ${editingAlias.last_name}`
+ : 'New Child Alias'}
+
+
+ {/* Form */}
+
+
+
+ {lbl('Email (optional)')}
+
+
+
+ {lbl('Avatar (optional)')}
+ setAvatarFile(e.target.files?.[0] || null)} />
+
+
+ {editingAlias && (
+
+ )}
+
+
+
+
+
+
+ );
+}
diff --git a/frontend/src/components/NavDrawer.jsx b/frontend/src/components/NavDrawer.jsx
index 4fc6f4b..856ca2b 100644
--- a/frontend/src/components/NavDrawer.jsx
+++ b/frontend/src/components/NavDrawer.jsx
@@ -13,13 +13,14 @@ const NAV_ICON = {
settings: ,
};
-export default function NavDrawer({ open, onClose, onMessages, onGroupMessages, onSchedule, onScheduleManager, onBranding, onSettings, onUsers, onGroupManager, onHostPanel, features = {}, currentPage = 'chat', isMobile = false, unreadMessages = false, unreadGroupMessages = false }) {
+export default function NavDrawer({ open, onClose, onMessages, onGroupMessages, onSchedule, onScheduleManager, onBranding, onSettings, onUsers, onGroupManager, onHostPanel, onAddChild, features = {}, currentPage = 'chat', isMobile = false, unreadMessages = false, unreadGroupMessages = false }) {
const { user } = useAuth();
const drawerRef = useRef(null);
const isAdmin = user?.role === 'admin';
const userGroupIds = features.userGroupMemberships || [];
const canAccessTools = isAdmin || user?.role === 'manager' || (features.teamToolManagers || []).some(gid => userGroupIds.includes(gid));
const hasUserGroups = userGroupIds.length > 0;
+ const showAddChild = features.loginType === 'guardian_only' && features.inGuardiansGroup;
useEffect(() => {
if (!open) return;
@@ -80,11 +81,16 @@ export default function NavDrawer({ open, onClose, onMessages, onGroupMessages,
)}
{/* Tools section */}
- {canAccessTools && (
+ {(canAccessTools || showAddChild) && (
<>
Tools
- {item(NAV_ICON.users, 'User Manager', onUsers, { active: currentPage === 'users' })}
- {features.groupManager && item(NAV_ICON.groups, 'Group Manager', onGroupManager, { active: currentPage === 'groups' })}
+ {canAccessTools && item(NAV_ICON.users, 'User Manager', onUsers, { active: currentPage === 'users' })}
+ {canAccessTools && features.groupManager && item(NAV_ICON.groups, 'Group Manager', onGroupManager, { active: currentPage === 'groups' })}
+ {showAddChild && item(
+ ,
+ 'Add Child Aliase',
+ onAddChild
+ )}
>
)}
diff --git a/frontend/src/components/ProfileModal.jsx b/frontend/src/components/ProfileModal.jsx
index d775c8b..8d83dd6 100644
--- a/frontend/src/components/ProfileModal.jsx
+++ b/frontend/src/components/ProfileModal.jsx
@@ -23,7 +23,7 @@ export default function ProfileModal({ onClose }) {
const [newPw, setNewPw] = useState('');
const [confirmPw, setConfirmPw] = useState('');
const [loading, setLoading] = useState(false);
- const [tab, setTab] = useState('profile'); // 'profile' | 'password' | 'notifications' | 'appearance' | 'add-child'
+ const [tab, setTab] = useState('profile'); // 'profile' | 'password' | 'notifications' | 'appearance'
const [pushTesting, setPushTesting] = useState(false);
const [pushResult, setPushResult] = useState(null);
const [notifPermission, setNotifPermission] = useState(
@@ -36,20 +36,8 @@ export default function ProfileModal({ onClose }) {
const [hideAdminTag, setHideAdminTag] = useState(!!user?.hide_admin_tag);
const [allowDm, setAllowDm] = useState(user?.allow_dm !== 0);
- // Minor age protection state
- const [loginType, setLoginType] = useState('all_ages');
- const [guardiansGroupId,setGuardiansGroupId] = useState(null);
- const [showAddChild, setShowAddChild] = useState(false);
- const [aliases, setAliases] = useState([]);
- // Add Child form state
- const [childList, setChildList] = useState([]); // pending aliases to add
- const [childForm, setChildForm] = useState({ firstName:'', lastName:'', email:'', dob:'', phone:'' });
- const [childFormAvatar, setChildFormAvatar] = useState(null);
- const [childSaving, setChildSaving] = useState(false);
- // Mixed Age: minor user search
- const [minorSearch, setMinorSearch] = useState('');
- const [minorResults, setMinorResults] = useState([]);
- const [selectedMinor, setSelectedMinor] = useState(null);
+ // Minor age protection — DOB/phone display only
+ const [loginType, setLoginType] = useState('all_ages');
const savedScale = parseFloat(localStorage.getItem(LS_FONT_KEY));
const [fontScale, setFontScale] = useState(
@@ -62,32 +50,13 @@ export default function ProfileModal({ onClose }) {
return () => window.removeEventListener('resize', onResize);
}, []);
- // Load login type + check if user is in guardians group
+ // Load login type for DOB/phone field visibility
useEffect(() => {
- Promise.all([api.getSettings(), api.getMyUserGroups()]).then(([{ settings: s }, { userGroups }]) => {
- const lt = s.feature_login_type || 'all_ages';
- const gid = parseInt(s.feature_guardians_group_id);
- setLoginType(lt);
- setGuardiansGroupId(gid || null);
- if (lt === 'guardian_only') {
- // In guardian_only mode all authenticated users are guardians — always show Add Child
- setShowAddChild(true);
- } else if (lt === 'mixed_age' && gid) {
- const inGroup = (userGroups || []).some(g => g.id === gid);
- setShowAddChild(inGroup);
- }
+ api.getSettings().then(({ settings: s }) => {
+ setLoginType(s.feature_login_type || 'all_ages');
}).catch(() => {});
- api.getAliases().then(({ aliases }) => setAliases(aliases || [])).catch(() => {});
}, []);
- useEffect(() => {
- if (loginType === 'mixed_age' && minorSearch.length >= 1) {
- api.searchMinorUsers(minorSearch).then(({ users }) => setMinorResults(users || [])).catch(() => {});
- } else {
- setMinorResults([]);
- }
- }, [minorSearch, loginType]);
-
const applyFontScale = (val) => {
setFontScale(val);
document.documentElement.style.setProperty('--font-scale', val);
@@ -109,46 +78,6 @@ export default function ProfileModal({ onClose }) {
}
};
- const handleSaveChildren = async () => {
- if (childList.length === 0) return;
- setChildSaving(true);
- try {
- if (loginType === 'mixed_age') {
- // Link each selected minor
- for (const minor of childList) {
- await api.linkMinor(minor.id);
- }
- toast('Guardian link request sent — awaiting manager approval', 'success');
- } else {
- // Create aliases
- for (const child of childList) {
- const { alias } = await api.createAlias({ firstName: child.firstName, lastName: child.lastName, email: child.email, dateOfBirth: child.dob, phone: child.phone });
- if (child.avatarFile) {
- await api.uploadAliasAvatar(alias.id, child.avatarFile);
- }
- }
- toast('Children saved', 'success');
- const { aliases: fresh } = await api.getAliases();
- setAliases(fresh || []);
- }
- setChildList([]);
- setChildForm({ firstName:'', lastName:'', email:'', dob:'', phone:'' });
- setSelectedMinor(null);
- } catch (e) {
- toast(e.message, 'error');
- } finally {
- setChildSaving(false);
- }
- };
-
- const handleRemoveAlias = async (aliasId) => {
- try {
- await api.deleteAlias(aliasId);
- setAliases(prev => prev.filter(a => a.id !== aliasId));
- toast('Child removed', 'success');
- } catch (e) { toast(e.message, 'error'); }
- };
-
const handleAvatarUpload = async (e) => {
const file = e.target.files?.[0];
if (!file) return;
@@ -219,7 +148,6 @@ export default function ProfileModal({ onClose }) {
- {showAddChild && }
@@ -447,122 +375,6 @@ export default function ProfileModal({ onClose }) {
)}
- {tab === 'add-child' && (
-
- {/* Existing saved aliases */}
- {aliases.length > 0 && (
-
-
Saved Children
-
- {aliases.map((a, i) => (
-
- {a.first_name} {a.last_name}
- {a.date_of_birth && {a.date_of_birth.slice(0,10)}}
-
-
- ))}
-
-
- )}
-
- {loginType === 'guardian_only' ? (
- <>
-
Add a Child
-
-
- {childList.length > 0 && (
-
- {childList.map((c, i) => (
-
- {c.firstName} {c.lastName}
- Pending save
-
-
- ))}
-
- )}
- >
- ) : loginType === 'mixed_age' ? (
- <>
-
Link a Child Account
-
Search for a minor user account to link to your guardian profile. The link requires manager approval.
-
setMinorSearch(e.target.value)} autoComplete="off" />
- {minorResults.length > 0 && (
-
- {minorResults.map(u => (
-
{ setSelectedMinor(u); setMinorSearch(''); setMinorResults([]); }}>
- {u.first_name} {u.last_name}
- {u.date_of_birth && {u.date_of_birth.slice(0,10)}}
-
- ))}
-
- )}
- {selectedMinor && (
-
-
{selectedMinor.first_name} {selectedMinor.last_name}
- {selectedMinor.date_of_birth &&
{selectedMinor.date_of_birth.slice(0,10)}
}
-
-
-
-
-
- )}
- {childList.length > 0 && (
-
- {childList.map((c, i) => (
-
- {c.first_name || c.firstName} {c.last_name || c.lastName}
- Pending approval
-
-
- ))}
-
- )}
- >
- ) : null}
-
-
-
- )}
-
{tab === 'appearance' && (
diff --git a/frontend/src/pages/Chat.jsx b/frontend/src/pages/Chat.jsx
index b0adb27..176faf2 100644
--- a/frontend/src/pages/Chat.jsx
+++ b/frontend/src/pages/Chat.jsx
@@ -16,6 +16,7 @@ import GlobalBar from '../components/GlobalBar.jsx';
import AboutModal from '../components/AboutModal.jsx';
import HelpModal from '../components/HelpModal.jsx';
import NavDrawer from '../components/NavDrawer.jsx';
+import AddChildAliasModal from '../components/AddChildAliasModal.jsx';
import SchedulePage from '../components/SchedulePage.jsx';
import MobileGroupManager from '../components/MobileGroupManager.jsx';
import './Chat.css';
@@ -48,6 +49,9 @@ export default function Chat() {
const [drawerOpen, setDrawerOpen] = useState(false);
const [features, setFeatures] = useState({ branding: false, groupManager: false, scheduleManager: false, appType: 'RosterChirp-Chat', teamToolManagers: [], isHostDomain: false, msgPublic: true, msgGroup: true, msgPrivateGroup: true, msgU2U: true });
const [helpDismissed, setHelpDismissed] = useState(true); // true until status loaded
+ const [addChildPending, setAddChildPending] = useState(false); // defer add-child popup until help closes
+ const addChildCheckedRef = useRef(false); // only auto-check aliases once per session
+ const modalRef = useRef(null); // always reflects current modal value in async callbacks
const [isMobile, setIsMobile] = useState(window.innerWidth < 768);
const [showSidebar, setShowSidebar] = useState(true);
@@ -80,28 +84,31 @@ export default function Chat() {
// Keep groupsRef in sync so visibility/reconnect handlers can read current groups
useEffect(() => { groupsRef.current = groups; }, [groups]);
- // Load feature flags + current user's group memberships on mount
+ // Load feature flags + current user's group memberships on mount (combined for consistent inGuardiansGroup)
const loadFeatures = useCallback(() => {
- api.getSettings().then(({ settings }) => {
- setFeatures(prev => ({
- ...prev,
- branding: settings.feature_branding === 'true',
- groupManager: settings.feature_group_manager === 'true',
- scheduleManager: settings.feature_schedule_manager === 'true',
- appType: settings.app_type || 'RosterChirp-Chat',
- teamToolManagers: JSON.parse(settings.team_tool_managers || settings.team_group_managers || '[]'),
- isHostDomain: settings.is_host_domain === 'true',
- msgPublic: settings.feature_msg_public !== 'false',
- msgGroup: settings.feature_msg_group !== 'false',
- msgPrivateGroup: settings.feature_msg_private_group !== 'false',
- msgU2U: settings.feature_msg_u2u !== 'false',
- loginType: settings.feature_login_type || 'all_ages',
- playersGroupId: settings.feature_players_group_id ? parseInt(settings.feature_players_group_id) : null,
- }));
- }).catch(() => {});
- api.getMyUserGroups().then(({ userGroups }) => {
- setFeatures(prev => ({ ...prev, userGroupMemberships: (userGroups || []).map(g => g.id) }));
- }).catch(() => {});
+ Promise.all([api.getSettings(), api.getMyUserGroups()])
+ .then(([{ settings: s }, { userGroups }]) => {
+ const memberships = (userGroups || []).map(g => g.id);
+ const guardiansGroupId = s.feature_guardians_group_id ? parseInt(s.feature_guardians_group_id) : null;
+ setFeatures(prev => ({
+ ...prev,
+ branding: s.feature_branding === 'true',
+ groupManager: s.feature_group_manager === 'true',
+ scheduleManager: s.feature_schedule_manager === 'true',
+ appType: s.app_type || 'RosterChirp-Chat',
+ teamToolManagers: JSON.parse(s.team_tool_managers || s.team_group_managers || '[]'),
+ isHostDomain: s.is_host_domain === 'true',
+ msgPublic: s.feature_msg_public !== 'false',
+ msgGroup: s.feature_msg_group !== 'false',
+ msgPrivateGroup: s.feature_msg_private_group !== 'false',
+ msgU2U: s.feature_msg_u2u !== 'false',
+ loginType: s.feature_login_type || 'all_ages',
+ playersGroupId: s.feature_players_group_id ? parseInt(s.feature_players_group_id) : null,
+ guardiansGroupId,
+ userGroupMemberships: memberships,
+ inGuardiansGroup: guardiansGroupId ? memberships.includes(guardiansGroupId) : false,
+ }));
+ }).catch(() => {});
}, []);
useEffect(() => {
@@ -110,6 +117,35 @@ export default function Chat() {
return () => window.removeEventListener('rosterchirp:settings-changed', loadFeatures);
}, [loadFeatures]);
+ // Keep modalRef in sync so async callbacks can read current modal without stale closure
+ useEffect(() => { modalRef.current = modal; }, [modal]);
+
+ // Auto-popup Add Child Alias modal when guardian_only user has no aliases yet
+ useEffect(() => {
+ if (addChildCheckedRef.current) return;
+ if (features.loginType !== 'guardian_only' || !features.inGuardiansGroup) return;
+ addChildCheckedRef.current = true;
+ api.getAliases().then(({ aliases }) => {
+ if (!(aliases || []).length) {
+ if (modalRef.current === 'help') {
+ setAddChildPending(true); // defer until help closes
+ } else if (!modalRef.current) {
+ setModal('addchild');
+ }
+ }
+ }).catch(() => {});
+ }, [features.loginType, features.inGuardiansGroup]);
+
+ // Close help — open deferred add-child popup if pending
+ const handleHelpClose = useCallback(() => {
+ if (addChildPending) {
+ setAddChildPending(false);
+ setModal('addchild');
+ } else {
+ setModal(null);
+ }
+ }, [addChildPending]);
+
// Register / refresh push subscription — FCM for Android/Chrome, Web Push for iOS
useEffect(() => {
if (!('serviceWorker' in navigator)) return;
@@ -601,12 +637,14 @@ export default function Chat() {
onSettings={() => { setDrawerOpen(false); setModal('settings'); }}
onUsers={() => { setDrawerOpen(false); setActiveGroupId(null); setChatHasText(false); setPage('users'); }}
onHostPanel={() => { setDrawerOpen(false); setActiveGroupId(null); setChatHasText(false); setPage('hostpanel'); }}
+ onAddChild={() => { setDrawerOpen(false); setModal('addchild'); }}
features={features} currentPage={page} isMobile={isMobile}
unreadMessages={hasUnreadChat} unreadGroupMessages={hasUnreadGroupMessages} />
{modal === 'profile' &&
setModal(null)} />}
{modal === 'settings' && setModal(null)} onFeaturesChanged={setFeatures} />}
{modal === 'branding' && setModal(null)} />}
- {modal === 'help' && setModal(null)} dismissed={helpDismissed} />}
+ {modal === 'help' && }
+ {modal === 'addchild' && setModal(null)} />}
{modal === 'about' && setModal(null)} />}
@@ -630,12 +668,14 @@ export default function Chat() {
onSettings={() => { setDrawerOpen(false); setModal('settings'); }}
onUsers={() => { setDrawerOpen(false); setActiveGroupId(null); setChatHasText(false); setPage('users'); }}
onHostPanel={() => { setDrawerOpen(false); setActiveGroupId(null); setChatHasText(false); setPage('hostpanel'); }}
+ onAddChild={() => { setDrawerOpen(false); setModal('addchild'); }}
features={features} currentPage={page} isMobile={isMobile}
unreadMessages={hasUnreadChat} unreadGroupMessages={hasUnreadGroupMessages} />
{modal === 'profile' &&
setModal(null)} />}
{modal === 'settings' && setModal(null)} onFeaturesChanged={setFeatures} />}
{modal === 'branding' && setModal(null)} />}
- {modal === 'help' && setModal(null)} dismissed={helpDismissed} />}
+ {modal === 'help' && }
+ {modal === 'addchild' && setModal(null)} />}
{modal === 'about' && setModal(null)} />}
@@ -689,12 +729,14 @@ export default function Chat() {
onSettings={() => { setDrawerOpen(false); setModal('settings'); }}
onUsers={() => { setDrawerOpen(false); setActiveGroupId(null); setChatHasText(false); setPage('users'); }}
onHostPanel={() => { setDrawerOpen(false); setActiveGroupId(null); setChatHasText(false); setPage('hostpanel'); }}
+ onAddChild={() => { setDrawerOpen(false); setModal('addchild'); }}
features={features} currentPage={page} isMobile={isMobile}
unreadMessages={hasUnreadChat} unreadGroupMessages={hasUnreadGroupMessages} />
{modal === 'profile' && setModal(null)} />}
{modal === 'settings' && setModal(null)} onFeaturesChanged={setFeatures} />}
{modal === 'branding' && setModal(null)} />}
- {modal === 'help' && setModal(null)} dismissed={helpDismissed} />}
+ {modal === 'help' && }
+ {modal === 'addchild' && setModal(null)} />}
{modal === 'about' && setModal(null)} />}
{modal === 'newchat' && setModal(null)} onCreated={(g) => { loadGroups(); setModal(null); setActiveGroupId(g.id); setPage('chat'); }} />}
@@ -721,6 +763,7 @@ export default function Chat() {
onSettings={() => { setDrawerOpen(false); setModal('settings'); }}
onUsers={() => { setDrawerOpen(false); setActiveGroupId(null); setChatHasText(false); setPage('users'); }}
onHostPanel={() => { setDrawerOpen(false); setActiveGroupId(null); setChatHasText(false); setPage('hostpanel'); }}
+ onAddChild={() => { setDrawerOpen(false); setModal('addchild'); }}
features={features}
currentPage={page}
isMobile={isMobile}
@@ -728,7 +771,8 @@ export default function Chat() {
{modal === 'profile' && setModal(null)} />}
{modal === 'settings' && setModal(null)} onFeaturesChanged={setFeatures} />}
{modal === 'branding' && setModal(null)} />}
- {modal === 'help' && setModal(null)} dismissed={helpDismissed} />}
+ {modal === 'help' && }
+ {modal === 'addchild' && setModal(null)} />}
{modal === 'about' && setModal(null)} />}
@@ -760,6 +804,7 @@ export default function Chat() {
onSettings={() => { setDrawerOpen(false); setModal('settings'); }}
onUsers={() => { setDrawerOpen(false); setActiveGroupId(null); setChatHasText(false); setPage('users'); }}
onHostPanel={() => { setDrawerOpen(false); setActiveGroupId(null); setChatHasText(false); setPage('hostpanel'); }}
+ onAddChild={() => { setDrawerOpen(false); setModal('addchild'); }}
features={features}
currentPage={page}
isMobile={isMobile}
@@ -774,7 +819,8 @@ export default function Chat() {
)}
{modal === 'about' && setModal(null)} />}
- {modal === 'help' && setModal(null)} dismissed={helpDismissed} />}
+ {modal === 'help' && }
+ {modal === 'addchild' && setModal(null)} />}
);
@@ -842,7 +888,8 @@ export default function Chat() {
{modal === 'newchat' && setModal(null)} onCreated={(g) => { loadGroups(); setModal(null); setActiveGroupId(g.id); }} />}
{modal === 'about' && setModal(null)} />}
- {modal === 'help' && setModal(null)} dismissed={helpDismissed} />}
+ {modal === 'help' && }
+ {modal === 'addchild' && setModal(null)} />}
);
}