prasi-bun/app/web/src/nova/ed/panel/popup/api/api-server.tsx

258 lines
8.6 KiB
TypeScript

import trim from "lodash.trim";
import { forwardRef } from "react";
import { useGlobal, useLocal } from "web-utils";
import { deploy_target } from "../../../../../../../db/db";
import { Modal } from "../../../../../utils/ui/modal";
import { Popover } from "../../../../../utils/ui/popover";
import { EDGlobal } from "../../../logic/ed-global";
import { EdApiTab } from "./api-tab";
import { server } from "./api-utils";
export const EdPopApi = () => {
const p = useGlobal(EDGlobal, "EDITOR");
return (
<Modal
open={p.ui.popup.api.open}
onOpenChange={(open) => {
p.ui.popup.api.open = open;
p.render();
}}
>
<EdApiServer
popover={{
onClose() {
p.ui.popup.api.open = false;
p.render();
},
}}
/>
</Modal>
);
};
export const EdApiServer = forwardRef<
HTMLDivElement,
{
popover: { onClose: () => void };
}
>(({ popover }, ref) => {
const p = useGlobal(EDGlobal, "EDITOR");
const local = useLocal(
{
tabs: [{ name: p.site.deploy_name, id: "" }] as deploy_target[],
active: 0,
open: false,
},
async () => {
const targets = await _db.deploy_target.findMany({
where: { id_site: p.site.id },
});
for (const t of targets) {
local.tabs.push(t);
}
local.render();
}
);
return (
<div
ref={ref}
className="flex flex-col w-[400px] min-h-[400px] items-stretch bg-white -mx-[8px] -my-[3px] text-[14px]"
>
<div className="flex bg-slate-100 p-1 border-b pb-0 select-none">
{local.tabs.map((e, idx) => {
return (
<div
key={idx}
className={cx(
"px-2 border border-b-0 flex cursor-pointer items-center space-x-2",
local.active === idx
? "bg-white hover:bg-blue-100"
: "px-2 border border-b-0 hover:bg-blue-100",
css`
margin-bottom: -1px;
`,
idx > 0 && `ml-1`
)}
onClick={() => {
local.active = idx;
local.render();
}}
>
<div>{e.name}</div>
{local.active === idx && (
<Popover
open={local.open}
backdrop={false}
onOpenChange={(open) => {
local.open = open;
local.render();
}}
content={
<div className="px-2 pb-2 flex flex-col space-y-2 items-center">
<svg
className="absolute top-2 right-2 cursor-pointer"
xmlns="http://www.w3.org/2000/svg"
width="16"
height="16"
viewBox="0 0 24 24"
fill="none"
stroke="currentColor"
onClick={() => {
local.open = false;
local.render();
}}
>
<path d="M18 6 6 18" />
<path d="m6 6 12 12" />
</svg>
<label>
<div>Deploy Name:</div>
<input
defaultValue={e.name}
autoFocus
spellCheck={false}
className="border p-1"
onKeyUp={(ev) => {
if (ev.key === "Enter") {
ev.currentTarget.blur();
}
}}
onBlur={async (ev) => {
const name = ev.currentTarget.value;
if (name !== e.name) {
if (confirm(`Rename ${e.name} to ${name}?`)) {
if (idx === 0) {
await _db.site.update({
where: { id: p.site.id },
data: { deploy_name: name },
});
} else {
const target = local.tabs[
idx
] as deploy_target;
await _db.deploy_target.update({
where: { id: target.id },
data: { name },
});
}
e.name = name;
local.open = false;
local.render();
}
}
}}
/>
</label>
{idx > 0 && (
<div
onClick={async () => {
if (
confirm(
"Are you sure to delete this deploy target ? This is cannot be reversed."
)
) {
const target = local.tabs[idx] as deploy_target;
await _db.deploy_target.delete({
where: { id: target.id },
});
local.active -= 1;
local.tabs.splice(idx, 1);
local.render();
}
}}
className="mt-2 rounded bg-red-500 text-white px-2 py-1 cursor-pointer hover:bg-red-700"
>
Delete
</div>
)}
</div>
}
>
<svg
onClick={() => {
local.open = true;
local.render();
}}
xmlns="http://www.w3.org/2000/svg"
width="9"
height="9"
viewBox="0 0 24 24"
fill="none"
stroke="currentColor"
strokeWidth="1"
>
<path d="M21.174 6.812a1 1 0 0 0-3.986-3.987L3.842 16.174a2 2 0 0 0-.5.83l-1.321 4.352a.5.5 0 0 0 .623.622l4.353-1.32a2 2 0 0 0 .83-.497z" />
<path d="m15 5 4 4" />
</svg>
</Popover>
)}
</div>
);
})}
<div
onClick={async () => {
const new_name = prompt("New Deploy Target Name:");
if (new_name) {
const new_target = await _db.deploy_target.create({
data: {
api_url: "",
domain: "",
id_site: p.site.id,
name: new_name,
},
});
local.tabs.push(new_target);
local.render();
}
}}
className="mb-1 ml-1 bg-white px-1 cursor-pointer hover:bg-blue-100 border flex items-center justify-center"
>
<svg
xmlns="http://www.w3.org/2000/svg"
width="12"
height="12"
viewBox="0 0 24 24"
fill="none"
stroke="currentColor"
strokeWidth="2"
strokeLinecap="round"
strokeLinejoin="round"
>
<path d="M5 12h14" />
<path d="M12 5v14" />
</svg>
</div>
</div>
<EdApiTab
onRender={(update) => {
popover.onClose = update;
}}
target={local.active > 0 ? local.tabs[local.active] : undefined}
id_site={p.site.id}
api_url={p.site.config.api_url}
onUpdate={async ({ api_url }) => {
p.render();
if (local.active === 0) {
p.site.config.api_url = trim(api_url, "/");
await p.sync?.site.update(p.site.id, {
config: { api_url: api_url },
});
} else {
const target = local.tabs[local.active];
await _db.deploy_target.update({
where: { id: target.id },
data: { api_url },
});
}
server.status = "ready";
p.render();
}}
/>
</div>
);
});