feat(03-02): add domaincheck frontend - input form, results display, i18n

- DomainInput component with text input and submit button
- ResultList component with green/red status badges (D-01)
- checkDomainAction fetches POST /modules/domaincheck/check with auth cookie
- Page renders within portal AppShell at /modules/domaincheck
- i18n keys added for both DE and EN locales
This commit is contained in:
2026-06-19 13:27:55 +02:00
parent 2e0a4ddc21
commit 1d9fd2280d
6 changed files with 231 additions and 0 deletions
@@ -0,0 +1,64 @@
'use client';
import { useTranslations } from 'next-intl';
import { useState } from 'react';
import { type DomainCheckResult, checkDomainAction } from './actions';
import { DomainInput } from './components/DomainInput';
import { ResultList } from './components/ResultList';
/**
* Domaincheck module page within the portal.
*
* Provides a form to enter a domain label, calls the API to check
* TLD variant availability, and displays color-coded results.
*
* Per D-05a: uses Card-style layout consistent with portal modules.
*/
export default function DomaincheckPage() {
const t = useTranslations('domaincheck');
const [results, setResults] = useState<DomainCheckResult[]>([]);
const [isLoading, setIsLoading] = useState(false);
const [error, setError] = useState<string | null>(null);
const handleSubmit = async (domain: string) => {
setIsLoading(true);
setError(null);
try {
const response = await checkDomainAction(domain);
setResults(response.results);
} catch {
setError(t('error'));
setResults([]);
} finally {
setIsLoading(false);
}
};
return (
<div className="mx-auto max-w-2xl space-y-6 p-6">
{/* Header */}
<div>
<h1 className="text-2xl font-bold tracking-tight">{t('title')}</h1>
<p className="text-sm text-muted-foreground mt-1">
{t('description')}
</p>
</div>
{/* Card panel (per D-05a) */}
<div className="rounded-lg border border-border bg-card p-6 shadow-sm space-y-4">
{/* Domain input */}
<DomainInput onSubmit={handleSubmit} isLoading={isLoading} />
{/* Error message */}
{error && (
<p className="text-sm text-destructive">{error}</p>
)}
{/* Results */}
<ResultList results={results} />
</div>
</div>
);
}