More react

- consolidated lang items
- proxy host paths work
This commit is contained in:
Jamie Curnow
2025-10-16 18:59:19 +10:00
parent 7af01d0fc7
commit f2b5b19a83
56 changed files with 946 additions and 928 deletions

View File

@@ -9,7 +9,7 @@ import { AccessClientFields, BasicAuthFields, Button, Loading } from "src/compon
import { useAccessList, useSetAccessList } from "src/hooks";
import { intl, T } from "src/locale";
import { validateString } from "src/modules/Validations";
import { showSuccess } from "src/notifications";
import { showObjectSuccess } from "src/notifications";
const showAccessListModal = (id: number | "new") => {
EasyModal.show(AccessListModal, { id });
@@ -72,7 +72,7 @@ const AccessListModal = EasyModal.create(({ id, visible, remove }: Props) => {
setAccessList(payload, {
onError: (err: any) => setErrorMsg(<T id={err.message} />),
onSuccess: () => {
showSuccess(intl.formatMessage({ id: "notification.access-saved" }));
showObjectSuccess("access-list", "saved");
remove();
},
onSettled: () => {
@@ -110,7 +110,7 @@ const AccessListModal = EasyModal.create(({ id, visible, remove }: Props) => {
<Form>
<Modal.Header closeButton>
<Modal.Title>
<T id={data?.id ? "access.edit" : "access.new"} />
<T id={data?.id ? "object.edit" : "object.add"} tData={{ object: "access-list" }} />
</Modal.Title>
</Modal.Header>
<Modal.Body className="p-0">
@@ -186,13 +186,13 @@ const AccessListModal = EasyModal.create(({ id, visible, remove }: Props) => {
</Field>
<div className="my-3">
<h3 className="py-2">
<T id="generic.flags.title" />
<T id="options" />
</h3>
<div className="divide-y">
<div>
<label className="row" htmlFor="satisfyAny">
<span className="col">
<T id="access.satisfy-any" />
<T id="access-list.satisfy-any" />
</span>
<span className="col-auto">
<Field name="satisfyAny" type="checkbox">
@@ -224,7 +224,7 @@ const AccessListModal = EasyModal.create(({ id, visible, remove }: Props) => {
<div>
<label className="row" htmlFor="passAuth">
<span className="col">
<T id="access.pass-auth" />
<T id="access-list.pass-auth" />
</span>
<span className="col-auto">
<Field name="passAuth" type="checkbox">

View File

@@ -13,8 +13,8 @@ import {
SSLOptionsFields,
} from "src/components";
import { useDeadHost, useSetDeadHost } from "src/hooks";
import { intl, T } from "src/locale";
import { showSuccess } from "src/notifications";
import { T } from "src/locale";
import { showObjectSuccess } from "src/notifications";
const showDeadHostModal = (id: number | "new") => {
EasyModal.show(DeadHostModal, { id });
@@ -42,7 +42,7 @@ const DeadHostModal = EasyModal.create(({ id, visible, remove }: Props) => {
setDeadHost(payload, {
onError: (err: any) => setErrorMsg(<T id={err.message} />),
onSuccess: () => {
showSuccess(intl.formatMessage({ id: "notification.dead-host-saved" }));
showObjectSuccess("dead-host", "saved");
remove();
},
onSettled: () => {
@@ -80,7 +80,7 @@ const DeadHostModal = EasyModal.create(({ id, visible, remove }: Props) => {
<Form>
<Modal.Header closeButton>
<Modal.Title>
<T id={data?.id ? "dead-host.edit" : "dead-host.new"} />
<T id={data?.id ? "object.edit" : "object.add"} tData={{ object: "dead-host" }} />
</Modal.Title>
</Modal.Header>
<Modal.Body className="p-0">

View File

@@ -7,7 +7,8 @@ import { Button } from "src/components";
import { T } from "src/locale";
interface ShowProps {
title: string;
title?: ReactNode;
tTitle?: string;
children: ReactNode;
onConfirm: () => Promise<void> | void;
invalidations?: any[];
@@ -19,59 +20,59 @@ const showDeleteConfirmModal = (props: ShowProps) => {
EasyModal.show(DeleteConfirmModal, props);
};
const DeleteConfirmModal = EasyModal.create(({ title, children, onConfirm, invalidations, visible, remove }: Props) => {
const queryClient = useQueryClient();
const [error, setError] = useState<ReactNode | null>(null);
const [isSubmitting, setIsSubmitting] = useState(false);
const DeleteConfirmModal = EasyModal.create(
({ title, tTitle, children, onConfirm, invalidations, visible, remove }: Props) => {
const queryClient = useQueryClient();
const [error, setError] = useState<ReactNode | null>(null);
const [isSubmitting, setIsSubmitting] = useState(false);
const onSubmit = async () => {
if (isSubmitting) return;
setIsSubmitting(true);
setError(null);
try {
await onConfirm();
remove();
// invalidate caches as requested
invalidations?.forEach((inv) => {
queryClient.invalidateQueries({ queryKey: inv });
});
} catch (err: any) {
setError(<T id={err.message} />);
}
setIsSubmitting(false);
};
const onSubmit = async () => {
if (isSubmitting) return;
setIsSubmitting(true);
setError(null);
try {
await onConfirm();
remove();
// invalidate caches as requested
invalidations?.forEach((inv) => {
queryClient.invalidateQueries({ queryKey: inv });
});
} catch (err: any) {
setError(<T id={err.message} />);
}
setIsSubmitting(false);
};
return (
<Modal show={visible} onHide={remove}>
<Modal.Header closeButton>
<Modal.Title>
<T id={title} />
</Modal.Title>
</Modal.Header>
<Modal.Body>
<Alert variant="danger" show={!!error} onClose={() => setError(null)} dismissible>
{error}
</Alert>
{children}
</Modal.Body>
<Modal.Footer>
<Button data-bs-dismiss="modal" onClick={remove} disabled={isSubmitting}>
<T id="cancel" />
</Button>
<Button
type="submit"
actionType="primary"
className="ms-auto btn-red"
data-bs-dismiss="modal"
isLoading={isSubmitting}
disabled={isSubmitting}
onClick={onSubmit}
>
<T id="action.delete" />
</Button>
</Modal.Footer>
</Modal>
);
});
return (
<Modal show={visible} onHide={remove}>
<Modal.Header closeButton>
<Modal.Title>{tTitle ? <T id={tTitle} /> : title ? title : null}</Modal.Title>
</Modal.Header>
<Modal.Body>
<Alert variant="danger" show={!!error} onClose={() => setError(null)} dismissible>
{error}
</Alert>
{children}
</Modal.Body>
<Modal.Footer>
<Button data-bs-dismiss="modal" onClick={remove} disabled={isSubmitting}>
<T id="cancel" />
</Button>
<Button
type="submit"
actionType="primary"
className="ms-auto btn-red"
data-bs-dismiss="modal"
isLoading={isSubmitting}
disabled={isSubmitting}
onClick={onSubmit}
>
<T id="action.delete" />
</Button>
</Modal.Footer>
</Modal>
);
},
);
export { showDeleteConfirmModal };

View File

@@ -46,7 +46,7 @@ const EventDetailsModal = EasyModal.create(({ id, visible, remove }: Props) => {
</Modal.Body>
<Modal.Footer>
<Button data-bs-dismiss="modal" onClick={remove}>
<T id="close" />
<T id="action.close" />
</Button>
</Modal.Footer>
</>

View File

@@ -0,0 +1,4 @@
.active {
border-color: var(--tblr-orange) !important;
}

View File

@@ -9,6 +9,7 @@ import { setPermissions } from "src/api/backend";
import { Button, Loading } from "src/components";
import { useUser } from "src/hooks";
import { T } from "src/locale";
import styles from "./PermissionsModal.module.css";
const showPermissionsModal = (id: number) => {
EasyModal.show(PermissionsModal, { id });
@@ -39,7 +40,18 @@ const PermissionsModal = EasyModal.create(({ id, visible, remove }: Props) => {
setIsSubmitting(false);
};
const getClasses = (active: boolean) => {
return cn("btn", active ? styles.active : null, {
active,
"bg-orange-lt": active,
});
};
const getPermissionButtons = (field: any, form: any) => {
const isManage = field.value === "manage";
const isView = field.value === "view";
const isHidden = field.value === "hidden";
return (
<div>
<div className="btn-group w-100" role="group">
@@ -53,7 +65,7 @@ const PermissionsModal = EasyModal.create(({ id, visible, remove }: Props) => {
checked={field.value === "manage"}
onChange={() => form.setFieldValue(field.name, "manage")}
/>
<label htmlFor={`${field.name}-manage`} className={cn("btn", { active: field.value === "manage" })}>
<label htmlFor={`${field.name}-manage`} className={getClasses(isManage)}>
<T id="permissions.manage" />
</label>
<input
@@ -66,7 +78,7 @@ const PermissionsModal = EasyModal.create(({ id, visible, remove }: Props) => {
checked={field.value === "view"}
onChange={() => form.setFieldValue(field.name, "view")}
/>
<label htmlFor={`${field.name}-view`} className={cn("btn", { active: field.value === "view" })}>
<label htmlFor={`${field.name}-view`} className={getClasses(isView)}>
<T id="permissions.view" />
</label>
<input
@@ -79,7 +91,7 @@ const PermissionsModal = EasyModal.create(({ id, visible, remove }: Props) => {
checked={field.value === "hidden"}
onChange={() => form.setFieldValue(field.name, "hidden")}
/>
<label htmlFor={`${field.name}-hidden`} className={cn("btn", { active: field.value === "hidden" })}>
<label htmlFor={`${field.name}-hidden`} className={getClasses(isHidden)}>
<T id="permissions.hidden" />
</label>
</div>
@@ -142,7 +154,7 @@ const PermissionsModal = EasyModal.create(({ id, visible, remove }: Props) => {
/>
<label
htmlFor={`${field.name}-user`}
className={cn("btn", { active: field.value === "user" })}
className={getClasses(field.value === "user")}
>
<T id="permissions.visibility.user" />
</label>
@@ -158,7 +170,7 @@ const PermissionsModal = EasyModal.create(({ id, visible, remove }: Props) => {
/>
<label
htmlFor={`${field.name}-all`}
className={cn("btn", { active: field.value === "all" })}
className={getClasses(field.value === "all")}
>
<T id="permissions.visibility.all" />
</label>
@@ -170,7 +182,7 @@ const PermissionsModal = EasyModal.create(({ id, visible, remove }: Props) => {
<>
<div className="mb-3">
<label htmlFor="ignored" className="form-label">
<T id="proxy-hosts.title" />
<T id="proxy-hosts" />
</label>
<Field name="proxyHosts">
{({ field, form }: any) => getPermissionButtons(field, form)}
@@ -178,7 +190,7 @@ const PermissionsModal = EasyModal.create(({ id, visible, remove }: Props) => {
</div>
<div className="mb-3">
<label htmlFor="ignored" className="form-label">
<T id="redirection-hosts.title" />
<T id="redirection-hosts" />
</label>
<Field name="redirectionHosts">
{({ field, form }: any) => getPermissionButtons(field, form)}
@@ -186,7 +198,7 @@ const PermissionsModal = EasyModal.create(({ id, visible, remove }: Props) => {
</div>
<div className="mb-3">
<label htmlFor="ignored" className="form-label">
<T id="dead-hosts.title" />
<T id="dead-hosts" />
</label>
<Field name="deadHosts">
{({ field, form }: any) => getPermissionButtons(field, form)}
@@ -194,7 +206,7 @@ const PermissionsModal = EasyModal.create(({ id, visible, remove }: Props) => {
</div>
<div className="mb-3">
<label htmlFor="ignored" className="form-label">
<T id="streams.title" />
<T id="streams" />
</label>
<Field name="streams">
{({ field, form }: any) => getPermissionButtons(field, form)}
@@ -202,7 +214,7 @@ const PermissionsModal = EasyModal.create(({ id, visible, remove }: Props) => {
</div>
<div className="mb-3">
<label htmlFor="ignored" className="form-label">
<T id="access.title" />
<T id="access-lists" />
</label>
<Field name="accessLists">
{({ field, form }: any) => getPermissionButtons(field, form)}
@@ -210,7 +222,7 @@ const PermissionsModal = EasyModal.create(({ id, visible, remove }: Props) => {
</div>
<div className="mb-3">
<label htmlFor="ignored" className="form-label">
<T id="certificates.title" />
<T id="certificates" />
</label>
<Field name="certificates">
{({ field, form }: any) => getPermissionButtons(field, form)}
@@ -225,8 +237,7 @@ const PermissionsModal = EasyModal.create(({ id, visible, remove }: Props) => {
</Button>
<Button
type="submit"
actionType="primary"
className="ms-auto"
className="ms-auto btn-orange"
data-bs-dismiss="modal"
isLoading={isSubmitting}
disabled={isSubmitting}

View File

@@ -10,14 +10,15 @@ import {
Button,
DomainNamesField,
Loading,
LocationsFields,
NginxConfigField,
SSLCertificateField,
SSLOptionsFields,
} from "src/components";
import { useProxyHost, useSetProxyHost } from "src/hooks";
import { intl, T } from "src/locale";
import { T } from "src/locale";
import { validateNumber, validateString } from "src/modules/Validations";
import { showSuccess } from "src/notifications";
import { showObjectSuccess } from "src/notifications";
const showProxyHostModal = (id: number | "new") => {
EasyModal.show(ProxyHostModal, { id });
@@ -45,7 +46,7 @@ const ProxyHostModal = EasyModal.create(({ id, visible, remove }: Props) => {
setProxyHost(payload, {
onError: (err: any) => setErrorMsg(<T id={err.message} />),
onSuccess: () => {
showSuccess(intl.formatMessage({ id: "notification.proxy-host-saved" }));
showObjectSuccess("proxy-host", "saved");
remove();
},
onSettled: () => {
@@ -95,7 +96,7 @@ const ProxyHostModal = EasyModal.create(({ id, visible, remove }: Props) => {
<Form>
<Modal.Header closeButton>
<Modal.Title>
<T id={data?.id ? "proxy-host.edit" : "proxy-host.new"} />
<T id={data?.id ? "object.edit" : "object.add"} tData={{ object: "proxy-host" }} />
</Modal.Title>
</Modal.Header>
<Modal.Body className="p-0">
@@ -251,7 +252,7 @@ const ProxyHostModal = EasyModal.create(({ id, visible, remove }: Props) => {
<AccessField />
<div className="my-3">
<h4 className="py-2">
<T id="generic.flags.title" />
<T id="options" />
</h4>
<div className="divide-y">
<div>
@@ -327,7 +328,7 @@ const ProxyHostModal = EasyModal.create(({ id, visible, remove }: Props) => {
</div>
</div>
<div className="tab-pane" id="tab-locations" role="tabpanel">
locations TODO
<LocationsFields initialValues={data?.locations || []} />
</div>
<div className="tab-pane" id="tab-ssl" role="tabpanel">
<SSLCertificateField

View File

@@ -14,9 +14,9 @@ import {
SSLOptionsFields,
} from "src/components";
import { useRedirectionHost, useSetRedirectionHost } from "src/hooks";
import { intl, T } from "src/locale";
import { T } from "src/locale";
import { validateString } from "src/modules/Validations";
import { showSuccess } from "src/notifications";
import { showObjectSuccess } from "src/notifications";
const showRedirectionHostModal = (id: number | "new") => {
EasyModal.show(RedirectionHostModal, { id });
@@ -44,7 +44,7 @@ const RedirectionHostModal = EasyModal.create(({ id, visible, remove }: Props) =
setRedirectionHost(payload, {
onError: (err: any) => setErrorMsg(<T id={err.message} />),
onSuccess: () => {
showSuccess(intl.formatMessage({ id: "notification.redirection-host-saved" }));
showObjectSuccess("redirection-host", "saved");
remove();
},
onSettled: () => {
@@ -90,7 +90,10 @@ const RedirectionHostModal = EasyModal.create(({ id, visible, remove }: Props) =
<Form>
<Modal.Header closeButton>
<Modal.Title>
<T id={data?.id ? "redirection-host.edit" : "redirection-host.new"} />
<T
id={data?.id ? "object.edit" : "object.add"}
tData={{ object: "redirection-host" }}
/>
</Modal.Title>
</Modal.Header>
<Modal.Body className="p-0">
@@ -211,7 +214,7 @@ const RedirectionHostModal = EasyModal.create(({ id, visible, remove }: Props) =
</div>
<div className="my-3">
<h4 className="py-2">
<T id="generic.flags.title" />
<T id="options" />
</h4>
<div className="divide-y">
<div>

View File

@@ -5,9 +5,9 @@ import { Alert } from "react-bootstrap";
import Modal from "react-bootstrap/Modal";
import { Button, Loading, SSLCertificateField, SSLOptionsFields } from "src/components";
import { useSetStream, useStream } from "src/hooks";
import { intl, T } from "src/locale";
import { T } from "src/locale";
import { validateNumber, validateString } from "src/modules/Validations";
import { showSuccess } from "src/notifications";
import { showObjectSuccess } from "src/notifications";
const showStreamModal = (id: number | "new") => {
EasyModal.show(StreamModal, { id });
@@ -35,7 +35,7 @@ const StreamModal = EasyModal.create(({ id, visible, remove }: Props) => {
setStream(payload, {
onError: (err: any) => setErrorMsg(<T id={err.message} />),
onSuccess: () => {
showSuccess(intl.formatMessage({ id: "notification.stream-saved" }));
showObjectSuccess("stream", "saved");
remove();
},
onSettled: () => {
@@ -72,7 +72,7 @@ const StreamModal = EasyModal.create(({ id, visible, remove }: Props) => {
<Form>
<Modal.Header closeButton>
<Modal.Title>
<T id={data?.id ? "stream.edit" : "stream.new"} />
<T id={data?.id ? "object.edit" : "object.add"} tData={{ object: "stream" }} />
</Modal.Title>
</Modal.Header>
<Modal.Body className="p-0">
@@ -180,7 +180,7 @@ const StreamModal = EasyModal.create(({ id, visible, remove }: Props) => {
className="form-label"
htmlFor="forwardingPort"
>
<T id="stream.forward-port" />
<T id="host.forward-port" />
</label>
<input
id="forwardingPort"

View File

@@ -7,7 +7,7 @@ import { Button, Loading } from "src/components";
import { useSetUser, useUser } from "src/hooks";
import { intl, T } from "src/locale";
import { validateEmail, validateString } from "src/modules/Validations";
import { showSuccess } from "src/notifications";
import { showObjectSuccess } from "src/notifications";
const showUserModal = (id: number | "me" | "new") => {
EasyModal.show(UserModal, { id });
@@ -48,7 +48,7 @@ const UserModal = EasyModal.create(({ id, visible, remove }: Props) => {
setUser(payload, {
onError: (err: any) => setErrorMsg(err.message),
onSuccess: () => {
showSuccess(intl.formatMessage({ id: "notification.user-saved" }));
showObjectSuccess("user", "saved");
remove();
},
onSettled: () => {
@@ -83,7 +83,7 @@ const UserModal = EasyModal.create(({ id, visible, remove }: Props) => {
<Form>
<Modal.Header closeButton>
<Modal.Title>
<T id={data?.id ? "user.edit" : "user.new"} />
<T id={data?.id ? "object.edit" : "object.add"} tData={{ object: "user" }} />
</Modal.Title>
</Modal.Header>
<Modal.Body>
@@ -172,7 +172,7 @@ const UserModal = EasyModal.create(({ id, visible, remove }: Props) => {
{currentUser && data && currentUser?.id !== data?.id ? (
<div className="my-3">
<h4 className="py-2">
<T id="user.flags.title" />
<T id="options" />
</h4>
<div className="divide-y">
<div>
@@ -227,8 +227,7 @@ const UserModal = EasyModal.create(({ id, visible, remove }: Props) => {
</Button>
<Button
type="submit"
actionType="primary"
className="ms-auto"
className="ms-auto btn-orange"
data-bs-dismiss="modal"
isLoading={isSubmitting}
disabled={isSubmitting}