diff --git a/src/.env.development b/src/.env.development index 9206f33..3452b5c 100644 --- a/src/.env.development +++ b/src/.env.development @@ -1 +1 @@ -VITE_API_URL=https://frm.so \ No newline at end of file +VITE_API_URL= \ No newline at end of file diff --git a/src/apps/Announcements/Announcement.js b/src/apps/Announcements/Announcement.js deleted file mode 100644 index 8b8fbb5..0000000 --- a/src/apps/Announcements/Announcement.js +++ /dev/null @@ -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) \ No newline at end of file diff --git a/src/apps/Announcements/Announcements.js b/src/apps/Announcements/Announcements.js deleted file mode 100644 index b281b2d..0000000 --- a/src/apps/Announcements/Announcements.js +++ /dev/null @@ -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) \ No newline at end of file diff --git a/src/apps/Announcements/Old.js b/src/apps/Announcements/Old.js deleted file mode 100644 index d609aa4..0000000 --- a/src/apps/Announcements/Old.js +++ /dev/null @@ -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) \ No newline at end of file diff --git a/src/apps/Announcements/Panel.js b/src/apps/Announcements/Panel.js deleted file mode 100644 index 5c576a7..0000000 --- a/src/apps/Announcements/Panel.js +++ /dev/null @@ -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 { - 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) \ No newline at end of file diff --git a/src/apps/Events/EventCard.js b/src/apps/Events/EventCard.js deleted file mode 100644 index 924dc21..0000000 --- a/src/apps/Events/EventCard.js +++ /dev/null @@ -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) \ No newline at end of file diff --git a/src/apps/Events/EventForm.js b/src/apps/Events/EventForm.js deleted file mode 100644 index cf3403d..0000000 --- a/src/apps/Events/EventForm.js +++ /dev/null @@ -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) \ No newline at end of file diff --git a/src/apps/Events/Events.js b/src/apps/Events/Events.js deleted file mode 100644 index 74223b9..0000000 --- a/src/apps/Events/Events.js +++ /dev/null @@ -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) \ No newline at end of file diff --git a/src/apps/Forum/Forum.js b/src/apps/Forum/Forum.js index a948114..8bd04c6 100644 --- a/src/apps/Forum/Forum.js +++ b/src/apps/Forum/Forum.js @@ -1,83 +1,83 @@ -import './ForumPanel.js' +// import './ForumPanel.js' -css(` - forum- { - font-family: 'Bona'; - } +// css(` +// forum- { +// font-family: 'Bona'; +// } - forum- input::placeholder { - font-family: 'Bona Nova'; - font-size: 0.9em; - color: var(--accent); - } +// forum- input::placeholder { +// font-family: 'Bona Nova'; +// font-size: 0.9em; +// color: var(--accent); +// } - input::placeholder { - font-family: Arial; - } +// 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"] { +// 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); - } -`) +// input[type="checkbox"]:checked { +// background-color: var(--red); +// } +// `) -class Forum extends Shadow { - render() { - ZStack(() => { - VStack(() => { +// class Forum extends Shadow { +// render() { +// ZStack(() => { +// VStack(() => { - ForumPanel() +// ForumPanel() - input("Message", "70%") - .paddingVertical(0.75, em) - .boxSizing("border-box") - .paddingHorizontal(2, em) - .color("var(--accent)") - .background("black") - .marginBottom(1, em) - .border("0.5px solid #6f5e4e") - .borderRadius(100, px) - .fontFamily("Arial") - .fontSize(1, em) - .onKeyDown(async function(e) { - if (e.key === "Enter") { - let msg = { - forum: global.currentNetwork.abbreviation, - text: this.value - } - await global.Socket.send({ - app: "FORUM", - operation: "SEND", - msg: msg - }) - this.value = "" - } - }) - }) - .gap(0.5, 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) - .minHeight(0) - .flex("1 1 auto") - } +// input("Message", "70%") +// .paddingVertical(0.75, em) +// .boxSizing("border-box") +// .paddingHorizontal(2, em) +// .color("var(--accent)") +// .background("black") +// .marginBottom(1, em) +// .border("0.5px solid #6f5e4e") +// .borderRadius(100, px) +// .fontFamily("Arial") +// .fontSize(1, em) +// .onKeyDown(async function(e) { +// if (e.key === "Enter") { +// let msg = { +// forum: global.currentNetwork.abbreviation, +// text: this.value +// } +// await global.Socket.send({ +// app: "FORUM", +// operation: "SEND", +// msg: msg +// }) +// this.value = "" +// } +// }) +// }) +// .gap(0.5, 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) +// .minHeight(0) +// .flex("1 1 auto") +// } -} +// } -register(Forum) \ No newline at end of file +// register(Forum) \ No newline at end of file diff --git a/src/apps/Forum/ForumPanel.js b/src/apps/Forum/ForumPanel.js index b548e83..ea0fb18 100644 --- a/src/apps/Forum/ForumPanel.js +++ b/src/apps/Forum/ForumPanel.js @@ -1,193 +1,193 @@ -import "../../components/LoadingCircle.js" +// import "../../components/LoadingCircle.js" -css(` - forumpanel- { - scrollbar-width: none; - -ms-overflow-style: none; - } +// css(` +// forumpanel- { +// scrollbar-width: none; +// -ms-overflow-style: none; +// } - forumpanel-::-webkit-scrollbar { - display: none; - width: 0px; - height: 0px; - } +// forumpanel-::-webkit-scrollbar { +// display: none; +// width: 0px; +// height: 0px; +// } - forumpanel-::-webkit-scrollbar-thumb { - background: transparent; - } +// forumpanel-::-webkit-scrollbar-thumb { +// background: transparent; +// } - forumpanel-::-webkit-scrollbar-track { - background: transparent; - } -`) +// forumpanel-::-webkit-scrollbar-track { +// background: transparent; +// } +// `) -class ForumPanel extends Shadow { - messages = [] - isSending = false +// class ForumPanel extends Shadow { +// messages = [] +// isSending = false - render() { - VStack(() => { - if(this.messages.length > 0) { - let previousDate = null +// render() { +// VStack(() => { +// if(this.messages.length > 0) { +// let previousDate = null - for(let i=0; i { - HStack(() => { - h3(isMe ? "Me" : message.sentBy) - .color(isMe ? "var(--quillred)" : "var(--brown") - .margin(0) +// VStack(() => { +// HStack(() => { +// h3(isMe ? "Me" : message.sentBy) +// .color(isMe ? "var(--quillred)" : "var(--brown") +// .margin(0) - h3(`${date} ${time}`) - .opacity(0.5) - .color("var(--brown)") - .margin(0) - .marginLeft(0.5, em) - .fontSize(1, em) +// h3(`${date} ${time}`) +// .opacity(0.5) +// .color("var(--brown)") +// .margin(0) +// .marginLeft(0.5, em) +// .fontSize(1, em) - if (message.edited) { - p("(edited)") - .color("var(--brown)") - .letterSpacing(0.8, "px") - .opacity(0.8) - .fontWeight("bold") - .paddingLeft(0.25, em) - .fontSize(0.9, em) - } - }) - .verticalAlign("center") - .marginBottom(0.1, em) +// if (message.edited) { +// p("(edited)") +// .color("var(--brown)") +// .letterSpacing(0.8, "px") +// .opacity(0.8) +// .fontWeight("bold") +// .paddingLeft(0.25, em) +// .fontSize(0.9, em) +// } +// }) +// .verticalAlign("center") +// .marginBottom(0.1, em) - p(message.text) - .color("var(--accent)") - .borderLeft("1.5px solid var(--divider)") - .borderBottomLeftRadius("7.5px") - .paddingLeft(0.5, em) - .marginHorizontal(0.2, em) - .paddingVertical(0.2, em) - .boxSizing("border-box") - }) - .marginBottom(0.05, em) - .onClick(async (finished, e) => { - if (finished) { - console.log(message.id) - let msg = { - forum: global.currentNetwork.abbreviation, - id: message.id, - text: "EDITED TEXT TEST!" - } - await global.Socket.send({ - app: "FORUM", - operation: "PUT", - msg: msg - }) - } - }) - } - } 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" }); - }); - if (!this.isSending) { - this.isSending = true - let res = await global.Socket.send({ - app: "FORUM", - operation: "GET", - msg: { - forum: global.currentNetwork.abbreviation, - by: "network", - authorId: -999 // default - } - }) - if(!res) console.error("failed to get messages") - if(res.msg.length > 0 && this.messages.length === 0) { - this.messages = res.msg.reverse() - this.rerender() - } - this.isSending = false - } - }) - .onEvent("new-post", this.onNewPost) - .onEvent("deleted-post", this.onDeletedPost) - .onEvent("edited-post", this.onEditedPost) - } +// p(message.text) +// .color("var(--accent)") +// .borderLeft("1.5px solid var(--divider)") +// .borderBottomLeftRadius("7.5px") +// .paddingLeft(0.5, em) +// .marginHorizontal(0.2, em) +// .paddingVertical(0.2, em) +// .boxSizing("border-box") +// }) +// .marginBottom(0.05, em) +// .onClick(async (finished, e) => { +// if (finished) { +// console.log(message.id) +// let msg = { +// forum: global.currentNetwork.abbreviation, +// id: message.id, +// text: "EDITED TEXT TEST!" +// } +// await global.Socket.send({ +// app: "FORUM", +// operation: "PUT", +// msg: msg +// }) +// } +// }) +// } +// } 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" }); +// }); +// if (!this.isSending) { +// this.isSending = true +// let res = await global.Socket.send({ +// app: "FORUM", +// operation: "GET", +// msg: { +// forum: global.currentNetwork.abbreviation, +// by: "network", +// authorId: -999 // default +// } +// }) +// if(!res) console.error("failed to get messages") +// if(res.msg.length > 0 && this.messages.length === 0) { +// this.messages = res.msg.reverse() +// this.rerender() +// } +// this.isSending = false +// } +// }) +// .onEvent("new-post", this.onNewPost) +// .onEvent("deleted-post", this.onDeletedPost) +// .onEvent("edited-post", this.onEditedPost) +// } - onNewPost = (e) => { - let newPost = e.detail - if (this.messages && !this.messages.some(post => post.id === newPost.id)) { - this.messages.unshift(newPost) - this.rerender() - } - } +// onNewPost = (e) => { +// let newPost = e.detail +// if (this.messages && !this.messages.some(post => post.id === newPost.id)) { +// this.messages.unshift(newPost) +// this.rerender() +// } +// } - onDeletedPost = (e) => { - let deletedId = e.detail - const i = this.messages.findIndex(post => post.id === deletedId) - if (i !== -1) this.messages.splice(i, 1); - this.rerender() - } +// onDeletedPost = (e) => { +// let deletedId = e.detail +// const i = this.messages.findIndex(post => post.id === deletedId) +// if (i !== -1) this.messages.splice(i, 1); +// this.rerender() +// } - onEditedPost = (e) => { - let editedPost = e.detail - const i = this.messages.findIndex(post => post.id === editedPost.id) - if (i !== -1) { - this.messages.splice(i, 1) - this.messages.unshift(editedPost) - } +// onEditedPost = (e) => { +// let editedPost = e.detail +// const i = this.messages.findIndex(post => post.id === editedPost.id) +// if (i !== -1) { +// this.messages.splice(i, 1) +// this.messages.unshift(editedPost) +// } - this.rerender() - } +// this.rerender() +// } - parseDate(str) { - // 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); - if (!match) return null; +// parseDate(str) { +// // 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); +// if (!match) return null; - const [, mm, dd, yyyy, hh, min, ampm] = match; - const date = `${mm}/${dd}/${yyyy}`; - const time = `${hh}:${min}${ampm.toLowerCase()}`; +// const [, mm, dd, yyyy, hh, min, ampm] = match; +// const date = `${mm}/${dd}/${yyyy}`; +// const time = `${hh}:${min}${ampm.toLowerCase()}`; - return { date, time }; - } +// return { date, time }; +// } - formatTime(str) { - const match = str.match(/-(\d+:\d+):\d+.*(am|pm)/i); - if (!match) return null; +// formatTime(str) { +// const match = str.match(/-(\d+:\d+):\d+.*(am|pm)/i); +// if (!match) return null; - const [_, hourMin, ampm] = match; - return hourMin + ampm.toLowerCase(); - } -} +// const [_, hourMin, ampm] = match; +// return hourMin + ampm.toLowerCase(); +// } +// } -register(ForumPanel) \ No newline at end of file +// register(ForumPanel) \ No newline at end of file diff --git a/src/apps/Jobs/JobCard.js b/src/apps/Jobs/JobCard.js deleted file mode 100644 index a7594ed..0000000 --- a/src/apps/Jobs/JobCard.js +++ /dev/null @@ -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) \ No newline at end of file diff --git a/src/apps/Jobs/JobForm.js b/src/apps/Jobs/JobForm.js deleted file mode 100644 index faefb0b..0000000 --- a/src/apps/Jobs/JobForm.js +++ /dev/null @@ -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) \ No newline at end of file diff --git a/src/apps/Jobs/Jobs.js b/src/apps/Jobs/Jobs.js deleted file mode 100644 index 5d327e8..0000000 --- a/src/apps/Jobs/Jobs.js +++ /dev/null @@ -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) \ No newline at end of file diff --git a/src/apps/Jobs/JobsGrid.js b/src/apps/Jobs/JobsGrid.js deleted file mode 100644 index 2af5d4f..0000000 --- a/src/apps/Jobs/JobsGrid.js +++ /dev/null @@ -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 `${text}`; - } - return `${text.slice(0, index)}${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) diff --git a/src/apps/Jobs/JobsSidebar.js b/src/apps/Jobs/JobsSidebar.js deleted file mode 100644 index 1546cec..0000000 --- a/src/apps/Jobs/JobsSidebar.js +++ /dev/null @@ -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) \ No newline at end of file diff --git a/src/apps/People/People.js b/src/apps/People/People.js deleted file mode 100644 index 6fc3130..0000000 --- a/src/apps/People/People.js +++ /dev/null @@ -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) \ No newline at end of file diff --git a/src/apps/People/PeopleCard.js b/src/apps/People/PeopleCard.js deleted file mode 100644 index 9797316..0000000 --- a/src/apps/People/PeopleCard.js +++ /dev/null @@ -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) \ No newline at end of file diff --git a/src/components/AddButton.js b/src/components/AddButton.js deleted file mode 100644 index 3e505a4..0000000 --- a/src/components/AddButton.js +++ /dev/null @@ -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) \ No newline at end of file diff --git a/src/components/AppWindow.js b/src/components/AppWindow.js index 51e6519..12bc7b0 100644 --- a/src/components/AppWindow.js +++ b/src/components/AppWindow.js @@ -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" class AppWindow extends Shadow { render() { ZStack(() => { let app = global.currentApp() - switch(app) { - case "Dashboard": - Announcements() - break; - - case "Jobs": - Jobs() - break; - - case "Events": - Events() - break; - - case "People": - People() - break; - - default: - if(window[app]) { - window[app]() - } else { - this.getCustomApp(app) - } - break; - } + if(window[app]) { + window[app]() + } else { + this.getCustomApp(app) + } }) .height(100, pct) .overflowY("scroll") @@ -44,6 +18,7 @@ class AppWindow extends Shadow { } async getCustomApp(app) { + if (app == "Dashboard") { app = "Announcements" } await import(`${util.HOST}/apps/${app.toLowerCase()}/${app.toLowerCase()}.js`); this.rerender() } diff --git a/src/components/AppWindowContainer.js b/src/components/AppWindowContainer.js index c996c3d..2a3d212 100644 --- a/src/components/AppWindowContainer.js +++ b/src/components/AppWindowContainer.js @@ -1,5 +1,6 @@ import "./AppWindow.js" import "../Profile/Profile.js" +import "./TopBar.js" class AppWindowContainer extends Shadow { render() { diff --git a/src/components/LoadingCircle.js b/src/components/LoadingCircle.js deleted file mode 100644 index f71a86c..0000000 --- a/src/components/LoadingCircle.js +++ /dev/null @@ -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) \ No newline at end of file diff --git a/src/components/SearchBar.js b/src/components/SearchBar.js deleted file mode 100644 index 58f33b7..0000000 --- a/src/components/SearchBar.js +++ /dev/null @@ -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) \ No newline at end of file diff --git a/vite.config.ts b/vite.config.ts index 82fd98d..1a5a653 100644 --- a/vite.config.ts +++ b/vite.config.ts @@ -43,6 +43,10 @@ export default defineConfig({ "/db": { target: "http://localhost:10002", changeOrigin: true + }, + "/apps": { + target: "http://localhost:10002", + changeOrigin: true } }, host: true,