Add photobooth connect code UI
This commit is contained in:
@@ -218,6 +218,11 @@ export type PhotoboothStatus = {
|
|||||||
metrics?: PhotoboothStatusMetrics | null;
|
metrics?: PhotoboothStatusMetrics | null;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
export type PhotoboothConnectCode = {
|
||||||
|
code: string;
|
||||||
|
expires_at: string | null;
|
||||||
|
};
|
||||||
|
|
||||||
export type EventAddonCheckout = {
|
export type EventAddonCheckout = {
|
||||||
addon_key: string;
|
addon_key: string;
|
||||||
quantity?: number;
|
quantity?: number;
|
||||||
@@ -2041,6 +2046,28 @@ export async function disableEventPhotobooth(slug: string, options?: { mode?: 'f
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export async function createEventPhotoboothConnectCode(
|
||||||
|
slug: string,
|
||||||
|
options?: { expires_in_minutes?: number }
|
||||||
|
): Promise<PhotoboothConnectCode> {
|
||||||
|
const body = options ? JSON.stringify(options) : undefined;
|
||||||
|
const headers = body ? { 'Content-Type': 'application/json' } : undefined;
|
||||||
|
|
||||||
|
const response = await authorizedFetch(`${photoboothEndpoint(slug)}/connect-codes`, {
|
||||||
|
method: 'POST',
|
||||||
|
body,
|
||||||
|
headers,
|
||||||
|
});
|
||||||
|
|
||||||
|
const data = await jsonOrThrow<{ data?: JsonValue }>(response, 'Failed to create photobooth connect code');
|
||||||
|
const record = (data.data ?? {}) as Record<string, JsonValue>;
|
||||||
|
|
||||||
|
return {
|
||||||
|
code: typeof record.code === 'string' ? record.code : '',
|
||||||
|
expires_at: typeof record.expires_at === 'string' ? record.expires_at : null,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
export async function submitTenantFeedback(payload: {
|
export async function submitTenantFeedback(payload: {
|
||||||
category: string;
|
category: string;
|
||||||
sentiment?: 'positive' | 'neutral' | 'negative';
|
sentiment?: 'positive' | 'neutral' | 'negative';
|
||||||
|
|||||||
@@ -1185,6 +1185,18 @@
|
|||||||
"postUrl": "Upload-URL",
|
"postUrl": "Upload-URL",
|
||||||
"responseFormat": "Antwort-Format"
|
"responseFormat": "Antwort-Format"
|
||||||
},
|
},
|
||||||
|
"connectCode": {
|
||||||
|
"label": "Verbindungscode",
|
||||||
|
"description": "Erstelle einen 6-stelligen Code für die Uploader-App.",
|
||||||
|
"expires": "Läuft ab: {{date}}",
|
||||||
|
"actions": {
|
||||||
|
"generate": "Verbindungscode erstellen",
|
||||||
|
"generated": "Verbindungscode erstellt"
|
||||||
|
},
|
||||||
|
"errors": {
|
||||||
|
"failed": "Verbindungscode konnte nicht erstellt werden."
|
||||||
|
}
|
||||||
|
},
|
||||||
"actions": {
|
"actions": {
|
||||||
"enable": "Photobooth aktivieren",
|
"enable": "Photobooth aktivieren",
|
||||||
"disable": "Deaktivieren",
|
"disable": "Deaktivieren",
|
||||||
|
|||||||
@@ -898,6 +898,18 @@
|
|||||||
"postUrl": "Upload URL",
|
"postUrl": "Upload URL",
|
||||||
"responseFormat": "Response format"
|
"responseFormat": "Response format"
|
||||||
},
|
},
|
||||||
|
"connectCode": {
|
||||||
|
"label": "Connect code",
|
||||||
|
"description": "Create a 6-digit code for the uploader app.",
|
||||||
|
"expires": "Expires: {{date}}",
|
||||||
|
"actions": {
|
||||||
|
"generate": "Generate connect code",
|
||||||
|
"generated": "Connect code created"
|
||||||
|
},
|
||||||
|
"errors": {
|
||||||
|
"failed": "Connect code could not be created."
|
||||||
|
}
|
||||||
|
},
|
||||||
"actions": {
|
"actions": {
|
||||||
"enable": "Activate photobooth",
|
"enable": "Activate photobooth",
|
||||||
"disable": "Disable",
|
"disable": "Disable",
|
||||||
|
|||||||
@@ -14,6 +14,7 @@ import {
|
|||||||
enableEventPhotobooth,
|
enableEventPhotobooth,
|
||||||
disableEventPhotobooth,
|
disableEventPhotobooth,
|
||||||
rotateEventPhotobooth,
|
rotateEventPhotobooth,
|
||||||
|
createEventPhotoboothConnectCode,
|
||||||
PhotoboothStatus,
|
PhotoboothStatus,
|
||||||
TenantEvent,
|
TenantEvent,
|
||||||
} from '../api';
|
} from '../api';
|
||||||
@@ -38,6 +39,9 @@ export default function MobileEventPhotoboothPage() {
|
|||||||
const [loading, setLoading] = React.useState(true);
|
const [loading, setLoading] = React.useState(true);
|
||||||
const [updating, setUpdating] = React.useState(false);
|
const [updating, setUpdating] = React.useState(false);
|
||||||
const [error, setError] = React.useState<string | null>(null);
|
const [error, setError] = React.useState<string | null>(null);
|
||||||
|
const [connectCode, setConnectCode] = React.useState<string | null>(null);
|
||||||
|
const [connectExpiresAt, setConnectExpiresAt] = React.useState<string | null>(null);
|
||||||
|
const [connectLoading, setConnectLoading] = React.useState(false);
|
||||||
const back = useBackNavigation(slug ? adminPath(`/mobile/events/${slug}`) : adminPath('/mobile/events'));
|
const back = useBackNavigation(slug ? adminPath(`/mobile/events/${slug}`) : adminPath('/mobile/events'));
|
||||||
|
|
||||||
const locale = i18n.language?.startsWith('en') ? 'en-GB' : 'de-DE';
|
const locale = i18n.language?.startsWith('en') ? 'en-GB' : 'de-DE';
|
||||||
@@ -124,6 +128,25 @@ export default function MobileEventPhotoboothPage() {
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
const handleGenerateConnectCode = async () => {
|
||||||
|
if (!slug) return;
|
||||||
|
setConnectLoading(true);
|
||||||
|
try {
|
||||||
|
const result = await createEventPhotoboothConnectCode(slug);
|
||||||
|
setConnectCode(result.code || null);
|
||||||
|
setConnectExpiresAt(result.expires_at ?? null);
|
||||||
|
toast.success(t('photobooth.connectCode.actions.generated', 'Verbindungscode erstellt'));
|
||||||
|
} catch (err) {
|
||||||
|
if (!isAuthError(err)) {
|
||||||
|
toast.error(
|
||||||
|
getApiErrorMessage(err, t('photobooth.connectCode.errors.failed', 'Verbindungscode konnte nicht erstellt werden.'))
|
||||||
|
);
|
||||||
|
}
|
||||||
|
} finally {
|
||||||
|
setConnectLoading(false);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
const activeMode = selectedMode ?? status?.mode ?? 'ftp';
|
const activeMode = selectedMode ?? status?.mode ?? 'ftp';
|
||||||
const isSpark = activeMode === 'sparkbooth';
|
const isSpark = activeMode === 'sparkbooth';
|
||||||
const spark = status?.sparkbooth ?? null;
|
const spark = status?.sparkbooth ?? null;
|
||||||
@@ -286,6 +309,32 @@ export default function MobileEventPhotoboothPage() {
|
|||||||
<Text fontSize="$xs" color={muted}>
|
<Text fontSize="$xs" color={muted}>
|
||||||
{t('photobooth.sparkbooth.hint', 'POST with media file or base64 "media" field; username/password required.')}
|
{t('photobooth.sparkbooth.hint', 'POST with media file or base64 "media" field; username/password required.')}
|
||||||
</Text>
|
</Text>
|
||||||
|
<YStack space="$2" marginTop="$2">
|
||||||
|
<Text fontSize="$xs" color={muted}>
|
||||||
|
{t('photobooth.connectCode.description', 'Create a 6-digit code for the uploader app.')}
|
||||||
|
</Text>
|
||||||
|
<CTAButton
|
||||||
|
label={
|
||||||
|
connectLoading
|
||||||
|
? t('common.processing', '...')
|
||||||
|
: t('photobooth.connectCode.actions.generate', 'Generate connect code')
|
||||||
|
}
|
||||||
|
onPress={handleGenerateConnectCode}
|
||||||
|
iconLeft={<PlugZap size={14} color={surface} />}
|
||||||
|
disabled={!isActive || updating || connectLoading}
|
||||||
|
style={{ width: '100%', paddingHorizontal: 10, paddingVertical: 10 }}
|
||||||
|
/>
|
||||||
|
{connectCode ? (
|
||||||
|
<CredentialRow label={t('photobooth.connectCode.label', 'Connect code')} value={connectCode} border={border} />
|
||||||
|
) : null}
|
||||||
|
{connectExpiresAt ? (
|
||||||
|
<Text fontSize="$xs" color={muted}>
|
||||||
|
{t('photobooth.connectCode.expires', 'Expires: {{date}}', {
|
||||||
|
date: formatEventDate(connectExpiresAt, locale),
|
||||||
|
})}
|
||||||
|
</Text>
|
||||||
|
) : null}
|
||||||
|
</YStack>
|
||||||
</>
|
</>
|
||||||
) : (
|
) : (
|
||||||
<>
|
<>
|
||||||
|
|||||||
Reference in New Issue
Block a user