diff --git a/resources/js/admin/mobile/BrandingPage.tsx b/resources/js/admin/mobile/BrandingPage.tsx
index d27c248a..7adae97b 100644
--- a/resources/js/admin/mobile/BrandingPage.tsx
+++ b/resources/js/admin/mobile/BrandingPage.tsx
@@ -6,7 +6,7 @@ import { Image as ImageIcon, RefreshCcw, UploadCloud, Trash2, ChevronDown, Save,
import { YStack, XStack } from '@tamagui/stacks';
import { SizableText as Text } from '@tamagui/text';
import { Pressable } from '@tamagui/react-native-web-lite';
-import { Slider } from 'tamagui';
+import { Slider, Tabs } from 'tamagui';
import { MobileShell, HeaderActionButton } from './components/MobileShell';
import { MobileCard, CTAButton, SkeletonCard } from './components/Primitives';
import { MobileColorInput, MobileField, MobileFileInput, MobileInput, MobileSelect, MobileTextArea } from './components/FormControls';
@@ -637,15 +637,28 @@ export default function MobileBrandingPage() {
-
-
- setActiveTab('branding')} />
- setActiveTab('watermark')} />
-
-
+ setActiveTab(value as TabKey)}
+ orientation="horizontal"
+ flexDirection="column"
+ >
+
+
+
+
+ {t('events.branding.titleShort', 'Branding')}
+
+
+
+
+ {t('events.watermark.tab', 'Wasserzeichen')}
+
+
+
+
- {activeTab === 'branding' ? (
- <>
+
{t('events.branding.previewTitle', 'Guest App Preview')}
@@ -1216,12 +1229,14 @@ export default function MobileBrandingPage() {
onChange={(value) => setForm((prev) => ({ ...prev, linkColor: value }))}
disabled={brandingDisabled}
/>
-
+
>
- >
- ) : (
- renderWatermarkTab()
- )}
+
+
+
+ {renderWatermarkTab()}
+
+
handleSave()} />
@@ -1744,27 +1759,6 @@ function UpgradeCard({
);
}
-function TabButton({ label, active, onPress }: { label: string; active: boolean; onPress: () => void }) {
- const { primary, surfaceMuted, border, surface, textStrong } = useAdminTheme();
- return (
-
-
-
- {label}
-
-
-
- );
-}
-
function ModeButton({
label,
active,
diff --git a/resources/js/admin/mobile/EventControlRoomPage.tsx b/resources/js/admin/mobile/EventControlRoomPage.tsx
index 091e8325..2a76821c 100644
--- a/resources/js/admin/mobile/EventControlRoomPage.tsx
+++ b/resources/js/admin/mobile/EventControlRoomPage.tsx
@@ -343,7 +343,6 @@ export default function MobileEventControlRoomPage() {
const liveResetRef = React.useRef(false);
const [fallbackAttempted, setFallbackAttempted] = React.useState(false);
const back = useBackNavigation(slug ? adminPath(`/mobile/events/${slug}`) : adminPath('/mobile/events'));
- const activeFilterBg = primary;
const saveControlRoomSettings = React.useCallback(
async (nextSettings: ControlRoomSettings) => {
@@ -1376,60 +1375,52 @@ export default function MobileEventControlRoomPage() {
{t('mobilePhotos.filtersTitle', 'Filter')}
- value && setModerationFilter(value as ModerationFilter)}
+ disableDeactivation
+ orientation="horizontal"
+ flexDirection="row"
alignItems="center"
padding="$1"
- borderRadius={999}
+ gap="$1.5"
+ borderRadius="$pill"
borderWidth={1}
borderColor={border}
backgroundColor={surfaceMuted}
- overflow="hidden"
>
- value && setModerationFilter(value as ModerationFilter)}
- >
-
- {MODERATION_FILTERS.map((option) => {
- const active = option.value === moderationFilter;
- const count = moderationCounts[option.value] ?? 0;
- return (
- {
+ const count = moderationCounts[option.value] ?? 0;
+ return (
+
+
+
+ {t(option.labelKey, option.fallback)}
+
+
-
-
- {t(option.labelKey, option.fallback)}
-
-
-
- {count}
-
-
-
-
- );
- })}
-
-
-
+
+ {count}
+
+
+
+
+ );
+ })}
+
@@ -1564,60 +1555,52 @@ export default function MobileEventControlRoomPage() {
{t('liveShowQueue.filterLabel', 'Live status')}
- value && setLiveStatusFilter(value as LiveShowQueueStatus)}
+ disableDeactivation
+ orientation="horizontal"
+ flexDirection="row"
alignItems="center"
padding="$1"
- borderRadius={999}
+ gap="$1.5"
+ borderRadius="$pill"
borderWidth={1}
borderColor={border}
backgroundColor={surfaceMuted}
- overflow="hidden"
>
- value && setLiveStatusFilter(value as LiveShowQueueStatus)}
- >
-
- {LIVE_STATUS_OPTIONS.map((option) => {
- const active = option.value === liveStatusFilter;
- const count = liveCounts[option.value] ?? 0;
- return (
- {
+ const count = liveCounts[option.value] ?? 0;
+ return (
+
+
+
+ {t(option.labelKey, option.fallback)}
+
+
-
-
- {t(option.labelKey, option.fallback)}
-
-
-
- {count}
-
-
-
-
- );
- })}
-
-
-
+
+ {count}
+
+
+
+
+ );
+ })}
+
diff --git a/resources/js/admin/mobile/EventMembersPage.tsx b/resources/js/admin/mobile/EventMembersPage.tsx
index 4475a163..862c5608 100644
--- a/resources/js/admin/mobile/EventMembersPage.tsx
+++ b/resources/js/admin/mobile/EventMembersPage.tsx
@@ -5,6 +5,7 @@ import { Trash2, Copy, RefreshCcw } from 'lucide-react';
import { YStack, XStack } from '@tamagui/stacks';
import { SizableText as Text } from '@tamagui/text';
import { Pressable } from '@tamagui/react-native-web-lite';
+import { ToggleGroup } from '@tamagui/toggle-group';
import { MobileShell, HeaderActionButton } from './components/MobileShell';
import { MobileCard, CTAButton, PillBadge, SkeletonCard } from './components/Primitives';
import { MobileField, MobileInput, MobileSelect } from './components/FormControls';
@@ -227,51 +228,67 @@ export default function MobileEventMembersPage() {
{t('events.members.filters.statusLabel', 'Status')}
-
- {statusOptions.map((option) => {
- const isActive = statusFilter === option.key;
- return (
- setStatusFilter(option.key)}>
-
-
- {option.label}
-
-
-
- );
- })}
-
+ value && setStatusFilter(value as typeof statusFilter)}
+ disableDeactivation
+ orientation="horizontal"
+ flexDirection="row"
+ flexWrap="wrap"
+ gap="$2"
+ padding="$1"
+ borderRadius="$pill"
+ borderWidth={1}
+ borderColor={border}
+ backgroundColor="$background"
+ >
+ {statusOptions.map((option) => (
+
+
+ {option.label}
+
+
+ ))}
+
{t('events.members.filters.roleLabel', 'Role')}
-
- {roleOptions.map((option) => {
- const isActive = roleFilter === option.key;
- return (
- setRoleFilter(option.key)}>
-
-
- {option.label}
-
-
-
- );
- })}
-
+ value && setRoleFilter(value as typeof roleFilter)}
+ disableDeactivation
+ orientation="horizontal"
+ flexDirection="row"
+ flexWrap="wrap"
+ gap="$2"
+ padding="$1"
+ borderRadius="$pill"
+ borderWidth={1}
+ borderColor={border}
+ backgroundColor="$background"
+ >
+ {roleOptions.map((option) => (
+
+
+ {option.label}
+
+
+ ))}
+
) : null}
diff --git a/resources/js/admin/mobile/EventRecapPage.tsx b/resources/js/admin/mobile/EventRecapPage.tsx
index 8b0d6698..cf3c3124 100644
--- a/resources/js/admin/mobile/EventRecapPage.tsx
+++ b/resources/js/admin/mobile/EventRecapPage.tsx
@@ -4,8 +4,8 @@ import { useTranslation } from 'react-i18next';
import { Share2, Sparkles, Trophy, Users, TrendingUp, Heart, Image as ImageIcon } from 'lucide-react';
import { YStack, XStack } from '@tamagui/stacks';
import { SizableText as Text } from '@tamagui/text';
-import { Pressable } from '@tamagui/react-native-web-lite';
import { Switch } from '@tamagui/switch';
+import { Tabs } from 'tamagui';
import { MobileShell } from './components/MobileShell';
import { MobileCard, CTAButton, PillBadge, SkeletonCard } from './components/Primitives';
import { LegalConsentSheet } from './components/LegalConsentSheet';
@@ -208,25 +208,31 @@ export default function MobileEventRecapPage() {
onBack={back}
>
-
- setActiveTab('overview')}
- />
- setActiveTab('engagement')}
- />
- setActiveTab('compliance')}
- />
-
+ setActiveTab(value as typeof activeTab)}
+ orientation="horizontal"
+ flexDirection="column"
+ >
+
+
+
+ {t('events.recap.tabs.overview', 'Overview')}
+
+
+
+
+ {t('events.recap.tabs.engagement', 'Engagement')}
+
+
+
+
+ {t('events.recap.tabs.compliance', 'Compliance')}
+
+
+
- {activeTab === 'overview' ? (
+
@@ -362,9 +368,9 @@ export default function MobileEventRecapPage() {
- ) : null}
+
- {activeTab === 'engagement' ? (
+
{engagementLoading ? (
@@ -550,13 +556,14 @@ export default function MobileEventRecapPage() {
)}
- ) : null}
+
- {activeTab === 'compliance' ? (
-
-
-
- ) : null}
+
+
+
+
+
+
void }) {
- const { primary, surfaceMuted, border, surface, textStrong } = useAdminTheme();
- return (
-
-
-
- {label}
-
-
-
- );
-}
-
function LeaderboardRow({ rank, name, value }: { rank: number; name: string; value: string }) {
const { textStrong, muted, border, surfaceMuted } = useAdminTheme();
return (
diff --git a/resources/js/admin/mobile/EventTasksPage.tsx b/resources/js/admin/mobile/EventTasksPage.tsx
index c6b50a56..58c10dcf 100644
--- a/resources/js/admin/mobile/EventTasksPage.tsx
+++ b/resources/js/admin/mobile/EventTasksPage.tsx
@@ -1170,7 +1170,6 @@ export default function MobileEventTasksPage() {
borderRadius={16}
borderWidth={1}
borderColor={border}
- backgroundColor={surfaceMuted}
overflow="hidden"
gap="$0"
>
@@ -1186,16 +1185,13 @@ export default function MobileEventTasksPage() {
key={tab.value}
value={tab.value}
flex={1}
- unstyled
paddingVertical="$2.5"
alignItems="center"
justifyContent="center"
- backgroundColor={isActive ? primary : 'transparent'}
borderRightWidth={index === arr.length - 1 ? 0 : 1}
borderRightColor={border}
- pressStyle={{ backgroundColor: isActive ? primary : surface }}
>
-
+
{tab.label}
diff --git a/resources/js/admin/mobile/EventsPage.tsx b/resources/js/admin/mobile/EventsPage.tsx
index c576145a..f039c121 100644
--- a/resources/js/admin/mobile/EventsPage.tsx
+++ b/resources/js/admin/mobile/EventsPage.tsx
@@ -235,9 +235,7 @@ function EventsList({
onEdit?: (slug: string) => void;
}) {
const { t } = useTranslation('management');
- const { text, muted, subtle, border, primary, surface, surfaceMuted, accentSoft, accent, shadow } = useAdminTheme();
- const activeBg = accentSoft;
- const activeBorder = accent;
+ const { text, muted, subtle, border, primary, surface, surfaceMuted, shadow } = useAdminTheme();
const statusCounts = React.useMemo(() => buildEventStatusCounts(events), [events]);
const filteredByStatus = React.useMemo(
@@ -313,56 +311,49 @@ function EventsList({
- value && onStatusChange(value as EventStatusKey)}
+ disableDeactivation
+ orientation="horizontal"
+ flexDirection="row"
alignItems="center"
padding="$1"
- borderRadius={999}
+ gap="$1.5"
+ borderRadius="$pill"
borderWidth={1}
borderColor={border}
backgroundColor={surfaceMuted}
>
- value && onStatusChange(value as EventStatusKey)}
- >
-
- {filters.map((filter) => {
- const active = filter.key === statusFilter;
- return (
-
-
-
- {filter.label}
-
-
-
- {filter.count}
-
-
-
-
- );
- })}
-
-
-
+ {filters.map((filter) => (
+
+
+
+ {filter.label}
+
+
+
+ {filter.count}
+
+
+
+
+ ))}
+
diff --git a/resources/js/admin/mobile/NotificationsPage.tsx b/resources/js/admin/mobile/NotificationsPage.tsx
index 2a38f026..c47a6931 100644
--- a/resources/js/admin/mobile/NotificationsPage.tsx
+++ b/resources/js/admin/mobile/NotificationsPage.tsx
@@ -5,6 +5,7 @@ import { Bell, Check, ChevronRight, RefreshCcw } from 'lucide-react';
import { YStack, XStack } from '@tamagui/stacks';
import { SizableText as Text } from '@tamagui/text';
import { Pressable } from '@tamagui/react-native-web-lite';
+import { ToggleGroup } from '@tamagui/toggle-group';
import { motion, useAnimationControls, type PanInfo } from 'framer-motion';
import { MobileShell, HeaderActionButton } from './components/MobileShell';
import { MobileCard, PillBadge, SkeletonCard, CTAButton } from './components/Primitives';
@@ -326,7 +327,7 @@ export default function MobileNotificationsPage() {
const [events, setEvents] = React.useState([]);
const [showEventPicker, setShowEventPicker] = React.useState(false);
const back = useBackNavigation(adminPath('/mobile/dashboard'));
- const { text, muted, border, warningBg, warningText, infoBg, primary, danger, accentSoft, subtle } = useAdminTheme();
+ const { text, muted, border, warningBg, warningText, infoBg, primary, danger, subtle } = useAdminTheme();
const warningIcon = warningText;
const infoIcon = primary;
const errorText = danger;
@@ -552,7 +553,22 @@ export default function MobileNotificationsPage() {
) : null}
-
+ value && updateFilters({ scope: value as NotificationScope | 'all' })}
+ disableDeactivation
+ orientation="horizontal"
+ flexDirection="row"
+ flexWrap="wrap"
+ gap="$2"
+ padding="$1"
+ borderRadius="$pill"
+ borderWidth={1}
+ borderColor={border}
+ backgroundColor="$background"
+ marginBottom="$2"
+ >
{([
{ key: 'all', label: t('notificationLogs.scope.all', 'All scopes') },
{ key: 'photos', label: t('notificationLogs.scope.photos', 'Photos') },
@@ -561,28 +577,22 @@ export default function MobileNotificationsPage() {
{ key: 'events', label: t('notificationLogs.scope.events', 'Events') },
{ key: 'package', label: t('notificationLogs.scope.package', 'Package') },
{ key: 'general', label: t('notificationLogs.scope.general', 'General') },
- ] as Array<{ key: NotificationScope | 'all'; label: string }>).map((filter) => {
- const active = scopeParam === filter.key;
- return (
- updateFilters({ scope: filter.key })} style={{ flexGrow: 1 }}>
-
-
- {filter.label}
-
-
-
- );
- })}
-
+ ] as Array<{ key: NotificationScope | 'all'; label: string }>).map((filter) => (
+
+
+ {filter.label}
+
+
+ ))}
+
{loading ? (
diff --git a/resources/js/admin/mobile/ProfileAccountPage.tsx b/resources/js/admin/mobile/ProfileAccountPage.tsx
index c4f99b01..253105d3 100644
--- a/resources/js/admin/mobile/ProfileAccountPage.tsx
+++ b/resources/js/admin/mobile/ProfileAccountPage.tsx
@@ -3,7 +3,7 @@ import { useTranslation } from 'react-i18next';
import { CheckCircle2, Lock, MailWarning, User } from 'lucide-react';
import { YStack, XStack } from '@tamagui/stacks';
import { SizableText as Text } from '@tamagui/text';
-import { Pressable } from '@tamagui/react-native-web-lite';
+import { Tabs } from 'tamagui';
import toast from 'react-hot-toast';
import { MobileShell } from './components/MobileShell';
import { MobileCard, CTAButton, PillBadge } from './components/Primitives';
@@ -300,6 +300,275 @@ export default function MobileProfileAccountPage() {
form.password.trim().length > 0 &&
form.passwordConfirmation.trim().length > 0;
const brandingDisabled = brandingLoading || brandingSaving;
+ const brandingContent = (
+ <>
+ {brandingError ? (
+
+
+ {brandingError}
+
+
+ ) : null}
+
+
+
+ {t('profile.branding.title', 'Standard-Branding')}
+
+
+ {t('profile.branding.hint', 'Diese Werte dienen als Vorschlag für neue Events und beschleunigen die Einrichtung.')}
+
+
+
+
+
+ {t('profile.branding.theme', 'Theme')}
+
+
+
+ setBrandingForm((prev) => ({ ...prev, mode: event.target.value as BrandingFormValues['mode'] }))}
+ disabled={brandingDisabled}
+ >
+
+
+
+
+
+
+ setBrandingForm((prev) => ({ ...prev, fontSize: event.target.value as BrandingFormValues['fontSize'] }))}
+ disabled={brandingDisabled}
+ >
+
+
+
+
+
+
+
+
+
+
+ {t('events.branding.colors', 'Colors')}
+
+
+ setBrandingForm((prev) => ({ ...prev, primary: value }))}
+ disabled={brandingDisabled}
+ />
+ setBrandingForm((prev) => ({ ...prev, accent: value }))}
+ disabled={brandingDisabled}
+ />
+ setBrandingForm((prev) => ({ ...prev, background: value }))}
+ disabled={brandingDisabled}
+ />
+ setBrandingForm((prev) => ({ ...prev, surface: value }))}
+ disabled={brandingDisabled}
+ />
+
+
+
+
+
+ {t('events.branding.fonts', 'Fonts')}
+
+
+
+ setBrandingForm((prev) => ({ ...prev, headingFont: event.target.value }))}
+ placeholder={t('events.branding.headingFontPlaceholder', 'Playfair Display')}
+ hasError={false}
+ disabled={brandingDisabled}
+ />
+
+
+ setBrandingForm((prev) => ({ ...prev, bodyFont: event.target.value }))}
+ placeholder={t('events.branding.bodyFontPlaceholder', 'Montserrat')}
+ hasError={false}
+ disabled={brandingDisabled}
+ />
+
+
+
+
+
+ >
+ );
+
+ const accountContent = (
+ <>
+ {error ? (
+
+
+ {error}
+
+
+ ) : null}
+
+
+
+
+
+
+
+
+ {form.name || profile?.email || t('profile.title', 'Profil')}
+
+
+ {form.email || profile?.email || '—'}
+
+
+
+
+ {profile?.email_verified ? (
+
+ ) : (
+
+ )}
+
+ {emailStatusLabel}
+
+
+ {emailHint}
+
+
+
+
+
+
+
+
+ {t('profile.sections.account.heading', 'Account-Informationen')}
+
+
+
+ {t('profile.sections.account.description', 'Passe Anzeigename, E-Mail-Adresse und Sprache der Admin-Oberfläche an.')}
+
+ {loading ? (
+
+ {t('profile.loading', 'Lädt ...')}
+
+ ) : (
+
+
+ setForm((prev) => ({ ...prev, name: event.target.value }))}
+ placeholder={t('profile.placeholders.name', 'z. B. Hochzeitsplanung Schmidt')}
+ hasError={false}
+ />
+
+
+ setForm((prev) => ({ ...prev, email: event.target.value }))}
+ placeholder="mail@beispiel.de"
+ type="email"
+ hasError={false}
+ />
+
+
+ setForm((prev) => ({ ...prev, preferredLocale: event.target.value }))}
+ >
+ {LOCALE_OPTIONS.map((option) => (
+
+ ))}
+
+
+
+
+ )}
+
+
+
+
+
+
+ {t('profile.sections.password.heading', 'Passwort ändern')}
+
+
+
+ {t('profile.sections.password.description', 'Wähle ein sicheres Passwort, um deinen Admin-Zugang zu schützen.')}
+
+
+
+ setForm((prev) => ({ ...prev, currentPassword: event.target.value }))}
+ placeholder="••••••••"
+ type="password"
+ hasError={false}
+ />
+
+
+ setForm((prev) => ({ ...prev, password: event.target.value }))}
+ placeholder="••••••••"
+ type="password"
+ hasError={false}
+ />
+
+
+ setForm((prev) => ({ ...prev, passwordConfirmation: event.target.value }))}
+ placeholder="••••••••"
+ type="password"
+ hasError={false}
+ />
+
+
+
+
+ >
+ );
return (
{brandingTabEnabled ? (
-
- setActiveTab('account')}
- />
- setActiveTab('branding')}
- />
-
- ) : null}
-
- {activeTab === 'branding' && brandingTabEnabled ? (
- <>
- {brandingError ? (
-
-
- {brandingError}
+ setActiveTab(value as TabKey)}
+ orientation="horizontal"
+ flexDirection="column"
+ >
+
+
+
+ {t('profile.tabs.account', 'Account')}
-
- ) : null}
-
-
-
- {t('profile.branding.title', 'Standard-Branding')}
-
-
- {t('profile.branding.hint', 'Diese Werte dienen als Vorschlag für neue Events und beschleunigen die Einrichtung.')}
-
-
-
-
-
- {t('profile.branding.theme', 'Theme')}
-
-
-
- setBrandingForm((prev) => ({ ...prev, mode: event.target.value as BrandingFormValues['mode'] }))}
- disabled={brandingDisabled}
- >
-
-
-
-
-
-
- setBrandingForm((prev) => ({ ...prev, fontSize: event.target.value as BrandingFormValues['fontSize'] }))}
- disabled={brandingDisabled}
- >
-
-
-
-
-
-
-
-
-
-
- {t('events.branding.colors', 'Colors')}
-
-
- setBrandingForm((prev) => ({ ...prev, primary: value }))}
- disabled={brandingDisabled}
- />
- setBrandingForm((prev) => ({ ...prev, accent: value }))}
- disabled={brandingDisabled}
- />
- setBrandingForm((prev) => ({ ...prev, background: value }))}
- disabled={brandingDisabled}
- />
- setBrandingForm((prev) => ({ ...prev, surface: value }))}
- disabled={brandingDisabled}
- />
-
-
-
-
-
- {t('events.branding.fonts', 'Fonts')}
-
-
-
- setBrandingForm((prev) => ({ ...prev, headingFont: event.target.value }))}
- placeholder={t('events.branding.headingFontPlaceholder', 'Playfair Display')}
- hasError={false}
- disabled={brandingDisabled}
- />
-
-
- setBrandingForm((prev) => ({ ...prev, bodyFont: event.target.value }))}
- placeholder={t('events.branding.bodyFontPlaceholder', 'Montserrat')}
- hasError={false}
- disabled={brandingDisabled}
- />
-
-
-
-
-
- >
+
+
+
+ {t('profile.tabs.branding', 'Standard-Branding')}
+
+
+
+
+ {accountContent}
+
+
+ {brandingContent}
+
+
) : (
- <>
- {error ? (
-
-
- {error}
-
-
- ) : null}
-
-
-
-
-
-
-
-
- {form.name || profile?.email || t('profile.title', 'Profil')}
-
-
- {form.email || profile?.email || '—'}
-
-
-
-
- {profile?.email_verified ? (
-
- ) : (
-
- )}
-
- {emailStatusLabel}
-
-
- {emailHint}
-
-
-
-
-
-
-
-
- {t('profile.sections.account.heading', 'Account-Informationen')}
-
-
-
- {t('profile.sections.account.description', 'Passe Anzeigename, E-Mail-Adresse und Sprache der Admin-Oberfläche an.')}
-
- {loading ? (
-
- {t('profile.loading', 'Lädt ...')}
-
- ) : (
-
-
- setForm((prev) => ({ ...prev, name: event.target.value }))}
- placeholder={t('profile.placeholders.name', 'z. B. Hochzeitsplanung Schmidt')}
- hasError={false}
- />
-
-
- setForm((prev) => ({ ...prev, email: event.target.value }))}
- placeholder="mail@beispiel.de"
- type="email"
- hasError={false}
- />
-
-
- setForm((prev) => ({ ...prev, preferredLocale: event.target.value }))}
- >
- {LOCALE_OPTIONS.map((option) => (
-
- ))}
-
-
-
-
- )}
-
-
-
-
-
-
- {t('profile.sections.password.heading', 'Passwort ändern')}
-
-
-
- {t('profile.sections.password.description', 'Wähle ein sicheres Passwort, um deinen Admin-Zugang zu schützen.')}
-
-
-
- setForm((prev) => ({ ...prev, currentPassword: event.target.value }))}
- placeholder="••••••••"
- type="password"
- hasError={false}
- />
-
-
- setForm((prev) => ({ ...prev, password: event.target.value }))}
- placeholder="••••••••"
- type="password"
- hasError={false}
- />
-
-
- setForm((prev) => ({ ...prev, passwordConfirmation: event.target.value }))}
- placeholder="••••••••"
- type="password"
- hasError={false}
- />
-
-
-
-
- >
+ accountContent
)}
);
}
-function TabButton({ label, active, onPress }: { label: string; active: boolean; onPress: () => void }) {
- const { primary, surfaceMuted, border, surface, text } = useAdminTheme();
- return (
-
-
-
- {label}
-
-
-
- );
-}
-
function ColorField({
label,
value,
diff --git a/resources/js/admin/mobile/__tests__/BrandingPage.test.tsx b/resources/js/admin/mobile/__tests__/BrandingPage.test.tsx
index 9eea65d1..93cececa 100644
--- a/resources/js/admin/mobile/__tests__/BrandingPage.test.tsx
+++ b/resources/js/admin/mobile/__tests__/BrandingPage.test.tsx
@@ -50,6 +50,7 @@ vi.mock('../components/FormControls', () => ({
MobileColorInput: (props: React.InputHTMLAttributes) => ,
MobileFileInput: (props: React.InputHTMLAttributes) => ,
MobileInput: (props: React.InputHTMLAttributes) => ,
+ MobileTextArea: (props: React.TextareaHTMLAttributes) => ,
MobileSelect: ({ children, ...props }: React.SelectHTMLAttributes) => (
),
@@ -111,16 +112,53 @@ vi.mock('@tamagui/react-native-web-lite', () => ({
),
}));
-vi.mock('tamagui', () => ({
- Slider: Object.assign(
- ({ children }: { children: React.ReactNode }) => {children}
,
- {
- Track: ({ children }: { children: React.ReactNode }) => {children}
,
- TrackActive: ({ children }: { children?: React.ReactNode }) => {children}
,
- Thumb: () => ,
- }
- ),
-}));
+vi.mock('tamagui', () => {
+ const TabsValueContext = React.createContext(null);
+ const TabsSetterContext = React.createContext<((value: string) => void) | null>(null);
+ const TabsRoot = ({
+ children,
+ value,
+ onValueChange,
+ }: {
+ children: React.ReactNode;
+ value?: string;
+ onValueChange?: (value: string) => void;
+ }) => (
+
+ {children}
+
+ );
+ const Tabs = Object.assign(TabsRoot, {
+ List: ({ children }: { children: React.ReactNode }) => {children}
,
+ Tab: ({ children, value }: { children: React.ReactNode; value: string }) => {
+ const setValue = React.useContext(TabsSetterContext);
+ return (
+
+ );
+ },
+ Content: ({ children, value }: { children: React.ReactNode; value: string }) => {
+ const active = React.useContext(TabsValueContext);
+ if (active !== value) {
+ return null;
+ }
+ return {children}
;
+ },
+ });
+
+ return {
+ Slider: Object.assign(
+ ({ children }: { children: React.ReactNode }) => {children}
,
+ {
+ Track: ({ children }: { children: React.ReactNode }) => {children}
,
+ TrackActive: ({ children }: { children?: React.ReactNode }) => {children}
,
+ Thumb: () => ,
+ }
+ ),
+ Tabs,
+ };
+});
const getEventMock = vi.fn();
const updateEventMock = vi.fn();
diff --git a/resources/js/admin/mobile/__tests__/EventRecapPage.test.tsx b/resources/js/admin/mobile/__tests__/EventRecapPage.test.tsx
index e73fb5c5..bd9df931 100644
--- a/resources/js/admin/mobile/__tests__/EventRecapPage.test.tsx
+++ b/resources/js/admin/mobile/__tests__/EventRecapPage.test.tsx
@@ -113,6 +113,14 @@ vi.mock('@tamagui/text', () => ({
SizableText: ({ children }: { children: React.ReactNode }) => {children},
}));
+vi.mock('tamagui', () => ({
+ Tabs: Object.assign(({ children }: { children: React.ReactNode }) => {children}
, {
+ List: ({ children }: { children: React.ReactNode }) => {children}
,
+ Tab: ({ children }: { children: React.ReactNode }) => ,
+ Content: ({ children }: { children: React.ReactNode }) => {children}
,
+ }),
+}));
+
vi.mock('@tamagui/react-native-web-lite', () => ({
Pressable: ({ children, onPress }: { children: React.ReactNode; onPress?: () => void }) => (