Files
redefined-designs/frontend/src/admin/Settings.tsx
T
bermudalambandClaude Opus 5 a700597440
SonarQube Analysis / sonarqube (pull_request) Canceled after 0s
Tests / lint (pull_request) Canceled after 0s
Tests / backend-unit (pull_request) Canceled after 0s
Tests / frontend-e2e (pull_request) Canceled after 0s
refactor: standardise antd imports and remove the avoidable any (#65)
Stage 1 of #65: this issue's original two lists. The type-checked gate it also owns follows in later stages.

Nine files imported antd from the barrel while the rest of the codebase used deep imports from antd/es. Both resolve to the same modules under antd v5 and Vite, so this is not the tree-shaking problem it would have been under v4 — the cost was that a documented convention had two spellings, and nobody reading a file could tell whether its style was deliberate or just old. Eighty-two imports converted, and every antd/es path was checked to exist before generating any of them rather than trusting a name-mangling rule.

The three `client: any` parameters in cartCheckout are now PoolClient. These functions run inside a transaction, and `any` removed exactly the check that would catch a pool-versus-client mix-up — which in this codebase means a query silently running outside the transaction it was meant to be part of, on the path that takes money.

publicCustomer took `any` and now takes a CustomerRow describing what it actually reads. Typed as its own shape rather than the whole table so that adding a column later — a password hash, a token, an internal note — cannot quietly start being echoed back to a customer.

The three `(window as any).paypal` casts are replaced by a declared interface for the injected SDK. It is deliberately narrow: it describes the three things this app calls, not the whole SDK, because a wider guess would be fiction and a wrong shape typed confidently is worse than an honest cast. The property is optional, since the SDK is absent until its script has loaded — which is the check both call sites already make.

Verified: 141 unit, 169 integration and 94 end-to-end passing. The end-to-end run is the one that matters here — an antd import migration can build cleanly and still break at runtime through styles or context, so a green tsc proves less than it appears to.

Lint drops from 8 warnings to 4 in the backend and 30 to 27 in the frontend, all of them the no-explicit-any this change removed. As a side effect the no-unsafe count that later stages exist to clear falls from 259 to 216 in the backend and 73 to 67 in the frontend, measured rather than estimated.

Refs #65
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-08-21 15:48:12 -05:00

47 lines
1.6 KiB
TypeScript

import { useEffect, useState } from 'react';
import Form from 'antd/es/form';
import InputNumber from 'antd/es/input-number';
import Button from 'antd/es/button';
import Typography from 'antd/es/typography';
import message from 'antd/es/message';
import Card from 'antd/es/card';
import { fetchAdminSettings, updateAdminSettings } from './adminSettingsApi';
const { Title, Text } = Typography;
export default function Settings() {
const [form] = Form.useForm();
const [loading, setLoading] = useState(true);
useEffect(() => {
fetchAdminSettings()
.then(s => {
form.setFieldsValue({ cartExpiryHours: s.cartExpiryHours });
})
// A rejection here used to leave the form spinning indefinitely.
.catch(() => message.error('Could not load settings'))
.finally(() => setLoading(false));
}, [form]);
async function handleSave() {
const values = await form.validateFields();
await updateAdminSettings(values);
message.success('Settings saved');
}
return (
<Card style={{ maxWidth: 480 }}>
<Title level={4}>Cart Settings</Title>
<Text type="secondary">
How long an item stays reserved in a customer's cart before it's automatically released back to available inventory.
</Text>
<Form form={form} layout="vertical" style={{ marginTop: 16 }} disabled={loading}>
<Form.Item name="cartExpiryHours" label="Cart expiry (hours)" rules={[{ required: true }]}>
<InputNumber min={0.5} step={0.5} style={{ width: '100%' }} />
</Form.Item>
<Button type="primary" onClick={handleSave} loading={loading}>Save</Button>
</Form>
</Card>
);
}