feat: integrate eTradeInfo component for auto-filling company information and enhance TIN input handling

This commit is contained in:
Marshal
2026-06-20 10:39:53 +00:00
parent 1f92a04ecf
commit 72b6c9875d
3 changed files with 55 additions and 37 deletions

View File

@@ -1,5 +1,6 @@
import { Injectable, BadRequestException } from "@nestjs/common";
import { HttpService } from "@nestjs/axios";
import { Agent } from "https";
import { firstValueFrom } from "rxjs";
import {
ETradeCompanyInfo,
@@ -12,6 +13,14 @@ export class ETradeService {
private readonly baseUrl = "https://etrade.gov.et/api";
private readonly referer = "https://etrade.gov.et/business-license-checker";
/**
* The eTrade server serves an incomplete TLS chain (it omits the intermediate
* CA cert), so Node rejects the handshake with UNABLE_TO_GET_ISSUER_CERT.
* Scope a relaxed agent to these outbound calls only — the rest of the app
* keeps full certificate verification.
*/
private readonly httpsAgent = new Agent({ rejectUnauthorized: false });
constructor(private readonly httpService: HttpService) {}
async getCompanyInfoByTin(tin: string): Promise<ETradeCompanyInfo> {
@@ -20,6 +29,7 @@ export class ETradeService {
const response = await firstValueFrom(
this.httpService.get<ETradeCompanyInfo>(url, {
headers: { Referer: this.referer },
httpsAgent: this.httpsAgent,
}),
);
return response.data;
@@ -44,6 +54,7 @@ export class ETradeService {
Lang: "en",
},
headers: { Referer: this.referer },
httpsAgent: this.httpsAgent,
}),
);
return response.data;

View File

@@ -3,24 +3,33 @@ import {
Button,
Group,
Loader,
SimpleGrid,
Stack,
Text,
TextInput,
} from "@mantine/core";
import { AlertCircle, CheckCircle2, RefreshCw } from "lucide-react";
import type { UseFormRegisterReturn } from "react-hook-form";
import { AlertCircle, CheckCircle2, Download } from "lucide-react";
import { useETradeData } from "@/hooks/useETradeData";
import type { CompanyRegistrationData } from "@edr/types";
interface ETradeInfoProps {
/** Current TIN value (drives button enablement). */
tin: string;
/** RHF registration for the TIN input — this is the form's primary TIN field. */
register: UseFormRegisterReturn;
/** Validation error for the TIN field, if any. */
error?: string;
onDataLoaded: (data: CompanyRegistrationData) => void;
}
export default function ETradeInfo({ tin, onDataLoaded }: ETradeInfoProps) {
export default function ETradeInfo({
tin,
register,
error,
onDataLoaded,
}: ETradeInfoProps) {
const mutation = useETradeData();
const isLoading = mutation.isPending;
const hasError = mutation.isError;
const hasData = mutation.data;
const handleFetch = async () => {
@@ -32,40 +41,42 @@ export default function ETradeInfo({ tin, onDataLoaded }: ETradeInfoProps) {
};
const errorMessage =
hasError && mutation.error
mutation.isError && mutation.error
? (mutation.error as any).message ||
"Failed to fetch company information. Please try again."
: null;
return (
<Stack gap="md">
<Group grow>
<Group align="flex-start" grow>
<TextInput
label="TIN Number"
label="TIN Number (10 digits)"
placeholder="1234567890"
value={tin}
disabled
readOnly
maxLength={10}
error={error}
{...register}
/>
<Button
variant="filled"
color="edr-green"
onClick={handleFetch}
disabled={!tin || tin.length !== 10 || isLoading}
leftSection={isLoading ? <Loader size={16} /> : <RefreshCw size={16} />}
leftSection={
isLoading ? <Loader size={16} /> : <Download size={16} />
}
mt="24px"
>
{isLoading ? "Fetching..." : "Fetch Info from eTrade"}
{isLoading ? "Getting..." : "Get Data"}
</Button>
</Group>
{hasError && errorMessage && (
{errorMessage && (
<Alert
icon={<AlertCircle size={16} />}
color="red"
title="Failed to fetch data"
>
{errorMessage}
{errorMessage} You can still fill in the details manually below.
</Alert>
)}

View File

@@ -614,6 +614,18 @@ export default function CompanyProfileForm({
<Stack gap="md">
{step === "company" && (
<>
<Text fw={600} size="sm" c="edr-text">
Enter your TIN to auto-fill company information from eTrade
</Text>
<ETradeInfo
tin={watch("tinNumber")}
register={register("tinNumber")}
error={errors.tinNumber?.message}
onDataLoaded={handleETradeDataLoaded}
/>
<Divider my="sm" />
<TextInput
label="Company Name"
placeholder="Global Logistics Ltd"
@@ -654,13 +666,6 @@ export default function CompanyProfileForm({
/>
</SimpleGrid>
<SimpleGrid cols={2} spacing="md">
<TextInput
label="TIN Number (10 digits)"
placeholder="1234567890"
maxLength={10}
error={errors.tinNumber?.message}
{...register("tinNumber")}
/>
<TextInput
label="VAT Number"
placeholder="VAT-12345"
@@ -668,23 +673,14 @@ export default function CompanyProfileForm({
error={errors.vatNumber?.message}
{...register("vatNumber")}
/>
<TextInput
label="FAN Number (16 digits)"
placeholder="1234567890123456"
maxLength={16}
error={errors.fanNumber?.message}
{...register("fanNumber")}
/>
</SimpleGrid>
<TextInput
label="FAN Number (16 digits)"
placeholder="1234567890123456"
maxLength={16}
error={errors.fanNumber?.message}
{...register("fanNumber")}
/>
<Divider my="sm" />
<Text fw={600} size="sm" c="edr-text">
Fetch Company Information from eTrade
</Text>
<ETradeInfo
tin={watch("tinNumber")}
onDataLoaded={handleETradeDataLoaded}
/>
{watch("licenceNumber") && (
<>