Wrap intl in span identifying translation

This commit is contained in:
Jamie Curnow
2025-10-02 23:06:51 +10:00
parent fcb08d3003
commit 227e818040
68 changed files with 1076 additions and 510 deletions

View File

@@ -0,0 +1,99 @@
import { IconLock, IconLockOpen2 } from "@tabler/icons-react";
import { Field, useFormikContext } from "formik";
import type { ReactNode } from "react";
import Select, { type ActionMeta, components, type OptionProps } from "react-select";
import type { AccessList } from "src/api/backend";
import { useAccessLists } from "src/hooks";
import { DateTimeFormat, intl, T } from "src/locale";
interface AccessOption {
readonly value: number;
readonly label: string;
readonly subLabel: string;
readonly icon: ReactNode;
}
const Option = (props: OptionProps<AccessOption>) => {
return (
<components.Option {...props}>
<div className="flex-fill">
<div className="font-weight-medium">
{props.data.icon} <strong>{props.data.label}</strong>
</div>
<div className="text-secondary mt-1 ps-3">{props.data.subLabel}</div>
</div>
</components.Option>
);
};
interface Props {
id?: string;
name?: string;
label?: string;
}
export function AccessField({ name = "accessListId", label = "access.title", id = "accessListId" }: Props) {
const { isLoading, isError, error, data } = useAccessLists();
const { setFieldValue } = useFormikContext();
const handleChange = (newValue: any, _actionMeta: ActionMeta<AccessOption>) => {
setFieldValue(name, newValue?.value);
};
const options: AccessOption[] =
data?.map((item: AccessList) => ({
value: item.id || 0,
label: item.name,
subLabel: intl.formatMessage(
{ id: "access.subtitle" },
{
users: item?.items?.length,
rules: item?.clients?.length,
date: item?.createdOn ? DateTimeFormat(item?.createdOn) : "N/A",
},
),
icon: <IconLock size={14} className="text-lime" />,
})) || [];
// Public option
options?.unshift({
value: 0,
label: intl.formatMessage({ id: "access.public" }),
subLabel: "No basic auth required",
icon: <IconLockOpen2 size={14} className="text-red" />,
});
return (
<Field name={name}>
{({ field, form }: any) => (
<div className="mb-3">
<label className="form-label" htmlFor={id}>
<T id={label} />
</label>
{isLoading ? <div className="placeholder placeholder-lg col-12 my-3 placeholder-glow" /> : null}
{isError ? <div className="invalid-feedback">{`${error}`}</div> : null}
{!isLoading && !isError ? (
<Select
className="react-select-container"
classNamePrefix="react-select"
defaultValue={options.find((o) => o.value === field.value) || options[0]}
options={options}
components={{ Option }}
styles={{
option: (base) => ({
...base,
height: "100%",
}),
}}
onChange={handleChange}
/>
) : null}
{form.errors[field.name] ? (
<div className="invalid-feedback">
{form.errors[field.name] && form.touched[field.name] ? form.errors[field.name] : null}
</div>
) : null}
</div>
)}
</Field>
);
}

View File

@@ -0,0 +1,36 @@
import { useFormikContext } from "formik";
import { T } from "src/locale";
interface Props {
id?: string;
name?: string;
}
export function BasicAuthField({ name = "items", id = "items" }: Props) {
const { setFieldValue } = useFormikContext();
return (
<>
<div className="row">
<div className="col-6">
<label className="form-label" htmlFor="...">
<T id="username" />
</label>
</div>
<div className="col-6">
<label className="form-label" htmlFor="...">
<T id="password" />
</label>
</div>
</div>
<div className="row mb-3">
<div className="col-6">
<input id="name" type="text" required autoComplete="off" className="form-control" />
</div>
<div className="col-6">
<input id="pw" type="password" required autoComplete="off" className="form-control" />
</div>
</div>
<button className="btn">+</button>
</>
);
}

View File

