Apps load from server
- Deleted /Jobs, /Announcements, /Events, /People - Commented out Forum and ForumPanel - Deleted /components/SearchBar, /components/LoadingCircle, /components/AddButton
This commit is contained in:
@@ -1 +1 @@
|
|||||||
VITE_API_URL=https://frm.so
|
VITE_API_URL=
|
||||||
@@ -1,90 +0,0 @@
|
|||||||
import util from "../../util"
|
|
||||||
import server from "../../_/code/bridge/server.js"
|
|
||||||
|
|
||||||
css(`
|
|
||||||
announcement- p {
|
|
||||||
font-size: 0.85em;
|
|
||||||
color: var(--darktext);
|
|
||||||
}
|
|
||||||
`)
|
|
||||||
|
|
||||||
class Announcement extends Shadow {
|
|
||||||
constructor(announcement) {
|
|
||||||
super()
|
|
||||||
this.announcement = announcement
|
|
||||||
}
|
|
||||||
|
|
||||||
render() {
|
|
||||||
VStack(() => {
|
|
||||||
HStack(() => {
|
|
||||||
h3(this.announcement.message)
|
|
||||||
.color("var(--text)")
|
|
||||||
.fontSize(1.3, em)
|
|
||||||
.fontWeight("normal")
|
|
||||||
.margin(0, em)
|
|
||||||
|
|
||||||
// Delete button
|
|
||||||
// if (this.announcement.creator_id === global.profile.id) {
|
|
||||||
// img(util.cssVariable("trash-src"), "1.5em")
|
|
||||||
// .marginRight(0.5, em)
|
|
||||||
// .onTap(() => {
|
|
||||||
// this.deleteAnnouncement(this.announcement)
|
|
||||||
// })
|
|
||||||
// }
|
|
||||||
})
|
|
||||||
.justifyContent("space-between")
|
|
||||||
.verticalAlign("center")
|
|
||||||
|
|
||||||
p(this.announcement.author ?? "Unknown author")
|
|
||||||
.marginTop(0.75, em)
|
|
||||||
p(this.convertDate(this.announcement.created) ?? "No date included")
|
|
||||||
.marginTop(0.25, em)
|
|
||||||
})
|
|
||||||
.paddingVertical(1.5, em)
|
|
||||||
.paddingHorizontal(3.5, em)
|
|
||||||
.marginHorizontal(1, em)
|
|
||||||
.borderRadius(10, px)
|
|
||||||
.background("var(--darkaccent)")
|
|
||||||
.border("1px solid var(--accent)")
|
|
||||||
.boxSizing("border-box")
|
|
||||||
}
|
|
||||||
|
|
||||||
async deleteAnnouncement(announcement) {
|
|
||||||
const result = await server.deleteAnnouncement(announcement.id, announcement.network_id, global.profile.id)
|
|
||||||
if (result.data === null) {
|
|
||||||
console.log("Failed to delete announcement")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
convertDate(rawDate) {
|
|
||||||
const parsed = new Date(rawDate);
|
|
||||||
|
|
||||||
if (isNaN(parsed.getTime())) return rawDate;
|
|
||||||
|
|
||||||
const month = parsed.toLocaleString("en-US", { month: "long", timeZone: "UTC" });
|
|
||||||
const day = parsed.getUTCDate();
|
|
||||||
const year = parsed.getUTCFullYear();
|
|
||||||
|
|
||||||
const hours24 = parsed.getUTCHours();
|
|
||||||
const minutes = parsed.getUTCMinutes();
|
|
||||||
|
|
||||||
const hours12 = hours24 % 12 || 12;
|
|
||||||
const ampm = hours24 >= 12 ? "PM" : "AM";
|
|
||||||
const paddedMinutes = String(minutes).padStart(2, "0");
|
|
||||||
|
|
||||||
const ordinal = (n) => {
|
|
||||||
const mod100 = n % 100;
|
|
||||||
if (mod100 >= 11 && mod100 <= 13) return `${n}th`;
|
|
||||||
switch (n % 10) {
|
|
||||||
case 1: return `${n}st`;
|
|
||||||
case 2: return `${n}nd`;
|
|
||||||
case 3: return `${n}rd`;
|
|
||||||
default: return `${n}th`;
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
return `${month} ${ordinal(day)}, ${year} at ${hours12}:${paddedMinutes} ${ampm}`;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
register(Announcement)
|
|
||||||
@@ -1,271 +0,0 @@
|
|||||||
import './Announcement.js'
|
|
||||||
import server from '../../_/code/bridge/server.js'
|
|
||||||
import '../../components/SearchBar.js'
|
|
||||||
|
|
||||||
css(`
|
|
||||||
announcements- {
|
|
||||||
font-family: 'Arial';
|
|
||||||
scrollbar-width: none;
|
|
||||||
-ms-overflow-style: none;
|
|
||||||
}
|
|
||||||
|
|
||||||
announcements- h1 {
|
|
||||||
font-family: 'Bona';
|
|
||||||
}
|
|
||||||
|
|
||||||
announcements- .VStack::-webkit-scrollbar {
|
|
||||||
display: none;
|
|
||||||
width: 0px;
|
|
||||||
height: 0px;
|
|
||||||
}
|
|
||||||
|
|
||||||
announcements- .VStack::-webkit-scrollbar-thumb {
|
|
||||||
background: transparent;
|
|
||||||
}
|
|
||||||
|
|
||||||
announcements- .VStack::-webkit-scrollbar-track {
|
|
||||||
background: transparent;
|
|
||||||
}
|
|
||||||
`)
|
|
||||||
|
|
||||||
class Announcements extends Shadow {
|
|
||||||
static searchableKeys = ['message', 'author'];
|
|
||||||
|
|
||||||
constructor() {
|
|
||||||
super()
|
|
||||||
this.announcements = global.currentNetwork.data.announcements.sort((a, b) => new Date(b.created) - new Date(a.created));
|
|
||||||
this.searchedAnnouncements = [];
|
|
||||||
this.searchText = "";
|
|
||||||
}
|
|
||||||
|
|
||||||
render() {
|
|
||||||
ZStack(() => {
|
|
||||||
|
|
||||||
VStack(() => {
|
|
||||||
SearchBar(this.searchText, "90vw")
|
|
||||||
|
|
||||||
VStack(() => {
|
|
||||||
if (!this.announcements || this.announcements == []) {
|
|
||||||
LoadingCircle()
|
|
||||||
} else if (this.searchText) {
|
|
||||||
if (this.searchedAnnouncements.length > 0) {
|
|
||||||
for (let i = 0; i < this.searchedAnnouncements.length; i++) {
|
|
||||||
AnnouncementCard(this.searchedAnnouncements[i])
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
h2("Could not find any announcements with your search criteria.")
|
|
||||||
.color("var(--divider)")
|
|
||||||
.fontWeight("bold")
|
|
||||||
.marginTop(7.5, em)
|
|
||||||
.marginBottom(0.5, em)
|
|
||||||
.textAlign("center")
|
|
||||||
}
|
|
||||||
} else if (this.announcements.length > 0) {
|
|
||||||
for (let i = 0; i < this.announcements.length; i++) {
|
|
||||||
AnnouncementCard(this.announcements[i])
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
h2("No Announcements")
|
|
||||||
.color("var(--divider)")
|
|
||||||
.fontWeight("bold")
|
|
||||||
.marginTop(7.5, em)
|
|
||||||
.marginBottom(0.5, em)
|
|
||||||
.textAlign("center")
|
|
||||||
}
|
|
||||||
})
|
|
||||||
.overflowY("scroll")
|
|
||||||
.gap(0.75, em)
|
|
||||||
|
|
||||||
if(global.currentNetwork.permissions.includes("announcements.add")) {
|
|
||||||
HStack(() => {
|
|
||||||
input("Image Upload", "0px", "0px")
|
|
||||||
.attr({ name: "image-upload", type: "file" })
|
|
||||||
.display("none")
|
|
||||||
.visibility("hidden")
|
|
||||||
.onChange((e) => {
|
|
||||||
const file = e.target.files[0]
|
|
||||||
if (!file) return
|
|
||||||
this.stagedImage = file
|
|
||||||
this.stagedImageURL = URL.createObjectURL(file)
|
|
||||||
this.rerender()
|
|
||||||
})
|
|
||||||
|
|
||||||
div("+")
|
|
||||||
.width(3, rem)
|
|
||||||
.height(3, rem)
|
|
||||||
.borderRadius(50, pct)
|
|
||||||
.border("1px solid color-mix(in srgb, var(--accent) 60%, transparent)")
|
|
||||||
.fontSize(2, em)
|
|
||||||
.transform("rotate(180deg)")
|
|
||||||
.zIndex(1001)
|
|
||||||
.display("flex")
|
|
||||||
.alignItems("center")
|
|
||||||
.justifyContent("center")
|
|
||||||
.transition("scale .2s")
|
|
||||||
.state("touched", function (touched) {
|
|
||||||
if(touched) {
|
|
||||||
this.scale("1.5")
|
|
||||||
this.color("var(--darkaccent)")
|
|
||||||
this.backgroundColor("var(--divider)")
|
|
||||||
} else {
|
|
||||||
this.scale("")
|
|
||||||
this.color("var(--divider)")
|
|
||||||
this.backgroundColor("var(--searchbackground)")
|
|
||||||
}
|
|
||||||
})
|
|
||||||
.onTouch(function (start) {
|
|
||||||
if(start) {
|
|
||||||
this.attr({touched: "true"})
|
|
||||||
} else {
|
|
||||||
this.attr({touched: ""})
|
|
||||||
}
|
|
||||||
})
|
|
||||||
.onClick((done) => {
|
|
||||||
if(done) {
|
|
||||||
const inputSelector = this.$('[name="image-upload"]');
|
|
||||||
inputSelector.click()
|
|
||||||
}
|
|
||||||
})
|
|
||||||
|
|
||||||
input("Add an Announcement")
|
|
||||||
.flex("1 1 auto")
|
|
||||||
.minWidth(0)
|
|
||||||
.color("var(--text)")
|
|
||||||
.background("var(--searchbackground)")
|
|
||||||
.paddingVertical(0, rem)
|
|
||||||
.fontSize(1, rem)
|
|
||||||
.paddingHorizontal(1, rem)
|
|
||||||
.borderRadius(100, px)
|
|
||||||
.border("1px solid color-mix(in srgb, var(--accent) 60%, transparent)")
|
|
||||||
.onTouch(function (start) {
|
|
||||||
if (start) {
|
|
||||||
this.style.backgroundColor = "var(--accent)"
|
|
||||||
} else {
|
|
||||||
setTimeout(() => {
|
|
||||||
$("appmenu-").display("none")
|
|
||||||
this.focus()
|
|
||||||
}, 20)
|
|
||||||
console.log($("appmenu-"))
|
|
||||||
this.style.backgroundColor = "var(--searchbackground)"
|
|
||||||
}
|
|
||||||
})
|
|
||||||
.addEventListener("blur", () => {
|
|
||||||
setTimeout(() => {
|
|
||||||
$("appmenu-").display("grid")
|
|
||||||
}, 20)
|
|
||||||
})
|
|
||||||
})
|
|
||||||
.width(100, pct)
|
|
||||||
.boxSizing("border-box")
|
|
||||||
.position("absolute")
|
|
||||||
.paddingHorizontal(1, rem)
|
|
||||||
.bottom(1, vh)
|
|
||||||
.gap(0.5, rem)
|
|
||||||
}
|
|
||||||
})
|
|
||||||
.boxSizing("border-box")
|
|
||||||
.height(100, pct)
|
|
||||||
.width(100, pct)
|
|
||||||
.onEvent("announcementsearch", this.onAnnouncementSearch)
|
|
||||||
.onEvent("new-announcement", this.onNewAnnouncement)
|
|
||||||
.onEvent("deleted-announcement", this.onDeletedAnnouncement)
|
|
||||||
.onEvent("edited-announcement", this.onEditedAnnouncement)
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
async handleUpload(file) {
|
|
||||||
try {
|
|
||||||
const body = new FormData();
|
|
||||||
body.append('image', file);
|
|
||||||
const res = await util.authFetch(`${util.HOST}/profile/upload-image`, {
|
|
||||||
method: "POST",
|
|
||||||
credentials: "include",
|
|
||||||
headers: {
|
|
||||||
"Accept": "application/json"
|
|
||||||
},
|
|
||||||
body: body
|
|
||||||
});
|
|
||||||
|
|
||||||
if(res.status === 401) {
|
|
||||||
return res.status
|
|
||||||
}
|
|
||||||
if (!res.ok) return res.status;
|
|
||||||
const data = await res.json()
|
|
||||||
global.profile = data.member
|
|
||||||
console.log(global.profile)
|
|
||||||
} catch (err) { // Network error / Error reaching server
|
|
||||||
console.error(err);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
addPhoto() {
|
|
||||||
console.log("hey")
|
|
||||||
}
|
|
||||||
|
|
||||||
onNewAnnouncement = (e) => {
|
|
||||||
let newAnnouncement = e.detail.announcement;
|
|
||||||
this.announcements.push(newAnnouncement)
|
|
||||||
this.announcements.sort((a, b) => new Date(b.created) - new Date(a.created));
|
|
||||||
this.rerender()
|
|
||||||
}
|
|
||||||
|
|
||||||
onDeletedAnnouncement = (e) => {
|
|
||||||
let deletedId = e.detail.id
|
|
||||||
const i = this.announcements.findIndex(ann => ann.id === deletedId)
|
|
||||||
if (i !== -1) this.announcements.splice(i, 1);
|
|
||||||
this.rerender()
|
|
||||||
}
|
|
||||||
|
|
||||||
onEditedAnnouncement = (e) => {
|
|
||||||
let editedAnnouncement = e.detail
|
|
||||||
const i = this.announcements.findIndex(ann => ann.id === editedAnnouncement.id)
|
|
||||||
if (i !== -1) {
|
|
||||||
this.announcements.splice(i, 1)
|
|
||||||
this.announcements.unshift(editedAnnouncement)
|
|
||||||
}
|
|
||||||
this.rerender()
|
|
||||||
}
|
|
||||||
|
|
||||||
onAnnouncementSearch = (e) => {
|
|
||||||
let searchText = e.detail.searchText.toLowerCase().trim();
|
|
||||||
if (!searchText) {
|
|
||||||
this.searchedAnnouncements = [];
|
|
||||||
} else {
|
|
||||||
this.searchedAnnouncements = this.announcements.filter(announcement =>
|
|
||||||
Announcements.searchableKeys.some(key =>
|
|
||||||
String(announcement[key]).toLowerCase().includes(searchText)
|
|
||||||
)
|
|
||||||
);
|
|
||||||
}
|
|
||||||
this.searchText = searchText
|
|
||||||
this.rerender()
|
|
||||||
}
|
|
||||||
|
|
||||||
async getAnnouncements(networkId) {
|
|
||||||
const fetchedAnnouncements = await server.getAnnouncements(networkId)
|
|
||||||
if (this.checkForUpdates(this.announcements, fetchedAnnouncements.data)) {
|
|
||||||
this.announcements = fetchedAnnouncements.data.sort((a, b) => new Date(b.created) - new Date(a.created));
|
|
||||||
global.currentNetwork.data.announcements = this.announcements
|
|
||||||
this.rerender()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
connectedCallback() {
|
|
||||||
this.getAnnouncements(global.currentNetwork.id)
|
|
||||||
}
|
|
||||||
|
|
||||||
checkForUpdates(currentAnnouncements, fetchedAnnouncements) {
|
|
||||||
if (currentAnnouncements.length !== fetchedAnnouncements.length) return true;
|
|
||||||
|
|
||||||
const currentMap = new Map(currentAnnouncements.map(ann => [ann.id, ann]));
|
|
||||||
|
|
||||||
for (const fetchedAnn of fetchedAnnouncements) {
|
|
||||||
const currentAnn = currentMap.get(fetchedAnn.id);
|
|
||||||
if (!currentAnn) return true;
|
|
||||||
if (currentAnn.updated_at !== fetchedAnn.updated_at) return true;
|
|
||||||
}
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
register(Announcements)
|
|
||||||
@@ -1,150 +0,0 @@
|
|||||||
import './Panel.js'
|
|
||||||
import server from '../../_/code/bridge/serverFunctions.js'
|
|
||||||
|
|
||||||
css(`
|
|
||||||
announcements- {
|
|
||||||
font-family: 'Bona';
|
|
||||||
}
|
|
||||||
|
|
||||||
announcements- input::placeholder {
|
|
||||||
font-family: 'Bona Nova';
|
|
||||||
font-size: 0.9em;
|
|
||||||
color: var(--accent);
|
|
||||||
}
|
|
||||||
|
|
||||||
input::placeholder {
|
|
||||||
font-family: Arial;
|
|
||||||
}
|
|
||||||
|
|
||||||
input[type="checkbox"] {
|
|
||||||
appearance: none; /* remove default style */
|
|
||||||
-webkit-appearance: none;
|
|
||||||
width: 1em;
|
|
||||||
height: 1em;
|
|
||||||
border: 1px solid var(--accent);
|
|
||||||
}
|
|
||||||
|
|
||||||
input[type="checkbox"]:checked {
|
|
||||||
background-color: var(--red);
|
|
||||||
}
|
|
||||||
`)
|
|
||||||
|
|
||||||
class Announcements extends Shadow {
|
|
||||||
announcements;
|
|
||||||
|
|
||||||
constructor() {
|
|
||||||
super()
|
|
||||||
this.announcements = global.currentNetwork.data.announcements.sort((a, b) => new Date(b.created) - new Date(a.created));
|
|
||||||
console.log(this.announcements)
|
|
||||||
}
|
|
||||||
|
|
||||||
render() {
|
|
||||||
ZStack(() => {
|
|
||||||
VStack(() => {
|
|
||||||
|
|
||||||
Panel(this.announcements)
|
|
||||||
|
|
||||||
input("Message", "70%")
|
|
||||||
.paddingVertical(0.75, em)
|
|
||||||
.boxSizing("border-box")
|
|
||||||
.paddingHorizontal(2, em)
|
|
||||||
.color("var(--text)")
|
|
||||||
.background("var(--searchbackground)")
|
|
||||||
.marginBottom(1, em)
|
|
||||||
.border("0.5px solid var(--accent)")
|
|
||||||
.outline("none")
|
|
||||||
.borderRadius(100, px)
|
|
||||||
.fontFamily("Arial")
|
|
||||||
.fontSize(1, em)
|
|
||||||
.onKeyDown(async function(e) {
|
|
||||||
if (e.key === "Enter") {
|
|
||||||
const result = await server.addAnnouncement(this.value, global.currentNetwork.id, global.profile.id)
|
|
||||||
if (result.data.status === 200) {
|
|
||||||
window.dispatchEvent(new CustomEvent('new-announcement', {
|
|
||||||
detail: { announcement: result.data.announcement }
|
|
||||||
}));
|
|
||||||
} else {
|
|
||||||
// error
|
|
||||||
}
|
|
||||||
this.value = ""
|
|
||||||
}
|
|
||||||
})
|
|
||||||
})
|
|
||||||
.gap(1, em)
|
|
||||||
.boxSizing("border-box")
|
|
||||||
.width(100, pct)
|
|
||||||
.height(100, pct)
|
|
||||||
.horizontalAlign("center")
|
|
||||||
.verticalAlign("end")
|
|
||||||
.minHeight(0)
|
|
||||||
})
|
|
||||||
.backgroundColor("var(--main)")
|
|
||||||
.boxSizing("border-box")
|
|
||||||
.paddingVertical(1, em)
|
|
||||||
.width(100, pct)
|
|
||||||
.height(100, pct)
|
|
||||||
.flex("1 1 auto")
|
|
||||||
.onEvent("new-announcement", this.onNewAnnouncement)
|
|
||||||
.onEvent("deleted-announcement", this.onDeletedAnnouncement)
|
|
||||||
.onEvent("edited-announcement", this.onEditedAnnouncement)
|
|
||||||
}
|
|
||||||
|
|
||||||
connectedCallback() {
|
|
||||||
this.getAnnouncements(global.currentNetwork.id)
|
|
||||||
}
|
|
||||||
|
|
||||||
checkForUpdates(currentAnnouncements, fetchedAnnouncements) {
|
|
||||||
if (currentAnnouncements.length !== fetchedAnnouncements.length) return true;
|
|
||||||
|
|
||||||
const currentMap = new Map(currentAnnouncements.map(ann => [ann.id, ann]));
|
|
||||||
|
|
||||||
for (const fetchedAnn of fetchedAnnouncements) {
|
|
||||||
const currentAnn = currentMap.get(fetchedAnn.id);
|
|
||||||
|
|
||||||
// new event added
|
|
||||||
if (!currentAnn) return true;
|
|
||||||
|
|
||||||
// existing event changed
|
|
||||||
if (currentAnn.updated_at !== fetchedAnn.updated_at) {
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
async getAnnouncements(networkId) {
|
|
||||||
const fetchedAnnouncements = await server.getAnnouncements(networkId)
|
|
||||||
if (this.checkForUpdates(this.announcements, fetchedAnnouncements.data)) {
|
|
||||||
console.log("found updates")
|
|
||||||
this.announcements = fetchedAnnouncements.data.sort((a, b) => new Date(b.created) - new Date(a.created));
|
|
||||||
global.currentNetwork.data.announcements = this.announcements
|
|
||||||
this.rerender()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
onNewAnnouncement = (e) => {
|
|
||||||
let newAnnouncement = e.detail.announcement;
|
|
||||||
this.announcements.push(newAnnouncement)
|
|
||||||
this.announcements.sort((a, b) => new Date(b.created) - new Date(a.created));
|
|
||||||
this.rerender()
|
|
||||||
}
|
|
||||||
|
|
||||||
onDeletedAnnouncement = (e) => {
|
|
||||||
let deletedId = e.detail.id
|
|
||||||
const i = this.announcements.findIndex(ann => ann.id === deletedId)
|
|
||||||
if (i !== -1) this.announcements.splice(i, 1);
|
|
||||||
this.rerender()
|
|
||||||
}
|
|
||||||
|
|
||||||
onEditedAnnouncement = (e) => {
|
|
||||||
let editedAnnouncement = e.detail
|
|
||||||
const i = this.announcements.findIndex(ann => ann.id === editedPost.id)
|
|
||||||
if (i !== -1) {
|
|
||||||
this.announcements.splice(i, 1)
|
|
||||||
this.announcements.unshift(editedAnnouncement)
|
|
||||||
}
|
|
||||||
this.rerender()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
register(Announcements)
|
|
||||||
@@ -1,151 +0,0 @@
|
|||||||
import "../../components/LoadingCircle.js"
|
|
||||||
import server from "../../_/code/bridge/server.js"
|
|
||||||
|
|
||||||
css(`
|
|
||||||
panel- {
|
|
||||||
scrollbar-width: none;
|
|
||||||
-ms-overflow-style: none;
|
|
||||||
}
|
|
||||||
|
|
||||||
panel-::-webkit-scrollbar {
|
|
||||||
display: none;
|
|
||||||
width: 0px;
|
|
||||||
height: 0px;
|
|
||||||
}
|
|
||||||
|
|
||||||
panel-::-webkit-scrollbar-thumb {
|
|
||||||
background: transparent;
|
|
||||||
}
|
|
||||||
|
|
||||||
panel-::-webkit-scrollbar-track {
|
|
||||||
background: transparent;
|
|
||||||
}
|
|
||||||
`)
|
|
||||||
|
|
||||||
class Panel extends Shadow {
|
|
||||||
announcements = []
|
|
||||||
isSending = false
|
|
||||||
|
|
||||||
constructor(announcements) {
|
|
||||||
super()
|
|
||||||
this.announcements = announcements
|
|
||||||
}
|
|
||||||
|
|
||||||
render() {
|
|
||||||
VStack(() => {
|
|
||||||
if(this.announcements.length > 0) {
|
|
||||||
let previousDate = null
|
|
||||||
|
|
||||||
for(let i=0; i<this.announcements.length; i++) {
|
|
||||||
let announcement = this.announcements[i]
|
|
||||||
const isMe = announcement.creator_id === global.profile.id
|
|
||||||
const dateParts = this.parseDate(announcement.created);
|
|
||||||
const { date, time } = dateParts;
|
|
||||||
|
|
||||||
if (previousDate !== date) {
|
|
||||||
previousDate = date;
|
|
||||||
|
|
||||||
p(date)
|
|
||||||
.textAlign("center")
|
|
||||||
.opacity(0.6)
|
|
||||||
.fontWeight("bold")
|
|
||||||
.paddingTop(1, em)
|
|
||||||
.paddingBottom(0.5, em)
|
|
||||||
.color("var(--quillred)")
|
|
||||||
.borderTop(`1px solid var(--${i == 0 ? "transparent" : "divider"})`)
|
|
||||||
}
|
|
||||||
|
|
||||||
VStack(() => {
|
|
||||||
HStack(() => {
|
|
||||||
h3(isMe ? "Me" : this.getAuthorName(announcement))
|
|
||||||
.color(isMe ? "var(--quillred)" : "var(--headertext")
|
|
||||||
.opacity(0.75)
|
|
||||||
.margin(0)
|
|
||||||
|
|
||||||
h3(`${date} ${time}`)
|
|
||||||
.opacity(0.5)
|
|
||||||
.color("var(--headertext)")
|
|
||||||
.margin(0)
|
|
||||||
.marginLeft(0.5, em)
|
|
||||||
.fontSize(1, em)
|
|
||||||
|
|
||||||
if (announcement.created !== announcement.updated_at) {
|
|
||||||
p("(edited)")
|
|
||||||
.color("var(--headertext)")
|
|
||||||
.letterSpacing(0.8, "px")
|
|
||||||
.opacity(0.5)
|
|
||||||
.fontWeight("bold")
|
|
||||||
.paddingLeft(0.25, em)
|
|
||||||
.fontSize(0.9, em)
|
|
||||||
}
|
|
||||||
})
|
|
||||||
.verticalAlign("center")
|
|
||||||
.marginBottom(0.1, em)
|
|
||||||
|
|
||||||
p(announcement.text)
|
|
||||||
.color("var(--text)")
|
|
||||||
.marginHorizontal(0.2, em)
|
|
||||||
.paddingVertical(0.2, em)
|
|
||||||
.boxSizing("border-box")
|
|
||||||
})
|
|
||||||
.marginBottom(0.05, em)
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
LoadingCircle()
|
|
||||||
}
|
|
||||||
})
|
|
||||||
.gap(1, em)
|
|
||||||
.fontSize(1.1, em)
|
|
||||||
.boxSizing("border-box")
|
|
||||||
.flex("1 1 auto")
|
|
||||||
.minHeight(0)
|
|
||||||
.overflowY("auto")
|
|
||||||
.width(100, pct)
|
|
||||||
.paddingBottom(2, em)
|
|
||||||
.paddingHorizontal(4, pct)
|
|
||||||
.backgroundColor("var(--main)")
|
|
||||||
.onAppear(async () => {
|
|
||||||
requestAnimationFrame(() => {
|
|
||||||
this.scrollTo({ top: 0, behavior: "smooth" });
|
|
||||||
});
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
getAuthorName(announcement) {
|
|
||||||
const members = global.currentNetwork.data.members;
|
|
||||||
const creator = members.find(m => m.id === announcement.creator_id);
|
|
||||||
if (creator) {
|
|
||||||
return `${creator.first_name} ${creator.last_name}`
|
|
||||||
} else {
|
|
||||||
return "No name"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
parseDate(str) {
|
|
||||||
// Format: YYYY-MM-DDTHH:MM:SS.mmmZ
|
|
||||||
const match = str.match(/^(\d{4})-(\d{2})-(\d{2})T(\d{2}):(\d{2}):\d{2}\.\d+Z$/);
|
|
||||||
if (!match) return null;
|
|
||||||
|
|
||||||
const [, yyyy, mm, dd, hh, min] = match;
|
|
||||||
|
|
||||||
// Convert 24h to 12h
|
|
||||||
const hour24 = parseInt(hh, 10);
|
|
||||||
const ampm = hour24 >= 12 ? 'pm' : 'am';
|
|
||||||
const hour12 = hour24 % 12 || 12;
|
|
||||||
|
|
||||||
const date = `${mm}/${dd}/${yyyy}`;
|
|
||||||
const time = `${hour12}:${min}${ampm}`;
|
|
||||||
|
|
||||||
return { date, time };
|
|
||||||
}
|
|
||||||
|
|
||||||
formatTime(str) {
|
|
||||||
const match = str.match(/-(\d+:\d+):\d+.*(am|pm)/i);
|
|
||||||
if (!match) return null;
|
|
||||||
|
|
||||||
const [_, hourMin, ampm] = match;
|
|
||||||
return hourMin + ampm.toLowerCase();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
register(Panel)
|
|
||||||
@@ -1,92 +0,0 @@
|
|||||||
import util from "../../util"
|
|
||||||
import server from "../../_/code/bridge/server.js"
|
|
||||||
|
|
||||||
css(`
|
|
||||||
eventcard- p {
|
|
||||||
font-size: 0.85em;
|
|
||||||
color: var(--darktext);
|
|
||||||
}
|
|
||||||
`)
|
|
||||||
|
|
||||||
class EventCard extends Shadow {
|
|
||||||
constructor(event) {
|
|
||||||
super()
|
|
||||||
this.event = event
|
|
||||||
}
|
|
||||||
|
|
||||||
render() {
|
|
||||||
VStack(() => {
|
|
||||||
HStack(() => {
|
|
||||||
h3(this.event.title)
|
|
||||||
.color("var(--text)")
|
|
||||||
.fontSize(1.3, em)
|
|
||||||
.fontWeight("normal")
|
|
||||||
.margin(0, em)
|
|
||||||
|
|
||||||
// Delete button
|
|
||||||
// if (this.event.creator_id === global.profile.id) {
|
|
||||||
// img(util.cssVariable("trash-src"), "1.5em")
|
|
||||||
// .marginRight(0.5, em)
|
|
||||||
// .onTap(() => {
|
|
||||||
// this.deleteEvent(this.event)
|
|
||||||
// })
|
|
||||||
// }
|
|
||||||
})
|
|
||||||
.justifyContent("space-between")
|
|
||||||
.verticalAlign("center")
|
|
||||||
|
|
||||||
p(this.event.location ?? "No location added")
|
|
||||||
.marginTop(0.75, em)
|
|
||||||
p(this.convertDate(this.event.time_start) ?? "No time included")
|
|
||||||
.marginTop(0.25, em)
|
|
||||||
p(this.event.description ?? "No description included")
|
|
||||||
.marginTop(0.75, em)
|
|
||||||
})
|
|
||||||
.paddingVertical(1.5, em)
|
|
||||||
.paddingHorizontal(3.5, em)
|
|
||||||
.marginHorizontal(1, em)
|
|
||||||
.borderRadius(10, px)
|
|
||||||
.background("var(--darkaccent)")
|
|
||||||
.border("1px solid var(--accent)")
|
|
||||||
.boxSizing("border-box")
|
|
||||||
}
|
|
||||||
|
|
||||||
async deleteEvent(event) {
|
|
||||||
const result = await server.deleteEvent(event.id, event.network_id, global.profile.id)
|
|
||||||
if (result.data === null) {
|
|
||||||
console.log("Failed to delete event")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
convertDate(rawDate) {
|
|
||||||
const parsed = new Date(rawDate);
|
|
||||||
|
|
||||||
if (isNaN(parsed.getTime())) return rawDate;
|
|
||||||
|
|
||||||
const month = parsed.toLocaleString("en-US", { month: "long", timeZone: "UTC" });
|
|
||||||
const day = parsed.getUTCDate();
|
|
||||||
const year = parsed.getUTCFullYear();
|
|
||||||
|
|
||||||
const hours24 = parsed.getUTCHours();
|
|
||||||
const minutes = parsed.getUTCMinutes();
|
|
||||||
|
|
||||||
const hours12 = hours24 % 12 || 12;
|
|
||||||
const ampm = hours24 >= 12 ? "PM" : "AM";
|
|
||||||
const paddedMinutes = String(minutes).padStart(2, "0");
|
|
||||||
|
|
||||||
const ordinal = (n) => {
|
|
||||||
const mod100 = n % 100;
|
|
||||||
if (mod100 >= 11 && mod100 <= 13) return `${n}th`;
|
|
||||||
switch (n % 10) {
|
|
||||||
case 1: return `${n}st`;
|
|
||||||
case 2: return `${n}nd`;
|
|
||||||
case 3: return `${n}rd`;
|
|
||||||
default: return `${n}th`;
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
return `${month} ${ordinal(day)}, ${year} at ${hours12}:${paddedMinutes} ${ampm}`;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
register(EventCard)
|
|
||||||
@@ -1,175 +0,0 @@
|
|||||||
import server from "../../_/code/bridge/server"
|
|
||||||
|
|
||||||
class EventForm extends Shadow {
|
|
||||||
inputStyles(el) {
|
|
||||||
return el
|
|
||||||
.background("var(--main)")
|
|
||||||
.color("var(--text)")
|
|
||||||
.border("1px solid var(--accent)")
|
|
||||||
.fontSize(0.9, rem)
|
|
||||||
.backgroundColor("var(--darkaccent)")
|
|
||||||
.borderRadius(12, px)
|
|
||||||
.outline("none")
|
|
||||||
.onTouch((start) => {
|
|
||||||
if (start) {
|
|
||||||
this.style.backgroundColor = "var(--accent)"
|
|
||||||
} else {
|
|
||||||
this.style.backgroundColor = "var(--darkaccent)"
|
|
||||||
}
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
errorMessage = ""
|
|
||||||
|
|
||||||
render() {
|
|
||||||
ZStack(() => {
|
|
||||||
p("X")
|
|
||||||
.color("var(--darkred)")
|
|
||||||
.fontSize(2, em)
|
|
||||||
.position("absolute")
|
|
||||||
.fontFamily("Arial")
|
|
||||||
.marginTop(1, rem)
|
|
||||||
.marginLeft(1, rem)
|
|
||||||
.onTap(() => {
|
|
||||||
this.toggle()
|
|
||||||
})
|
|
||||||
|
|
||||||
form(() => {
|
|
||||||
VStack(() => {
|
|
||||||
h1("Create an Event")
|
|
||||||
.color("var(--text)")
|
|
||||||
.textAlign("center")
|
|
||||||
.fontFamily("Arial")
|
|
||||||
.marginTop(1.5, em)
|
|
||||||
|
|
||||||
input("Title", "70%")
|
|
||||||
.attr({ name: "title", type: "text" })
|
|
||||||
.margin(1, em)
|
|
||||||
.padding(1, em)
|
|
||||||
.styles(this.inputStyles)
|
|
||||||
input("Location", "70%")
|
|
||||||
.attr({ name: "location", type: "text" })
|
|
||||||
.margin(1, em)
|
|
||||||
.padding(1, em)
|
|
||||||
.styles(this.inputStyles)
|
|
||||||
input("Start Time", "70%")
|
|
||||||
.attr({ name: "time_start", type: "datetime-local" })
|
|
||||||
.margin(1, em)
|
|
||||||
.padding(1, em)
|
|
||||||
.styles(this.inputStyles)
|
|
||||||
input("Description", "70%")
|
|
||||||
.attr({ name: "description", type: "text" })
|
|
||||||
.margin(1, em)
|
|
||||||
.padding(1, em)
|
|
||||||
.styles(this.inputStyles)
|
|
||||||
HStack(() => {
|
|
||||||
button("==>")
|
|
||||||
.padding(1, em)
|
|
||||||
.fontSize(0.9, rem)
|
|
||||||
.borderRadius(12, px)
|
|
||||||
.background("var(--searchbackground)")
|
|
||||||
.color("var(--text)")
|
|
||||||
.border("1px solid var(--accent)")
|
|
||||||
.boxSizing("border-box")
|
|
||||||
.onTouch(function (start) {
|
|
||||||
if (start) {
|
|
||||||
this.style.backgroundColor = "var(--accent)"
|
|
||||||
} else {
|
|
||||||
this.style.backgroundColor = "var(--searchbackground)"
|
|
||||||
}
|
|
||||||
})
|
|
||||||
})
|
|
||||||
.width(70, vw)
|
|
||||||
.margin("auto")
|
|
||||||
.fontSize(0.9, rem)
|
|
||||||
.paddingLeft(0, em)
|
|
||||||
.paddingRight(2, em)
|
|
||||||
.marginVertical(1, em)
|
|
||||||
.border("1px solid transparent")
|
|
||||||
|
|
||||||
p("")
|
|
||||||
.dynamicText("errormessage", "{{}}")
|
|
||||||
.margin("auto")
|
|
||||||
.marginTop(1, em)
|
|
||||||
.color("var(--text)")
|
|
||||||
.fontFamily("Arial")
|
|
||||||
.opacity(.7)
|
|
||||||
.padding(0.5, em)
|
|
||||||
.backgroundColor("var(--darkred)")
|
|
||||||
.width(100, pct)
|
|
||||||
.textAlign("center")
|
|
||||||
.boxSizing("border-box")
|
|
||||||
})
|
|
||||||
.horizontalAlign("center")
|
|
||||||
})
|
|
||||||
.onSubmit((e) => {
|
|
||||||
e.preventDefault()
|
|
||||||
const data = {
|
|
||||||
title: e.target.$('[name="title"]').value,
|
|
||||||
location: e.target.$('[name="location"]').value,
|
|
||||||
time_start: e.target.$('[name="time_start"]').value,
|
|
||||||
description: e.target.$('[name="description"]').value,
|
|
||||||
};
|
|
||||||
this.handleSend(data)
|
|
||||||
})
|
|
||||||
})
|
|
||||||
.position("fixed")
|
|
||||||
.height(window.visualViewport.height - 20, px)
|
|
||||||
.width(100, pct)
|
|
||||||
.top(100, vh)
|
|
||||||
.background("var(--main)")
|
|
||||||
.zIndex(4)
|
|
||||||
.borderTopLeftRadius("10px")
|
|
||||||
.borderTopRightRadius("10px")
|
|
||||||
.boxSizing("border-box")
|
|
||||||
.border("1px solid var(--accent)")
|
|
||||||
.transition("top .3s")
|
|
||||||
}
|
|
||||||
|
|
||||||
async handleSend(eventData) {
|
|
||||||
if (!eventData.title) {
|
|
||||||
this.$(".VStack > p")
|
|
||||||
.attr({ errorMessage: 'Events must include a title.' })
|
|
||||||
.display("")
|
|
||||||
|
|
||||||
return;
|
|
||||||
} else {
|
|
||||||
this.$(".VStack > p").style.display = "none"
|
|
||||||
}
|
|
||||||
|
|
||||||
const date = new Date(eventData.time_start);
|
|
||||||
const timestamp = eventData.time_start ? date.toISOString() : null;
|
|
||||||
const newEvent = {
|
|
||||||
title: eventData.title,
|
|
||||||
location: eventData.location ?? null,
|
|
||||||
time_start: timestamp ?? null,
|
|
||||||
description: eventData.description ?? null
|
|
||||||
}
|
|
||||||
|
|
||||||
const { data } = await server.addEvent(newEvent, global.currentNetwork.id, global.profile.id)
|
|
||||||
if (data.status === 200) {
|
|
||||||
console.log("Added new event: ", data)
|
|
||||||
this.toggle()
|
|
||||||
window.dispatchEvent(new CustomEvent('new-event', {
|
|
||||||
detail: { event: data.event }
|
|
||||||
}));
|
|
||||||
} else {
|
|
||||||
console.log("Failed to add new event: ", data)
|
|
||||||
this.$(".VStack > p")
|
|
||||||
.attr({ errorMessage: data.error })
|
|
||||||
.display("")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
toggle() {
|
|
||||||
if(this.style.top === "15vh") {
|
|
||||||
this.style.top = "100vh"
|
|
||||||
this.pointerEvents = "none"
|
|
||||||
} else {
|
|
||||||
this.style.top = "15vh"
|
|
||||||
this.pointerEvents = "auto"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
register(EventForm)
|
|
||||||
@@ -1,178 +0,0 @@
|
|||||||
import "../../components/TopBar.js"
|
|
||||||
import "../../components/LoadingCircle.js"
|
|
||||||
import "./EventCard.js"
|
|
||||||
import "./EventForm.js"
|
|
||||||
import server from "../../_/code/bridge/server.js"
|
|
||||||
import "../../components/SearchBar.js"
|
|
||||||
|
|
||||||
css(`
|
|
||||||
events- {
|
|
||||||
font-family: 'Arial';
|
|
||||||
scrollbar-width: none;
|
|
||||||
-ms-overflow-style: none;
|
|
||||||
}
|
|
||||||
|
|
||||||
events- h1 {
|
|
||||||
font-family: 'Bona';
|
|
||||||
}
|
|
||||||
|
|
||||||
events- .VStack::-webkit-scrollbar {
|
|
||||||
display: none;
|
|
||||||
width: 0px;
|
|
||||||
height: 0px;
|
|
||||||
}
|
|
||||||
|
|
||||||
events- .VStack::-webkit-scrollbar-thumb {
|
|
||||||
background: transparent;
|
|
||||||
}
|
|
||||||
|
|
||||||
events- .VStack::-webkit-scrollbar-track {
|
|
||||||
background: transparent;
|
|
||||||
}
|
|
||||||
`)
|
|
||||||
|
|
||||||
class Events extends Shadow {
|
|
||||||
static searchableKeys = ['title', 'description', 'location'];
|
|
||||||
|
|
||||||
constructor() {
|
|
||||||
super()
|
|
||||||
this.events = global.currentNetwork.data.events;
|
|
||||||
this.searchedEvents = [];
|
|
||||||
this.searchText = "";
|
|
||||||
}
|
|
||||||
|
|
||||||
render() {
|
|
||||||
ZStack(() => {
|
|
||||||
|
|
||||||
EventForm()
|
|
||||||
|
|
||||||
VStack(() => {
|
|
||||||
HStack(() => {
|
|
||||||
SearchBar(this.searchText, "75vw")
|
|
||||||
AddButton()
|
|
||||||
})
|
|
||||||
|
|
||||||
VStack(() => {
|
|
||||||
if (!this.events || this.events == []) {
|
|
||||||
LoadingCircle()
|
|
||||||
} else if (this.searchText) {
|
|
||||||
if (this.searchedEvents.length > 0) {
|
|
||||||
for (let i = 0; i < this.searchedEvents.length; i++) {
|
|
||||||
EventCard(this.searchedEvents[i])
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
h2("Could not find any events with your search criteria.")
|
|
||||||
.color("var(--divider)")
|
|
||||||
.fontWeight("bold")
|
|
||||||
.marginTop(7.5, em)
|
|
||||||
.marginBottom(0.5, em)
|
|
||||||
.textAlign("center")
|
|
||||||
}
|
|
||||||
} else if (this.events.length > 0) {
|
|
||||||
for (let i = 0; i < this.events.length; i++) {
|
|
||||||
EventCard(this.events[i])
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
h2("No Events")
|
|
||||||
.color("var(--divider)")
|
|
||||||
.fontWeight("bold")
|
|
||||||
.marginTop(7.5, em)
|
|
||||||
.marginBottom(0.5, em)
|
|
||||||
.textAlign("center")
|
|
||||||
}
|
|
||||||
})
|
|
||||||
.overflowY("scroll")
|
|
||||||
.gap(0.75, em)
|
|
||||||
})
|
|
||||||
.boxSizing("border-box")
|
|
||||||
.height(100, pct)
|
|
||||||
.width(100, pct)
|
|
||||||
.onEvent("eventsearch", this.onEventSearch)
|
|
||||||
.onEvent("new-event", this.onNewEvent)
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
onNewEvent = (e) => {
|
|
||||||
let newEvent = e.detail.event;
|
|
||||||
this.events.push(newEvent)
|
|
||||||
this.rerender()
|
|
||||||
}
|
|
||||||
|
|
||||||
onEventSearch = (e) => {
|
|
||||||
let searchText = e.detail.searchText.toLowerCase().trim();
|
|
||||||
if (!searchText) {
|
|
||||||
this.searchedEvents = [];
|
|
||||||
} else {
|
|
||||||
this.searchedEvents = this.events.filter(event =>
|
|
||||||
Events.searchableKeys.some(key =>
|
|
||||||
String(event[key]).toLowerCase().includes(searchText)
|
|
||||||
)
|
|
||||||
);
|
|
||||||
}
|
|
||||||
this.searchText = searchText
|
|
||||||
this.rerender()
|
|
||||||
}
|
|
||||||
|
|
||||||
async getEvents(networkId) {
|
|
||||||
const fetchedEvents = await server.getEvents(networkId)
|
|
||||||
if (this.checkForUpdates(this.events, fetchedEvents.data)) {
|
|
||||||
this.events = fetchedEvents.data
|
|
||||||
this.rerender()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
connectedCallback() {
|
|
||||||
this.getEvents(global.currentNetwork.id)
|
|
||||||
}
|
|
||||||
|
|
||||||
checkForUpdates(currentEvents, fetchedEvents) {
|
|
||||||
if (currentEvents.length !== fetchedEvents.length) return true;
|
|
||||||
|
|
||||||
const currentMap = new Map(currentEvents.map(event => [event.id, event]));
|
|
||||||
|
|
||||||
for (const fetchedEvent of fetchedEvents) {
|
|
||||||
const currentEvent = currentMap.get(fetchedEvent.id);
|
|
||||||
|
|
||||||
// new event added
|
|
||||||
if (!currentEvent) return true;
|
|
||||||
|
|
||||||
// existing event changed
|
|
||||||
if (currentEvent.updated_at !== fetchedEvent.updated_at) {
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
convertDate(rawDate) {
|
|
||||||
const parsed = new Date(rawDate);
|
|
||||||
|
|
||||||
if (isNaN(parsed.getTime())) return rawDate;
|
|
||||||
|
|
||||||
const month = parsed.toLocaleString("en-US", { month: "long", timeZone: "UTC" });
|
|
||||||
const day = parsed.getUTCDate();
|
|
||||||
const year = parsed.getUTCFullYear();
|
|
||||||
|
|
||||||
const hours24 = parsed.getUTCHours();
|
|
||||||
const minutes = parsed.getUTCMinutes();
|
|
||||||
|
|
||||||
const hours12 = hours24 % 12 || 12;
|
|
||||||
const ampm = hours24 >= 12 ? "PM" : "AM";
|
|
||||||
const paddedMinutes = String(minutes).padStart(2, "0");
|
|
||||||
|
|
||||||
const ordinal = (n) => {
|
|
||||||
const mod100 = n % 100;
|
|
||||||
if (mod100 >= 11 && mod100 <= 13) return `${n}th`;
|
|
||||||
switch (n % 10) {
|
|
||||||
case 1: return `${n}st`;
|
|
||||||
case 2: return `${n}nd`;
|
|
||||||
case 3: return `${n}rd`;
|
|
||||||
default: return `${n}th`;
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
return `${month} ${ordinal(day)}, ${year} at ${hours12}:${paddedMinutes} ${ampm}`;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
register(Events)
|
|
||||||
@@ -1,83 +1,83 @@
|
|||||||
import './ForumPanel.js'
|
// import './ForumPanel.js'
|
||||||
|
|
||||||
css(`
|
// css(`
|
||||||
forum- {
|
// forum- {
|
||||||
font-family: 'Bona';
|
// font-family: 'Bona';
|
||||||
}
|
// }
|
||||||
|
|
||||||
forum- input::placeholder {
|
// forum- input::placeholder {
|
||||||
font-family: 'Bona Nova';
|
// font-family: 'Bona Nova';
|
||||||
font-size: 0.9em;
|
// font-size: 0.9em;
|
||||||
color: var(--accent);
|
// color: var(--accent);
|
||||||
}
|
// }
|
||||||
|
|
||||||
input::placeholder {
|
// input::placeholder {
|
||||||
font-family: Arial;
|
// font-family: Arial;
|
||||||
}
|
// }
|
||||||
|
|
||||||
input[type="checkbox"] {
|
// input[type="checkbox"] {
|
||||||
appearance: none; /* remove default style */
|
// appearance: none; /* remove default style */
|
||||||
-webkit-appearance: none;
|
// -webkit-appearance: none;
|
||||||
width: 1em;
|
// width: 1em;
|
||||||
height: 1em;
|
// height: 1em;
|
||||||
border: 1px solid var(--accent);
|
// border: 1px solid var(--accent);
|
||||||
}
|
// }
|
||||||
|
|
||||||
input[type="checkbox"]:checked {
|
// input[type="checkbox"]:checked {
|
||||||
background-color: var(--red);
|
// background-color: var(--red);
|
||||||
}
|
// }
|
||||||
`)
|
// `)
|
||||||
|
|
||||||
class Forum extends Shadow {
|
// class Forum extends Shadow {
|
||||||
render() {
|
// render() {
|
||||||
ZStack(() => {
|
// ZStack(() => {
|
||||||
VStack(() => {
|
// VStack(() => {
|
||||||
|
|
||||||
ForumPanel()
|
// ForumPanel()
|
||||||
|
|
||||||
input("Message", "70%")
|
// input("Message", "70%")
|
||||||
.paddingVertical(0.75, em)
|
// .paddingVertical(0.75, em)
|
||||||
.boxSizing("border-box")
|
// .boxSizing("border-box")
|
||||||
.paddingHorizontal(2, em)
|
// .paddingHorizontal(2, em)
|
||||||
.color("var(--accent)")
|
// .color("var(--accent)")
|
||||||
.background("black")
|
// .background("black")
|
||||||
.marginBottom(1, em)
|
// .marginBottom(1, em)
|
||||||
.border("0.5px solid #6f5e4e")
|
// .border("0.5px solid #6f5e4e")
|
||||||
.borderRadius(100, px)
|
// .borderRadius(100, px)
|
||||||
.fontFamily("Arial")
|
// .fontFamily("Arial")
|
||||||
.fontSize(1, em)
|
// .fontSize(1, em)
|
||||||
.onKeyDown(async function(e) {
|
// .onKeyDown(async function(e) {
|
||||||
if (e.key === "Enter") {
|
// if (e.key === "Enter") {
|
||||||
let msg = {
|
// let msg = {
|
||||||
forum: global.currentNetwork.abbreviation,
|
// forum: global.currentNetwork.abbreviation,
|
||||||
text: this.value
|
// text: this.value
|
||||||
}
|
// }
|
||||||
await global.Socket.send({
|
// await global.Socket.send({
|
||||||
app: "FORUM",
|
// app: "FORUM",
|
||||||
operation: "SEND",
|
// operation: "SEND",
|
||||||
msg: msg
|
// msg: msg
|
||||||
})
|
// })
|
||||||
this.value = ""
|
// this.value = ""
|
||||||
}
|
// }
|
||||||
})
|
// })
|
||||||
})
|
// })
|
||||||
.gap(0.5, em)
|
// .gap(0.5, em)
|
||||||
.boxSizing("border-box")
|
// .boxSizing("border-box")
|
||||||
.width(100, pct)
|
// .width(100, pct)
|
||||||
.height(100, pct)
|
// .height(100, pct)
|
||||||
.horizontalAlign("center")
|
// .horizontalAlign("center")
|
||||||
.verticalAlign("end")
|
// .verticalAlign("end")
|
||||||
.minHeight(0)
|
// .minHeight(0)
|
||||||
})
|
// })
|
||||||
.backgroundColor("var(--main)")
|
// .backgroundColor("var(--main)")
|
||||||
.boxSizing("border-box")
|
// .boxSizing("border-box")
|
||||||
.paddingVertical(1, em)
|
// .paddingVertical(1, em)
|
||||||
.width(100, pct)
|
// .width(100, pct)
|
||||||
.minHeight(0)
|
// .minHeight(0)
|
||||||
.flex("1 1 auto")
|
// .flex("1 1 auto")
|
||||||
}
|
// }
|
||||||
|
|
||||||
|
|
||||||
}
|
// }
|
||||||
|
|
||||||
register(Forum)
|
// register(Forum)
|
||||||
@@ -1,193 +1,193 @@
|
|||||||
import "../../components/LoadingCircle.js"
|
// import "../../components/LoadingCircle.js"
|
||||||
|
|
||||||
css(`
|
// css(`
|
||||||
forumpanel- {
|
// forumpanel- {
|
||||||
scrollbar-width: none;
|
// scrollbar-width: none;
|
||||||
-ms-overflow-style: none;
|
// -ms-overflow-style: none;
|
||||||
}
|
// }
|
||||||
|
|
||||||
forumpanel-::-webkit-scrollbar {
|
// forumpanel-::-webkit-scrollbar {
|
||||||
display: none;
|
// display: none;
|
||||||
width: 0px;
|
// width: 0px;
|
||||||
height: 0px;
|
// height: 0px;
|
||||||
}
|
// }
|
||||||
|
|
||||||
forumpanel-::-webkit-scrollbar-thumb {
|
// forumpanel-::-webkit-scrollbar-thumb {
|
||||||
background: transparent;
|
// background: transparent;
|
||||||
}
|
// }
|
||||||
|
|
||||||
forumpanel-::-webkit-scrollbar-track {
|
// forumpanel-::-webkit-scrollbar-track {
|
||||||
background: transparent;
|
// background: transparent;
|
||||||
}
|
// }
|
||||||
`)
|
// `)
|
||||||
|
|
||||||
class ForumPanel extends Shadow {
|
// class ForumPanel extends Shadow {
|
||||||
messages = []
|
// messages = []
|
||||||
isSending = false
|
// isSending = false
|
||||||
|
|
||||||
render() {
|
// render() {
|
||||||
VStack(() => {
|
// VStack(() => {
|
||||||
if(this.messages.length > 0) {
|
// if(this.messages.length > 0) {
|
||||||
let previousDate = null
|
// let previousDate = null
|
||||||
|
|
||||||
for(let i=0; i<this.messages.length; i++) {
|
// for(let i=0; i<this.messages.length; i++) {
|
||||||
let message = this.messages[i]
|
// let message = this.messages[i]
|
||||||
const isMe = message.authorId === global.profile.id
|
// const isMe = message.authorId === global.profile.id
|
||||||
const dateParts = this.parseDate(message.time);
|
// const dateParts = this.parseDate(message.time);
|
||||||
const { date, time } = dateParts;
|
// const { date, time } = dateParts;
|
||||||
|
|
||||||
if (previousDate !== date) {
|
// if (previousDate !== date) {
|
||||||
previousDate = date;
|
// previousDate = date;
|
||||||
|
|
||||||
p(date)
|
// p(date)
|
||||||
.textAlign("center")
|
// .textAlign("center")
|
||||||
.opacity(0.6)
|
// .opacity(0.6)
|
||||||
.fontWeight("bold")
|
// .fontWeight("bold")
|
||||||
.paddingTop(1, em)
|
// .paddingTop(1, em)
|
||||||
.paddingBottom(0.5, em)
|
// .paddingBottom(0.5, em)
|
||||||
.color("var(--quillred)")
|
// .color("var(--quillred)")
|
||||||
.borderTop(`1px solid var(--${i == 0 ? "transparent" : "divider"})`)
|
// .borderTop(`1px solid var(--${i == 0 ? "transparent" : "divider"})`)
|
||||||
}
|
// }
|
||||||
|
|
||||||
VStack(() => {
|
// VStack(() => {
|
||||||
HStack(() => {
|
// HStack(() => {
|
||||||
h3(isMe ? "Me" : message.sentBy)
|
// h3(isMe ? "Me" : message.sentBy)
|
||||||
.color(isMe ? "var(--quillred)" : "var(--brown")
|
// .color(isMe ? "var(--quillred)" : "var(--brown")
|
||||||
.margin(0)
|
// .margin(0)
|
||||||
|
|
||||||
h3(`${date} ${time}`)
|
// h3(`${date} ${time}`)
|
||||||
.opacity(0.5)
|
// .opacity(0.5)
|
||||||
.color("var(--brown)")
|
// .color("var(--brown)")
|
||||||
.margin(0)
|
// .margin(0)
|
||||||
.marginLeft(0.5, em)
|
// .marginLeft(0.5, em)
|
||||||
.fontSize(1, em)
|
// .fontSize(1, em)
|
||||||
|
|
||||||
if (message.edited) {
|
// if (message.edited) {
|
||||||
p("(edited)")
|
// p("(edited)")
|
||||||
.color("var(--brown)")
|
// .color("var(--brown)")
|
||||||
.letterSpacing(0.8, "px")
|
// .letterSpacing(0.8, "px")
|
||||||
.opacity(0.8)
|
// .opacity(0.8)
|
||||||
.fontWeight("bold")
|
// .fontWeight("bold")
|
||||||
.paddingLeft(0.25, em)
|
// .paddingLeft(0.25, em)
|
||||||
.fontSize(0.9, em)
|
// .fontSize(0.9, em)
|
||||||
}
|
// }
|
||||||
})
|
// })
|
||||||
.verticalAlign("center")
|
// .verticalAlign("center")
|
||||||
.marginBottom(0.1, em)
|
// .marginBottom(0.1, em)
|
||||||
|
|
||||||
p(message.text)
|
// p(message.text)
|
||||||
.color("var(--accent)")
|
// .color("var(--accent)")
|
||||||
.borderLeft("1.5px solid var(--divider)")
|
// .borderLeft("1.5px solid var(--divider)")
|
||||||
.borderBottomLeftRadius("7.5px")
|
// .borderBottomLeftRadius("7.5px")
|
||||||
.paddingLeft(0.5, em)
|
// .paddingLeft(0.5, em)
|
||||||
.marginHorizontal(0.2, em)
|
// .marginHorizontal(0.2, em)
|
||||||
.paddingVertical(0.2, em)
|
// .paddingVertical(0.2, em)
|
||||||
.boxSizing("border-box")
|
// .boxSizing("border-box")
|
||||||
})
|
// })
|
||||||
.marginBottom(0.05, em)
|
// .marginBottom(0.05, em)
|
||||||
.onClick(async (finished, e) => {
|
// .onClick(async (finished, e) => {
|
||||||
if (finished) {
|
// if (finished) {
|
||||||
console.log(message.id)
|
// console.log(message.id)
|
||||||
let msg = {
|
// let msg = {
|
||||||
forum: global.currentNetwork.abbreviation,
|
// forum: global.currentNetwork.abbreviation,
|
||||||
id: message.id,
|
// id: message.id,
|
||||||
text: "EDITED TEXT TEST!"
|
// text: "EDITED TEXT TEST!"
|
||||||
}
|
// }
|
||||||
await global.Socket.send({
|
// await global.Socket.send({
|
||||||
app: "FORUM",
|
// app: "FORUM",
|
||||||
operation: "PUT",
|
// operation: "PUT",
|
||||||
msg: msg
|
// msg: msg
|
||||||
})
|
// })
|
||||||
}
|
// }
|
||||||
})
|
// })
|
||||||
}
|
// }
|
||||||
} else {
|
// } else {
|
||||||
LoadingCircle()
|
// LoadingCircle()
|
||||||
}
|
// }
|
||||||
})
|
// })
|
||||||
.gap(1, em)
|
// .gap(1, em)
|
||||||
.fontSize(1.1, em)
|
// .fontSize(1.1, em)
|
||||||
.boxSizing("border-box")
|
// .boxSizing("border-box")
|
||||||
.flex("1 1 auto")
|
// .flex("1 1 auto")
|
||||||
.minHeight(0)
|
// .minHeight(0)
|
||||||
.overflowY("auto")
|
// .overflowY("auto")
|
||||||
.width(100, pct)
|
// .width(100, pct)
|
||||||
.paddingBottom(2, em)
|
// .paddingBottom(2, em)
|
||||||
.paddingHorizontal(4, pct)
|
// .paddingHorizontal(4, pct)
|
||||||
.backgroundColor("var(--main)")
|
// .backgroundColor("var(--main)")
|
||||||
.onAppear(async () => {
|
// .onAppear(async () => {
|
||||||
requestAnimationFrame(() => {
|
// requestAnimationFrame(() => {
|
||||||
this.scrollTo({ top: 0, behavior: "smooth" });
|
// this.scrollTo({ top: 0, behavior: "smooth" });
|
||||||
});
|
// });
|
||||||
if (!this.isSending) {
|
// if (!this.isSending) {
|
||||||
this.isSending = true
|
// this.isSending = true
|
||||||
let res = await global.Socket.send({
|
// let res = await global.Socket.send({
|
||||||
app: "FORUM",
|
// app: "FORUM",
|
||||||
operation: "GET",
|
// operation: "GET",
|
||||||
msg: {
|
// msg: {
|
||||||
forum: global.currentNetwork.abbreviation,
|
// forum: global.currentNetwork.abbreviation,
|
||||||
by: "network",
|
// by: "network",
|
||||||
authorId: -999 // default
|
// authorId: -999 // default
|
||||||
}
|
// }
|
||||||
})
|
// })
|
||||||
if(!res) console.error("failed to get messages")
|
// if(!res) console.error("failed to get messages")
|
||||||
if(res.msg.length > 0 && this.messages.length === 0) {
|
// if(res.msg.length > 0 && this.messages.length === 0) {
|
||||||
this.messages = res.msg.reverse()
|
// this.messages = res.msg.reverse()
|
||||||
this.rerender()
|
// this.rerender()
|
||||||
}
|
// }
|
||||||
this.isSending = false
|
// this.isSending = false
|
||||||
}
|
// }
|
||||||
})
|
// })
|
||||||
.onEvent("new-post", this.onNewPost)
|
// .onEvent("new-post", this.onNewPost)
|
||||||
.onEvent("deleted-post", this.onDeletedPost)
|
// .onEvent("deleted-post", this.onDeletedPost)
|
||||||
.onEvent("edited-post", this.onEditedPost)
|
// .onEvent("edited-post", this.onEditedPost)
|
||||||
}
|
// }
|
||||||
|
|
||||||
onNewPost = (e) => {
|
// onNewPost = (e) => {
|
||||||
let newPost = e.detail
|
// let newPost = e.detail
|
||||||
if (this.messages && !this.messages.some(post => post.id === newPost.id)) {
|
// if (this.messages && !this.messages.some(post => post.id === newPost.id)) {
|
||||||
this.messages.unshift(newPost)
|
// this.messages.unshift(newPost)
|
||||||
this.rerender()
|
// this.rerender()
|
||||||
}
|
// }
|
||||||
}
|
// }
|
||||||
|
|
||||||
onDeletedPost = (e) => {
|
// onDeletedPost = (e) => {
|
||||||
let deletedId = e.detail
|
// let deletedId = e.detail
|
||||||
const i = this.messages.findIndex(post => post.id === deletedId)
|
// const i = this.messages.findIndex(post => post.id === deletedId)
|
||||||
if (i !== -1) this.messages.splice(i, 1);
|
// if (i !== -1) this.messages.splice(i, 1);
|
||||||
this.rerender()
|
// this.rerender()
|
||||||
}
|
// }
|
||||||
|
|
||||||
onEditedPost = (e) => {
|
// onEditedPost = (e) => {
|
||||||
let editedPost = e.detail
|
// let editedPost = e.detail
|
||||||
const i = this.messages.findIndex(post => post.id === editedPost.id)
|
// const i = this.messages.findIndex(post => post.id === editedPost.id)
|
||||||
if (i !== -1) {
|
// if (i !== -1) {
|
||||||
this.messages.splice(i, 1)
|
// this.messages.splice(i, 1)
|
||||||
this.messages.unshift(editedPost)
|
// this.messages.unshift(editedPost)
|
||||||
}
|
// }
|
||||||
|
|
||||||
this.rerender()
|
// this.rerender()
|
||||||
}
|
// }
|
||||||
|
|
||||||
parseDate(str) {
|
// parseDate(str) {
|
||||||
// Format: MM.DD.YYYY-HH:MM:SSxxxxxx(am|pm)
|
// // Format: MM.DD.YYYY-HH:MM:SSxxxxxx(am|pm)
|
||||||
const match = str.match(/^(\d{1,2})\.(\d{1,2})\.(\d{4})-(\d{1,2}):(\d{2}).*(am|pm)$/i);
|
// const match = str.match(/^(\d{1,2})\.(\d{1,2})\.(\d{4})-(\d{1,2}):(\d{2}).*(am|pm)$/i);
|
||||||
if (!match) return null;
|
// if (!match) return null;
|
||||||
|
|
||||||
const [, mm, dd, yyyy, hh, min, ampm] = match;
|
// const [, mm, dd, yyyy, hh, min, ampm] = match;
|
||||||
const date = `${mm}/${dd}/${yyyy}`;
|
// const date = `${mm}/${dd}/${yyyy}`;
|
||||||
const time = `${hh}:${min}${ampm.toLowerCase()}`;
|
// const time = `${hh}:${min}${ampm.toLowerCase()}`;
|
||||||
|
|
||||||
return { date, time };
|
// return { date, time };
|
||||||
}
|
// }
|
||||||
|
|
||||||
formatTime(str) {
|
// formatTime(str) {
|
||||||
const match = str.match(/-(\d+:\d+):\d+.*(am|pm)/i);
|
// const match = str.match(/-(\d+:\d+):\d+.*(am|pm)/i);
|
||||||
if (!match) return null;
|
// if (!match) return null;
|
||||||
|
|
||||||
const [_, hourMin, ampm] = match;
|
// const [_, hourMin, ampm] = match;
|
||||||
return hourMin + ampm.toLowerCase();
|
// return hourMin + ampm.toLowerCase();
|
||||||
}
|
// }
|
||||||
}
|
// }
|
||||||
|
|
||||||
register(ForumPanel)
|
// register(ForumPanel)
|
||||||
@@ -1,75 +0,0 @@
|
|||||||
import util from "../../util.js"
|
|
||||||
import server from "../../_/code/bridge/server.js"
|
|
||||||
|
|
||||||
css(`
|
|
||||||
jobcard- p {
|
|
||||||
font-size: 0.85em;
|
|
||||||
color: var(--darktext);
|
|
||||||
}
|
|
||||||
`)
|
|
||||||
|
|
||||||
class JobCard extends Shadow {
|
|
||||||
constructor(job) {
|
|
||||||
super()
|
|
||||||
this.job = job
|
|
||||||
}
|
|
||||||
|
|
||||||
render() {
|
|
||||||
VStack(() => {
|
|
||||||
HStack(() => {
|
|
||||||
h3(this.job.title)
|
|
||||||
.color("var(--text)")
|
|
||||||
.fontSize(1.3, em)
|
|
||||||
.fontWeight("normal")
|
|
||||||
.margin(0, em)
|
|
||||||
|
|
||||||
// Delete button
|
|
||||||
// if (this.job.creator_id === global.profile.id) {
|
|
||||||
// img(util.cssVariable("trash-src"), "1.5em")
|
|
||||||
// .marginRight(0.5, em)
|
|
||||||
// .onTap(() => {
|
|
||||||
// this.deleteJob(this.job)
|
|
||||||
// })
|
|
||||||
// }
|
|
||||||
})
|
|
||||||
.justifyContent("space-between")
|
|
||||||
.verticalAlign("center")
|
|
||||||
|
|
||||||
p(this.job.company ?? "No company added")
|
|
||||||
.marginTop(0.75, em)
|
|
||||||
p(this.job.location ?? "No location added")
|
|
||||||
.marginTop(0.25, em)
|
|
||||||
p(this.salary_number ? this.salaryLabel(this.job.salary_number, this.job.salary_period) : "No salary added")
|
|
||||||
.marginTop(0.75, em)
|
|
||||||
})
|
|
||||||
.paddingVertical(1.5, em)
|
|
||||||
.paddingHorizontal(3.5, em)
|
|
||||||
.marginHorizontal(1, em)
|
|
||||||
.borderRadius(10, px)
|
|
||||||
.background("var(--darkaccent)")
|
|
||||||
.border("1px solid var(--accent)")
|
|
||||||
.boxSizing("border-box")
|
|
||||||
}
|
|
||||||
|
|
||||||
salaryLabel(number, period) {
|
|
||||||
const formattedNumber = new Intl.NumberFormat('en-US', {
|
|
||||||
minimumFractionDigits: 2,
|
|
||||||
maximumFractionDigits: 2
|
|
||||||
}).format(Number(number));
|
|
||||||
|
|
||||||
if (period === "one-time") {
|
|
||||||
return `One-time payment of $${formattedNumber}`
|
|
||||||
} else {
|
|
||||||
return `$${formattedNumber}/${period}`
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
async deleteJob(job) {
|
|
||||||
const result = await server.deleteJob(job.id, job.network_id, global.profile.id)
|
|
||||||
if (result.data === null) {
|
|
||||||
console.log("Failed to delete job")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
register(JobCard)
|
|
||||||
@@ -1,204 +0,0 @@
|
|||||||
import server from "../../_/code/bridge/server"
|
|
||||||
|
|
||||||
class JobForm extends Shadow {
|
|
||||||
inputStyles(el) {
|
|
||||||
return el
|
|
||||||
.background("var(--main)")
|
|
||||||
.color("var(--text)")
|
|
||||||
.border("1px solid var(--accent)")
|
|
||||||
.fontSize(0.9, rem)
|
|
||||||
.backgroundColor("var(--darkaccent)")
|
|
||||||
.borderRadius(12, px)
|
|
||||||
.outline("none")
|
|
||||||
.onTouch((start) => {
|
|
||||||
if (start) {
|
|
||||||
this.style.backgroundColor = "var(--accent)"
|
|
||||||
} else {
|
|
||||||
this.style.backgroundColor = "var(--darkaccent)"
|
|
||||||
}
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
errorMessage = ""
|
|
||||||
|
|
||||||
render() {
|
|
||||||
ZStack(() => {
|
|
||||||
p("X")
|
|
||||||
.color("var(--darkred)")
|
|
||||||
.fontSize(2, em)
|
|
||||||
.position("absolute")
|
|
||||||
.fontFamily("Arial")
|
|
||||||
.marginTop(1, rem)
|
|
||||||
.marginLeft(1, rem)
|
|
||||||
.onTap(() => {
|
|
||||||
this.toggle()
|
|
||||||
})
|
|
||||||
|
|
||||||
form(() => {
|
|
||||||
VStack(() => {
|
|
||||||
h1("Create a Job")
|
|
||||||
.color("var(--text)")
|
|
||||||
.textAlign("center")
|
|
||||||
.fontFamily("Arial")
|
|
||||||
.marginTop(1.5, em)
|
|
||||||
|
|
||||||
input("Title", "70%")
|
|
||||||
.attr({ name: "title", type: "text" })
|
|
||||||
.margin(1, em)
|
|
||||||
.padding(1, em)
|
|
||||||
.styles(this.inputStyles)
|
|
||||||
input("Location", "70%")
|
|
||||||
.attr({ name: "location", type: "text" })
|
|
||||||
.margin(1, em)
|
|
||||||
.padding(1, em)
|
|
||||||
.styles(this.inputStyles)
|
|
||||||
input("Company", "70%")
|
|
||||||
.attr({ name: "company", type: "text" })
|
|
||||||
.margin(1, em)
|
|
||||||
.padding(1, em)
|
|
||||||
.styles(this.inputStyles)
|
|
||||||
HStack(() => {
|
|
||||||
input("Salary", "30%")
|
|
||||||
.attr({ name: "salary_number", type: "number", min: "0", step: "0.01" })
|
|
||||||
.padding(1, em)
|
|
||||||
.marginHorizontal(1, em)
|
|
||||||
.styles(this.inputStyles)
|
|
||||||
select(() => {
|
|
||||||
option("One-time")
|
|
||||||
.attr({ value: "one-time"})
|
|
||||||
option("Hourly")
|
|
||||||
.attr({ value: "hour"})
|
|
||||||
option("Monthly")
|
|
||||||
.attr({ value: "month"})
|
|
||||||
option("Yearly")
|
|
||||||
.attr({ value: "year"})
|
|
||||||
})
|
|
||||||
.attr({ name: "salary_period" })
|
|
||||||
.width(40, pct)
|
|
||||||
.padding(1, em)
|
|
||||||
.marginHorizontal(1, em)
|
|
||||||
.styles(this.inputStyles)
|
|
||||||
})
|
|
||||||
.margin(1, em)
|
|
||||||
.boxSizing("border-box")
|
|
||||||
.verticalAlign("center")
|
|
||||||
.horizontalAlign("center")
|
|
||||||
|
|
||||||
input("Description", "70%")
|
|
||||||
.attr({ name: "description", type: "text" })
|
|
||||||
.margin(1, em)
|
|
||||||
.padding(1, em)
|
|
||||||
.styles(this.inputStyles)
|
|
||||||
HStack(() => {
|
|
||||||
button("==>")
|
|
||||||
.padding(1, em)
|
|
||||||
.fontSize(0.9, rem)
|
|
||||||
.borderRadius(12, px)
|
|
||||||
.background("var(--searchbackground)")
|
|
||||||
.color("var(--text)")
|
|
||||||
.border("1px solid var(--accent)")
|
|
||||||
.boxSizing("border-box")
|
|
||||||
.onTouch(function (start) {
|
|
||||||
if (start) {
|
|
||||||
this.style.backgroundColor = "var(--accent)"
|
|
||||||
} else {
|
|
||||||
this.style.backgroundColor = "var(--searchbackground)"
|
|
||||||
}
|
|
||||||
})
|
|
||||||
})
|
|
||||||
.width(70, vw)
|
|
||||||
.margin("auto")
|
|
||||||
.fontSize(0.9, rem)
|
|
||||||
.paddingLeft(0, em)
|
|
||||||
.paddingRight(2, em)
|
|
||||||
.marginVertical(1, em)
|
|
||||||
.border("1px solid transparent")
|
|
||||||
|
|
||||||
p("")
|
|
||||||
.dynamicText("errormessage", "{{}}")
|
|
||||||
.margin("auto")
|
|
||||||
.marginTop(1, em)
|
|
||||||
.color("var(--text)")
|
|
||||||
.fontFamily("Arial")
|
|
||||||
.opacity(.7)
|
|
||||||
.padding(0.5, em)
|
|
||||||
.backgroundColor("var(--darkred)")
|
|
||||||
.width(100, pct)
|
|
||||||
.textAlign("center")
|
|
||||||
.boxSizing("border-box")
|
|
||||||
})
|
|
||||||
.horizontalAlign("center")
|
|
||||||
})
|
|
||||||
.onSubmit((e) => {
|
|
||||||
e.preventDefault()
|
|
||||||
const data = {
|
|
||||||
title: e.target.$('[name="title"]').value,
|
|
||||||
location: e.target.$('[name="location"]').value,
|
|
||||||
company: e.target.$('[name="company"]').value,
|
|
||||||
salary_number: e.target.$('[name="salary_number"]').value,
|
|
||||||
salary_period: e.target.$('[name="salary_period"]').value,
|
|
||||||
description: e.target.$('[name="description"]').value,
|
|
||||||
};
|
|
||||||
this.handleSend(data)
|
|
||||||
})
|
|
||||||
})
|
|
||||||
.position("fixed")
|
|
||||||
.height(window.visualViewport.height - 20, px)
|
|
||||||
.width(100, pct)
|
|
||||||
.top(100, vh)
|
|
||||||
.background("var(--main)")
|
|
||||||
.zIndex(4)
|
|
||||||
.borderTopLeftRadius("10px")
|
|
||||||
.borderTopRightRadius("10px")
|
|
||||||
.boxSizing("border-box")
|
|
||||||
.border("1px solid var(--accent)")
|
|
||||||
.transition("top .3s")
|
|
||||||
}
|
|
||||||
|
|
||||||
async handleSend(jobData) {
|
|
||||||
if (!jobData.title) {
|
|
||||||
this.$(".VStack > p")
|
|
||||||
.attr({ errorMessage: 'Jobs must include a title.' })
|
|
||||||
.display("")
|
|
||||||
|
|
||||||
return;
|
|
||||||
} else {
|
|
||||||
this.$(".VStack > p").style.display = "none"
|
|
||||||
}
|
|
||||||
|
|
||||||
const newJob = {
|
|
||||||
title: jobData.title,
|
|
||||||
location: jobData.location.trim() === '' ? null : jobData.location.trim(),
|
|
||||||
company: jobData.company.trim() === '' ? null : jobData.company.trim(),
|
|
||||||
salary_number: jobData.salary_number.trim() === '' ? null : jobData.salary_number,
|
|
||||||
salary_period: jobData.salary_number.trim() === '' ? null : jobData.salary_period,
|
|
||||||
description: jobData.description.trim() === '' ? null : jobData.description.trim()
|
|
||||||
}
|
|
||||||
|
|
||||||
const { data } = await server.addJob(newJob, global.currentNetwork.id, global.profile.id)
|
|
||||||
if (data.status === 200) {
|
|
||||||
console.log("Added new job: ", data)
|
|
||||||
this.toggle()
|
|
||||||
window.dispatchEvent(new CustomEvent('new-job', {
|
|
||||||
detail: { job: data.job }
|
|
||||||
}));
|
|
||||||
} else {
|
|
||||||
console.log("Failed to add new event: ", data)
|
|
||||||
this.$(".VStack > p")
|
|
||||||
.attr({ errorMessage: data.error })
|
|
||||||
.display("")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
toggle() {
|
|
||||||
if(this.style.top === "15vh") {
|
|
||||||
this.style.top = "100vh"
|
|
||||||
this.pointerEvents = "none"
|
|
||||||
} else {
|
|
||||||
this.style.top = "15vh"
|
|
||||||
this.pointerEvents = "auto"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
register(JobForm)
|
|
||||||
@@ -1,153 +0,0 @@
|
|||||||
import "./JobsSidebar.js"
|
|
||||||
import "./JobsGrid.js"
|
|
||||||
import "./JobCard.js"
|
|
||||||
import "./JobForm.js"
|
|
||||||
import "../../components/SearchBar.js"
|
|
||||||
import "../../components/AddButton.js"
|
|
||||||
import server from "../../_/code/bridge/server.js"
|
|
||||||
|
|
||||||
css(`
|
|
||||||
jobs- {
|
|
||||||
font-family: 'Arial';
|
|
||||||
scrollbar-width: none;
|
|
||||||
-ms-overflow-style: none;
|
|
||||||
}
|
|
||||||
|
|
||||||
jobs- h1 {
|
|
||||||
font-family: 'Bona';
|
|
||||||
}
|
|
||||||
|
|
||||||
jobs- .VStack::-webkit-scrollbar {
|
|
||||||
display: none;
|
|
||||||
width: 0px;
|
|
||||||
height: 0px;
|
|
||||||
}
|
|
||||||
|
|
||||||
jobs- .VStack::-webkit-scrollbar-thumb {
|
|
||||||
background: transparent;
|
|
||||||
}
|
|
||||||
|
|
||||||
jobs- .VStack::-webkit-scrollbar-track {
|
|
||||||
background: transparent;
|
|
||||||
}
|
|
||||||
`)
|
|
||||||
|
|
||||||
class Jobs extends Shadow {
|
|
||||||
static searchableKeys = ['title', 'description', 'location', 'company', 'salary_period', 'salary_number'];
|
|
||||||
|
|
||||||
constructor() {
|
|
||||||
super()
|
|
||||||
this.jobs = global.currentNetwork.data.jobs;
|
|
||||||
this.searchedJobs = [];
|
|
||||||
this.searchText = ""; // for mainting searchText after re-render
|
|
||||||
}
|
|
||||||
|
|
||||||
render() {
|
|
||||||
ZStack(() => {
|
|
||||||
|
|
||||||
JobForm()
|
|
||||||
|
|
||||||
VStack(() => {
|
|
||||||
HStack(() => {
|
|
||||||
SearchBar(this.searchText, "75vw")
|
|
||||||
AddButton()
|
|
||||||
})
|
|
||||||
|
|
||||||
VStack(() => {
|
|
||||||
if (!this.jobs || this.jobs == []) {
|
|
||||||
LoadingCircle()
|
|
||||||
} else if (this.searchText) {
|
|
||||||
if (this.searchedJobs.length > 0) {
|
|
||||||
for (let i = 0; i < this.searchedJobs.length; i++) {
|
|
||||||
JobCard(this.searchedJobs[i])
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
h2("Could not find any jobs with your search criteria.")
|
|
||||||
.color("var(--divider)")
|
|
||||||
.fontWeight("bold")
|
|
||||||
.marginTop(7.5, em)
|
|
||||||
.marginBottom(0.5, em)
|
|
||||||
.textAlign("center")
|
|
||||||
}
|
|
||||||
} else if (this.jobs.length > 0) {
|
|
||||||
for (let i = 0; i < this.jobs.length; i++) {
|
|
||||||
JobCard(this.jobs[i])
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
h2("No Jobs")
|
|
||||||
.color("var(--divider)")
|
|
||||||
.fontWeight("bold")
|
|
||||||
.marginTop(7.5, em)
|
|
||||||
.marginBottom(0.5, em)
|
|
||||||
.textAlign("center")
|
|
||||||
}
|
|
||||||
})
|
|
||||||
.overflowY("scroll")
|
|
||||||
.gap(0.75, em)
|
|
||||||
})
|
|
||||||
.boxSizing("border-box")
|
|
||||||
.height(100, pct)
|
|
||||||
.width(100, pct)
|
|
||||||
.onEvent("jobsearch", this.onJobSearch)
|
|
||||||
.onEvent("new-job", this.onNewJob)
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
onNewJob = (e) => {
|
|
||||||
let newJob = e.detail.job;
|
|
||||||
this.jobs.push(newJob)
|
|
||||||
this.rerender()
|
|
||||||
}
|
|
||||||
|
|
||||||
onJobSearch = (e) => {
|
|
||||||
let searchText = e.detail.searchText.toLowerCase().trim();
|
|
||||||
if (!searchText) {
|
|
||||||
this.searchedJobs = [];
|
|
||||||
} else {
|
|
||||||
this.searchedJobs = this.jobs.filter(job =>
|
|
||||||
Jobs.searchableKeys.some(key => {
|
|
||||||
if (key === "salary_number" || key === "salary_period") {
|
|
||||||
return String(job["salary_number"]).toLowerCase().includes(searchText.replace(/\/.*$/, '').replace(/[,$]/g, '')) && String(job["salary_period"]).toLowerCase().includes(searchText.replace(/^[^/]*\/?/, ''));
|
|
||||||
} else {
|
|
||||||
return String(job[key]).toLowerCase().includes(searchText);
|
|
||||||
}
|
|
||||||
})
|
|
||||||
);
|
|
||||||
}
|
|
||||||
this.searchText = searchText
|
|
||||||
this.rerender()
|
|
||||||
}
|
|
||||||
|
|
||||||
async getJobs(networkId) {
|
|
||||||
const fetchedJobs = await server.getJobs(networkId)
|
|
||||||
if (this.checkForUpdates(this.jobs, fetchedJobs.data)) {
|
|
||||||
this.jobs = fetchedJobs
|
|
||||||
this.rerender()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
connectedCallback() {
|
|
||||||
this.getJobs(global.currentNetwork.id)
|
|
||||||
}
|
|
||||||
|
|
||||||
checkForUpdates(currentJobs, fetchedJobs) {
|
|
||||||
if (currentJobs.length !== fetchedJobs.length) return true;
|
|
||||||
|
|
||||||
const currentMap = new Map(currentJobs.map(job => [job.id, job]));
|
|
||||||
|
|
||||||
for (const fetchedJob of fetchedJobs) {
|
|
||||||
const currentJob = currentMap.get(fetchedJob.id);
|
|
||||||
|
|
||||||
// new job added
|
|
||||||
if (!currentJob) return true;
|
|
||||||
|
|
||||||
// existing job changed
|
|
||||||
if (currentJob.updated_at !== fetchedJob.updated_at) {
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
register(Jobs)
|
|
||||||
@@ -1,60 +0,0 @@
|
|||||||
class JobsGrid extends Shadow {
|
|
||||||
jobs;
|
|
||||||
|
|
||||||
constructor(jobs) {
|
|
||||||
super()
|
|
||||||
this.jobs = jobs
|
|
||||||
}
|
|
||||||
|
|
||||||
boldUntilFirstSpace(text) {
|
|
||||||
const index = text.indexOf(' ');
|
|
||||||
if (index === -1) {
|
|
||||||
// No spaces — bold the whole thing
|
|
||||||
return `<b>${text}</b>`;
|
|
||||||
}
|
|
||||||
return `<b>${text.slice(0, index)}</b>${text.slice(index)}`;
|
|
||||||
}
|
|
||||||
|
|
||||||
render() {
|
|
||||||
VStack(() => {
|
|
||||||
h3("Results")
|
|
||||||
.marginTop(0.1, em)
|
|
||||||
.marginBottom(1, em)
|
|
||||||
.marginLeft(0.4, em)
|
|
||||||
.color("var(--accent2)")
|
|
||||||
|
|
||||||
if (this.jobs.length > 0) {
|
|
||||||
ZStack(() => {
|
|
||||||
for (let i = 0; i < this.jobs.length; i++) {
|
|
||||||
VStack(() => {
|
|
||||||
p(this.jobs[i].title)
|
|
||||||
.fontSize(1.2, em)
|
|
||||||
.fontWeight("bold")
|
|
||||||
.marginBottom(0.5, em)
|
|
||||||
p(this.jobs[i].company)
|
|
||||||
p(this.jobs[i].city + ", " + this.jobs[i].state)
|
|
||||||
.marginBottom(0.5, em)
|
|
||||||
p(this.boldUntilFirstSpace(this.jobs[i].salary))
|
|
||||||
})
|
|
||||||
.padding(1, em)
|
|
||||||
.borderRadius(5, "px")
|
|
||||||
.background("var(--darkbrown)")
|
|
||||||
}
|
|
||||||
})
|
|
||||||
.display("grid")
|
|
||||||
.gridTemplateColumns("repeat(auto-fill, minmax(250px, 1fr))")
|
|
||||||
.gap(1, em)
|
|
||||||
} else {
|
|
||||||
p("No Jobs!")
|
|
||||||
}
|
|
||||||
})
|
|
||||||
.height(100, vh)
|
|
||||||
.paddingLeft(2, em)
|
|
||||||
.paddingRight(2, em)
|
|
||||||
.paddingTop(2, em)
|
|
||||||
.gap(0, em)
|
|
||||||
.width(100, "%")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
register(JobsGrid)
|
|
||||||
@@ -1,26 +0,0 @@
|
|||||||
class JobsSidebar extends Shadow {
|
|
||||||
render() {
|
|
||||||
VStack(() => {
|
|
||||||
h3("Location")
|
|
||||||
.color("var(--accent2)")
|
|
||||||
.marginBottom(0, em)
|
|
||||||
|
|
||||||
HStack(() => {
|
|
||||||
input("Location", "100%")
|
|
||||||
.paddingLeft(3, em)
|
|
||||||
.paddingVertical(0.75, em)
|
|
||||||
.backgroundImage("/_/icons/locationPin.svg")
|
|
||||||
.backgroundRepeat("no-repeat")
|
|
||||||
.backgroundSize("18px 18px")
|
|
||||||
.backgroundPosition("10px center")
|
|
||||||
})
|
|
||||||
})
|
|
||||||
.paddingTop(1, em)
|
|
||||||
.paddingLeft(3, em)
|
|
||||||
.paddingRight(3, em)
|
|
||||||
.gap(1, em)
|
|
||||||
.minWidth(10, vw)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
register(JobsSidebar)
|
|
||||||
@@ -1,81 +0,0 @@
|
|||||||
import "../../components/TopBar.js"
|
|
||||||
import "./PeopleCard.js"
|
|
||||||
|
|
||||||
css(`
|
|
||||||
people- {
|
|
||||||
font-family: 'Arial';
|
|
||||||
}
|
|
||||||
|
|
||||||
people- h1 {
|
|
||||||
font-family: 'Bona';
|
|
||||||
}
|
|
||||||
|
|
||||||
people- p {
|
|
||||||
color: var(--darktext);
|
|
||||||
}
|
|
||||||
`)
|
|
||||||
|
|
||||||
class People extends Shadow {
|
|
||||||
people = "";
|
|
||||||
|
|
||||||
constructor() {
|
|
||||||
super()
|
|
||||||
this.people = global.currentNetwork.data.members;
|
|
||||||
}
|
|
||||||
|
|
||||||
render() {
|
|
||||||
VStack(() => {
|
|
||||||
|
|
||||||
VStack(() => {
|
|
||||||
if (this.people == "") {
|
|
||||||
LoadingCircle()
|
|
||||||
} else if (this.people.length > 0) {
|
|
||||||
for (let i = 0; i < this.people.length; i++) {
|
|
||||||
PeopleCard(this.people[i])
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
h2("No Members")
|
|
||||||
.color("var(--brown)")
|
|
||||||
.fontWeight("bold")
|
|
||||||
.marginTop(7.5, em)
|
|
||||||
.marginBottom(0.5, em)
|
|
||||||
.textAlign("center")
|
|
||||||
p("Invite people to this network!")
|
|
||||||
.textAlign("center")
|
|
||||||
.color("var(--darkbrown)")
|
|
||||||
}
|
|
||||||
})
|
|
||||||
.overflow("scroll")
|
|
||||||
.gap(0.8, em)
|
|
||||||
})
|
|
||||||
.position("relative")
|
|
||||||
.boxSizing("border-box")
|
|
||||||
.height(100, pct)
|
|
||||||
.width(100, pct)
|
|
||||||
}
|
|
||||||
|
|
||||||
convertDate(rawDate) {
|
|
||||||
const parsed = new Date(rawDate);
|
|
||||||
|
|
||||||
if (isNaN(parsed.getTime())) return rawDate;
|
|
||||||
|
|
||||||
const month = parsed.toLocaleString("en-US", { month: "long", timeZone: "UTC" });
|
|
||||||
const day = parsed.getUTCDate();
|
|
||||||
const year = parsed.getUTCFullYear();
|
|
||||||
|
|
||||||
const ordinal = (n) => {
|
|
||||||
const mod100 = n % 100;
|
|
||||||
if (mod100 >= 11 && mod100 <= 13) return `${n}th`;
|
|
||||||
switch (n % 10) {
|
|
||||||
case 1: return `${n}st`;
|
|
||||||
case 2: return `${n}nd`;
|
|
||||||
case 3: return `${n}rd`;
|
|
||||||
default: return `${n}th`;
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
return `${month} ${ordinal(day)}, ${year}`;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
register(People)
|
|
||||||
@@ -1,69 +0,0 @@
|
|||||||
import util from "../../util"
|
|
||||||
|
|
||||||
class PeopleCard extends Shadow {
|
|
||||||
constructor(person) {
|
|
||||||
super()
|
|
||||||
this.person = person
|
|
||||||
this.imgSrc = null
|
|
||||||
}
|
|
||||||
|
|
||||||
render() {
|
|
||||||
HStack(() => {
|
|
||||||
HStack(() => {
|
|
||||||
if (this.imgSrc && !this.imgSrc.includes("null")) {
|
|
||||||
img(this.imgSrc, "3em", "3em")
|
|
||||||
.borderRadius(100, pct)
|
|
||||||
}
|
|
||||||
})
|
|
||||||
.boxSizing("border-box")
|
|
||||||
.height(3, em)
|
|
||||||
.width(3, em)
|
|
||||||
.border("1px solid var(--accent)")
|
|
||||||
.borderRadius(100, pct)
|
|
||||||
.background("var(--darkaccent)")
|
|
||||||
|
|
||||||
VStack(() => {
|
|
||||||
p(this.person.first_name + " " + this.person.last_name)
|
|
||||||
.color("var(--headertext)")
|
|
||||||
.marginVertical(0, em)
|
|
||||||
})
|
|
||||||
.verticalAlign("center")
|
|
||||||
.horizontalAlign("left")
|
|
||||||
})
|
|
||||||
.width(100, pct)
|
|
||||||
.boxSizing("border-box")
|
|
||||||
.paddingHorizontal(1.75, em)
|
|
||||||
.gap(1, em)
|
|
||||||
}
|
|
||||||
|
|
||||||
connectedCallback() {
|
|
||||||
if(this.person.image_path) {
|
|
||||||
this.imgSrc = `${util.HOST}${this.person.image_path}`
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
convertDate(rawDate) {
|
|
||||||
const parsed = new Date(rawDate);
|
|
||||||
|
|
||||||
if (isNaN(parsed.getTime())) return rawDate;
|
|
||||||
|
|
||||||
const month = parsed.toLocaleString("en-US", { month: "long", timeZone: "UTC" });
|
|
||||||
const day = parsed.getUTCDate();
|
|
||||||
const year = parsed.getUTCFullYear();
|
|
||||||
|
|
||||||
const ordinal = (n) => {
|
|
||||||
const mod100 = n % 100;
|
|
||||||
if (mod100 >= 11 && mod100 <= 13) return `${n}th`;
|
|
||||||
switch (n % 10) {
|
|
||||||
case 1: return `${n}st`;
|
|
||||||
case 2: return `${n}nd`;
|
|
||||||
case 3: return `${n}rd`;
|
|
||||||
default: return `${n}th`;
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
return `${month} ${ordinal(day)}, ${year}`;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
register(PeopleCard)
|
|
||||||
@@ -1,33 +0,0 @@
|
|||||||
class AddButton extends Shadow {
|
|
||||||
render() {
|
|
||||||
p("+")
|
|
||||||
.fontWeight("bolder")
|
|
||||||
.paddingVertical(0.75, em)
|
|
||||||
.boxSizing("border-box")
|
|
||||||
.paddingHorizontal(1, em)
|
|
||||||
.background("var(--searchbackground)")
|
|
||||||
.color("var(--accent)")
|
|
||||||
.marginBottom(1, em)
|
|
||||||
.border("1px solid var(--accent)")
|
|
||||||
.borderRadius(15, px)
|
|
||||||
.onTap(() => {
|
|
||||||
this.handleAdd()
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
handleAdd() {
|
|
||||||
const app = global.currentApp()
|
|
||||||
switch (app) {
|
|
||||||
case "Jobs":
|
|
||||||
$("jobform-").toggle()
|
|
||||||
break;
|
|
||||||
case "Events":
|
|
||||||
$("eventform-").toggle()
|
|
||||||
break;
|
|
||||||
default:
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
register(AddButton)
|
|
||||||
@@ -1,40 +1,14 @@
|
|||||||
import "../apps/Forum/Forum.js"
|
|
||||||
import "../apps/Messages/Messages.js"
|
|
||||||
import "../apps/Jobs/Jobs.js"
|
|
||||||
import "../apps/People/People.js"
|
|
||||||
import "../apps/Events/Events.js"
|
|
||||||
import "../apps/Announcements/Announcements.js"
|
|
||||||
import util from "../util.js"
|
import util from "../util.js"
|
||||||
|
|
||||||
class AppWindow extends Shadow {
|
class AppWindow extends Shadow {
|
||||||
render() {
|
render() {
|
||||||
ZStack(() => {
|
ZStack(() => {
|
||||||
let app = global.currentApp()
|
let app = global.currentApp()
|
||||||
switch(app) {
|
if(window[app]) {
|
||||||
case "Dashboard":
|
window[app]()
|
||||||
Announcements()
|
} else {
|
||||||
break;
|
this.getCustomApp(app)
|
||||||
|
}
|
||||||
case "Jobs":
|
|
||||||
Jobs()
|
|
||||||
break;
|
|
||||||
|
|
||||||
case "Events":
|
|
||||||
Events()
|
|
||||||
break;
|
|
||||||
|
|
||||||
case "People":
|
|
||||||
People()
|
|
||||||
break;
|
|
||||||
|
|
||||||
default:
|
|
||||||
if(window[app]) {
|
|
||||||
window[app]()
|
|
||||||
} else {
|
|
||||||
this.getCustomApp(app)
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
})
|
})
|
||||||
.height(100, pct)
|
.height(100, pct)
|
||||||
.overflowY("scroll")
|
.overflowY("scroll")
|
||||||
@@ -44,6 +18,7 @@ class AppWindow extends Shadow {
|
|||||||
}
|
}
|
||||||
|
|
||||||
async getCustomApp(app) {
|
async getCustomApp(app) {
|
||||||
|
if (app == "Dashboard") { app = "Announcements" }
|
||||||
await import(`${util.HOST}/apps/${app.toLowerCase()}/${app.toLowerCase()}.js`);
|
await import(`${util.HOST}/apps/${app.toLowerCase()}/${app.toLowerCase()}.js`);
|
||||||
this.rerender()
|
this.rerender()
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,5 +1,6 @@
|
|||||||
import "./AppWindow.js"
|
import "./AppWindow.js"
|
||||||
import "../Profile/Profile.js"
|
import "../Profile/Profile.js"
|
||||||
|
import "./TopBar.js"
|
||||||
|
|
||||||
class AppWindowContainer extends Shadow {
|
class AppWindowContainer extends Shadow {
|
||||||
render() {
|
render() {
|
||||||
|
|||||||
@@ -1,25 +0,0 @@
|
|||||||
class LoadingCircle extends Shadow {
|
|
||||||
render() {
|
|
||||||
div()
|
|
||||||
.borderRadius(100, pct)
|
|
||||||
.width(2, em).height(2, em)
|
|
||||||
.x(45, pct).y(50, pct)
|
|
||||||
.center()
|
|
||||||
.backgroundColor("var(--accent")
|
|
||||||
.transition("transform 1.75s ease-in-out")
|
|
||||||
.onAppear(function () {
|
|
||||||
let growing = true;
|
|
||||||
|
|
||||||
setInterval(() => {
|
|
||||||
if (growing) {
|
|
||||||
this.style.transform = "scale(1.5)";
|
|
||||||
} else {
|
|
||||||
this.style.transform = "scale(0.7)";
|
|
||||||
}
|
|
||||||
growing = !growing;
|
|
||||||
}, 750);
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
register(LoadingCircle)
|
|
||||||
@@ -1,72 +0,0 @@
|
|||||||
css(`
|
|
||||||
searchbar- input::placeholder {
|
|
||||||
color: #5C504D;
|
|
||||||
}
|
|
||||||
`)
|
|
||||||
|
|
||||||
class SearchBar extends Shadow {
|
|
||||||
searchText
|
|
||||||
width
|
|
||||||
|
|
||||||
constructor(searchText, width) {
|
|
||||||
super()
|
|
||||||
this.searchText = searchText
|
|
||||||
this.width = width
|
|
||||||
}
|
|
||||||
|
|
||||||
render() {
|
|
||||||
form(() => {
|
|
||||||
input("Search", this.width)
|
|
||||||
.attr({ name: "searchText", type: "text" })
|
|
||||||
.attr({ value: this.searchText ? this.searchText : "" })
|
|
||||||
.paddingVertical(0.75, em)
|
|
||||||
.boxSizing("border-box")
|
|
||||||
.paddingHorizontal(1, em)
|
|
||||||
.background("var(--searchbackground)")
|
|
||||||
.color("gray")
|
|
||||||
.marginBottom(1, em)
|
|
||||||
.marginLeft(1, em)
|
|
||||||
.marginRight(0.5, em)
|
|
||||||
.border("1px solid color-mix(in srgb, var(--accent) 60%, transparent)")
|
|
||||||
.borderRadius(100, px)
|
|
||||||
.fontFamily("Arial")
|
|
||||||
.fontSize(1, em)
|
|
||||||
.cursor("not-allowed")
|
|
||||||
.onTouch(function (start) {
|
|
||||||
if (start) {
|
|
||||||
this.style.backgroundColor = "var(--accent)"
|
|
||||||
} else {
|
|
||||||
this.style.backgroundColor = "var(--searchbackground)"
|
|
||||||
}
|
|
||||||
})
|
|
||||||
})
|
|
||||||
.onSubmit(async (e) => {
|
|
||||||
e.preventDefault();
|
|
||||||
const data = new FormData(e.target);
|
|
||||||
this.dispatchSearchEvent(data.get("searchText"))
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
dispatchSearchEvent(searchText) {
|
|
||||||
const app = global.currentApp();
|
|
||||||
switch (app) {
|
|
||||||
case "Jobs":
|
|
||||||
window.dispatchEvent(new CustomEvent('jobsearch', {
|
|
||||||
detail: { searchText: searchText }
|
|
||||||
}));
|
|
||||||
break;
|
|
||||||
case "Events":
|
|
||||||
window.dispatchEvent(new CustomEvent('eventsearch', {
|
|
||||||
detail: { searchText: searchText }
|
|
||||||
}));
|
|
||||||
break;
|
|
||||||
case "Announcements":
|
|
||||||
window.dispatchEvent(new CustomEvent('announcementsearch', {
|
|
||||||
detail: { searchText: searchText }
|
|
||||||
}));
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
register(SearchBar)
|
|
||||||
@@ -43,6 +43,10 @@ export default defineConfig({
|
|||||||
"/db": {
|
"/db": {
|
||||||
target: "http://localhost:10002",
|
target: "http://localhost:10002",
|
||||||
changeOrigin: true
|
changeOrigin: true
|
||||||
|
},
|
||||||
|
"/apps": {
|
||||||
|
target: "http://localhost:10002",
|
||||||
|
changeOrigin: true
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
host: true,
|
host: true,
|
||||||
|
|||||||
Reference in New Issue
Block a user