Frontend - Development

Frontend Component Patterns

Component Hierarchy

components/
├── atoms/        # Basic UI elements
│   ├── haddot    # Status indicators
│   ├── divider   # Layout elements
│   └── spinner   # Loading states
├── molecules/    # Simple combinations
│   ├── stepdots  # Progress indicators
│   ├── github-sign-in  # Auth buttons
│   └── copiable-field  # Utility inputs
└── organisms/    # Complex components
    ├── ProjectsList    # Main views
    ├── ServiceCard     # Service management
    └── DomainSetup     # Configuration flows

Form Patterns

Domain Configuration Form

const formSchema = z.object({
  domain: z.string().regex(/^[a-z0-9]+([-.]{1}[a-z0-9]+)*\.[a-z]{2,5}$/, {
    message: "Invalid domain name",
  }),
});

interface DomainNameFormProps {
  domain?: DomainResponseDto;
  main?: boolean;
}

Project Creation Form

Uses a multi-step approach with validation:

  1. Repository Selection

    • Authorization selection
    • Repository search
    • Branch selection
  2. Resource Configuration

    • Memory allocation (512MB - 8GB)
    • CPU allocation (1-8 cores)
    • Disk space (256MB - 2GB)

State Management Patterns

Configuration Provider

const ConfigProvider: FC<{ children: ReactNode }> = ({ children }) => {
  const { data: config } = useQuery('config', fetchConfig);
  return (
    <ConfigContext.Provider value={config}>
      {children}
    </ConfigContext.Provider>
  );
};

Authentication Guard

const AuthenticatedGuard: FC = () => {
  const { isAuth } = useAuthentication();
  return isAuth ? <Outlet /> : <Navigate to="/" />;
};

Feedback Patterns

Toast Notifications

Used for:

  • Action confirmations
  • Error messages
  • Status updates
  • Connection state changes

Loading States

  • Skeleton loaders for content
  • Spinners for actions
  • Progress indicators for multi-step operations

Dialog Patterns

Action Confirmation

  • Delete confirmations
  • Save prompts
  • Setting changes

Resource Creation

  • Multi-step wizards
  • Form validation
  • Error handling

Visual Component Guidelines

Status Indicators

  • Use consistent color coding
  • Provide clear state feedback
  • Include hover explanations

Action Buttons

  • Clear labeling
  • Consistent positioning
  • Appropriate variants:
    • Primary actions
    • Destructive actions
    • Secondary options

Domain Setup Flow

Domain Validation

const formSchema = z.object({
  domain: z.string().regex(/^[a-z0-9]+([-.]{1}[a-z0-9]+)*\.[a-z]{2,5}$/, {
    message: "Invalid domain name",
  }),
  main: z.boolean().optional(),
});

Domain Status Checking

The frontend implements a multi-step domain verification process:

  1. Primary DNS (A record)
  2. Wildcard DNS (*.domain)
  3. Challenge verification
  4. Domain linking

Network Connection Patterns

Service Port Configuration

interface ConfigNetworkFormType {
  port: string;
  domain: string;
  subdomain: string;
}

// Port selection from service information
const portOptions = serviceInformations.ports.map(port => ({
  label: port,
  value: port,
}));

Domain Selection

// Available domains list
const domainOptions = domains?.map(domain => ({
  label: domain,
  value: domain,
}));

Real-time Status Updates

WebSocket Integration

// Socket message structure
interface SocketMessage {
  event: string;
  scope: string;
  target: string;
  data: unknown;
}

// Status change handling
socket.on("message", (msg: SocketMessage) => {
  if (msg.event === "status_change") {
    // Update project VM status
    project.vm.status = msg.data.status;
  }
});

Form Management

Multi-step Forms

The frontend implements wizard-style forms for complex configurations:

  1. Domain Setup

    • Domain name input
    • DNS record verification
    • Challenge verification
    • Status monitoring
  2. Network Configuration

    • Port selection
    • Domain/subdomain selection
    • Connection validation

Network Connection Form

const ConfigNetworkForm = ({ projectId, serviceInformations }) => {
  const methods = useForm<ConfigNetworkFormType>({
    resolver: zodResolver(schema),
    mode: "onChange",
  });

  // Create network connection
  const onSubmit = (data: ConfigNetworkFormType) => {
    createRedirection({
      projectId,
      port: parseInt(data.port),
      domain: `${data.subdomain}.${data.domain}`,
    });
  };
};

UI Feedback Patterns

Loading States

const { data: domains, isFetching } = useGetDomainsQuery();

return isFetching ? (
  <LoadingSpinner />
) : (
  <DomainList domains={domains} />
);

Error Handling

try {
  await createDomain(domainData);
  toast({
    title: "Domain created",
    variant: "default",
  });
} catch (error) {
  toast({
    title: "Error",
    description: error.message,
    variant: "destructive",
  });
}

Data Management

Network Connection State

interface NetworkConnectionDto {
  id: string;
  domain: string;
  port: number;
  projectId: string;
}

// RTK Query endpoint
const networksApi = backendApi.injectEndpoints({
  endpoints: (builder) => ({
    getNetworksConnection: builder.query<NetworkConnectionDto[], string>({
      query: (projectId) => `network-connection/project/${projectId}`,
    }),
    createNetworkConnection: builder.mutation<
      NetworkConnectionDto,
      CreateNetworkConnectionDto
    >({
      query: (body) => ({
        url: "network-connection",
        method: "POST",
        body,
      }),
    }),
  }),
});

Domain State Management

interface DomainResponseDto {
  id: string;
  domain: string;
  main: boolean;
  primaryBinding: string;
  wildcardBinding: string;
  challengeBinding: string;
  linked: boolean;
}

const domainsApi = {
  getAllDomains: builder.query<DomainResponseDto[], void>({
    query: () => "/domains",
  }),
  getDomainStatus: builder.query<DomainStatusDto, string>({
    query: (id) => `/domains/${id}/status`,
  }),
};

Component Integration

Network Configuration Dialog

const NetworksTab: FC<NetworksTabProps> = ({
  serviceInformations,
  projectId,
}) => (
  <Dialog>
    <DialogTrigger>
      <Button>
        <Plus /> Add Network Connection
      </Button>
    </DialogTrigger>
    <DialogContent>
      <ConfigNetworkForm
        projectId={projectId}
        serviceInformations={serviceInformations}
      />
    </DialogContent>
  </Dialog>
);

Domain Status Display

const DomainStatus: FC<DomainStatusProps> = ({ domain }) => {
  const { data: status } = useGetDomainStatusQuery(domain.id);

  return (
    <div>
      <StatusIndicator
        status={status?.primaryStatus}
        label="Primary DNS"
      />
      <StatusIndicator
        status={status?.wildcardStatus}
        label="Wildcard DNS"
      />
      <StatusIndicator
        status={status?.challengeStatus}
        label="Challenge"
      />
    </div>
  );
};
Previous
Deployment
Next
State