74 lines
2.4 KiB
TypeScript
74 lines
2.4 KiB
TypeScript
/**
|
|
* FetchInvitationStep — first step in the import flow.
|
|
*
|
|
* Receives an invitation ID, fetches the invitation from the sync server,
|
|
* resolves its template, and auto-advances once loaded.
|
|
* Shows a loading spinner while fetching and an error state with retry/cancel.
|
|
*/
|
|
|
|
import React, { useState, useEffect, useCallback } from 'react';
|
|
import { Box, Text } from 'ink';
|
|
import { colors } from '../../../../theme.js';
|
|
import type { FetchStepProps } from '../types.js';
|
|
|
|
export function FetchInvitationStep({
|
|
invitationId,
|
|
appService,
|
|
onComplete,
|
|
isActive,
|
|
}: FetchStepProps): React.ReactElement {
|
|
const [status, setStatus] = useState<'loading' | 'error'>('loading');
|
|
const [errorMessage, setErrorMessage] = useState<string | null>(null);
|
|
|
|
/**
|
|
* Fetch the invitation and its template, then auto-advance.
|
|
*/
|
|
const fetchInvitation = useCallback(async () => {
|
|
setStatus('loading');
|
|
setErrorMessage(null);
|
|
|
|
try {
|
|
// Create/fetch the invitation instance (fetches from sync server if needed)
|
|
const invitation = await appService.createInvitation(invitationId);
|
|
|
|
// Resolve the template for display in later steps
|
|
const template = await appService.engine.getTemplate(invitation.data.templateIdentifier);
|
|
|
|
// Auto-advance — hand the loaded data to the flow controller
|
|
onComplete(invitation, template ?? null);
|
|
} catch (error) {
|
|
const message = error instanceof Error ? error.message : String(error);
|
|
setErrorMessage(message);
|
|
setStatus('error');
|
|
}
|
|
}, [invitationId, appService, onComplete]);
|
|
|
|
// Kick off the fetch on mount
|
|
useEffect(() => {
|
|
if (isActive) {
|
|
fetchInvitation();
|
|
}
|
|
}, [isActive, fetchInvitation]);
|
|
|
|
return (
|
|
<Box flexDirection="column">
|
|
{status === 'loading' && (
|
|
<Box flexDirection="column">
|
|
<Text color={colors.info}>Fetching invitation...</Text>
|
|
<Text color={colors.textMuted} dimColor>ID: {invitationId}</Text>
|
|
</Box>
|
|
)}
|
|
|
|
{status === 'error' && (
|
|
<Box flexDirection="column">
|
|
<Text color={colors.error} bold>Failed to fetch invitation</Text>
|
|
<Text color={colors.textMuted} wrap="wrap">{errorMessage}</Text>
|
|
<Box marginTop={1}>
|
|
<Text color={colors.textMuted}>Press Enter to retry or Esc to cancel</Text>
|
|
</Box>
|
|
</Box>
|
|
)}
|
|
</Box>
|
|
);
|
|
}
|