diff --git a/app/composables/users.ts b/app/composables/users.ts index eaea966..512f201 100644 --- a/app/composables/users.ts +++ b/app/composables/users.ts @@ -1,5 +1,22 @@ export async function useUser() { - const { fetch, session } = useUserSession() + const { fetch, user } = useUserSession() + + async function toggleWeatherTab() { + const weatherTab = !user.value.weatherTab + try { + await $fetch('/api/users/me', { + method: 'PATCH', + body: JSON.stringify({ + weatherTab, + }), + }) + await fetch() + useSuccessToast(weatherTab ? 'Weather tab enabled!' : 'Weather tab disabled!') + } + catch (error) { + useErrorToast('An error occurred while updating the weather tab', String(error)) + } + } async function deleteAvatar() { try { @@ -54,5 +71,6 @@ export async function useUser() { deleteAvatar, uploadAvatar, updateUser, + toggleWeatherTab, } } diff --git a/app/pages/index.vue b/app/pages/index.vue index 3f8e31a..894e129 100644 --- a/app/pages/index.vue +++ b/app/pages/index.vue @@ -14,6 +14,7 @@ const { user, loggedIn } = await useUserSession() const { categories } = await useCategories() const { getTabsForCategory } = await useTabs() const { canCreateCategory } = await useUserLimits() +const { toggleWeatherTab } = await useUser() // Modals const createCategoryModal = ref(false) @@ -46,7 +47,7 @@ function openCreateTab(category: CategoryType) { const currentEditCategory = ref(null) // DropDown Items -const items = [[ +const itemsCategory = [[ { label: 'Edit Category', icon: 'i-ph:pencil-duotone', @@ -67,6 +68,21 @@ const items = [[ }, ]] +const itemsSpecialTabs = computed(() => [[ + { + label: user.value.weatherTab ? 'Remove Weather Tab' : 'Add Weather Tab', + icon: user.value.weatherTab ? 'i-ph:cloud-slash-duotone' : 'i-ph:cloud-duotone', + click: async () => await toggleWeatherTab(), + }, + /** + * { + * label: 'Add Clock tab', + * icon: 'i-ph:clock-duotone', + * click: () => console.log('Add Weather Tab'), + * }, + */ +]]) + defineShortcuts({ c: () => { if (canCreateCategory()) { @@ -118,7 +134,7 @@ defineShortcuts({ - +
C + + +
@@ -152,7 +180,7 @@ defineShortcuts({ :key="category.id" > diff --git a/server/database/migrations/0001_certain_kat_farrell.sql b/server/database/migrations/0001_certain_kat_farrell.sql new file mode 100644 index 0000000..4bd1ee3 --- /dev/null +++ b/server/database/migrations/0001_certain_kat_farrell.sql @@ -0,0 +1 @@ +ALTER TABLE `users` ADD `weather_tab` integer DEFAULT 0; \ No newline at end of file diff --git a/server/database/migrations/meta/0001_snapshot.json b/server/database/migrations/meta/0001_snapshot.json new file mode 100644 index 0000000..e34b0c8 --- /dev/null +++ b/server/database/migrations/meta/0001_snapshot.json @@ -0,0 +1,355 @@ +{ + "version": "6", + "dialect": "sqlite", + "id": "97cc3d07-6e8f-44eb-aeb6-dfe3ff52cb43", + "prevId": "2764f724-b59e-4de8-b7e7-691122ed7e73", + "tables": { + "categories": { + "name": "categories", + "columns": { + "id": { + "name": "id", + "type": "integer", + "primaryKey": true, + "notNull": true, + "autoincrement": false + }, + "name": { + "name": "name", + "type": "text", + "primaryKey": false, + "notNull": false, + "autoincrement": false, + "default": "''" + }, + "name_visible": { + "name": "name_visible", + "type": "integer", + "primaryKey": false, + "notNull": false, + "autoincrement": false, + "default": true + }, + "icon": { + "name": "icon", + "type": "text", + "primaryKey": false, + "notNull": false, + "autoincrement": false, + "default": "'i-ph:circle-wavy-question-duotone'" + }, + "color": { + "name": "color", + "type": "text", + "primaryKey": false, + "notNull": false, + "autoincrement": false, + "default": "'gray'" + }, + "user_id": { + "name": "user_id", + "type": "integer", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "created_at": { + "name": "created_at", + "type": "integer", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "updated_at": { + "name": "updated_at", + "type": "integer", + "primaryKey": false, + "notNull": false, + "autoincrement": false + } + }, + "indexes": {}, + "foreignKeys": { + "categories_user_id_users_id_fk": { + "name": "categories_user_id_users_id_fk", + "tableFrom": "categories", + "tableTo": "users", + "columnsFrom": [ + "user_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {} + }, + "tabs": { + "name": "tabs", + "columns": { + "id": { + "name": "id", + "type": "integer", + "primaryKey": true, + "notNull": true, + "autoincrement": false + }, + "name": { + "name": "name", + "type": "text", + "primaryKey": false, + "notNull": false, + "autoincrement": false, + "default": "''" + }, + "primary": { + "name": "primary", + "type": "integer", + "primaryKey": false, + "notNull": false, + "autoincrement": false, + "default": false + }, + "icon": { + "name": "icon", + "type": "text", + "primaryKey": false, + "notNull": false, + "autoincrement": false, + "default": "'i-ph:circle-wavy-question-duotone'" + }, + "color": { + "name": "color", + "type": "text", + "primaryKey": false, + "notNull": false, + "autoincrement": false, + "default": "'gray'" + }, + "link": { + "name": "link", + "type": "text", + "primaryKey": false, + "notNull": false, + "autoincrement": false, + "default": "''" + }, + "category_id": { + "name": "category_id", + "type": "integer", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "created_at": { + "name": "created_at", + "type": "integer", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "updated_at": { + "name": "updated_at", + "type": "integer", + "primaryKey": false, + "notNull": false, + "autoincrement": false + } + }, + "indexes": {}, + "foreignKeys": { + "tabs_category_id_categories_id_fk": { + "name": "tabs_category_id_categories_id_fk", + "tableFrom": "tabs", + "tableTo": "categories", + "columnsFrom": [ + "category_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {} + }, + "users": { + "name": "users", + "columns": { + "id": { + "name": "id", + "type": "integer", + "primaryKey": true, + "notNull": true, + "autoincrement": false + }, + "username": { + "name": "username", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "name": { + "name": "name", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "email": { + "name": "email", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "github_id": { + "name": "github_id", + "type": "text", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "github_token": { + "name": "github_token", + "type": "text", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "google_id": { + "name": "google_id", + "type": "text", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "google_token": { + "name": "google_token", + "type": "text", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "description": { + "name": "description", + "type": "text", + "primaryKey": false, + "notNull": false, + "autoincrement": false, + "default": "''" + }, + "avatar": { + "name": "avatar", + "type": "text", + "primaryKey": false, + "notNull": false, + "autoincrement": false, + "default": "''" + }, + "private": { + "name": "private", + "type": "integer", + "primaryKey": false, + "notNull": false, + "autoincrement": false, + "default": false + }, + "language": { + "name": "language", + "type": "text", + "primaryKey": false, + "notNull": false, + "autoincrement": false, + "default": "'en-EN'" + }, + "location": { + "name": "location", + "type": "text", + "primaryKey": false, + "notNull": false, + "autoincrement": false, + "default": "'unknown'" + }, + "weather_tab": { + "name": "weather_tab", + "type": "integer", + "primaryKey": false, + "notNull": false, + "autoincrement": false, + "default": 0 + }, + "subscription": { + "name": "subscription", + "type": "text", + "primaryKey": false, + "notNull": false, + "autoincrement": false, + "default": "'free'" + }, + "created_at": { + "name": "created_at", + "type": "integer", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "updated_at": { + "name": "updated_at", + "type": "integer", + "primaryKey": false, + "notNull": false, + "autoincrement": false + } + }, + "indexes": { + "users_username_unique": { + "name": "users_username_unique", + "columns": [ + "username" + ], + "isUnique": true + }, + "users_email_unique": { + "name": "users_email_unique", + "columns": [ + "email" + ], + "isUnique": true + }, + "users_github_id_unique": { + "name": "users_github_id_unique", + "columns": [ + "github_id" + ], + "isUnique": true + }, + "users_google_id_unique": { + "name": "users_google_id_unique", + "columns": [ + "google_id" + ], + "isUnique": true + } + }, + "foreignKeys": {}, + "compositePrimaryKeys": {}, + "uniqueConstraints": {} + } + }, + "enums": {}, + "_meta": { + "schemas": {}, + "tables": {}, + "columns": {} + }, + "internal": { + "indexes": {} + } +} \ No newline at end of file diff --git a/server/database/migrations/meta/_journal.json b/server/database/migrations/meta/_journal.json index afffdcb..13c35ac 100644 --- a/server/database/migrations/meta/_journal.json +++ b/server/database/migrations/meta/_journal.json @@ -8,6 +8,13 @@ "when": 1725385265625, "tag": "0000_grey_marvel_zombies", "breakpoints": true + }, + { + "idx": 1, + "version": "6", + "when": 1725482023280, + "tag": "0001_certain_kat_farrell", + "breakpoints": true } ] } \ No newline at end of file diff --git a/server/database/schema.ts b/server/database/schema.ts index c797ef4..6f5668a 100644 --- a/server/database/schema.ts +++ b/server/database/schema.ts @@ -17,6 +17,7 @@ export const users = sqliteTable('users', { private: integer('private', { mode: 'boolean' }).default(false), language: text('language').default('en-EN'), location: text('location').default('unknown'), + weatherTab: integer('weather_tab', { mode: 'boolean' }).default(0), subscription: text('subscription', { enum: Subscription }).default('free'), ...timestamps, }) diff --git a/server/routes/auth/github.get.ts b/server/routes/auth/github.get.ts index afd8e4b..38b87bd 100644 --- a/server/routes/auth/github.get.ts +++ b/server/routes/auth/github.get.ts @@ -52,6 +52,7 @@ export default oauthGitHubEventHandler({ location: 'unknown', private: false, subscription: 'free', + weatherTab: false, }) await replaceUserSession(event, { diff --git a/server/routes/auth/google.get.ts b/server/routes/auth/google.get.ts index de50a23..92e2e04 100644 --- a/server/routes/auth/google.get.ts +++ b/server/routes/auth/google.get.ts @@ -53,6 +53,7 @@ export default oauthGoogleEventHandler({ location: 'unknown', private: false, subscription: 'free', + weatherTab: false, }) await replaceUserSession(event, { diff --git a/types/auth.d.ts b/types/auth.d.ts index 2773c18..29f0257 100644 --- a/types/auth.d.ts +++ b/types/auth.d.ts @@ -15,6 +15,7 @@ declare module '#auth-utils' { language: string location: string subscription: Subscription + weatherTab: boolean } interface UserSession { diff --git a/types/types.ts b/types/types.ts index d9b6340..21e73bc 100644 --- a/types/types.ts +++ b/types/types.ts @@ -75,6 +75,7 @@ export const UpdateUserSchema = z.object({ location: z.string().optional(), language: z.string().optional(), private: z.boolean().optional().default(false), + weatherTab: z.boolean().optional().default(false), }) export const UpdateUserSchemaType = z.infer