diff --git a/app/srv/ws/sync/editor/code/watcher.ts b/app/srv/ws/sync/editor/code/watcher.ts index 0571e2f5..27b5e7e3 100644 --- a/app/srv/ws/sync/editor/code/watcher.ts +++ b/app/srv/ws/sync/editor/code/watcher.ts @@ -26,7 +26,6 @@ export const Code = { }; export const startCodeWatcher = async (code: DBCode) => { - console.log(code) if (code.id && Code.watchers[code.id]) { return; } @@ -79,7 +78,7 @@ export const startCodeWatcher = async (code: DBCode) => { clearTimeout(Code.timeout[code.id]); Code.timeout[code.id] = setTimeout(() => { codeBuild(code); - }, 1000); + }, 100); } if (path) { diff --git a/app/web/src/render/ed/logic/ed-global.ts b/app/web/src/render/ed/logic/ed-global.ts index 904ada03..a7cf0ae9 100644 --- a/app/web/src/render/ed/logic/ed-global.ts +++ b/app/web/src/render/ed/logic/ed-global.ts @@ -117,6 +117,7 @@ export const EDGlobal = { name: "site", log: "", loading: false, + error: false, show_log: false, list: {} as Record, }, diff --git a/app/web/src/render/ed/logic/ed-sync.tsx b/app/web/src/render/ed/logic/ed-sync.tsx index 02049260..70b4dc6a 100644 --- a/app/web/src/render/ed/logic/ed-sync.tsx +++ b/app/web/src/render/ed/logic/ed-sync.tsx @@ -63,13 +63,20 @@ export const edInitSync = (p: PG) => { page_id: params.page_id, events: { code(arg) { + p.ui.popup.code.error = false; + if (arg.event === "code-loading") { p.ui.popup.code.log = ""; p.ui.popup.code.loading = true; p.render(); } else if (arg.event === "code-done") { - if (typeof arg.content === "string") + if (typeof arg.content === "string") { + if (arg.content !== "OK") { + p.ui.popup.code.error = true; + } + p.ui.popup.code.log += arg.content; + } p.ui.popup.code.loading = false; p.render(); } else { diff --git a/app/web/src/render/ed/panel/popup/code/assign.tsx b/app/web/src/render/ed/panel/popup/code/assign.tsx index 65aecb41..90c74082 100644 --- a/app/web/src/render/ed/panel/popup/code/assign.tsx +++ b/app/web/src/render/ed/panel/popup/code/assign.tsx @@ -1,5 +1,207 @@ -import { FC } from "react"; +import { FC, ReactNode } from "react"; +import { useGlobal, useLocal } from "web-utils"; +import { EDGlobal } from "../../../logic/ed-global"; +import { Popover } from "../../../../../utils/ui/popover"; +import { fuzzy } from "../../../../../utils/ui/fuzzy"; -export const CodeAssign: FC<{ id_code: string }> = () => { - return
mantap
; +const assign = { + loading: true, + list: [] as Awaited>, + render: () => {}, }; + +export const CodeAssign: FC<{ id_code: string; onClose: () => void }> = ({ + onClose, + id_code, +}) => { + const local = useLocal({}, async () => { + assign.list = await getAssign(id_code); + assign.loading = false; + assign.render(); + }); + assign.render = local.render; + + return ( +
+ +
+ +
+ ); +}; + +const AssignList: FC<{ id_code: string; mode: "page" | "comp" }> = ({ + id_code, + mode, +}) => { + const p = useGlobal(EDGlobal, "EDITOR"); + const local = useLocal( + { + search: "", + list: [] as { id: string; name: string; url?: string }[], + popover: false, + loading: true, + }, + async () => { + if (mode === "page") { + const list = await db.page.findMany({ + where: { + id_site: p.site.id, + is_deleted: false, + }, + select: { + name: true, + id: true, + url: true, + }, + }); + + local.list = list.map((e) => ({ + id: e.id, + name: e.name, + url: e.url, + })); + } else { + const list = await db.component_site.findMany({ + where: { + id_site: p.site.id, + }, + select: { + component_group: { select: { id: true, name: true } }, + }, + }); + local.list = list.map((e) => ({ + id: e.component_group.id, + name: e.component_group.name, + })); + } + local.loading = false; + local.render(); + } + ); + + const filtered = fuzzy( + local.list, + mode === "page" ? { pk: "id", search: ["name", "url"] } : "name", + local.search + ); + + return ( +
+ { + local.popover = open; + local.render(); + }} + arrow={false} + backdrop={false} + popoverClassName={cx( + "p-0 shadow-lg border border-blue-500 bg-white", + css` + top: -7px !important; + ` + )} + content={ +
+ {local.loading && ( +
+ Loading... +
+ )} + {!local.loading && + filtered.map((e) => { + let label: any = e.name; + if (mode === "page") { + label = ( +
+
{e.name}
+
+ {e.url} +
+
+ ); + } + + return ( +
+ {label} +
+ ); + })} +
+ } + > +
+ { + local.search = e.currentTarget.value; + local.render(); + }} + spellCheck={false} + placeholder={`Search ${ + mode === "page" ? "Page" : "Component Group" + }...`} + className="flex flex-1 outline-none p-1 text-sm focus:border-blue-500 border border-transparent" + /> +
+
+
+
+ ); +}; + +const getAssign = async (id_code: string) => { + return await db.code_assign.findMany({ + where: { + id_code: { equals: id_code }, + }, + }); +}; + +const selectStyle = css` + flex: 1; + + .sel__control { + border: 0px; + border-radius: 0px; + outline: none; + min-height: 29px; + border-left: 1px solid #ececeb; + border-right: 1px solid #ececeb; + } + .sel__control--is-focused { + box-shadow: none !important; + border-bottom: 1px solid blue; + } + .sel__menu { + border-radius: 0px; + background: white; + border: 1px solid #ececeb; + } + .sel__value-container { + padding-left: 5px; + } + .sel__option { + padding: 2px 5px; + border-bottom: 1px solid #ececeb; + } + .sel__option--is-selected, + .sel__option--is-focused { + background: #e0e9fa; + } + .sel__indicator { + width: 15px; + opacity: 0.7; + } + .sel__placeholder { + color: #ccc; + } +`; diff --git a/app/web/src/render/ed/panel/popup/code/code.tsx b/app/web/src/render/ed/panel/popup/code/code.tsx index 04a9e2b5..ed11f02c 100644 --- a/app/web/src/render/ed/panel/popup/code/code.tsx +++ b/app/web/src/render/ed/panel/popup/code/code.tsx @@ -18,7 +18,7 @@ import { CodeNameItem, CodeNameList, codeName } from "./name-list"; export const EdPopCode = () => { const p = useGlobal(EDGlobal, "EDITOR"); - const local = useLocal({ namePicker: false }); + const local = useLocal({ namePicker: false, codeAssign: false }); useEffect(() => { (async () => { @@ -77,7 +77,6 @@ export const EdPopCode = () => { name: p.ui.popup.code.name, }); - console.log(id_code); if (id_code) { p.ui.popup.code.id = id_code; p.render(); @@ -116,7 +115,7 @@ export const EdPopCode = () => { onClick={async () => { if ( prompt( - "Are you sure want to delete this code?\n type 'yes' to confirm:" + "Are you sure want to delete this code?\ntype 'yes' to confirm:" ) === "yes" ) { await db.code.delete({ @@ -140,7 +139,23 @@ export const EdPopCode = () => { > } + open={local.codeAssign} + onOpenChange={(open) => { + local.codeAssign = open; + local.render(); + }} + backdrop={false} + placement="bottom" + popoverClassName="p-0 shadow-lg bg-white" + content={ + { + local.codeAssign = false; + local.render(); + }} + id_code={p.ui.popup.code.id} + /> + } className="flex items-center border-l relative border-l hover:bg-blue-50 cursor-pointer px-2 transition-all" >
{ content="STDOUT Log" delay={0} placement="bottom" - className="flex items-stretch relative border-l" + className={cx( + "flex items-stretch relative border-l", + p.ui.popup.code.error && "bg-red-500 text-white" + )} onClick={() => { p.ui.popup.code.show_log = !p.ui.popup.code.show_log; p.render(); @@ -199,11 +217,12 @@ export const EdPopCode = () => { )}
- {local.namePicker && ( + {(local.namePicker || local.codeAssign) && (
{ local.namePicker = false; + local.codeAssign = false; local.render(); }} >
diff --git a/app/web/src/render/ed/panel/popup/code/name-list.tsx b/app/web/src/render/ed/panel/popup/code/name-list.tsx index a5504b0e..5fb16855 100644 --- a/app/web/src/render/ed/panel/popup/code/name-list.tsx +++ b/app/web/src/render/ed/panel/popup/code/name-list.tsx @@ -93,6 +93,7 @@ export const CodeNameList: FC<{ spellCheck={false} onBlur={async (e) => { local.newopen = false; + local.render(); const nc = await db.code.create({ data: { diff --git a/app/web/src/utils/ui/fuzzy.tsx b/app/web/src/utils/ui/fuzzy.tsx index 0cb312c3..794a4177 100644 --- a/app/web/src/utils/ui/fuzzy.tsx +++ b/app/web/src/utils/ui/fuzzy.tsx @@ -2,6 +2,38 @@ import uFuzzy from "@leeoniya/ufuzzy"; const uf = new uFuzzy({}); export const fuzzy = ( + array: T[], + field: keyof T | { pk: keyof T; search: (keyof T)[] }, + search: string +) => { + if (typeof field === "string") { + return fuzzySingle(array, field, search); + } + const result: Record = {}; + + if (typeof field === "object") { + for (const f of field.search) { + const res = fuzzySingle(array, f, search); + let idx = 0; + for (const row of res) { + idx++; + const id = row[field.pk] as any; + if (!result[id]) { + result[id] = { idx, row }; + } else { + result[id].row[f] = row[f]; + } + } + } + } + const final: any = {}; + for (const i of Object.values(result)) { + final[i.idx] = i.row; + } + return Object.values(final) as T[]; +}; + +const fuzzySingle = ( array: T[], field: keyof T, search: string