onboarding tracking is now wired, the tour can be replayed from Settings, install‑banner reset is included, and empty states in Tasks/Members/Guest Messages now have guided CTAs.
What changed:
- Onboarding tracking: admin_app_opened on first authenticated dashboard load; event_created, branding_configured,
and invite_created on their respective actions.
- Tour replay: Settings now has an “Experience” section to replay the tour (clears tour seen flag and opens via ?tour=1).
- Empty states: Tasks, Members, and Guest Messages now include richer copy + quick actions.
- New helpers + copy: Tour storage helpers, new translations, and related UI wiring.
This commit is contained in:
@@ -8,7 +8,7 @@ import { Pressable } from '@tamagui/react-native-web-lite';
|
||||
import { MobileShell, HeaderActionButton } from './components/MobileShell';
|
||||
import { MobileCard, CTAButton, PillBadge, SkeletonCard } from './components/Primitives';
|
||||
import { MobileField, MobileInput, MobileSelect } from './components/FormControls';
|
||||
import { EventMember, getEventMembers, inviteEventMember, removeEventMember } from '../api';
|
||||
import { EventMember, getEventMembers, inviteEventMember, removeEventMember, trackOnboarding } from '../api';
|
||||
import { isAuthError } from '../auth/tokens';
|
||||
import { getApiErrorMessage } from '../lib/apiError';
|
||||
import toast from 'react-hot-toast';
|
||||
@@ -26,6 +26,7 @@ export default function MobileEventMembersPage() {
|
||||
const [invite, setInvite] = React.useState({ name: '', email: '', role: 'member' as EventMember['role'] });
|
||||
const [saving, setSaving] = React.useState(false);
|
||||
const [inviteLink, setInviteLink] = React.useState<string | null>(null);
|
||||
const emailInputRef = React.useRef<HTMLInputElement | null>(null);
|
||||
const [search, setSearch] = React.useState('');
|
||||
const [confirmRemove, setConfirmRemove] = React.useState<EventMember | null>(null);
|
||||
|
||||
@@ -67,6 +68,7 @@ export default function MobileEventMembersPage() {
|
||||
});
|
||||
setMembers((prev) => [member, ...prev]);
|
||||
setInvite({ name: '', email: '', role: 'member' });
|
||||
void trackOnboarding('invite_created', { event_slug: slug });
|
||||
toast.success(t('events.members.inviteSuccess', 'Einladung gesendet'));
|
||||
} catch (err) {
|
||||
if (!isAuthError(err)) {
|
||||
@@ -130,6 +132,7 @@ export default function MobileEventMembersPage() {
|
||||
value={invite.email}
|
||||
onChange={(e) => setInvite((prev) => ({ ...prev, email: e.target.value }))}
|
||||
placeholder="alex@example.com"
|
||||
ref={emailInputRef}
|
||||
/>
|
||||
</MobileField>
|
||||
<MobileField label={t('events.members.role', 'Role')}>
|
||||
@@ -188,9 +191,19 @@ export default function MobileEventMembersPage() {
|
||||
))}
|
||||
</YStack>
|
||||
) : members.length === 0 ? (
|
||||
<Text fontSize="$sm" color="#4b5563">
|
||||
{t('events.members.empty', 'Noch keine Einladungen.')}
|
||||
</Text>
|
||||
<YStack space="$2">
|
||||
<Text fontSize="$sm" fontWeight="700" color="#111827">
|
||||
{t('events.members.emptyTitle', 'Invite your team')}
|
||||
</Text>
|
||||
<Text fontSize="$xs" color="#4b5563">
|
||||
{t('events.members.emptyBody', 'Send the first invite so helpers can access the event.')}
|
||||
</Text>
|
||||
<CTAButton
|
||||
label={t('events.members.emptyAction', 'Send first invite')}
|
||||
onPress={() => emailInputRef.current?.focus()}
|
||||
fullWidth={false}
|
||||
/>
|
||||
</YStack>
|
||||
) : (
|
||||
<YStack space="$2">
|
||||
{members
|
||||
|
||||
Reference in New Issue
Block a user