Files
2023-04-25 10:47:44 +10:00

187 lines
6.8 KiB
JavaScript

import React, { useState, useEffect } from "react";
import { Card, Button, FormBuilder, Loader } from "../../components/";
import {
settings_get, settings_put, createFormBackend, FormObjToJSON, nop,
} from "../../helpers/";
import { Backend } from "../../model/";
import { t } from "../../locales/";
import "./form.scss";
/*
* This is the automatic form generation that we uses in:
* login page, admin console, form filetype (eg: mysql & ldap plugin)
*
* FAQ:
* Why reinvent the wheel? no existing library were fitting all the use cases so we made our own
* How does that work?
* 1. window.CONFIG["connection"] contains all the valid connections one can make with all sort
of prefilled data
* 2. Backend.all gives the specs of the login form as generated by the relevant backend plugin
* 3. The FormBuilder component generates the form from the specs generated by createFormBackend
*/
export function Form({
onLoadingChange = nop, onError = nop, onSubmit = nop,
}) {
const [enabledBackends, setEnabledBackends] = useState([]);
const [selectedTab, setSelectedTab] = useState(null);
const [hasUserInteracted, setHasUserInteracted] = useState(false);
useEffect(() => {
Backend.all().then((backend) => {
onLoadingChange(false);
const backends = window.CONFIG["connections"].reduce((acc, conn) => {
const f = createFormBackend(backend, conn);
if (Object.keys(f).length > 0) {
acc.push(f);
}
return acc;
}, []);
setEnabledBackends(backends);
setSelectedTab((function() {
const select = settings_get("login_tab");
if (select !== null && select < backends.length) {
setSelectedTab(select);
}
if (backends.length === 0) return null;
else if (backends.length < 4) return 0;
else if (backends.length < 5) return 1;
return 2;
}()));
}).catch((err) => onError(err));
return () => {
settings_put("login_tab", selectedTab);
};
}, []);
const onFormChange = (p) => {
setEnabledBackends(enabledBackends.map((backend) => (backend)));
};
const onSubmitForm = (e) => {
e.preventDefault();
const formData = FormObjToJSON((() => {
const tmp = enabledBackends[selectedTab];
return tmp[Object.keys(tmp)[0]];
})());
delete formData["image"];
delete formData["label"];
delete formData["advanced"];
onSubmit(formData);
};
const onTypeChange = (tabn) => {
setHasUserInteracted(true);
setSelectedTab(tabn);
};
const renderForm = ($input, props, struct, onChange) => {
if (struct.type === "image") {
return (
<div className="center">
{ $input }
</div>
);
} else if (struct.enabled === true) {
return null;
} else if (struct.label === "advanced") {
return (
<label style={{ color: "rgba(0,0,0,0.4)" }}>
{ $input }
{ t("Advanced") }
</label>
);
}
return (
<label htmlFor={props.params["id"]}
className={`no-select input_type_${props.params["type"]}`}>
<div>
{ $input }
</div>
</label>
);
};
return (
<div className="no-select component_page_connection_form">
{
enabledBackends.length > 1 && (
<Card role="navigation"
className={`buttons ${((window.innerWidth < 600) ? "scroll-x" : "")}`}>
{
enabledBackends.map((backend, i) => {
const key = Object.keys(backend)[0];
if (!backend[key]) return null;
return (
<Button
key={`menu-${i}`}
className={i === selectedTab ? "active primary" : ""}
onClick={() => onTypeChange(i)}
>
{ backend[key].label.value }
</Button>
);
})
}
</Card>
)
}
{
enabledBackends.map((form, i) => {
const key = Object.keys(form)[0];
if (selectedTab !== i) return null;
const auth = window.CONFIG["auth"];
if (auth.indexOf(key) !== -1 || auth.indexOf(form[key].label.value) !== -1) {
return hasUserInteracted === false && enabledBackends.length > 1 ? (
<Button
onClick={() => onSubmit({
middleware: true,
label: form[key].label.value,
})}
theme="emphasis"
style={{ padding: "8px" }}
key={`sso-${i}`}>
{ t("CONNECT") }
</Button>
) : (
<LoaderWithTimeout
key={`loading-${i}`}
timeout={100}
callback={() => onSubmit({
middleware: true,
label: form[key].label.value,
})} />
);
}
return (
<Card className="formBody" key={`form${i}`}>
<form onSubmit={(e) => onSubmitForm(e)}>
<FormBuilder
form={form[key]}
onChange={onFormChange}
render={renderForm}
autoComplete />
<Button theme="emphasis">{ t("CONNECT") }</Button>
</form>
</Card>
);
})
}
</div>
);
}
function LoaderWithTimeout({ callback = nop, timeout = 0 }) {
useEffect(() => {
const t = setTimeout(() => callback(), timeout);
return () => {
clearTimeout(t);
};
});
return (
<Loader />
);
}