From 84b83dfe353d505d31dccfcbead7d230ef193022 Mon Sep 17 00:00:00 2001 From: Kieran Klukas <92754843+kcoderhtml@users.noreply.github.com> Date: Mon, 29 Apr 2024 13:14:31 -0400 Subject: [PATCH 01/20] Add LogSnag integration for tracking user signups --- auth.config.mjs | 53 ++++++++++++++++++++++++++++++++++++++++++++++++ bun.lockb | Bin 285569 -> 286684 bytes package.json | 1 + 3 files changed, 54 insertions(+) diff --git a/auth.config.mjs b/auth.config.mjs index 024d8ca..e9f937d 100644 --- a/auth.config.mjs +++ b/auth.config.mjs @@ -2,6 +2,13 @@ import Slack from "@auth/core/providers/slack"; import { defineConfig } from "auth-astro"; import { db, like, and, User, Organization } from "astro:db"; +import { LogSnag } from "logsnag"; + +const logsnag = new LogSnag({ + token: "f269dd8e8ec57f9a73737e76c5e0024a", + project: "magicsnap", +}); + export default defineConfig({ providers: [ Slack({ @@ -49,6 +56,29 @@ export default defineConfig({ role: "admin", }); + await logsnag.track({ + channel: "signups", + event: "signup", + user_id: profile["https://slack.com/user_id"], + description: "User signed up", + icon: "🚀", + tags: { + team: profile["https://slack.com/team_id"], + role: "admin", + }, + }); + + await logsnag.identify({ + user_id: profile["https://slack.com/user_id"], + properties: { + email: profile.email, + name: profile.name, + image: profile.picture, + team: profile["https://slack.com/team_id"], + role: "admin", + }, + }); + role[0] = { role: "admin" }; } else { await db.insert(User).values({ @@ -60,6 +90,29 @@ export default defineConfig({ role: "user", }); + await logsnag.track({ + channel: "signups", + event: "signup", + user_id: profile["https://slack.com/user_id"], + description: "User signed up", + icon: "🚀", + tags: { + team: profile["https://slack.com/team_id"], + role: "user", + }, + }); + + await logsnag.identify({ + user_id: profile["https://slack.com/user_id"], + properties: { + email: profile.email, + name: profile.name, + image: profile.picture, + team: profile["https://slack.com/team_id"], + role: "user", + }, + }); + role[0] = { role: "user" }; } } else { diff --git a/bun.lockb b/bun.lockb index 048c98c8c1841dd5b72490a9c774e3346b2fa66d..1439a7df9a776254e5ba5098fe326657a5d80522 100755 GIT binary patch delta 49446 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 delta 50088 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 diff --git a/package.json b/package.json index b96307e..c6a2096 100644 --- a/package.json +++ b/package.json @@ -19,6 +19,7 @@ "astro": "^4.5.10", "auth-astro": "^4.1.1", "jwt-decode": "^4.0.0", + "logsnag": "^1.0.0", "magick.css": "^1.0.5", "normalize.css": "^8.0.1", "prettier": "^3.2.5", From 157bb97c119e1e26bbdfc660bc1d24c96065335f Mon Sep 17 00:00:00 2001 From: Kieran Klukas <92754843+kcoderhtml@users.noreply.github.com> Date: Mon, 29 Apr 2024 13:14:35 -0400 Subject: [PATCH 02/20] Add LogSnag integration for tracking successful team creation --- src/pages/join/[joinCode].astro | 15 +++++++++++++++ 1 file changed, 15 insertions(+) diff --git a/src/pages/join/[joinCode].astro b/src/pages/join/[joinCode].astro index 85c628c..130e609 100644 --- a/src/pages/join/[joinCode].astro +++ b/src/pages/join/[joinCode].astro @@ -9,6 +9,13 @@ import { Invite } from "astro:db"; import { like } from "astro:db"; import { Organization } from "astro:db"; +import { LogSnag } from "logsnag"; + +const logsnag = new LogSnag({ + token: "f269dd8e8ec57f9a73737e76c5e0024a", + project: "magicsnap", +}); + type ExtendedSession = { team: string; teamName: string; @@ -75,6 +82,14 @@ if (session && session.user.role === "guest") { name: session.teamName, image: session.teamImage, }); + + await logsnag.track({ + channel: "join-code", + event: "Successful Team Creation", + icon: "🪄", + user_id: session.user.id, + notify: true, + }); } --- From bdec21c1fb08ee33e1c20c8a93620813cc64964e Mon Sep 17 00:00:00 2001 From: Kieran Klukas <92754843+kcoderhtml@users.noreply.github.com> Date: Mon, 29 Apr 2024 13:19:52 -0400 Subject: [PATCH 03/20] Add LogSnag integration for tracking event status changes --- src/pages/dashboard.astro | 14 ++++++++++++++ 1 file changed, 14 insertions(+) diff --git a/src/pages/dashboard.astro b/src/pages/dashboard.astro index 6fd4572..a0583af 100644 --- a/src/pages/dashboard.astro +++ b/src/pages/dashboard.astro @@ -44,6 +44,13 @@ if (!session) { }); } +import { LogSnag } from "logsnag"; + +const logsnag = new LogSnag({ + token: "f269dd8e8ec57f9a73737e76c5e0024a", + project: "magicsnap", +}); + import { db, Event, User } from "astro:db"; import { like } from "astro:db"; @@ -102,6 +109,13 @@ if (Astro.request.method === "POST") { statusNotGoing: statusNotGoing.join(","), }) .where(like(Event.id, eventID)); + + await logsnag.track({ + channel: "actions", + event: "event_status_change", + icon: "📅", + user_id: session.user.id, + }); } } else if (data.get("delete") != null) { if (session.user.role != "admin") { From 1c33bb3e5034270196647895f13e761e34645c02 Mon Sep 17 00:00:00 2001 From: Kieran Klukas <92754843+kcoderhtml@users.noreply.github.com> Date: Mon, 29 Apr 2024 13:19:56 -0400 Subject: [PATCH 04/20] Add LogSnag integration for tracking message sent events --- src/pages/messages.astro | 14 ++++++++++++++ 1 file changed, 14 insertions(+) diff --git a/src/pages/messages.astro b/src/pages/messages.astro index 2bc3f47..f961a6a 100644 --- a/src/pages/messages.astro +++ b/src/pages/messages.astro @@ -43,6 +43,13 @@ if (!session) { }); } +import { LogSnag } from "logsnag"; + +const logsnag = new LogSnag({ + token: "f269dd8e8ec57f9a73737e76c5e0024a", + project: "magicsnap", +}); + import { db, like, and, User, Organization } from "astro:db"; let users = await db.select().from(User).where(like(User.team, session.team)); @@ -146,6 +153,13 @@ if (Astro.request.method === "POST") { ok: true, message: `Message sent successfully to ${userList.length} users.`, }; + + await logsnag.track({ + channel: "actions", + event: "message_sent", + icon: "📧", + user_id: session.user.id, + }); } } } else { From bb452b461bf323dd1bfab7077091a61b42ded951 Mon Sep 17 00:00:00 2001 From: Kieran Klukas <92754843+kcoderhtml@users.noreply.github.com> Date: Mon, 29 Apr 2024 13:29:10 -0400 Subject: [PATCH 05/20] Update LogSnag integration to track user signups with different roles --- auth.config.mjs | 51 +++++++++++++++++++++++++++++++++++++++++++++++-- 1 file changed, 49 insertions(+), 2 deletions(-) diff --git a/auth.config.mjs b/auth.config.mjs index e9f937d..df2f44b 100644 --- a/auth.config.mjs +++ b/auth.config.mjs @@ -60,7 +60,7 @@ export default defineConfig({ channel: "signups", event: "signup", user_id: profile["https://slack.com/user_id"], - description: "User signed up", + description: "User signed up as an admin", icon: "🚀", tags: { team: profile["https://slack.com/team_id"], @@ -68,6 +68,18 @@ export default defineConfig({ }, }); + await logsnag.track({ + channel: "actions", + event: "joined_team", + user_id: profile["https://slack.com/user_id"], + description: "User joined a team", + icon: "🤝", + tags: { + team: profile["https://slack.com/team_id"], + role: "admin", + }, + }); + await logsnag.identify({ user_id: profile["https://slack.com/user_id"], properties: { @@ -94,7 +106,7 @@ export default defineConfig({ channel: "signups", event: "signup", user_id: profile["https://slack.com/user_id"], - description: "User signed up", + description: "User signed up as a user", icon: "🚀", tags: { team: profile["https://slack.com/team_id"], @@ -102,6 +114,18 @@ export default defineConfig({ }, }); + await logsnag.track({ + channel: "actions", + event: "joined_team", + user_id: profile["https://slack.com/user_id"], + description: "User joined a team", + icon: "🤝", + tags: { + team: profile["https://slack.com/team_id"], + role: "user", + }, + }); + await logsnag.identify({ user_id: profile["https://slack.com/user_id"], properties: { @@ -117,6 +141,29 @@ export default defineConfig({ } } else { role[0] = { role: "guest" }; + + await logsnag.track({ + channel: "signups", + event: "signup", + user_id: profile["https://slack.com/user_id"], + description: "User signed up as a guest", + icon: "🚀", + tags: { + team: profile["https://slack.com/team_id"], + role: "guest", + }, + }); + + await logsnag.identify({ + user_id: profile["https://slack.com/user_id"], + properties: { + email: profile.email, + name: profile.name, + image: profile.picture, + team: profile["https://slack.com/team_id"], + role: "guest", + }, + }); } } From ff8a65eb7b0b9ae5a1160b30559ea8482e5e2d37 Mon Sep 17 00:00:00 2001 From: Kieran Klukas <92754843+kcoderhtml@users.noreply.github.com> Date: Mon, 29 Apr 2024 13:40:40 -0400 Subject: [PATCH 06/20] Refactor LogSnag integration to use consistent event names --- src/pages/join/[joinCode].astro | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/pages/join/[joinCode].astro b/src/pages/join/[joinCode].astro index 130e609..39ca5b4 100644 --- a/src/pages/join/[joinCode].astro +++ b/src/pages/join/[joinCode].astro @@ -85,7 +85,7 @@ if (session && session.user.role === "guest") { await logsnag.track({ channel: "join-code", - event: "Successful Team Creation", + event: "create-team", icon: "🪄", user_id: session.user.id, notify: true, From 07ebe731abe69968ffa050360d489c2fb6674623 Mon Sep 17 00:00:00 2001 From: Kieran Klukas <92754843+kcoderhtml@users.noreply.github.com> Date: Mon, 29 Apr 2024 13:44:03 -0400 Subject: [PATCH 07/20] Add LogSnag integration for tracking reminder events --- src/pages/api/remind.ts | 13 +++++++++++++ 1 file changed, 13 insertions(+) diff --git a/src/pages/api/remind.ts b/src/pages/api/remind.ts index d90005d..804eff0 100644 --- a/src/pages/api/remind.ts +++ b/src/pages/api/remind.ts @@ -1,5 +1,11 @@ import type { APIRoute } from "astro" import { db, User, Organization, Event } from "astro:db"; +import { LogSnag } from "logsnag"; + +const logsnag = new LogSnag({ + token: "f269dd8e8ec57f9a73737e76c5e0024a", + project: "magicsnap", +}); export const POST: APIRoute = async ({ request }) => { // get authorization header @@ -20,6 +26,13 @@ export const POST: APIRoute = async ({ request }) => { return diffHours < 24 && diffHours > 0 }) + await logsnag.track({ + channel: "api", + event: "reminder-sent", + description: `Sent reminder to ${users.length} users in ${organizations.length} different organizations about ${eventsHappeningToday.length} events happening today`, + icon: "📬", + }); + return new Response(JSON.stringify({ ok: true, eventsHappeningToday: eventsHappeningToday, users: users, organizations: organizations }), { status: 200 }) From 2a95c89f29546e7b0d332f730e703c0b875e714f Mon Sep 17 00:00:00 2001 From: Kieran Klukas <92754843+kcoderhtml@users.noreply.github.com> Date: Mon, 29 Apr 2024 13:44:51 -0400 Subject: [PATCH 08/20] Add LogSnag integration for tracking event create and delete actions --- src/pages/dashboard.astro | 14 ++++++++++++++ 1 file changed, 14 insertions(+) diff --git a/src/pages/dashboard.astro b/src/pages/dashboard.astro index a0583af..ec4fdd9 100644 --- a/src/pages/dashboard.astro +++ b/src/pages/dashboard.astro @@ -124,6 +124,13 @@ if (Astro.request.method === "POST") { const eventID = data.get("eventID") as string; await db.delete(Event).where(like(Event.id, eventID)); + + await logsnag.track({ + channel: "actions", + event: "event_delete", + icon: "📅", + user_id: session.user.id, + }); } else if (data.get("newEvent") != null) { if (!data.has("name") || !data.has("date") || !data.has("time") || !data.has("location") || !data.has("comments")) { @@ -149,6 +156,13 @@ if (Astro.request.method === "POST") { .join(","), }; await db.insert(Event).values(event); + + await logsnag.track({ + channel: "actions", + event: "event_create", + icon: "📅", + user_id: session.user.id, + }); } } catch (error) { if (error instanceof Error) { From 2fee6404efc45f85dcb92cdb251b2b386cd506f6 Mon Sep 17 00:00:00 2001 From: Kieran Klukas <92754843+kcoderhtml@users.noreply.github.com> Date: Mon, 29 Apr 2024 13:57:43 -0400 Subject: [PATCH 09/20] Add LogSnag integration for tracking role changes in users.astro --- src/pages/users.astro | 15 +++++++++++++++ 1 file changed, 15 insertions(+) diff --git a/src/pages/users.astro b/src/pages/users.astro index 77c69c0..010b57d 100644 --- a/src/pages/users.astro +++ b/src/pages/users.astro @@ -44,6 +44,13 @@ if (!session) { }); } +import { LogSnag } from "logsnag"; + +const logsnag = new LogSnag({ + token: "f269dd8e8ec57f9a73737e76c5e0024a", + project: "magicsnap", +}); + import { db, User } from "astro:db"; import { like } from "astro:db"; @@ -55,6 +62,14 @@ if (Astro.request.method === "POST") { const role = data.get("selected") as string; if ((role === "admin" || role === "user" || role === "guest") && userId !== session.user.id) { await db.update(User).set({ role }).where(like(User.userId, userId)); + + await logsnag.track({ + channel: "actions", + event: "role_change", + description: `User ${session.user.name} changed the role of user ${userId} to ${role}`, + icon: "🛡️", + user_id: session.user.id, + }); } else { error = "You cannot change your own role."; } From 68044cbf7e3b7c811384d50b0349ecab0442019b Mon Sep 17 00:00:00 2001 From: Kieran Klukas <92754843+kcoderhtml@users.noreply.github.com> Date: Mon, 29 Apr 2024 14:10:34 -0400 Subject: [PATCH 10/20] Update LogSnag integration to track team name updates and deletions --- src/pages/settings.astro | 22 ++++++++++++++++++++++ 1 file changed, 22 insertions(+) diff --git a/src/pages/settings.astro b/src/pages/settings.astro index 8891d09..f946a2d 100644 --- a/src/pages/settings.astro +++ b/src/pages/settings.astro @@ -42,6 +42,13 @@ if (!session) { }); } +import { LogSnag } from "logsnag"; + +const logsnag = new LogSnag({ + token: "f269dd8e8ec57f9a73737e76c5e0024a", + project: "magicsnap", +}); + let error = ""; import { db, like, Organization, Event, User } from "astro:db"; @@ -54,6 +61,13 @@ if (Astro.request.method === "POST") { .update(Organization) .set({ name }) .where(like(Organization.team, session.team)); + + await logsnag.track({ + channel: "actions", + event: "team_name_updated", + icon: "🏷️", + user_id: session.user.id, + }); } else { error = "You do not have the necessary permissions to update the team name."; @@ -64,6 +78,14 @@ if (Astro.request.method === "POST") { await db.delete(Event).where(like(Event.team, session.team)); await db.delete(User).where(like(User.team, session.team)); + await logsnag.track({ + channel: "actions", + event: "team_deleted", + icon: "🗑️", + user_id: session.user.id, + notify: true, + }); + return new Response(null, { status: 302, headers: new Headers({ From e681c3cecd511f2b21ddc7488c0d864ab33eabe1 Mon Sep 17 00:00:00 2001 From: Kieran Klukas <92754843+kcoderhtml@users.noreply.github.com> Date: Mon, 29 Apr 2024 14:14:13 -0400 Subject: [PATCH 11/20] Update LogSnag integration to use environment variable for token --- auth.config.mjs | 2 +- src/pages/api/remind.ts | 2 +- src/pages/dashboard.astro | 2 +- src/pages/join/[joinCode].astro | 2 +- src/pages/messages.astro | 2 +- src/pages/settings.astro | 2 +- src/pages/users.astro | 2 +- 7 files changed, 7 insertions(+), 7 deletions(-) diff --git a/auth.config.mjs b/auth.config.mjs index df2f44b..73ae31c 100644 --- a/auth.config.mjs +++ b/auth.config.mjs @@ -5,7 +5,7 @@ import { db, like, and, User, Organization } from "astro:db"; import { LogSnag } from "logsnag"; const logsnag = new LogSnag({ - token: "f269dd8e8ec57f9a73737e76c5e0024a", + token: process.env.LOGSNAG_TOKEN || "", project: "magicsnap", }); diff --git a/src/pages/api/remind.ts b/src/pages/api/remind.ts index 804eff0..4feeb07 100644 --- a/src/pages/api/remind.ts +++ b/src/pages/api/remind.ts @@ -3,7 +3,7 @@ import { db, User, Organization, Event } from "astro:db"; import { LogSnag } from "logsnag"; const logsnag = new LogSnag({ - token: "f269dd8e8ec57f9a73737e76c5e0024a", + token: process.env.LOGSNAG_TOKEN || "", project: "magicsnap", }); diff --git a/src/pages/dashboard.astro b/src/pages/dashboard.astro index ec4fdd9..153719b 100644 --- a/src/pages/dashboard.astro +++ b/src/pages/dashboard.astro @@ -47,7 +47,7 @@ if (!session) { import { LogSnag } from "logsnag"; const logsnag = new LogSnag({ - token: "f269dd8e8ec57f9a73737e76c5e0024a", + token: process.env.LOGSNAG_TOKEN || "", project: "magicsnap", }); diff --git a/src/pages/join/[joinCode].astro b/src/pages/join/[joinCode].astro index 39ca5b4..517736c 100644 --- a/src/pages/join/[joinCode].astro +++ b/src/pages/join/[joinCode].astro @@ -12,7 +12,7 @@ import { Organization } from "astro:db"; import { LogSnag } from "logsnag"; const logsnag = new LogSnag({ - token: "f269dd8e8ec57f9a73737e76c5e0024a", + token: process.env.LOGSNAG_TOKEN || "", project: "magicsnap", }); diff --git a/src/pages/messages.astro b/src/pages/messages.astro index f961a6a..1a5b6b2 100644 --- a/src/pages/messages.astro +++ b/src/pages/messages.astro @@ -46,7 +46,7 @@ if (!session) { import { LogSnag } from "logsnag"; const logsnag = new LogSnag({ - token: "f269dd8e8ec57f9a73737e76c5e0024a", + token: process.env.LOGSNAG_TOKEN || "", project: "magicsnap", }); diff --git a/src/pages/settings.astro b/src/pages/settings.astro index f946a2d..e0696a6 100644 --- a/src/pages/settings.astro +++ b/src/pages/settings.astro @@ -45,7 +45,7 @@ if (!session) { import { LogSnag } from "logsnag"; const logsnag = new LogSnag({ - token: "f269dd8e8ec57f9a73737e76c5e0024a", + token: process.env.LOGSNAG_TOKEN || "", project: "magicsnap", }); diff --git a/src/pages/users.astro b/src/pages/users.astro index 010b57d..41ebe18 100644 --- a/src/pages/users.astro +++ b/src/pages/users.astro @@ -47,7 +47,7 @@ if (!session) { import { LogSnag } from "logsnag"; const logsnag = new LogSnag({ - token: "f269dd8e8ec57f9a73737e76c5e0024a", + token: process.env.LOGSNAG_TOKEN || "", project: "magicsnap", }); From de034d40a0a1dec8ec150aad67fb07a66d23ca0c Mon Sep 17 00:00:00 2001 From: Kieran Klukas <92754843+kcoderhtml@users.noreply.github.com> Date: Mon, 29 Apr 2024 14:23:17 -0400 Subject: [PATCH 12/20] feat: add allergies as a entry in the user db --- auth.config.mjs | 11 +++++++---- db/config.ts | 1 + 2 files changed, 8 insertions(+), 4 deletions(-) diff --git a/auth.config.mjs b/auth.config.mjs index 73ae31c..2a3d646 100644 --- a/auth.config.mjs +++ b/auth.config.mjs @@ -91,7 +91,7 @@ export default defineConfig({ }, }); - role[0] = { role: "admin" }; + role[0].role = "admin"; } else { await db.insert(User).values({ userId: profile["https://slack.com/user_id"], @@ -137,10 +137,10 @@ export default defineConfig({ }, }); - role[0] = { role: "user" }; + role[0].role = "user"; } } else { - role[0] = { role: "guest" }; + role[0].role = "guest"; await logsnag.track({ channel: "signups", @@ -175,7 +175,8 @@ export default defineConfig({ team: profile["https://slack.com/team_id"], teamName: profile["https://slack.com/team_name"], teamImage: profile["https://slack.com/team_image_230"], - role: role[0].role || "user", + role: role[0].role || "guest", + allergies: role[0].allergies || "", }; }, }), @@ -189,6 +190,7 @@ export default defineConfig({ token.teamImage = user.teamImage; token.role = user.role; token.id = user.id; + token.allergies = user.allergies; } return token; }, @@ -200,6 +202,7 @@ export default defineConfig({ session.teamImage = token.teamImage; session.user.role = token.role; session.user.id = token.id; + session.user.allergies = token.allergies; } return session; }, diff --git a/db/config.ts b/db/config.ts index 7a382e3..6afc52a 100644 --- a/db/config.ts +++ b/db/config.ts @@ -20,6 +20,7 @@ const User = defineTable({ email: column.text(), image: column.text(), role: column.text(), + allergies: column.text({ optional: true }), }, indexes: { userIdx: { on: ["userId"], unique: true }, From c666ef6e281d26dea67c60ab27ef5627b9e661c6 Mon Sep 17 00:00:00 2001 From: Kieran Klukas <92754843+kcoderhtml@users.noreply.github.com> Date: Mon, 29 Apr 2024 14:34:06 -0400 Subject: [PATCH 13/20] chore: remove allergies from session --- auth.config.mjs | 3 --- 1 file changed, 3 deletions(-) diff --git a/auth.config.mjs b/auth.config.mjs index 2a3d646..cd3c6ef 100644 --- a/auth.config.mjs +++ b/auth.config.mjs @@ -176,7 +176,6 @@ export default defineConfig({ teamName: profile["https://slack.com/team_name"], teamImage: profile["https://slack.com/team_image_230"], role: role[0].role || "guest", - allergies: role[0].allergies || "", }; }, }), @@ -190,7 +189,6 @@ export default defineConfig({ token.teamImage = user.teamImage; token.role = user.role; token.id = user.id; - token.allergies = user.allergies; } return token; }, @@ -202,7 +200,6 @@ export default defineConfig({ session.teamImage = token.teamImage; session.user.role = token.role; session.user.id = token.id; - session.user.allergies = token.allergies; } return session; }, From cf4bd5beaf99009abeda1de4af80e35ae3139d1e Mon Sep 17 00:00:00 2001 From: Kieran Klukas <92754843+kcoderhtml@users.noreply.github.com> Date: Mon, 29 Apr 2024 14:34:18 -0400 Subject: [PATCH 14/20] Refactor settings page to update user information and track changes with LogSnag --- src/pages/settings.astro | 143 +++++++++++++++++++++++++++++++-------- 1 file changed, 115 insertions(+), 28 deletions(-) diff --git a/src/pages/settings.astro b/src/pages/settings.astro index e0696a6..8a2b134 100644 --- a/src/pages/settings.astro +++ b/src/pages/settings.astro @@ -55,45 +55,76 @@ import { db, like, Organization, Event, User } from "astro:db"; if (Astro.request.method === "POST") { const data = await Astro.request.formData(); - if (data.has("updateName") && session.user.role === "admin") { - const name = data.get("name") as string; + console.log(data); + if (data.has("updateName")) { + if (session.user.role === "admin") { + const name = data.get("name") as string; + await db + .update(Organization) + .set({ name }) + .where(like(Organization.team, session.team)); + + await logsnag.track({ + channel: "actions", + event: "team_name_updated", + icon: "🏷️", + user_id: session.user.id, + }); + } else { + error = + "You do not have the necessary permissions to update the team name."; + } + } else if (data.has("delete")) { + if (session.user.role === "admin") { + await db + .delete(Organization) + .where(like(Organization.team, session.team)); + await db.delete(Event).where(like(Event.team, session.team)); + await db.delete(User).where(like(User.team, session.team)); + + await logsnag.track({ + channel: "actions", + event: "team_deleted", + icon: "🗑️", + user_id: session.user.id, + notify: true, + }); + + return new Response(null, { + status: 302, + headers: new Headers({ + Location: "/signout", + }), + }); + } else { + error = "You do not have the necessary permissions to delete the team."; + } + } else if (data.has("updateEmail")) { + const email = data.get("email") as string; await db - .update(Organization) - .set({ name }) - .where(like(Organization.team, session.team)); + .update(User) + .set({ email }) + .where(like(User.userId, session.user.id)); await logsnag.track({ channel: "actions", - event: "team_name_updated", - icon: "🏷️", + event: "email_updated", + icon: "📧", user_id: session.user.id, }); - } else { - error = - "You do not have the necessary permissions to update the team name."; - } - - if (data.has("delete") && session.user.role === "admin") { - await db.delete(Organization).where(like(Organization.team, session.team)); - await db.delete(Event).where(like(Event.team, session.team)); - await db.delete(User).where(like(User.team, session.team)); + } else if (data.has("updateAllergies")) { + const allergies = data.get("allergies") as string; + await db + .update(User) + .set({ allergies }) + .where(like(User.userId, session.user.id)); await logsnag.track({ channel: "actions", - event: "team_deleted", - icon: "🗑️", + event: "allergies_updated", + icon: "🥜", user_id: session.user.id, - notify: true, }); - - return new Response(null, { - status: 302, - headers: new Headers({ - Location: "/signout", - }), - }); - } else { - error = "You do not have the necessary permissions to delete the team."; } } @@ -103,6 +134,10 @@ const team = ( .from(Organization) .where(like(Organization.team, session.team)) )[0]; + +const user = ( + await db.select().from(User).where(like(User.userId, session.user.id)) +)[0]; --- @@ -120,6 +155,58 @@ const team = ( }
+

Personal Settings

+
+ + + + + +
+ +
+ + + + + +
+ +
+ + + + + +
+ +

Team Settings