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,
|
||||
domain: true,
|
||||
},
|
||||
where: {
|
||||
is_deleted: false,
|
||||
},
|
||||
},
|
||||
},
|
||||
});
|
||||
|
|
|
|||
|
|
@ -52,6 +52,7 @@
|
|||
"react-dnd": "^16.0.1",
|
||||
"react-dom": "18.2.0",
|
||||
"react-is": "^18.2.0",
|
||||
"react-select": "^5.7.7",
|
||||
"react-use-error-boundary": "^3.0.0",
|
||||
"react-virtuoso": "^4.6.2",
|
||||
"safe-stable-stringify": "^2.4.3",
|
||||
|
|
|
|||
|
|
@ -21,9 +21,16 @@ export const edRoute = async (p: PG) => {
|
|||
!p.page.cur.snapshot ||
|
||||
!p.page.list[p.page.cur.id]
|
||||
) {
|
||||
if (p.page.cur.snapshot && p.page.list[p.page.cur.id]) {
|
||||
console.log("loading page from cache");
|
||||
} else {
|
||||
let loadFromServer = true;
|
||||
if (p.page.list[params.page_id]) {
|
||||
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";
|
||||
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.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) {
|
||||
clientStartSync({
|
||||
user_id: session.data.user.id,
|
||||
|
|
|
|||
|
|
@ -1,20 +1,161 @@
|
|||
import { ReactNode } from "react";
|
||||
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 (
|
||||
<Popover>
|
||||
<div className="ed-pop-user">
|
||||
<div className="ed-pop-user-list">
|
||||
{users.map((user) => (
|
||||
<div key={user.id} className="ed-pop-user-item">
|
||||
<div className="ed-pop-user-item-avatar">
|
||||
<img src="" alt="" />
|
||||
</div>
|
||||
<div className="ed-pop-user-item-username">{user.username}</div>
|
||||
<Popover
|
||||
backdrop={false}
|
||||
placement={placement}
|
||||
autoFocus={false}
|
||||
className="outline-none"
|
||||
content={
|
||||
<div
|
||||
className={cx("text-[14px] min-w-[200px] outline-none -mx-2 -my-1")}
|
||||
>
|
||||
<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>
|
||||
))}
|
||||
{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>
|
||||
}
|
||||
>
|
||||
{children}
|
||||
</Popover>
|
||||
);
|
||||
};
|
||||
|
|
|
|||
|
|
@ -10,6 +10,7 @@ import { useGlobal, useLocal } from "web-utils";
|
|||
import { Loading } from "../../../../utils/ui/loading";
|
||||
import { Modal } from "../../../../utils/ui/modal";
|
||||
import { EDGlobal } from "../../logic/ed-global";
|
||||
import { EdPopUser } from "./site-user";
|
||||
|
||||
type GItem = {
|
||||
id: string;
|
||||
|
|
@ -23,13 +24,14 @@ type GItem = {
|
|||
}
|
||||
| { type: "site"; domain: string }
|
||||
);
|
||||
const conf = { group: null as any };
|
||||
|
||||
export const EdPopSite = () => {
|
||||
const p = useGlobal(EDGlobal, "EDITOR");
|
||||
const local = useLocal(
|
||||
{
|
||||
status: "init" as "init" | "loading" | "ready",
|
||||
group: [] as NodeModel<GItem>[],
|
||||
group: (conf.group || []) as NodeModel<GItem>[],
|
||||
},
|
||||
() => {
|
||||
p.ui.popup.site = () => {};
|
||||
|
|
@ -84,12 +86,13 @@ export const EdPopSite = () => {
|
|||
});
|
||||
}
|
||||
local.group = group;
|
||||
conf.group = group;
|
||||
|
||||
local.status = "ready";
|
||||
local.render();
|
||||
};
|
||||
useEffect(() => {
|
||||
if (p.ui.popup.site && local.status !== "loading") {
|
||||
if (p.ui.popup.site && local.status !== "loading" && !conf.group) {
|
||||
reload();
|
||||
}
|
||||
}, [p.ui.popup.site]);
|
||||
|
|
@ -139,12 +142,12 @@ const SitePicker = ({
|
|||
const orglen = group.filter((e) => e.parent === "site-root").length;
|
||||
return (
|
||||
<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>
|
||||
{orglen} Organization{orglen > 1 ? "s" : ""}
|
||||
</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 () => {
|
||||
const neworg = prompt("New Organization Name");
|
||||
if (neworg) {
|
||||
|
|
@ -167,6 +170,18 @@ const SitePicker = ({
|
|||
></div>
|
||||
<div>New</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>
|
||||
|
||||
<DndProvider backend={MultiBackend} options={getBackendOptions()}>
|
||||
|
|
@ -265,20 +280,25 @@ const SitePicker = ({
|
|||
item.name = e.currentTarget.value;
|
||||
local.render();
|
||||
}}
|
||||
onBlur={() => {
|
||||
item.name = node.text;
|
||||
item.renaming = false;
|
||||
local.render();
|
||||
}}
|
||||
onKeyDown={async (e) => {
|
||||
if (e.key === "Enter") {
|
||||
onBlur={async () => {
|
||||
if (item.renaming) {
|
||||
await db.org.update({
|
||||
where: { id: item.id },
|
||||
data: { name: item.name },
|
||||
});
|
||||
item.renaming = false;
|
||||
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 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
|
||||
users={item.users}
|
||||
onDel={async (u) => {
|
||||
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 && (
|
||||
<div className="px-2 text-slate-500 text-[13px]">
|
||||
Drop here...
|
||||
|
|
@ -336,7 +374,13 @@ const SitePicker = ({
|
|||
}
|
||||
|
||||
return (
|
||||
<div
|
||||
<a
|
||||
href={`/ed/${item.id}`}
|
||||
onClick={(e) => {
|
||||
e.preventDefault();
|
||||
e.stopPropagation();
|
||||
navigate(`/ed/${item.id}`);
|
||||
}}
|
||||
className={cx(
|
||||
"flex flex-col ml-2 mt-1 mb-1 w-[150px] text-[14px] border bg-white hover:bg-blue-100 cursor-pointer",
|
||||
css`
|
||||
|
|
@ -361,10 +405,10 @@ const SitePicker = ({
|
|||
</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
|
||||
</div>
|
||||
</div>
|
||||
</a>
|
||||
);
|
||||
}}
|
||||
/>
|
||||
|
|
|
|||
Loading…
Reference in New Issue