204 lines
7.2 KiB
JavaScript
204 lines
7.2 KiB
JavaScript
import "./DesktopPeopleToolbar.js"
|
|
import "./DesktopPeopleTable.js"
|
|
import "./DesktopPeopleDetail.js"
|
|
import server from "/people/@server/index.js"
|
|
|
|
css(`
|
|
people- {
|
|
font-family: 'Arial';
|
|
scrollbar-width: none;
|
|
-ms-overflow-style: none;
|
|
}
|
|
people- select {
|
|
appearance: none;
|
|
-webkit-appearance: none;
|
|
background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='10' height='6'%3E%3Cpath d='M0 0l5 6 5-6z' fill='%23888'/%3E%3C/svg%3E");
|
|
background-repeat: no-repeat;
|
|
background-position: right 0.65em center;
|
|
padding-right: 1.8em !important;
|
|
}
|
|
people- textarea {
|
|
font-family: 'Arial';
|
|
}
|
|
people- input::placeholder {
|
|
color: var(--headertext);
|
|
opacity: 0.35;
|
|
}
|
|
`)
|
|
|
|
class People extends Shadow {
|
|
|
|
_people = (global.currentNetwork.data.members || []).map(p => ({ ...p, _online: this.isOnline(p) }))
|
|
#lastUpdated;
|
|
|
|
selectedMemberId = null
|
|
searchText = ""
|
|
filterRole = ""
|
|
filterTier = ""
|
|
sortKey = "joined"
|
|
sortDir = "asc"
|
|
tableEl = null
|
|
|
|
get people() { return this._people }
|
|
set people(val) {
|
|
this._people = val
|
|
this.#lastUpdated = Date.now()
|
|
global.currentNetwork.data.members = val
|
|
}
|
|
|
|
constructor() {
|
|
super()
|
|
}
|
|
|
|
isOnline(member) {
|
|
return global.socket.connectedUsers.includes(member.email)
|
|
}
|
|
|
|
get filteredSortedPeople() {
|
|
let list = this.people;
|
|
|
|
if (this.searchText) {
|
|
const q = this.searchText.toLowerCase();
|
|
list = list.filter(p =>
|
|
[p.first_name, p.last_name, p.email, p.title, p.county, p.city, p.state]
|
|
.some(v => v?.toLowerCase().includes(q))
|
|
);
|
|
}
|
|
|
|
if (this.filterRole) {
|
|
list = list.filter(p => (p.roles || []).includes(this.filterRole));
|
|
}
|
|
|
|
if (this.filterTier) {
|
|
list = list.filter(p => String(p.plan_name) === this.filterTier);
|
|
}
|
|
|
|
// Sort
|
|
const key = this.sortKey;
|
|
const dir = this.sortDir === "asc" ? 1 : -1;
|
|
list = [...list].sort((a, b) => {
|
|
let av, bv;
|
|
if (key === "name") { av = `${a.first_name} ${a.last_name}`; bv = `${b.first_name} ${b.last_name}`; }
|
|
else if (key === "email") { av = a.email || ""; bv = b.email || ""; }
|
|
else if (key === "title") { av = a.title || ""; bv = b.title || ""; }
|
|
else if (key === "county") { av = a.county || ""; bv = b.county || ""; }
|
|
else if (key === "roles") { av = (a.roles || [])[0] || ""; bv = (b.roles || [])[0] || ""; }
|
|
else if (key === "tier") { av = a.plan_name || 0; bv = b.plan_name || 0; }
|
|
else if (key === "joined") {
|
|
av = new Date(a.joined_network || a.created || 0).getTime();
|
|
bv = new Date(b.joined_network || b.created || 0).getTime();
|
|
}
|
|
if (typeof av === "string") return av.localeCompare(bv) * dir;
|
|
return (av - bv) * dir;
|
|
});
|
|
|
|
return list;
|
|
}
|
|
|
|
get selectedMember() {
|
|
return this.people.find(p => p.id === this.selectedMemberId) || null;
|
|
}
|
|
|
|
handleSort(key) {
|
|
if (this.sortKey === key) {
|
|
this.sortDir = this.sortDir === "asc" ? "desc" : "asc";
|
|
} else {
|
|
this.sortKey = key;
|
|
this.sortDir = "asc";
|
|
}
|
|
this.rerender();
|
|
}
|
|
|
|
render() {
|
|
const filtered = this.filteredSortedPeople;
|
|
const detailOpen = !!this.selectedMember;
|
|
|
|
VStack(() => {
|
|
DesktopPeopleToolbar(
|
|
filtered,
|
|
this.searchText,
|
|
this.filterRole,
|
|
this.filterTier,
|
|
(text) => {
|
|
this.searchText = text
|
|
this.tableEl.people = this.filteredSortedPeople
|
|
this.tableEl.rerender()
|
|
},
|
|
(role) => { this.filterRole = role; this.rerender(); },
|
|
(tier) => { this.filterTier = tier; this.rerender(); }
|
|
)
|
|
|
|
HStack(() => {
|
|
// Table — shrinks when drawer open
|
|
VStack(() => {
|
|
this.tableEl = DesktopPeopleTable(
|
|
filtered,
|
|
this.selectedMemberId,
|
|
this.sortKey,
|
|
this.sortDir,
|
|
(id) => {
|
|
this.selectedMemberId = this.selectedMemberId === id ? null : id;
|
|
this.rerender();
|
|
},
|
|
(key) => this.handleSort(key)
|
|
)
|
|
})
|
|
.flex(1).height(100, pct).overflow("hidden")
|
|
|
|
// Slide-out detail drawer
|
|
if (detailOpen) {
|
|
VStack(() => {
|
|
// Drawer header with close button
|
|
HStack(() => {
|
|
p(`${this.selectedMember.first_name} ${this.selectedMember.last_name}`)
|
|
.margin(0).fontSize(0.88, em).fontWeight("600")
|
|
.color("var(--headertext)").flex(1).minWidth(0)
|
|
.overflow("hidden").whiteSpace("nowrap").textOverflow("ellipsis")
|
|
|
|
button("✕")
|
|
.border("none").background("transparent")
|
|
.color("var(--headertext)").opacity(0.4)
|
|
.fontSize(0.82, em).cursor("pointer").padding(0.25, em)
|
|
.borderRadius(0.3, em)
|
|
.onClick((done) => {if(!done) return; this.selectedMemberId = null; this.rerender(); })
|
|
})
|
|
.paddingHorizontal(1.25, em).paddingVertical(0.78, em)
|
|
.borderBottom("1px solid var(--divider)")
|
|
.alignItems("center").flexShrink(0)
|
|
|
|
VStack(() => {
|
|
DesktopPeopleDetail(this.selectedMember,
|
|
(updatedPerson) => this.updatePeople(updatedPerson)
|
|
)
|
|
})
|
|
.flex(1).overflow("hidden")
|
|
})
|
|
.width(360, px)
|
|
.height(100, pct)
|
|
.borderLeft("1px solid var(--divider)")
|
|
.flexShrink(0)
|
|
.overflow("hidden")
|
|
}
|
|
})
|
|
.flex(1).minHeight(0).width(100, pct).overflow("hidden")
|
|
})
|
|
.height(100, pct).width(100, pct).overflow("hidden")
|
|
.onAppear(async () => {
|
|
const res = await server.getPeople(global.currentNetwork.id);
|
|
if (!res.error && res.length > 0) {
|
|
if((this.people.length !== res.length) || !this.#lastUpdated) {
|
|
this.people = res.map(p => ({ ...p, _online: this.isOnline(p) }));
|
|
this.rerender();
|
|
}
|
|
}
|
|
})
|
|
}
|
|
|
|
updatePeople(person) {
|
|
this.people = this.people.map(p => p.id === person.id ? person : p)
|
|
this.rerender()
|
|
}
|
|
}
|
|
|
|
register(People)
|