fix(native): read { items, counts, limits } from custom-domains GET
The /api/custom-domains endpoint changed shape with the slot-pool split
in commit f2b81ee — it now returns { items, counts, limits } where it
used to return a bare CustomDomain[]. The hook was still matching
Array.isArray(res) or res.domains and silently fell back to an empty
list, so a successful POST went unreflected on the blocker page (user
reported "kein fehler aber domain taucht nicht in der liste" after
adding communications@only4subscribers.com).
Now reads items / counts / limits when present, prefers the API-driven
counts and limits over the client-side derivation (still kept as a
fallback for the stale-bundle window between deploys). Legacy bare-
array + { domains } shapes still resolve too in case a cached client
hits this code path before the new backend lands.
This commit is contained in:
parent
63b6d2ff11
commit
c2323c1aba
@ -97,19 +97,36 @@ export function isValidDomain(input: string): boolean {
|
|||||||
*/
|
*/
|
||||||
export function useCustomDomains(plan: Plan): UseCustomDomainsReturn {
|
export function useCustomDomains(plan: Plan): UseCustomDomainsReturn {
|
||||||
const [domains, setDomains] = useState<CustomDomain[]>([]);
|
const [domains, setDomains] = useState<CustomDomain[]>([]);
|
||||||
|
const [apiCounts, setApiCounts] = useState<CountsByType | null>(null);
|
||||||
|
const [apiLimits, setApiLimits] = useState<LimitsByType | null>(null);
|
||||||
const [loading, setLoading] = useState(true);
|
const [loading, setLoading] = useState(true);
|
||||||
const [error, setError] = useState<string | null>(null);
|
const [error, setError] = useState<string | null>(null);
|
||||||
|
|
||||||
const fetchDomains = useCallback(async () => {
|
const fetchDomains = useCallback(async () => {
|
||||||
try {
|
try {
|
||||||
// Backend (`server/api/custom-domains/index.get.ts`) gibt Array DIREKT zurück,
|
// Backend `GET /api/custom-domains` returns
|
||||||
// kein { domains: [...] }-Wrapper.
|
// { items: CustomDomain[], counts: { web, mail }, limits: { web, mail } }
|
||||||
const res = await apiFetch<CustomDomain[] | { domains?: CustomDomain[] }>(
|
// since the slot-pool split (commit f2b81ee). Legacy fall-throughs cover
|
||||||
'/api/custom-domains',
|
// an older shape (bare array or { domains }) in case a cached client
|
||||||
);
|
// ever hits this code path before the deploy lands.
|
||||||
const arr = Array.isArray(res) ? res : (res?.domains ?? []);
|
const res = await apiFetch<
|
||||||
console.log('[useCustomDomains] fetched:', arr.length, 'domains', arr.slice(0, 3));
|
| CustomDomain[]
|
||||||
|
| { domains?: CustomDomain[] }
|
||||||
|
| { items?: CustomDomain[]; counts?: CountsByType; limits?: LimitsByType }
|
||||||
|
>('/api/custom-domains');
|
||||||
|
let arr: CustomDomain[] = [];
|
||||||
|
let counts: CountsByType | null = null;
|
||||||
|
let limits: LimitsByType | null = null;
|
||||||
|
if (Array.isArray(res)) {
|
||||||
|
arr = res;
|
||||||
|
} else if (res) {
|
||||||
|
arr = (res as any).items ?? (res as any).domains ?? [];
|
||||||
|
counts = (res as any).counts ?? null;
|
||||||
|
limits = (res as any).limits ?? null;
|
||||||
|
}
|
||||||
setDomains(arr);
|
setDomains(arr);
|
||||||
|
setApiCounts(counts);
|
||||||
|
setApiLimits(limits);
|
||||||
setError(null);
|
setError(null);
|
||||||
} catch (e: any) {
|
} catch (e: any) {
|
||||||
console.error('[useCustomDomains] fetch failed:', e?.message ?? e);
|
console.error('[useCustomDomains] fetch failed:', e?.message ?? e);
|
||||||
@ -177,7 +194,10 @@ export function useCustomDomains(plan: Plan): UseCustomDomainsReturn {
|
|||||||
|
|
||||||
const tier = deriveTier(plan, domains);
|
const tier = deriveTier(plan, domains);
|
||||||
|
|
||||||
const countsByType: CountsByType = {
|
// Prefer API-driven counts/limits when the backend returned the new shape;
|
||||||
|
// fall back to local derivation so the UI works during a stale-bundle moment.
|
||||||
|
const countsByType: CountsByType =
|
||||||
|
apiCounts ?? {
|
||||||
web: domains.filter(
|
web: domains.filter(
|
||||||
(d) => d.status !== 'approved' && (d.type === 'web' || !d.type),
|
(d) => d.status !== 'approved' && (d.type === 'web' || !d.type),
|
||||||
).length,
|
).length,
|
||||||
@ -185,14 +205,11 @@ export function useCustomDomains(plan: Plan): UseCustomDomainsReturn {
|
|||||||
(d) => d.status !== 'approved' && d.type === 'mail_domain',
|
(d) => d.status !== 'approved' && d.type === 'mail_domain',
|
||||||
).length,
|
).length,
|
||||||
};
|
};
|
||||||
|
const limits: LimitsByType =
|
||||||
// Provisional client-side limits — match plan-features.ts on the backend
|
apiLimits ?? {
|
||||||
// (free/pro: 5+5, legend: 10+10). Will be replaced by API-driven values
|
web: plan === 'legend' ? 10 : 5,
|
||||||
// once GET /api/custom-domains routes the new { items, counts, limits }
|
mail: plan === 'legend' ? 10 : 5,
|
||||||
// response shape through the hook.
|
};
|
||||||
const webLimit = plan === 'legend' ? 10 : 5;
|
|
||||||
const mailLimit = plan === 'legend' ? 10 : 5;
|
|
||||||
const limits: LimitsByType = { web: webLimit, mail: mailLimit };
|
|
||||||
|
|
||||||
return {
|
return {
|
||||||
domains,
|
domains,
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user