fix search

This commit is contained in:
rizrmd 2024-06-16 01:42:21 -07:00
parent bf7ed71c06
commit 46aeccc7f9
17 changed files with 251 additions and 207 deletions

View File

@ -2,6 +2,7 @@ import { FC } from "react";
import { BaseForm } from "../form/base/BaseForm";
import { FilterLocal } from "./utils/types";
import { useLocal } from "lib/utils/use-local";
import { getFilter } from "./utils/get-filter";
export const FilterContent: FC<{
mode: string;
@ -56,8 +57,18 @@ export const FilterContent: FC<{
}
}
.search-focus {
width: 250px !important;
.field.search {
&.focused,
&.filled {
input {
width: 250px !important;
}
}
&.filled {
.field-outer {
border: 2px solid blue;
}
}
}
}
@ -79,7 +90,13 @@ export const FilterContent: FC<{
<BaseForm
data={filter.data}
on_submit={(form) => {
console.log("skrg nyubmit");
const f = getFilter(filter.name);
if (f) {
for (const list of Object.values(f.list.ref)) {
list.reload();
}
}
}}
render={internal.render}
>
@ -97,4 +114,3 @@ export const FilterContent: FC<{
</div>
);
};

View File

@ -85,11 +85,8 @@ export const FilterField: FC<{
type: "input",
sub_type: "search",
placeholder: "Search...",
onFocus(e) {
e.currentTarget.classList.add("search-focus");
},
onBlur(e) {
e.currentTarget.classList.remove("search-focus");
filter.form?.submit();
},
}}
/>
@ -187,4 +184,3 @@ export const FilterField: FC<{
</BaseField>
);
};

View File

