This commit is contained in:
rizky 2024-04-15 01:21:16 -07:00
parent 9cde4fb165
commit ad557ca5c0
5 changed files with 136 additions and 24 deletions

View File

@ -32,6 +32,11 @@ export const Form: FC<FMProps> = (props) => {
promises: [], promises: [],
done: [], done: [],
}, },
submit: {
timeout: null as any,
promises: [],
done: [],
},
}, },
field_def: {}, field_def: {},
props: {} as any, props: {} as any,
@ -90,11 +95,6 @@ export const Form: FC<FMProps> = (props) => {
} }
const toaster_el = document.getElementsByClassName("prasi-toaster")[0]; const toaster_el = document.getElementsByClassName("prasi-toaster")[0];
const childs = get(
body,
"props.meta.item.component.props.body.content.childs"
) as any[];
return ( return (
<form <form
onSubmit={(e) => { onSubmit={(e) => {

View File

@ -7,20 +7,47 @@ export const TypeCustom: FC<{ field: FieldLocal; fm: FMLocal }> = ({
field, field,
fm, fm,
}) => { }) => {
const local = useLocal({ custom: null as any }, async () => { const local = useLocal({
if (field.custom) { custom: null as any,
local.custom = await field.custom(); exec: false,
local.render(); result: null as any,
}
}); });
if (!local.custom && field.custom) {
console.log("field", field.custom);
local.custom = field.custom;
}
if (!local.exec) {
local.exec = true;
const callback = (value: any, should_render: boolean) => {
local.result = value;
if (should_render) {
local.render();
setTimeout(() => {
local.exec = false;
}, 100);
}
};
if (field.custom) {
const res = local.custom();
if (res instanceof Promise) {
res.then((value) => {
callback(value, true);
});
} else {
callback(res, false);
}
}
}
let el = null as any; let el = null as any;
if (local.custom) { if (local.result) {
if (isValidElement(local.custom)) { if (isValidElement(local.result)) {
el = local.custom; el = local.result;
} else { } else {
if (local.custom.field === "text") { if (local.result.field === "text") {
el = <FieldTypeText field={field} fm={fm} prop={local.custom} />; el = <FieldTypeText field={field} fm={fm} prop={local.result} />;
} }
} }
} }

View File

@ -1,21 +1,34 @@
import { FC } from "react"; import { FC } from "react";
import { FMLocal, FieldLocal } from "../../typings"; import { FMLocal, FieldLocal } from "../../typings";
import { useLocal } from "@/utils/use-local"; import { useLocal } from "@/utils/use-local";
import parser from "any-date-parser";
export type PropTypeText = { export type PropTypeText = {
type: "text" | "password" | "number"; type: "text" | "password" | "number" | "date" | "datetime";
}; };
const parse = parser.exportAsFunctionAny("en-US");
export const FieldTypeText: FC<{ export const FieldTypeText: FC<{
field: FieldLocal; field: FieldLocal;
fm: FMLocal; fm: FMLocal;
prop: PropTypeText; prop: PropTypeText;
}> = ({ field, fm, prop }) => { }> = ({ field, fm, prop }) => {
const input = useLocal({}); const input = useLocal({});
const value = fm.data[field.name]; let value: any = fm.data[field.name];
field.input = input; field.input = input;
field.prop = prop; field.prop = prop;
if (["date", "datetime"].includes(prop.type)) {
if (typeof value === "string") {
let date = parse(value);
if (typeof date === "object" && date instanceof Date) {
if (prop.type === "date") value = date.toISOString().substring(0, 10);
else if (prop.type === "datetime") value = date.toISOString();
}
}
}
return ( return (
<> <>
<input <input

View File

@ -63,7 +63,7 @@ export type FMInternal = {
status: "init" | "resizing" | "loading" | "saving" | "ready"; status: "init" | "resizing" | "loading" | "saving" | "ready";
data: any; data: any;
reload: () => Promise<void>; reload: () => Promise<void>;
submit: () => Promise<void>; submit: () => Promise<boolean>;
events: { events: {
on_change: (name: string, new_value: any) => void; on_change: (name: string, new_value: any) => void;
}; };
@ -82,6 +82,11 @@ export type FMInternal = {
promises: Promise<void>[]; promises: Promise<void>[];
done: any[]; done: any[];
}; };
submit: {
promises: Promise<boolean>[];
timeout: ReturnType<typeof setTimeout>;
done: any[];
};
}; };
props: Exclude<FMProps, "body" | "PassProp">; props: Exclude<FMProps, "body" | "PassProp">;
size: { size: {
@ -204,6 +209,6 @@ export type CustomField =
| { field: "relation"; type: "has-many" | "has-one" }; | { field: "relation"; type: "has-many" | "has-one" };
export const FieldTypeCustom = `type CustomField = export const FieldTypeCustom = `type CustomField =
{ field: "text", type: "text" | "password" | "number" } { field: "text", type: "text" | "password" | "number" | "date" | "datetime" }
| { field: "relation", type: "has-many" | "has-one" } | { field: "relation", type: "has-many" | "has-one" }
`; `;

View File

@ -1,6 +1,6 @@
import { parseGenField } from "@/gen/utils"; import { parseGenField } from "@/gen/utils";
import get from "lodash.get"; import get from "lodash.get";
import { Loader2 } from "lucide-react"; import { AlertTriangle, Check, Loader2 } from "lucide-react";
import { toast } from "sonner"; import { toast } from "sonner";
import { FMLocal, FMProps } from "../typings"; import { FMLocal, FMProps } from "../typings";
import { editorFormData } from "./ed-data"; import { editorFormData } from "./ed-data";
@ -89,10 +89,77 @@ export const formInit = (fm: FMLocal, props: FMProps) => {
return promise; return promise;
}; };
fm.submit = async () => { fm.submit = () => {
if (typeof fm.props.on_submit === "function") { const promise = new Promise<boolean>(async (done) => {
fm.props.on_submit({ fm, form: fm.data, error: fm.error.object }); fm.internal.submit.done.push(done);
} clearTimeout(fm.internal.submit.timeout);
fm.internal.submit.timeout = setTimeout(async () => {
const done_all = (val: boolean) => {
for (const d of fm.internal.submit.done) {
d(val);
}
fm.internal.submit.done = [];
fm.render();
};
if (typeof fm.props.on_submit === "function") {
if (fm.props.sonar === "on") {
toast.loading(
<>
<Loader2 className="c-h-4 c-w-4 c-animate-spin" />
Submitting...
</>
);
}
const success = await fm.props.on_submit({
fm,
form: fm.data,
error: fm.error.object,
});
toast.dismiss();
done_all(success);
if (fm.props.sonar === "on") {
setTimeout(() => {
toast.dismiss();
if (!success) {
toast.error(
<div className="c-flex c-text-red-600 c-items-center">
<AlertTriangle className="c-h-4 c-w-4 c-mr-1" />
Save Failed, please correct{" "}
{Object.keys(fm.error.list).length} errors.
</div>,
{
dismissible: true,
className: css`
background: #ffecec;
border: 2px solid red;
`,
}
);
} else {
toast.success(
<div className="c-flex c-text-green-700 c-items-center">
<Check className="c-h-4 c-w-4 c-mr-1 " />
Done
</div>,
{
className: css`
background: #e4ffed;
border: 2px solid green;
`,
}
);
}
}, 100);
}
}
}, 100);
});
fm.internal.submit.promises.push(promise);
return promise;
}; };
if (typeof fm.props.on_init === "function") { if (typeof fm.props.on_init === "function") {
fm.props.on_init({ fm, submit: fm.submit, reload: fm.reload }); fm.props.on_init({ fm, submit: fm.submit, reload: fm.reload });