diff --git a/components/app/home/membership-card.vue b/components/app/home/membership-card.vue index 0d12774..aa40d4d 100644 --- a/components/app/home/membership-card.vue +++ b/components/app/home/membership-card.vue @@ -20,27 +20,27 @@ const membershipGradient: Record, string> = { - +
{{ user.name }} - + {{ user.memberId }}
-
+
Class of {{ user.graduationYear }} - + {{ user.memberType?.[0].toLocaleUpperCase() }}{{ user.memberType?.slice(1, user.memberType?.length) }} member
- + Coming back? diff --git a/components/app/home/news.vue b/components/app/home/news.vue new file mode 100644 index 0000000..0c8b5d4 --- /dev/null +++ b/components/app/home/news.vue @@ -0,0 +1,37 @@ + + + diff --git a/components/app/home/page.vue b/components/app/home/page.vue index 860c788..b5d8888 100644 --- a/components/app/home/page.vue +++ b/components/app/home/page.vue @@ -9,8 +9,6 @@ defineProps<{ const auth = useCurrentUser() const authLoaded = useIsCurrentUserLoaded() -// TODO @qin-guan: Error state handling -const { data: user, isLoading: userIsLoading } = useUser() const state = reactive({ showLoginScreen: false, @@ -40,12 +38,8 @@ watch([authLoaded, auth], (values) => {
- - What's Happening - - -
- +
+
diff --git a/composables/news.ts b/composables/news.ts new file mode 100644 index 0000000..82777ae --- /dev/null +++ b/composables/news.ts @@ -0,0 +1,15 @@ +import { useQuery } from '@tanstack/vue-query' +import type { NewsArticle } from '~/shared/types' + +const queryKeyFactory = { + newsArticles: ['newsArticles'], +} + +export function useNewsArticles() { + const firebaseCurrentUser = useCurrentUser() + return useQuery({ + queryKey: queryKeyFactory.newsArticles, + queryFn: () => $api('/api/news'), + enabled: computed(() => !!firebaseCurrentUser.value), // Only run when user exists + }) +} diff --git a/plugins/vue-query.ts b/plugins/vue-query.ts index 42feede..9af711a 100644 --- a/plugins/vue-query.ts +++ b/plugins/vue-query.ts @@ -7,8 +7,8 @@ export default defineNuxtPlugin((nuxtApp) => { queryClientConfig: { defaultOptions: { queries: { - cacheTime: 1000 * 60 * 60 * 24, - staleTime: 1000 * 60 * 60 * 24, + cacheTime: 1000 * 60, + staleTime: 1000 * 60, }, }, }, diff --git a/server/api/news/index.get.ts b/server/api/news/index.get.ts new file mode 100644 index 0000000..b96387e --- /dev/null +++ b/server/api/news/index.get.ts @@ -0,0 +1,7 @@ +export default defineProtectedEventHandler((event) => { + return event.context.database.query.news.findMany() +}, { + cache: { + maxAge: 60, + }, +}) diff --git a/server/db/schema.ts b/server/db/schema.ts index ebe7c04..3e6a7d1 100644 --- a/server/db/schema.ts +++ b/server/db/schema.ts @@ -83,6 +83,16 @@ export const bookings = sqliteTable('bookings', { }), }) +export const news = sqliteTable('news', { + id: text('id').primaryKey().$defaultFn(() => createId()), + title: text('title').notNull(), + description: text('description').notNull(), + heroImageAlt: text('hero_image_alt').notNull(), + heroImageUrl: text('hero_image_url').notNull(), + ctaTitle: text('cta_title').notNull(), + ctaUrl: text('cta_url').notNull(), +}) + // Relations export const usersRelations = relations(users, ({ many }) => ({ diff --git a/server/drizzle/0005_married_paladin.sql b/server/drizzle/0005_married_paladin.sql new file mode 100644 index 0000000..7044886 --- /dev/null +++ b/server/drizzle/0005_married_paladin.sql @@ -0,0 +1,9 @@ +CREATE TABLE `news` ( + `id` text PRIMARY KEY NOT NULL, + `title` text NOT NULL, + `description` text NOT NULL, + `hero_image_alt` text NOT NULL, + `hero_image_url` text NOT NULL, + `cta_title` text NOT NULL, + `cta_url` text NOT NULL +); diff --git a/server/drizzle/meta/0004_snapshot.json b/server/drizzle/meta/0004_snapshot.json index 66755e0..4c34298 100644 --- a/server/drizzle/meta/0004_snapshot.json +++ b/server/drizzle/meta/0004_snapshot.json @@ -272,7 +272,7 @@ "name": "firebase_id", "type": "text", "primaryKey": false, - "notNull": true, + "notNull": false, "autoincrement": false }, "name": { @@ -402,4 +402,4 @@ "tables": {}, "columns": {} } -} \ No newline at end of file +} diff --git a/server/drizzle/meta/0005_snapshot.json b/server/drizzle/meta/0005_snapshot.json new file mode 100644 index 0000000..1c7796c --- /dev/null +++ b/server/drizzle/meta/0005_snapshot.json @@ -0,0 +1,463 @@ +{ + "version": "5", + "dialect": "sqlite", + "id": "fea5e090-394d-41fd-9cc6-201a3a3a13a4", + "prevId": "012392f9-c83b-4919-951e-09c52279dc94", + "tables": { + "bookings": { + "name": "bookings", + "columns": { + "id": { + "name": "id", + "type": "text", + "primaryKey": true, + "notNull": true, + "autoincrement": false + }, + "user_id": { + "name": "user_id", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "slot_id": { + "name": "slot_id", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "status": { + "name": "status", + "type": "text", + "primaryKey": false, + "notNull": false, + "autoincrement": false + } + }, + "indexes": {}, + "foreignKeys": { + "bookings_user_id_users_id_fk": { + "name": "bookings_user_id_users_id_fk", + "tableFrom": "bookings", + "tableTo": "users", + "columnsFrom": [ + "user_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "cascade" + }, + "bookings_slot_id_slots_id_fk": { + "name": "bookings_slot_id_slots_id_fk", + "tableFrom": "bookings", + "tableTo": "slots", + "columnsFrom": [ + "slot_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "cascade" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {} + }, + "events": { + "name": "events", + "columns": { + "id": { + "name": "id", + "type": "text", + "primaryKey": true, + "notNull": true, + "autoincrement": false + }, + "name": { + "name": "name", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "description": { + "name": "description", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "location": { + "name": "location", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "badge_image": { + "name": "badge_image", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "start_date_time": { + "name": "start_date_time", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "end_date_time": { + "name": "end_date_time", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + } + }, + "indexes": {}, + "foreignKeys": {}, + "compositePrimaryKeys": {}, + "uniqueConstraints": {} + }, + "locations": { + "name": "locations", + "columns": { + "id": { + "name": "id", + "type": "text", + "primaryKey": true, + "notNull": true, + "autoincrement": false + }, + "name": { + "name": "name", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + } + }, + "indexes": {}, + "foreignKeys": {}, + "compositePrimaryKeys": {}, + "uniqueConstraints": {} + }, + "news": { + "name": "news", + "columns": { + "id": { + "name": "id", + "type": "text", + "primaryKey": true, + "notNull": true, + "autoincrement": false + }, + "title": { + "name": "title", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "description": { + "name": "description", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "hero_image_alt": { + "name": "hero_image_alt", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "hero_image_url": { + "name": "hero_image_url", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "cta_title": { + "name": "cta_title", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "cta_url": { + "name": "cta_url", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + } + }, + "indexes": {}, + "foreignKeys": {}, + "compositePrimaryKeys": {}, + "uniqueConstraints": {} + }, + "slot_exceptions": { + "name": "slot_exceptions", + "columns": { + "id": { + "name": "id", + "type": "text", + "primaryKey": true, + "notNull": true, + "autoincrement": false + }, + "slot_id": { + "name": "slot_id", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "date": { + "name": "date", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + } + }, + "indexes": {}, + "foreignKeys": { + "slot_exceptions_slot_id_slots_id_fk": { + "name": "slot_exceptions_slot_id_slots_id_fk", + "tableFrom": "slot_exceptions", + "tableTo": "slots", + "columnsFrom": [ + "slot_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "cascade" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {} + }, + "slots": { + "name": "slots", + "columns": { + "id": { + "name": "id", + "type": "text", + "primaryKey": true, + "notNull": true, + "autoincrement": false + }, + "location_id": { + "name": "location_id", + "type": "text", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "start_date": { + "name": "start_date", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "end_date": { + "name": "end_date", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "recurrence_day": { + "name": "recurrence_day", + "type": "text", + "primaryKey": false, + "notNull": false, + "autoincrement": false + } + }, + "indexes": {}, + "foreignKeys": { + "slots_location_id_locations_id_fk": { + "name": "slots_location_id_locations_id_fk", + "tableFrom": "slots", + "tableTo": "locations", + "columnsFrom": [ + "location_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "cascade" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {} + }, + "users": { + "name": "users", + "columns": { + "id": { + "name": "id", + "type": "text", + "primaryKey": true, + "notNull": true, + "autoincrement": false + }, + "member_id": { + "name": "member_id", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "firebase_id": { + "name": "firebase_id", + "type": "text", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "name": { + "name": "name", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "email": { + "name": "email", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "graduation_year": { + "name": "graduation_year", + "type": "integer", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "member_type": { + "name": "member_type", + "type": "text", + "primaryKey": false, + "notNull": false, + "autoincrement": false + } + }, + "indexes": { + "users_member_id_unique": { + "name": "users_member_id_unique", + "columns": [ + "member_id" + ], + "isUnique": true + }, + "users_firebase_id_unique": { + "name": "users_firebase_id_unique", + "columns": [ + "firebase_id" + ], + "isUnique": true + }, + "users_email_unique": { + "name": "users_email_unique", + "columns": [ + "email" + ], + "isUnique": true + } + }, + "foreignKeys": {}, + "compositePrimaryKeys": {}, + "uniqueConstraints": {} + }, + "users_events": { + "name": "users_events", + "columns": { + "user_id": { + "name": "user_id", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "event_id": { + "name": "event_id", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "admission_key": { + "name": "admission_key", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + } + }, + "indexes": {}, + "foreignKeys": { + "users_events_user_id_users_id_fk": { + "name": "users_events_user_id_users_id_fk", + "tableFrom": "users_events", + "tableTo": "users", + "columnsFrom": [ + "user_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "cascade" + }, + "users_events_event_id_events_id_fk": { + "name": "users_events_event_id_events_id_fk", + "tableFrom": "users_events", + "tableTo": "events", + "columnsFrom": [ + "event_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "cascade" + } + }, + "compositePrimaryKeys": { + "users_events_user_id_event_id_pk": { + "columns": [ + "event_id", + "user_id" + ] + } + }, + "uniqueConstraints": {} + } + }, + "enums": {}, + "_meta": { + "schemas": {}, + "tables": {}, + "columns": {} + } +} \ No newline at end of file diff --git a/server/drizzle/meta/_journal.json b/server/drizzle/meta/_journal.json index 4c339cc..622482f 100644 --- a/server/drizzle/meta/_journal.json +++ b/server/drizzle/meta/_journal.json @@ -1 +1,48 @@ -{"version":"5","dialect":"sqlite","entries":[{"idx":0,"version":"5","when":1694905369882,"tag":"0000_majestic_shen","breakpoints":true},{"idx":1,"version":"5","when":1695126561835,"tag":"0001_wealthy_valeria_richards","breakpoints":true},{"idx":2,"version":"5","when":1695127050309,"tag":"0002_peaceful_phil_sheldon","breakpoints":true},{"idx":3,"version":"5","when":1695127082331,"tag":"0003_young_pyro","breakpoints":true},{"idx":4,"version":"5","when":1695128452007,"tag":"0004_special_tenebrous","breakpoints":true}]} \ No newline at end of file +{ + "version": "5", + "dialect": "sqlite", + "entries": [ + { + "idx": 0, + "version": "5", + "when": 1694905369882, + "tag": "0000_majestic_shen", + "breakpoints": true + }, + { + "idx": 1, + "version": "5", + "when": 1695126561835, + "tag": "0001_wealthy_valeria_richards", + "breakpoints": true + }, + { + "idx": 2, + "version": "5", + "when": 1695127050309, + "tag": "0002_peaceful_phil_sheldon", + "breakpoints": true + }, + { + "idx": 3, + "version": "5", + "when": 1695127082331, + "tag": "0003_young_pyro", + "breakpoints": true + }, + { + "idx": 4, + "version": "5", + "when": 1695128452007, + "tag": "0004_special_tenebrous", + "breakpoints": true + }, + { + "idx": 5, + "version": "5", + "when": 1695474910735, + "tag": "0005_married_paladin", + "breakpoints": true + } + ] +} \ No newline at end of file diff --git a/shared/types.ts b/shared/types.ts index f812390..a4ee391 100644 --- a/shared/types.ts +++ b/shared/types.ts @@ -1,5 +1,5 @@ import type { InferSelectModel } from 'drizzle-orm' -import type { events, users } from '~/server/db/schema' +import type { events, news, users } from '~/server/db/schema' export type User = InferSelectModel export type UserRestricted = Pick @@ -7,3 +7,5 @@ export type UserRestrictedWithAdmissionKey = UserRestricted & { admissionKey: st export type Event = InferSelectModel export type EventWithAttendees = Event & { attendees: UserRestrictedWithAdmissionKey[] } + +export type NewsArticle = InferSelectModel