@ -43,17 +43,6 @@ export const MasterFilter: FC<FilterProps> = ({
wf.filter.ref[_item.id] = filter;
wf.list.render();
}
// if (!filter_window.prasi_filter) {
// filter_window.prasi_filter = {};
// }
// const pf = filter_window.prasi_filter;
// if (pf) {
// const pathname = getPathname();
// if (!pf[pathname]) pf[pathname] = {};
// if (!pf[pathname][name]) pf[pathname][name] = {};
// pf[pathname][name][_item.id] = filter;
// }
}
if (mode === "popup") {
@ -111,4 +100,3 @@ export const MasterFilter: FC<FilterProps> = ({
</>
);
};

View File

@ -0,0 +1,33 @@
import { GFCol } from "lib/gen/utils";
import { parseGenField } from "../../../..";
import { getFilter } from "../utils/get-filter";
import { parseSingleFilter } from "./single-filter";
import { softDeleteFilter } from "./soft-delete-filter";
export const filterWhere = (filter_name: string, p: any) => {
const f = getFilter(filter_name);
let where: any = {};
if (f) {
let fields: GFCol[] = [];
if (p.gen__fields) {
fields = parseGenField(p.gen__fields);
}
for (const pf of Object.values(f.filter.ref)) {
const w = parseSingleFilter(pf, fields);
for (const [k, v] of Object.entries(w)) {
where[k] = v;
}
}
}
if (p && p.opt__feature && p.sft__fields && p.sft__type) {
where = softDeleteFilter(where, {
feature: p.opt_feature,
field: p.sft__fields,
type: p.sft__type,
});
}
return where;
};

View File

@ -0,0 +1,114 @@
import { GFCol } from "lib/gen/utils";
import { FilterLocal } from "../utils/types";
export const parseSingleFilter = (filter: FilterLocal, fields: GFCol[]) => {
const where: any = {};
const AND: any[] = [];
const OR: any[] = [];
if (filter) {
for (const [name, value] of Object.entries(filter.data)) {
const type = filter.types[name];
const modifier = filter.modifiers[name];
switch (type) {
case "search-all":
fields
.filter((e) => e.type === "varchar" || e.type === "text")
.map((e) => {
OR.push({
[e.name]: {
contains: "%" + value + "%",
mode: "insensitive",
},
});
});
break;
case "text":
{
if (modifier === "contains")
where[name] = {
contains: "%" + value + "%",
mode: "insensitive",
};
else if (modifier === "starts_with")
where[name] = {
contains: value + "%",
mode: "insensitive",
};
else if (modifier === "ends_with")
where[name] = {
contains: "%" + value,
mode: "insensitive",
};
else if (modifier === "not_equal") {
where[name] = {
NOT: value,
};
} else if (modifier === "equal") {
where[name] = {
value,
};
}
}
break;
case "date":
{
let is_value_valid = true;
// TODO: pastikan value bisa diparse pakai any-date-parser
if (is_value_valid) {
if (modifier === "between") {
AND.push({ [name]: { gt: value } });
AND.push({ [name]: { lt: value } });
} else if (modifier === "greater_than") {
AND.push({ [name]: { gt: value } });
} else if (modifier === "less_than") {
AND.push({ [name]: { lt: value } });
}
}
}
break;
case "number":
{
if (modifier === "equal") {
AND.push({ [name]: { value } });
} else if (modifier === "not_equal") {
AND.push({ [name]: { NOT: value } });
} else if (modifier === "greater_than") {
AND.push({ [name]: { gt: value } });
} else if (modifier === "less_than") {
AND.push({ [name]: { lt: value } });
} else if (modifier === "between") {
AND.push({ [name]: { gt: value } });
AND.push({ [name]: { lt: value } });
}
}
break;
case "boolean":
{
if (modifier === "is_true") {
AND.push({ [name]: true });
} else if (modifier === "is_false") {
AND.push({ [name]: false });
}
}
break;
case "options":
{
if (modifier === "equal") {
AND.push({ [name]: { value } });
} else if (modifier === "not_equal") {
AND.push({ [name]: { NOT: value } });
} else if (modifier === "includes") {
AND.push({ [name]: { hasEvery: value } });
} else if (modifier === "excludes") {
AND.push({ [name]: { notIn: value } });
}
}
break;
}
}
}
if (AND.length > 0) where.AND = AND;
if (OR.length > 0) where.OR = OR;
return where;
};

View File

@ -1,4 +1,4 @@
import { isEmptyString } from "./is-empty-string";
import { isEmptyString } from "../../../utils/is-empty-string";
export const softDeleteFilter = (
where: any,
@ -11,16 +11,15 @@ export const softDeleteFilter = (
const feature = soft.feature || [];
if (!feature.find((e) => e === "soft_delete")) return where;
const defaultParam = typeof where === "object" ? where : {};
if (isEmptyString(soft.field) || isEmptyString(soft.type))
return defaultParam;
const result = {
AND: [
typeof defaultParam === "object" ? { ...defaultParam } : {},
{
[soft.field]:
soft.type === "boolean"
? false
:null,
[soft.field]: soft.type === "boolean" ? false : null,
},
],
};

View File

@ -1,104 +0,0 @@
import { getPathname } from "lib/utils/pathname";
import { filter_window } from "./types";
export const filterWhere = (filter_name: string) => {
const pf = filter_window.prasi_filter?.[getPathname()]?.[filter_name];
const where: any = {};
const AND: any[] = [];
if (pf) {
console.log(pf);
for (const [k, filter] of Object.entries(pf)) {
for (const [name, value] of Object.entries(filter.data)) {
const type = filter.types[name];
const modifier = filter.modifiers[name];
switch (type) {
case "text":
{
if (modifier === "contains")
where[name] = {
contains: "%" + value + "%",
mode: "insensitive",
};
else if (modifier === "starts_with")
where[name] = {
contains: value + "%",
mode: "insensitive",
};
else if (modifier === "ends_with")
where[name] = {
contains: "%" + value,
mode: "insensitive",
};
else if (modifier === "not_equal") {
where[name] = {
NOT: value,
};
} else if (modifier === "equal") {
where[name] = {
value,
};
}
}
break;
case "date":
{
let is_value_valid = true;
// TODO: pastikan value bisa diparse pakai any-date-parser
if (is_value_valid) {
if (modifier === "between") {
AND.push({ [name]: { gt: value } });
AND.push({ [name]: { lt: value } });
} else if (modifier === "greater_than") {
AND.push({ [name]: { gt: value } });
} else if (modifier === "less_than") {
AND.push({ [name]: { lt: value } });
}
}
}
break;
case "number":
{
if (modifier === "equal") {
AND.push({ [name]: { value } });
} else if (modifier === "not_equal") {
AND.push({ [name]: { NOT: value } });
} else if (modifier === "greater_than") {
AND.push({ [name]: { gt: value } });
} else if (modifier === "less_than") {
AND.push({ [name]: { lt: value } });
} else if (modifier === "between") {
AND.push({ [name]: { gt: value } });
AND.push({ [name]: { lt: value } });
}
}
break;
case "boolean":
{
if (modifier === "is_true") {
AND.push({ [name]: true });
} else if (modifier === "is_false") {
AND.push({ [name]: false });
}
}
break;
case "options":
{
if (modifier === "equal") {
AND.push({ [name]: { value } });
} else if (modifier === "not_equal") {
AND.push({ [name]: { NOT: value } });
} else if (modifier === "includes") {
AND.push({ [name]: { hasEvery: value } });
} else if (modifier === "excludes") {
AND.push({ [name]: { notIn: value } });
}
}
break;
}
}
}
if (AND.length > 0) where.AND = AND;
}
return where;
};

View File

@ -25,7 +25,6 @@ export const getFilter = (name: string) => {
},
},
};
return pf[pathname][name];
}
};

View File

@ -36,6 +36,7 @@ export const BaseField = (prop: {
<label
className={cx(
"field",
name,
"c-flex",
css`
padding: 5px 0px 0px 10px;
@ -48,7 +49,13 @@ export const BaseField = (prop: {
w === "⅓" && "c-w-1/3",
w === "¼" && "c-w-1/4",
mode === "horizontal" && "c-flex-row c-items-center",
mode === "vertical" && "c-flex-col c-space-y-1"
mode === "vertical" && "c-flex-col c-space-y-1",
field.focused && "focused",
field.disabled && "disabled",
typeof fm.data[name] !== "undefined" &&
fm.data[name] !== null &&
fm.data[name] !== "" &&
"filled"
)}
>
{mode !== "hidden" && <Label field={field} fm={fm} />}

View File

@ -108,6 +108,7 @@ export const BaseForm = <T extends Record<string, any>>(
if (form.status === "init") {
form.status = "ready";
}
if (typeof props.is_form === "boolean") {
if (!props.is_form) {
return (
@ -132,6 +133,16 @@ export const BaseForm = <T extends Record<string, any>>(
}
}
if (form.internal.width === 0) {
if (form.internal.init_render > 30) {
return <>Failed to render BaseForm</>;
}
setTimeout(() => {
form.internal.init_render++;
form.render();
}, 50);
}
return (
<form
onSubmit={(e) => {
@ -165,4 +176,3 @@ export const BaseForm = <T extends Record<string, any>>(
</form>
);
};

View File

@ -6,6 +6,7 @@ export const default_base_form_local = {
data: {} as any,
internal: {
width: 0,
init_render: 0,
},
fm: null as null | FMLocal,
fields: {} as Record<string, any>,

View File

@ -140,6 +140,7 @@ export const FieldInput: FC<{
<div
className={cx(
"field-inner c-flex-1 c-flex c-items-center",
field.focused && "focused",
field.disabled && "c-pointer-events-none"
)}
>
@ -193,4 +194,3 @@ export const FieldInput: FC<{
</div>
);
};

View File

@ -3,7 +3,7 @@ import { useLocal } from "@/utils/use-local";
import parser from "any-date-parser";
import Datepicker from "lib/comps/custom/Datepicker";
import { EyeIcon, EyeOff } from "lucide-react";
import { FC, FocusEvent, MouseEvent } from "react";
import { FC, FocusEvent, KeyboardEvent, MouseEvent } from "react";
import { FMLocal, FieldLocal, FieldProp } from "../../typings";
import { FieldMoney } from "./TypeMoney";
import { FieldRichText } from "./TypeRichText";
@ -199,4 +199,3 @@ const isTimeString = (time: any) => {
const timePattern = /^([01]\d|2[0-3]):([0-5]\d)(:[0-5]\d)?$/;
return timePattern.test(time);
};

View File

@ -22,12 +22,11 @@ import DataGrid, {
} from "react-data-grid";
import { createPortal } from "react-dom";
import { Toaster, toast } from "sonner";
import { filterWhere } from "../filter/utils/filter-where";
import { filterWhere } from "../filter/parser/filter-where";
import { getFilter } from "../filter/utils/get-filter";
import { Skeleton } from "../ui/skeleton";
import "./TableList.css";
import { sortTree } from "./utils/sort-tree";
import { getFilter } from "../filter/utils/get-filter";
import { callback } from "chart.js/dist/helpers/helpers.core";
type OnRowClick = (arg: {
row: any;
@ -50,6 +49,7 @@ type TableListProp = {
on_init: (arg?: any) => any;
mode: "table" | "list" | "grid" | "auto";
_item: PrasiItem;
__props: any;
gen_fields: string[];
row_click: OnRowClick;
selected: SelectedRow;
@ -99,9 +99,8 @@ export const TableList: FC<TableListProp> = ({
show_header,
value,
cache_row,
__props,
}) => {
const where = get(w, `prasi_filter.${filter_name}`) ? {} : {};
const whereQuery = filterWhere("hello");
if (mode === "auto") {
if (w.isMobile) {
mode = "list";
@ -209,10 +208,11 @@ export const TableList: FC<TableListProp> = ({
local.render();
const orderBy = local.sort.orderBy || undefined;
const where = filterWhere(filter_name, __props);
const load_args: any = {
async reload() {},
where,
orderBy,
where,
paging: {
take: local.paging.take > 0 ? local.paging.take : undefined,
skip: local.paging.skip,
@ -228,19 +228,14 @@ export const TableList: FC<TableListProp> = ({
} else {
local.data = [...local.data, ...data];
}
local.status = "ready";
local.render();
};
if (result instanceof Promise) result.then(callback);
else callback(result);
}
}, [
on_load,
local.sort.orderBy,
where,
local.paging.take,
local.paging.skip,
]);
}, [on_load, local.sort.orderBy, local.paging.take, local.paging.skip]);
if (filter_name) {
const f = getFilter(filter_name);
@ -470,7 +465,7 @@ export const TableList: FC<TableListProp> = ({
if (local.data.length === 0) {
const load_args: any = {
async reload() {},
where,
where: {},
paging: {
take: local.paging.take > 0 ? local.paging.take : undefined,
skip: local.paging.skip,

View File

@ -34,47 +34,40 @@ export const on_load = ({
}
return `\
(arg: TableOnLoad) => {
if (isEditor) return [${JSON.stringify(sample)}];
(arg: TableOnLoad) => {
if (isEditor)
return [${JSON.stringify(sample)}];
return new Promise(async (done) => {
return new Promise(async (done) => {
let where = arg.where;
try {
if (!isEditor)
where = softDeleteFilter(where, {
feature: opt__feature,
field: sft__fields,
type: sft__type,
});
} catch (e) {}
if (arg.mode === 'count') {
return await db.${table}.count({
where: {
...where,
}
});
}
const items = await db.${table}.findMany({
select: ${JSON.stringify(select, null, 2).split("\n").join("\n ")},
orderBy: arg.orderBy || {
${pk}: "desc"
},
if (arg.mode === "count") {
return await db.${table}.count({
where: {
...where,
},
...arg.paging,
});
}
done(items);
})
}
const items = await db.${table}.findMany({
select: ${JSON.stringify(select, null, 2).split("\n").join("\n ")},
orderBy: arg.orderBy || {
id: "desc",
},
where: {
...where,
},
...arg.paging,
});
type TableOnLoad = {
reload: () => Promise<void>;
orderBy?: Record<string, "asc" | "desc">;
paging: { take: number; skip: number };
mode: 'count' | 'query';
where?: Record<string, any>;
}
`;
done(items);
});
};
type TableOnLoad = {
reload: () => Promise<void>;
orderBy?: Record<string, "asc" | "desc">;
paging: { take: number; skip: number };
mode: "count" | "query";
where?: any;
}`;
};

View File

@ -51,40 +51,39 @@ export const FilterField = lazify(
);
/** Generator */
export { generateMasterDetail } from "@/comps/md/gen/md-gen";
export { genTableEdit } from "@/comps/form/gen/gen-table-edit";
export { generateFilter as genereteFilter } from "@/comps/filter/gen/gen-filter";
export { generateRelation } from "@/comps/form/gen/gen-rel";
export { genTableEdit } from "@/comps/form/gen/gen-table-edit";
export { generateMasterDetail } from "@/comps/md/gen/md-gen";
export { parseGenField } from "@/gen/utils";
/** ETC */
export { filterModifier } from "@/comps/filter/utils/filter-modifier";
export { filterWhere } from "@/comps/filter/utils/filter-where";
export { generateField } from "@/comps/form/gen/gen-field";
export { generateForm } from "@/comps/form/gen/gen-form";
export {
FMLocal,
FieldTypeCustom,
fieldType,
formType,
} from "@/comps/form/typings";
export { MasterDetailType } from "@/comps/md/utils/typings";
export { FormatValue } from "@/utils/format-value";
export { GetValue } from "@/utils/get-value";
export { TableListType } from "@/comps/list/utils/typings";
export { generateTableList } from "@/comps/md/gen/gen-table-list";
export { generateSelect } from "@/comps/md/gen/md-select";
export { MasterDetailType } from "@/comps/md/utils/typings";
export { Button, FloatButton } from "@/comps/ui/button";
export { prasi_gen } from "@/gen/prasi_gen";
export { FormatValue } from "@/utils/format-value";
export { GetValue } from "@/utils/get-value";
export { password } from "@/utils/password";
export { generateTableList } from "@/comps/md/gen/gen-table-list";
export { generateForm } from "@/comps/form/gen/gen-form";
export { generateSelect } from "@/comps/md/gen/md-select";
export {generateField} from "@/comps/form/gen/gen-field";
/** Session */
export { Login } from "@/preset/login/Login";
export { generateLogin } from "@/preset/login/utils/generate";
export { logout } from "@/preset/login/utils/logout";
export {
registerSession,
RG,
UserSession,
registerSession,
} from "@/preset/login/utils/register";
export { Login } from "@/preset/login/Login";
export { logout } from "@/preset/login/utils/logout";
export { generateLogin } from "@/preset/login/utils/generate";
export { Card } from "@/comps/custom/Card";
@ -95,22 +94,21 @@ export { Layout } from "@/preset/menu/Layout";
export { Menu, MenuIcon } from "@/preset/menu/Menu";
/*Panel Tab*/
export { ShowHidePanel } from "@/comps/custom/ShowHidePanel";
export { PanelTab } from "@/comps/tab/Tab";
export { PanelBody } from "@/comps/tab/parts/PanelBody";
export { PanelHeader } from "@/comps/tab/parts/PanelHead";
export { ShowHidePanel } from "@/comps/custom/ShowHidePanel";
/*Popup*/
export { Popup } from "@/comps/popup/PopUp";
// Detail
export { Detail } from "@/comps/custom/Detail";
export { ButtonUpload } from "@/preset/profile/ButtonUpload";
export { Profile } from "@/preset/profile/Profile";
export { generateProfile } from "@/preset/profile/utils/generate";
export { ButtonUpload } from "@/preset/profile/ButtonUpload";
export { longDate, shortDate, timeAgo, formatTime } from "@/utils/date";
export { formatTime, longDate, shortDate, timeAgo } from "@/utils/date";
export { getPathname } from "./utils/pathname";
export * from "@/comps/ui/typeahead";
export * from "@/comps/ui/input";
export { softDeleteFilter } from "@/utils/soft-delete-filter";
export * from "@/comps/ui/typeahead";