All files / src/features/identity/components SignupForm.tsx

100% Statements 9/9
100% Branches 6/6
100% Functions 3/3
100% Lines 8/8

Press n or j to go to the next uncovered block, b, p or k for the previous block.

1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110                                                                        19x 19x 1x   1x                                             18x   18x                                 12x 12x                                                    
/**
 * SignupForm - Presentational component for signup
 * Renders the signup form UI based on props, no hooks or side effects
 */
 
import { IconAlertTriangle, IconCheck } from '@tabler/icons-react';
import { Alert, Button, Card, Code, Group, Stack, Text, TextInput, Title } from '@mantine/core';
 
export interface SignupFormProps {
  // Form state
  username: string;
  onUsernameChange: (value: string) => void;
  onSubmit: (e: React.FormEvent) => void;
 
  // Loading states
  isLoading: boolean;
  loadingText?: string;
 
  // Error state
  error?: string | null;
 
  // Success state
  successData?: {
    account_id: string;
    root_kid: string;
  } | null;
}
 
export function SignupForm({
  username,
  onUsernameChange,
  onSubmit,
  isLoading,
  loadingText,
  error,
  successData,
}: SignupFormProps) {
  if (successData) {
    return (
      <Stack gap="md" maw={500} mx="auto" mt="xl">
        <Alert icon={<IconCheck size={16} />} title="Account Created" color="green">
          Your account has been created successfully.
        </Alert>
 
        <Card shadow="sm" padding="lg" radius="md" withBorder>
          <Stack gap="sm">
            <Text fw={500}>Account Details</Text>
            <Text size="sm">
              <strong>Account ID:</strong> <Code>{successData.account_id}</Code>
            </Text>
            <Text size="sm">
              <strong>Root Key ID:</strong> <Code>{successData.root_kid}</Code>
            </Text>
          </Stack>
        </Card>
 
        <Text size="xs" c="dimmed" ta="center">
          Your keys were generated locally.
          <br />
          (Key persistence will be added in a future update)
        </Text>
      </Stack>
    );
  }
 
  return (
    <Stack gap="md" maw={500} mx="auto" mt="xl">
      <div>
        <Title order={2}>Create Account</Title>
        <Text c="dimmed" size="sm" mt="xs">
          Sign up for TinyCongress with cryptographic identity
        </Text>
      </div>
 
      <Card shadow="sm" padding="lg" radius="md" withBorder>
        <form onSubmit={onSubmit}>
          <Stack gap="md">
            <TextInput
              label="Username"
              placeholder="alice"
              required
              value={username}
              onChange={(e) => {
                onUsernameChange(e.currentTarget.value);
              }}
              disabled={isLoading}
            />
 
            {error ? (
              <Alert icon={<IconAlertTriangle size={16} />} title="Signup failed" color="red">
                {error}
              </Alert>
            ) : null}
 
            <Group justify="flex-end">
              <Button type="submit" loading={isLoading}>
                {loadingText ?? 'Sign Up'}
              </Button>
            </Group>
          </Stack>
        </form>
      </Card>
 
      <Text size="xs" c="dimmed" ta="center">
        Your keys are generated locally and never leave your device.
      </Text>
    </Stack>
  );
}