@@ -10,7 +10,6 @@ interface DNSProviderOption {
readonly label: string;
readonly credentials: string;
}
export function DNSProviderFields() {
const { values, setFieldValue } = useFormikContext();
const { data: dnsProviders, isLoading } = useDnsProviders();
@@ -100,6 +99,7 @@ export function DNSProviderFields() {
<input
id="propagationSeconds"
type="number"
x
className="form-control"
min={0}
max={600}

View File

@@ -1,10 +1,11 @@
import { Field, useFormikContext } from "formik";
import type { ReactNode } from "react";
import type { ActionMeta, MultiValue } from "react-select";
import CreatableSelect from "react-select/creatable";
import { intl } from "src/locale";
import { intl, T } from "src/locale";
import { validateDomain, validateDomains } from "src/modules/Validations";
export type SelectOption = {
type SelectOption = {
label: string;
value: string;
color?: string;
@@ -35,14 +36,14 @@ export function DomainNamesField({
setFieldValue(name, doms);
};
const helperTexts: string[] = [];
const helperTexts: ReactNode[] = [];
if (maxDomains) {
helperTexts.push(intl.formatMessage({ id: "domain-names.max" }, { count: maxDomains }));
helperTexts.push(<T id="domain-names.max" data={{ count: maxDomains }} />);
}
if (!isWildcardPermitted) {
helperTexts.push(intl.formatMessage({ id: "domain-names.wildcards-not-permitted" }));
helperTexts.push(<T id="domain-names.wildcards-not-permitted" />);
} else if (!dnsProviderWildcardSupported) {
helperTexts.push(intl.formatMessage({ id: "domain-names.wildcards-not-supported" }));
helperTexts.push(<T id="domain-names.wildcards-not-supported" />);
}
return (
@@ -50,7 +51,7 @@ export function DomainNamesField({
{({ field, form }: any) => (
<div className="mb-3">
<label className="form-label" htmlFor={id}>
{intl.formatMessage({ id: label })}
<T id={label} />
</label>
<CreatableSelect
className="react-select-container"
@@ -68,8 +69,8 @@ export function DomainNamesField({
{form.errors[field.name] && form.touched[field.name] ? (
<small className="text-danger">{form.errors[field.name]}</small>
) : helperTexts.length ? (
helperTexts.map((i) => (
<small key={i} className="text-info">
helperTexts.map((i, idx) => (
<small key={idx} className="text-info">
{i}
</small>
))

View File

@@ -1,6 +1,6 @@
import CodeEditor from "@uiw/react-textarea-code-editor";
import { Field } from "formik";
import { intl } from "src/locale";
import { intl, T } from "src/locale";
interface Props {
id?: string;
@@ -17,7 +17,7 @@ export function NginxConfigField({
{({ field }: any) => (
<div className="mt-3">
<label htmlFor={id} className="form-label">
{intl.formatMessage({ id: label })}
<T id={label} />
</label>
<CodeEditor
language="nginx"

View File

@@ -3,7 +3,7 @@ import { Field, useFormikContext } from "formik";
import Select, { type ActionMeta, components, type OptionProps } from "react-select";
import type { Certificate } from "src/api/backend";
import { useCertificates } from "src/hooks";
import { DateTimeFormat, intl } from "src/locale";
import { DateTimeFormat, T } from "src/locale";
interface CertOption {
readonly value: number | "new";
@@ -106,7 +106,7 @@ export function SSLCertificateField({
{({ field, form }: any) => (
<div className="mb-3">
<label className="form-label" htmlFor={id}>
{intl.formatMessage({ id: label })}
<T id={label} />
</label>
{isLoading ? <div className="placeholder placeholder-lg col-12 my-3 placeholder-glow" /> : null}
{isError ? <div className="invalid-feedback">{`${error}`}</div> : null}

View File

@@ -1,7 +1,7 @@
import cn from "classnames";
import { Field, useFormikContext } from "formik";
import { DNSProviderFields, DomainNamesField } from "src/components";
import { intl } from "src/locale";
import { T } from "src/locale";
interface Props {
forHttp?: boolean; // the sslForced, http2Support, hstsEnabled, hstsSubdomains fields
@@ -49,7 +49,7 @@ export function SSLOptionsFields({ forHttp = true, forceDNSForNew, requireDomain
disabled={!hasCertificate}
/>
<span className="form-check-label">
{intl.formatMessage({ id: "domains.force-ssl" })}
<T id="domains.force-ssl" />
</span>
</label>
)}
@@ -67,7 +67,7 @@ export function SSLOptionsFields({ forHttp = true, forceDNSForNew, requireDomain
disabled={!hasCertificate}
/>
<span className="form-check-label">
{intl.formatMessage({ id: "domains.http2-support" })}
<T id="domains.http2-support" />
</span>
</label>
)}
@@ -87,7 +87,7 @@ export function SSLOptionsFields({ forHttp = true, forceDNSForNew, requireDomain
disabled={!hasCertificate || !sslForced}
/>
<span className="form-check-label">
{intl.formatMessage({ id: "domains.hsts-enabled" })}
<T id="domains.hsts-enabled" />
</span>
</label>
)}
@@ -105,7 +105,7 @@ export function SSLOptionsFields({ forHttp = true, forceDNSForNew, requireDomain
disabled={!hasCertificate || !hstsEnabled}
/>
<span className="form-check-label">
{intl.formatMessage({ id: "domains.hsts-subdomains" })}
<T id="domains.hsts-subdomains" />
</span>
</label>
)}
@@ -131,7 +131,7 @@ export function SSLOptionsFields({ forHttp = true, forceDNSForNew, requireDomain
onChange={(e) => handleToggleChange(e, field.name)}
/>
<span className="form-check-label">
{intl.formatMessage({ id: "domains.use-dns" })}
<T id="domains.use-dns" />
</span>
</label>
)}

View File

@@ -1,3 +1,5 @@
export * from "./AccessField";
export * from "./BasicAuthField";
export * from "./DNSProviderFields";
export * from "./DomainNamesField";
export * from "./NginxConfigField";