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:
Repository Selection
- Authorization selection
- Repository search
- Branch selection
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:
- Primary DNS (A record)
- Wildcard DNS (*.domain)
- Challenge verification
- 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:
Domain Setup
- Domain name input
- DNS record verification
- Challenge verification
- Status monitoring
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>
);
};