diff --git a/client/src/lib/api.ts b/client/src/lib/api.ts index 3c12c2b..69b2606 100644 --- a/client/src/lib/api.ts +++ b/client/src/lib/api.ts @@ -330,4 +330,52 @@ export class API { }); } + // University calendar preferences + public static async getCalendarPreferences(): Promise<{ + global: any; + event_types: Record; + uni_cal_categories: Record; + }> { + const baseUrl = await this.getBaseUrl(); + const token = await this.getJwtToken(); + const response = await fetch(`${baseUrl}/calendar_preferences`, { + method: 'GET', + headers: { + 'Authorization': `Bearer ${token}` + } + }); + return response.json(); + } + + public static async setUniCalCategoryPreference(category: string, preferences: { + color_id?: string; + title_template?: string; + description_template?: string; + }): Promise { + const baseUrl = await this.getBaseUrl(); + const token = await this.getJwtToken(); + const response = await fetch(`${baseUrl}/calendar_preferences/uni_cal:${category}`, { + method: 'PUT', + headers: { + 'Authorization': `Bearer ${token}`, + 'Content-Type': 'application/json' + }, + body: JSON.stringify({ calendar_preference: preferences }) + }); + return response.json(); + } + + // Set color for all university calendar categories at once + // colorId should be a Google Calendar color ID (1-11) + public static async setAllUniCalCategoriesColor(colorId: string): Promise { + const categories = [ + 'holiday', 'term_dates', 'registration', 'deadline', 'finals', + 'graduation', 'academic', 'campus_event', 'meeting', 'exhibit', + 'announcement', 'other' + ]; + await Promise.all( + categories.map(cat => this.setUniCalCategoryPreference(cat, { color_id: colorId })) + ); + } + } \ No newline at end of file diff --git a/client/src/lib/components/Settings.svelte b/client/src/lib/components/Settings.svelte index b1df0cc..2f9ff51 100644 --- a/client/src/lib/components/Settings.svelte +++ b/client/src/lib/components/Settings.svelte @@ -9,6 +9,25 @@ import { Button, SelectOutlined, snackbar, Switch } from "m3-svelte"; import { onMount } from "svelte"; + // Google Calendar color ID to hex mapping + const COLOR_ID_TO_HEX: Record = { + "1": "#7986cb", // Lavender + "2": "#33b679", // Sage + "3": "#8e24aa", // Grape + "4": "#e67c73", // Flamingo + "5": "#f6bf26", // Banana + "6": "#f4511e", // Tangerine + "7": "#039be5", // Peacock + "8": "#616161", // Graphite + "9": "#3f51b5", // Blueberry + "10": "#0b8043", // Basil + "11": "#d50000", // Tomato + }; + + const HEX_TO_COLOR_ID: Record = Object.fromEntries( + Object.entries(COLOR_ID_TO_HEX).map(([id, hex]) => [hex, id]) + ); + let userSettings = $state(undefined); let email = $state(undefined); let currentEnvironment = $state('prod'); @@ -19,6 +38,7 @@ let showEnvSwitcher = $state(false); let showClearDataButton = $state(false); let isRefreshingFlags = $state(false); + let uniCalColor = $state("#616161"); // Default to Graphite $effect(() => { userSettings = $storedUserSettings; @@ -74,6 +94,19 @@ } catch (e) { console.error('Failed to fetch connected accounts:', e); } + + // Fetch uni cal color preference + try { + const calPrefs = await API.getCalendarPreferences(); + // Check if any uni_cal_category has a color set (they should all be the same) + const firstCategory = Object.values(calPrefs.uni_cal_categories || {})[0]; + if (firstCategory?.color_id) { + const colorId = String(firstCategory.color_id); + uniCalColor = COLOR_ID_TO_HEX[colorId] || "#616161"; + } + } catch (e) { + // Calendar preferences might not exist yet, that's okay + } } catch (error) { console.error('Failed to load settings:', error); } @@ -142,6 +175,15 @@ } } + const uniCalColorGetterSetter = { + get value() { return uniCalColor; }, + set value(value: string) { + uniCalColor = value; + const colorId = HEX_TO_COLOR_ID[value] || "8"; // Default to Graphite + API.setAllUniCalCategoriesColor(colorId); + } + } + function toggleUniversityCategory(categoryId: string) { if (!userSettings) return; const currentCategories = userSettings.university_event_categories ?? []; @@ -476,6 +518,29 @@ +
+

University Events Color

+
+
+ +
+
+ {#if syncUniversityEventsValue && availableCategories.length > 0}

Select event types to sync: