fix site
This commit is contained in:
parent
49e1569b07
commit
286be55e1d
|
|
@ -15,6 +15,9 @@ export const site_group = async function (this: SyncConnection) {
|
||||||
name: true,
|
name: true,
|
||||||
domain: true,
|
domain: true,
|
||||||
},
|
},
|
||||||
|
where: {
|
||||||
|
is_deleted: false,
|
||||||
|
},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
|
|
||||||
|
|
@ -52,6 +52,7 @@
|
||||||
"react-dnd": "^16.0.1",
|
"react-dnd": "^16.0.1",
|
||||||
"react-dom": "18.2.0",
|
"react-dom": "18.2.0",
|
||||||
"react-is": "^18.2.0",
|
"react-is": "^18.2.0",
|
||||||
|
"react-select": "^5.7.7",
|
||||||
"react-use-error-boundary": "^3.0.0",
|
"react-use-error-boundary": "^3.0.0",
|
||||||
"react-virtuoso": "^4.6.2",
|
"react-virtuoso": "^4.6.2",
|
||||||
"safe-stable-stringify": "^2.4.3",
|
"safe-stable-stringify": "^2.4.3",
|
||||||
|
|
|
||||||
|
|
@ -21,9 +21,16 @@ export const edRoute = async (p: PG) => {
|
||||||
!p.page.cur.snapshot ||
|
!p.page.cur.snapshot ||
|
||||||
!p.page.list[p.page.cur.id]
|
!p.page.list[p.page.cur.id]
|
||||||
) {
|
) {
|
||||||
if (p.page.cur.snapshot && p.page.list[p.page.cur.id]) {
|
let loadFromServer = true;
|
||||||
console.log("loading page from cache");
|
if (p.page.list[params.page_id]) {
|
||||||
} else {
|
const cur = p.page.list[params.page_id];
|
||||||
|
p.page.cur = cur.page;
|
||||||
|
p.page.doc = cur.doc;
|
||||||
|
loadFromServer = false;
|
||||||
|
treeRebuild(p);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (loadFromServer) {
|
||||||
p.status = "loading";
|
p.status = "loading";
|
||||||
const page = await p.sync.page.load(params.page_id);
|
const page = await p.sync.page.load(params.page_id);
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -17,6 +17,23 @@ export const edInitSync = (p: PG) => {
|
||||||
p.user.id = session.data.user.id;
|
p.user.id = session.data.user.id;
|
||||||
p.user.username = session.data.user.username;
|
p.user.username = session.data.user.username;
|
||||||
|
|
||||||
|
if (p.sync) {
|
||||||
|
if (!params.page_id && params.site_id) {
|
||||||
|
db.page
|
||||||
|
.findFirst({
|
||||||
|
where: {
|
||||||
|
is_deleted: false,
|
||||||
|
is_default_layout: false,
|
||||||
|
id_site: params.site_id,
|
||||||
|
},
|
||||||
|
select: { id: true },
|
||||||
|
})
|
||||||
|
.then((e) => {
|
||||||
|
if (e) navigate(`/ed/${params.site_id}/${e.id}`);
|
||||||
|
});
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
if (!p.sync) {
|
if (!p.sync) {
|
||||||
clientStartSync({
|
clientStartSync({
|
||||||
user_id: session.data.user.id,
|
user_id: session.data.user.id,
|
||||||
|
|
|
||||||
|
|
@ -1,20 +1,161 @@
|
||||||
|
import { ReactNode } from "react";
|
||||||
import { Popover } from "../../../../utils/ui/popover";
|
import { Popover } from "../../../../utils/ui/popover";
|
||||||
|
import { Placement } from "@floating-ui/react";
|
||||||
|
import Select from "react-select";
|
||||||
|
import { useLocal } from "web-utils";
|
||||||
|
|
||||||
|
const user = {
|
||||||
|
loading: null as null | Promise<void>,
|
||||||
|
all: [] as { id: string; username: string }[],
|
||||||
|
};
|
||||||
|
export const EdPopUser = ({
|
||||||
|
users,
|
||||||
|
children,
|
||||||
|
placement = "right",
|
||||||
|
onDel,
|
||||||
|
onAdd,
|
||||||
|
}: {
|
||||||
|
users: { id: string; username: string }[];
|
||||||
|
children: ReactNode;
|
||||||
|
placement?: Placement;
|
||||||
|
onAdd?: (u: { id: string; username: string }) => void | Promise<void>;
|
||||||
|
onDel?: (u: { id: string; username: string }) => void | Promise<void>;
|
||||||
|
}) => {
|
||||||
|
const local = useLocal(
|
||||||
|
{
|
||||||
|
menuOpen: false,
|
||||||
|
index: {} as Record<string, { id: string; username: string }>,
|
||||||
|
},
|
||||||
|
async () => {
|
||||||
|
if (!user.loading) {
|
||||||
|
user.loading = new Promise(async (done) => {
|
||||||
|
const res = await db.user.findMany({
|
||||||
|
select: {
|
||||||
|
id: true,
|
||||||
|
username: true,
|
||||||
|
},
|
||||||
|
});
|
||||||
|
if (res) {
|
||||||
|
user.all = res;
|
||||||
|
}
|
||||||
|
local.render();
|
||||||
|
done();
|
||||||
|
});
|
||||||
|
} else {
|
||||||
|
await user.loading;
|
||||||
|
local.render();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
);
|
||||||
|
|
||||||
|
local.index = {};
|
||||||
|
if (users) {
|
||||||
|
for (const r of users) {
|
||||||
|
local.index[r.id] = r;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
export const EdPopUser = (users: { id: string; username: string }[]) => {
|
|
||||||
return (
|
return (
|
||||||
<Popover>
|
<Popover
|
||||||
<div className="ed-pop-user">
|
backdrop={false}
|
||||||
<div className="ed-pop-user-list">
|
placement={placement}
|
||||||
{users.map((user) => (
|
autoFocus={false}
|
||||||
<div key={user.id} className="ed-pop-user-item">
|
className="outline-none"
|
||||||
<div className="ed-pop-user-item-avatar">
|
content={
|
||||||
<img src="" alt="" />
|
<div
|
||||||
</div>
|
className={cx("text-[14px] min-w-[200px] outline-none -mx-2 -my-1")}
|
||||||
<div className="ed-pop-user-item-username">{user.username}</div>
|
>
|
||||||
|
<div
|
||||||
|
className={cx(css`
|
||||||
|
.sel__control {
|
||||||
|
border: 0px;
|
||||||
|
border-radius: 0px;
|
||||||
|
border-bottom: 1px solid #ececeb;
|
||||||
|
outline: none;
|
||||||
|
}
|
||||||
|
.sel__control--is-focused {
|
||||||
|
box-shadow: none !important;
|
||||||
|
border: 0px;
|
||||||
|
}
|
||||||
|
.sel__menu {
|
||||||
|
border-radius: 0px;
|
||||||
|
border: 0px;
|
||||||
|
background: white;
|
||||||
|
margin: 0px;
|
||||||
|
}
|
||||||
|
.sel__value-container {
|
||||||
|
padding-left: 5px;
|
||||||
|
}
|
||||||
|
.sel__option {
|
||||||
|
padding: 2px 5px;
|
||||||
|
border-bottom: 1px solid #ececeb;
|
||||||
|
&:hover {
|
||||||
|
background: #e0e9fa;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
`)}
|
||||||
|
>
|
||||||
|
<div className="border-b px-1 pt-2 pb-1">Existing user:</div>
|
||||||
|
<div className="flex flex-col ml-4 border-l">
|
||||||
|
{users.map((user) => (
|
||||||
|
<div
|
||||||
|
key={user.id}
|
||||||
|
className={
|
||||||
|
" bg-blue-50 hover:bg-blue-100 border-b pl-2 flex justify-between items-center"
|
||||||
|
}
|
||||||
|
>
|
||||||
|
<div className="flex-1">{user.username}</div>
|
||||||
|
{onDel && (
|
||||||
|
<div
|
||||||
|
className="p-1 hover:bg-red-600 hover:text-white cursor-pointer"
|
||||||
|
onClick={() => {
|
||||||
|
onDel(user);
|
||||||
|
}}
|
||||||
|
dangerouslySetInnerHTML={{
|
||||||
|
__html: `<svg width="15" height="15" viewBox="0 0 15 15" fill="none" xmlns="http://www.w3.org/2000/svg"><path d="M5.5 1C5.22386 1 5 1.22386 5 1.5C5 1.77614 5.22386 2 5.5 2H9.5C9.77614 2 10 1.77614 10 1.5C10 1.22386 9.77614 1 9.5 1H5.5ZM3 3.5C3 3.22386 3.22386 3 3.5 3H5H10H11.5C11.7761 3 12 3.22386 12 3.5C12 3.77614 11.7761 4 11.5 4H11V12C11 12.5523 10.5523 13 10 13H5C4.44772 13 4 12.5523 4 12V4L3.5 4C3.22386 4 3 3.77614 3 3.5ZM5 4H10V12H5V4Z" fill="currentColor" fill-rule="evenodd" clip-rule="evenodd"></path></svg>`,
|
||||||
|
}}
|
||||||
|
></div>
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
|
))}
|
||||||
</div>
|
</div>
|
||||||
))}
|
{user.all.length > 0 && onAdd && (
|
||||||
|
<Select
|
||||||
|
options={user.all
|
||||||
|
.filter((e) => {
|
||||||
|
if (local.index[e.id]) return false;
|
||||||
|
return true;
|
||||||
|
})
|
||||||
|
.map((user) => ({
|
||||||
|
label: user.username,
|
||||||
|
value: user.id,
|
||||||
|
}))}
|
||||||
|
value={null}
|
||||||
|
menuIsOpen={local.menuOpen}
|
||||||
|
onInputChange={(e) => {
|
||||||
|
if (e) {
|
||||||
|
local.menuOpen = true;
|
||||||
|
} else {
|
||||||
|
local.menuOpen = false;
|
||||||
|
}
|
||||||
|
local.render();
|
||||||
|
}}
|
||||||
|
autoFocus
|
||||||
|
onChange={async (e) => {
|
||||||
|
if (e) {
|
||||||
|
onAdd({ username: e.label, id: e.value });
|
||||||
|
}
|
||||||
|
}}
|
||||||
|
placeholder={"Add User"}
|
||||||
|
className={cx("outline-none border-t -mt-[1px]")}
|
||||||
|
classNamePrefix={"sel"}
|
||||||
|
/>
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
}
|
||||||
|
>
|
||||||
|
{children}
|
||||||
</Popover>
|
</Popover>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
|
||||||
|
|
@ -10,6 +10,7 @@ import { useGlobal, useLocal } from "web-utils";
|
||||||
import { Loading } from "../../../../utils/ui/loading";
|
import { Loading } from "../../../../utils/ui/loading";
|
||||||
import { Modal } from "../../../../utils/ui/modal";
|
import { Modal } from "../../../../utils/ui/modal";
|
||||||
import { EDGlobal } from "../../logic/ed-global";
|
import { EDGlobal } from "../../logic/ed-global";
|
||||||
|
import { EdPopUser } from "./site-user";
|
||||||
|
|
||||||
type GItem = {
|
type GItem = {
|
||||||
id: string;
|
id: string;
|
||||||
|
|
@ -23,13 +24,14 @@ type GItem = {
|
||||||
}
|
}
|
||||||
| { type: "site"; domain: string }
|
| { type: "site"; domain: string }
|
||||||
);
|
);
|
||||||
|
const conf = { group: null as any };
|
||||||
|
|
||||||
export const EdPopSite = () => {
|
export const EdPopSite = () => {
|
||||||
const p = useGlobal(EDGlobal, "EDITOR");
|
const p = useGlobal(EDGlobal, "EDITOR");
|
||||||
const local = useLocal(
|
const local = useLocal(
|
||||||
{
|
{
|
||||||
status: "init" as "init" | "loading" | "ready",
|
status: "init" as "init" | "loading" | "ready",
|
||||||
group: [] as NodeModel<GItem>[],
|
group: (conf.group || []) as NodeModel<GItem>[],
|
||||||
},
|
},
|
||||||
() => {
|
() => {
|
||||||
p.ui.popup.site = () => {};
|
p.ui.popup.site = () => {};
|
||||||
|
|
@ -84,12 +86,13 @@ export const EdPopSite = () => {
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
local.group = group;
|
local.group = group;
|
||||||
|
conf.group = group;
|
||||||
|
|
||||||
local.status = "ready";
|
local.status = "ready";
|
||||||
local.render();
|
local.render();
|
||||||
};
|
};
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
if (p.ui.popup.site && local.status !== "loading") {
|
if (p.ui.popup.site && local.status !== "loading" && !conf.group) {
|
||||||
reload();
|
reload();
|
||||||
}
|
}
|
||||||
}, [p.ui.popup.site]);
|
}, [p.ui.popup.site]);
|
||||||
|
|
@ -139,12 +142,12 @@ const SitePicker = ({
|
||||||
const orglen = group.filter((e) => e.parent === "site-root").length;
|
const orglen = group.filter((e) => e.parent === "site-root").length;
|
||||||
return (
|
return (
|
||||||
<div className="flex flex-1 flex-col">
|
<div className="flex flex-1 flex-col">
|
||||||
<div className="border-b text-[20px] pt-[15px] pb-[5px] pl-1 flex space-x-3 items-center">
|
<div className="border-b text-[20px] pt-[15px] pb-[5px] pl-1 flex items-center">
|
||||||
<div>
|
<div>
|
||||||
{orglen} Organization{orglen > 1 ? "s" : ""}
|
{orglen} Organization{orglen > 1 ? "s" : ""}
|
||||||
</div>
|
</div>
|
||||||
<div
|
<div
|
||||||
className="text-[12px] bg-white border rounded mx-2 px-2 hover:bg-blue-100 cursor-pointer flex items-center space-x-1 "
|
className="text-[12px] bg-white border rounded ml-2 px-2 hover:bg-blue-100 cursor-pointer flex items-center space-x-1 "
|
||||||
onClick={async () => {
|
onClick={async () => {
|
||||||
const neworg = prompt("New Organization Name");
|
const neworg = prompt("New Organization Name");
|
||||||
if (neworg) {
|
if (neworg) {
|
||||||
|
|
@ -167,6 +170,18 @@ const SitePicker = ({
|
||||||
></div>
|
></div>
|
||||||
<div>New</div>
|
<div>New</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
<div
|
||||||
|
className={cx(
|
||||||
|
"text-[12px] bg-white border rounded px-2 hover:bg-blue-100 cursor-pointer flex items-center ml-1 space-x-1 "
|
||||||
|
)}
|
||||||
|
onClick={() => {
|
||||||
|
conf.group = null;
|
||||||
|
reload();
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
Refresh
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<DndProvider backend={MultiBackend} options={getBackendOptions()}>
|
<DndProvider backend={MultiBackend} options={getBackendOptions()}>
|
||||||
|
|
@ -265,20 +280,25 @@ const SitePicker = ({
|
||||||
item.name = e.currentTarget.value;
|
item.name = e.currentTarget.value;
|
||||||
local.render();
|
local.render();
|
||||||
}}
|
}}
|
||||||
onBlur={() => {
|
onBlur={async () => {
|
||||||
item.name = node.text;
|
if (item.renaming) {
|
||||||
item.renaming = false;
|
|
||||||
local.render();
|
|
||||||
}}
|
|
||||||
onKeyDown={async (e) => {
|
|
||||||
if (e.key === "Enter") {
|
|
||||||
await db.org.update({
|
await db.org.update({
|
||||||
where: { id: item.id },
|
where: { id: item.id },
|
||||||
data: { name: item.name },
|
data: { name: item.name },
|
||||||
});
|
});
|
||||||
|
item.renaming = false;
|
||||||
reload();
|
reload();
|
||||||
}
|
}
|
||||||
}}
|
}}
|
||||||
|
onKeyDown={async (e) => {
|
||||||
|
if (e.key === "Escape") {
|
||||||
|
item.name = node.text;
|
||||||
|
item.renaming = false;
|
||||||
|
local.render();
|
||||||
|
} else if (e.key === "Enter") {
|
||||||
|
e.currentTarget.blur();
|
||||||
|
}
|
||||||
|
}}
|
||||||
/>
|
/>
|
||||||
) : (
|
) : (
|
||||||
<>
|
<>
|
||||||
|
|
@ -298,10 +318,28 @@ const SitePicker = ({
|
||||||
</div>
|
</div>
|
||||||
</>
|
</>
|
||||||
)}
|
)}
|
||||||
<div className="text-[12px] bg-white border rounded px-2 hover:bg-blue-100 cursor-pointer min-h-[20px] flex items-center ">
|
<EdPopUser
|
||||||
Team: {item.users.length} user
|
users={item.users}
|
||||||
{item.users.length > 1 ? "s" : ""}
|
onDel={async (u) => {
|
||||||
</div>
|
await db.org_user.deleteMany({
|
||||||
|
where: { id_org: item.id, id_user: u.id },
|
||||||
|
});
|
||||||
|
item.users = item.users.filter((e) => e.id !== u.id);
|
||||||
|
local.render();
|
||||||
|
}}
|
||||||
|
onAdd={async (u) => {
|
||||||
|
await db.org_user.create({
|
||||||
|
data: { id_org: item.id, id_user: u.id },
|
||||||
|
});
|
||||||
|
item.users = [...item.users, u];
|
||||||
|
local.render();
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
<div className="text-[12px] bg-white border rounded px-2 hover:bg-blue-100 cursor-pointer min-h-[20px] flex items-center ">
|
||||||
|
Team: {item.users.length} user
|
||||||
|
{item.users.length > 1 ? "s" : ""}
|
||||||
|
</div>
|
||||||
|
</EdPopUser>
|
||||||
{isDropTarget && (
|
{isDropTarget && (
|
||||||
<div className="px-2 text-slate-500 text-[13px]">
|
<div className="px-2 text-slate-500 text-[13px]">
|
||||||
Drop here...
|
Drop here...
|
||||||
|
|
@ -336,7 +374,13 @@ const SitePicker = ({
|
||||||
}
|
}
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div
|
<a
|
||||||
|
href={`/ed/${item.id}`}
|
||||||
|
onClick={(e) => {
|
||||||
|
e.preventDefault();
|
||||||
|
e.stopPropagation();
|
||||||
|
navigate(`/ed/${item.id}`);
|
||||||
|
}}
|
||||||
className={cx(
|
className={cx(
|
||||||
"flex flex-col ml-2 mt-1 mb-1 w-[150px] text-[14px] border bg-white hover:bg-blue-100 cursor-pointer",
|
"flex flex-col ml-2 mt-1 mb-1 w-[150px] text-[14px] border bg-white hover:bg-blue-100 cursor-pointer",
|
||||||
css`
|
css`
|
||||||
|
|
@ -361,10 +405,10 @@ const SitePicker = ({
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div className="edit px-1 bg-white border-l-4 border-blue-400 text-blue-400 hover:bg-blue-500 hover:text-white">
|
<div className="edit px-1 bg-blue-50 border-l-4 border-blue-300 text-blue-400 hover:bg-blue-500 hover:text-white">
|
||||||
EDIT
|
EDIT
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</a>
|
||||||
);
|
);
|
||||||
}}
|
}}
|
||||||
/>
|
/>
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue