Account Information
diff --git a/apps/web/app/settings/notifications/page.jsx b/apps/web/app/settings/notifications/page.jsx
deleted file mode 100644
index 25609da..0000000
--- a/apps/web/app/settings/notifications/page.jsx
+++ /dev/null
@@ -1,418 +0,0 @@
-'use client';
-
-import { useState, useEffect } from 'react';
-import { useForm } from 'react-hook-form';
-import { zodResolver } from '@hookform/resolvers/zod';
-import { Bell, Users, Music, MessageSquare, Megaphone, Shield, Info, ToggleLeft, ToggleRight } from 'lucide-react';
-import SettingsPageWrapper, { useSettingsContext } from '@/components/SettingsPageWrapper';
-import { NotificationToggle } from '@/components/NotificationToggle';
-import { notificationSchema, getDefaultNotificationPreferences } from '@/lib/schemas/notificationSchema';
-import { useNotificationPreferences, useNotificationPreferencesUpdate } from '@/hooks/useNotificationPreferences';
-
-// Inner component that uses the context (must be inside SettingsPageWrapper)
-function NotificationSettingsContent() {
- const { setHasUnsavedChanges, setFormSubmitHandler, setFormResetHandler } = useSettingsContext();
-
- // Fetch notification preferences using TanStack Query
- const { data: notificationData, isLoading: loading, error: notificationError } = useNotificationPreferences();
- const notificationUpdate = useNotificationPreferencesUpdate();
-
- const {
- register,
- handleSubmit,
- formState: { errors, isDirty },
- setValue,
- watch,
- reset,
- } = useForm({
- resolver: zodResolver(notificationSchema),
- defaultValues: getDefaultNotificationPreferences(),
- });
-
- // Watch all form values
- const notificationsEnabled = watch('notifications_enabled');
- const emailFrequency = watch('email_frequency');
-
- // Store original form values for cancel
- const [originalValues, setOriginalValues] = useState(null);
-
- // Update unsaved changes indicator when form is dirty
- useEffect(() => {
- setHasUnsavedChanges(isDirty);
- }, [isDirty, setHasUnsavedChanges]);
-
- // Initialize form with fetched notification preferences
- useEffect(() => {
- if (notificationData) {
- // Set all form values from API response
- Object.keys(notificationData).forEach(key => {
- if (key !== 'message') { // Exclude success message from form data
- setValue(key, notificationData[key], { shouldDirty: false });
- }
- });
-
- setOriginalValues(notificationData);
- } else if (!loading && !notificationError) {
- // If no data and not loading, use defaults
- const defaultValues = getDefaultNotificationPreferences();
- Object.keys(defaultValues).forEach(key => {
- setValue(key, defaultValues[key], { shouldDirty: false });
- });
- setOriginalValues(defaultValues);
- }
- }, [notificationData, loading, notificationError, setValue]);
-
- // Form submission handler
- const onSubmit = async (data) => {
- try {
- // Ensure security alerts are always enabled
- const submitData = {
- ...data,
- security_alerts_inapp: true,
- security_alerts_email: true,
- };
-
- // Use TanStack Query mutation to update preferences
- const updatedPreferences = await notificationUpdate.mutateAsync(submitData);
-
- // Reset form with updated data
- reset(updatedPreferences);
- setOriginalValues(updatedPreferences);
- setHasUnsavedChanges(false);
- } catch (error) {
- console.error('Failed to update notification preferences:', error);
- // Error toast is handled by the mutation hook
- throw error;
- }
- };
-
- // Register form submit handler with the wrapper
- useEffect(() => {
- const submitFn = () => {
- return handleSubmit(onSubmit)();
- };
- setFormSubmitHandler(() => submitFn);
- }, [handleSubmit, setFormSubmitHandler]);
-
- // Register form reset handler with the wrapper
- useEffect(() => {
- const resetFn = () => {
- if (originalValues) {
- reset(originalValues);
- }
- };
- setFormResetHandler(() => resetFn);
- }, [reset, originalValues, setFormResetHandler]);
-
- // Master toggle handler
- const handleMasterToggle = (enabled) => {
- const allNotificationFields = [
- 'friend_requests_inapp', 'friend_requests_email',
- 'new_followers_inapp', 'new_followers_email',
- 'comments_inapp', 'comments_email',
- 'playlist_invites_inapp', 'playlist_invites_email',
- 'playlist_updates_inapp', 'playlist_updates_email',
- 'song_of_day_inapp', 'song_of_day_email',
- 'system_announcements_inapp', 'system_announcements_email',
- ];
-
- allNotificationFields.forEach(field => {
- setValue(field, enabled, { shouldDirty: true });
- });
-
- // Keep security alerts enabled
- setValue('security_alerts_inapp', true, { shouldDirty: true });
- setValue('security_alerts_email', true, { shouldDirty: true });
- };
-
- // Loading state
- if (loading) {
- return (
- <>
-
-
-
-
-
Notifications
-
- Configure your notification preferences
-
-
-
-
-
- >
- );
- }
-
- // Error state
- if (notificationError) {
- return (
- <>
-
-
-
-
-
Notifications
-
- Configure your notification preferences
-
-
-
-
-
-
-
Error loading notification preferences: {notificationError.message}
-
-
- >
- );
- }
-
- return (
-
- {/* Section Header */}
-
-
-
-
-
Notifications
-
- Configure your notification preferences
-
-
-
-
-
- {/* Section Content */}
-
-
- );
-}
-
-// Outer component that wraps content with SettingsPageWrapper
-export default function NotificationSettingsPage() {
- return (
-
-
-
- );
-}
diff --git a/apps/web/app/settings/privacy/page.jsx b/apps/web/app/settings/privacy/page.jsx
deleted file mode 100644
index f923cfb..0000000
--- a/apps/web/app/settings/privacy/page.jsx
+++ /dev/null
@@ -1,412 +0,0 @@
-'use client';
-
-import { useState, useEffect } from 'react';
-import { useForm } from 'react-hook-form';
-import { zodResolver } from '@hookform/resolvers/zod';
-import { Shield, Info, Globe, Users, Lock, Eye, EyeOff, Search, Rss } from 'lucide-react';
-import SettingsPageWrapper, { useSettingsContext } from '@/components/SettingsPageWrapper';
-import { PrivacyToggle, PrivacyRadioGroup } from '@/components/PrivacyToggle';
-import { privacySchema, getDefaultPrivacySettings } from '@/lib/schemas/privacySchema';
-import { usePrivacySettings, usePrivacySettingsUpdate } from '@/hooks/usePrivacySettings';
-
-// Inner component that uses the context (must be inside SettingsPageWrapper)
-function PrivacySettingsContent() {
- const { setHasUnsavedChanges, setFormSubmitHandler, setFormResetHandler } = useSettingsContext();
-
- // Fetch privacy settings using TanStack Query
- const { data: privacyData, isLoading: loading, error: privacyError } = usePrivacySettings();
-
- // Privacy settings update mutation hook
- const privacyUpdate = usePrivacySettingsUpdate();
-
- const {
- register,
- handleSubmit,
- formState: { errors, isDirty },
- setValue,
- watch,
- reset,
- } = useForm({
- resolver: zodResolver(privacySchema),
- defaultValues: getDefaultPrivacySettings(),
- });
-
- // Watch all form values
- const profileVisibility = watch('profile_visibility');
- const playlistVisibility = watch('playlist_visibility');
- const listeningActivityVisible = watch('listening_activity_visible');
- const songOfDayVisibility = watch('song_of_day_visibility');
- const friendRequestSetting = watch('friend_request_setting');
- const searchable = watch('searchable');
- const activityFeedVisible = watch('activity_feed_visible');
-
- // Store original form values for cancel
- const [originalValues, setOriginalValues] = useState(null);
-
- // Update unsaved changes indicator when form is dirty
- useEffect(() => {
- setHasUnsavedChanges(isDirty);
- }, [isDirty, setHasUnsavedChanges]);
-
- // Update form when privacy data loads
- useEffect(() => {
- if (privacyData) {
- // Set form values
- const formValues = {
- profile_visibility: privacyData.profile_visibility || 'public',
- playlist_visibility: privacyData.playlist_visibility || 'public',
- listening_activity_visible: privacyData.listening_activity_visible ?? true,
- song_of_day_visibility: privacyData.song_of_day_visibility || 'public',
- friend_request_setting: privacyData.friend_request_setting || 'everyone',
- searchable: privacyData.searchable ?? true,
- activity_feed_visible: privacyData.activity_feed_visible ?? true,
- };
-
- setValue('profile_visibility', formValues.profile_visibility);
- setValue('playlist_visibility', formValues.playlist_visibility);
- setValue('listening_activity_visible', formValues.listening_activity_visible);
- setValue('song_of_day_visibility', formValues.song_of_day_visibility);
- setValue('friend_request_setting', formValues.friend_request_setting);
- setValue('searchable', formValues.searchable);
- setValue('activity_feed_visible', formValues.activity_feed_visible);
-
- // Store original values for cancel
- setOriginalValues(formValues);
- }
- }, [privacyData, setValue]);
-
- // Form submission handler using the mutation hook
- const onSubmit = async (data) => {
- try {
- // Use the mutation hook to update privacy settings
- const updatedPrivacy = await privacyUpdate.mutateAsync(data);
-
- // Privacy data will be updated via cache invalidation
- // Update form values with response data
- const formValues = {
- profile_visibility: updatedPrivacy.profile_visibility || 'public',
- playlist_visibility: updatedPrivacy.playlist_visibility || 'public',
- listening_activity_visible: updatedPrivacy.listening_activity_visible ?? true,
- song_of_day_visibility: updatedPrivacy.song_of_day_visibility || 'public',
- friend_request_setting: updatedPrivacy.friend_request_setting || 'everyone',
- searchable: updatedPrivacy.searchable ?? true,
- activity_feed_visible: updatedPrivacy.activity_feed_visible ?? true,
- };
-
- reset(formValues);
- setOriginalValues(formValues);
- setHasUnsavedChanges(false);
- } catch (error) {
- // Error is handled by the mutation hook (toast notification)
- // Re-throw to allow form to handle error state if needed
- throw error;
- }
- };
-
- // Register form submit handler with the wrapper
- useEffect(() => {
- const submitFn = () => {
- return handleSubmit(onSubmit)();
- };
- setFormSubmitHandler(() => submitFn);
- }, [handleSubmit, setFormSubmitHandler]);
-
- // Register form reset handler with the wrapper
- useEffect(() => {
- const resetFn = () => {
- if (originalValues) {
- reset(originalValues);
- }
- };
- setFormResetHandler(() => resetFn);
- }, [reset, originalValues, setFormResetHandler]);
-
- if (loading) {
- return (
- <>
-
-
-
-
-
Privacy
-
- Control who can see your activity and playlists
-
-
-
-
-
- >
- );
- }
-
- if (privacyError) {
- return (
- <>
-
-
-
-
-
Privacy
-
- Control who can see your activity and playlists
-
-
-
-
-
-
-
Error loading privacy settings: {privacyError.message}
-
-
- >
- );
- }
-
- return (
-
- {/* Section Header */}
-
-
-
-
-
Privacy
-
- Control who can see your activity and playlists
-
-
-
-
-
- {/* Section Content */}
-
-
- );
-}
-
-// Outer component that wraps content with SettingsPageWrapper
-export default function PrivacySettingsPage() {
- return (
-
-
-
- );
-}
diff --git a/apps/web/app/settings/profile/page.jsx b/apps/web/app/settings/profile/page.jsx
index 48045f2..54bbd29 100644
--- a/apps/web/app/settings/profile/page.jsx
+++ b/apps/web/app/settings/profile/page.jsx
@@ -166,7 +166,7 @@ function ProfileSettingsContent() {
return (
{/* Section Header */}
-
+
@@ -179,7 +179,7 @@ function ProfileSettingsContent() {
{/* Section Content */}
-