From 003e353073466a2b44b2126d1f9cfb254bbdb173 Mon Sep 17 00:00:00 2001 From: sageTheDM Date: Tue, 1 Oct 2024 09:29:52 +0200 Subject: [PATCH] Refactoring settings --- app/components/Login.tsx | 2 +- app/components/Settings.tsx | 1049 ----------------- app/components/settings/ColorSettings.tsx | 29 + app/components/settings/Settings.tsx | 866 ++++++++++++++ app/components/{ => settings}/settingUtils.ts | 0 app/components/{ => settings}/theme.ts | 0 app/page.tsx | 4 +- 7 files changed, 898 insertions(+), 1052 deletions(-) delete mode 100644 app/components/Settings.tsx create mode 100644 app/components/settings/ColorSettings.tsx create mode 100644 app/components/settings/Settings.tsx rename app/components/{ => settings}/settingUtils.ts (100%) rename app/components/{ => settings}/theme.ts (100%) diff --git a/app/components/Login.tsx b/app/components/Login.tsx index 1d8a4e6..300ed66 100644 --- a/app/components/Login.tsx +++ b/app/components/Login.tsx @@ -3,7 +3,7 @@ import { createAccount, checkCredentials, } from '../backend/database'; -import Settings from './Settings'; // Import the Settings component +import Settings from './settings/Settings'; // Import the Settings component const Login: React.FC = () => { // State to handle popup visibility diff --git a/app/components/Settings.tsx b/app/components/Settings.tsx deleted file mode 100644 index a89ae80..0000000 --- a/app/components/Settings.tsx +++ /dev/null @@ -1,1049 +0,0 @@ -import React, { useState, useEffect } from 'react'; -import { applyTheme, applyCustomTheme } from './theme'; -import { exportSettings, importSettings } from './settingUtils'; // Import utility functions -import { getAllLocalStorageItems } from '../backend/GetLocalStorage'; -import { - changePassword, - changeData, - deleteAccount, -} from '../backend/database'; - - -const Settings: React.FC<{ closeSettings: () => void; accountName: string }> = ({ closeSettings, accountName }) => { - - const getItemFromLocalStorage = (key: string) => { - const item = localStorage.getItem(key); - - if (item) { - try { - return JSON.parse(item); // Attempt to parse the item - } catch (e) { - console.error(`Error parsing JSON for key "${key}":`, e); - return false; // Return false if parsing fails - } - } - - return false; // Default to false if item is null or empty - }; - - interface SettingsProps { - closeSettings: () => void; - accountName: string; - handleLogout: () => void; // Add this line to accept handleLogout as a prop - } - - // Active section - const [activeSection, setActiveSection] = useState(() => localStorage.getItem('activeSection') || 'general'); - - // Language setting - const [preferredLanguage, setPreferredLanguage] = useState(() => localStorage.getItem('preferredLanguage') || 'en'); - - // Currency setting - const [preferredCurrency, setPreferredCurrency] = useState(() => localStorage.getItem('preferredCurrency') || 'usd'); - - // Date and time format settings - const [dateFormat, setDateFormat] = useState(() => localStorage.getItem('dateFormat') || 'mm/dd/yyyy'); - const [timeFormat, setTimeFormat] = useState(() => localStorage.getItem('timeFormat') || '12-hour'); - const [timeZone, setTimeZone] = useState(() => localStorage.getItem('timeZone') || 'GMT'); - - // Online AI and chat history settings - const [selectedOption, setSelectedOption] = useState('Offline'); // Default to 'Offline' - const [disableChatHistory, setDisableChatHistory] = useState(() => getItemFromLocalStorage('disableChatHistory')); - const [disableAIMemory, setDisableAIMemory] = useState(() => getItemFromLocalStorage('disableAIMemory')); - const [openSourceMode, setOpenSourceMode] = useState(() => getItemFromLocalStorage('openSourceMode')); - - // User credentials - const [newName, setNewName] = useState(() => localStorage.getItem('newName') || ''); - const [newEmail, setNewEmail] = useState(() => localStorage.getItem('newEmail') || ''); - const [newPassword, setNewPassword] = useState(() => localStorage.getItem('newPassword') || ''); - const [currentPassword, setCurrentPassword] = useState(''); - - // Measurement setting - const [preferredMeasurement, setPreferredMeasurement] = useState(() => localStorage.getItem('preferredMeasurement') || 'Metric'); - - // Theme settings - const [backgroundColor, setBackgroundColor] = useState(() => getComputedStyle(document.documentElement).getPropertyValue('--background-color').trim()); - const [textColor, setTextColor] = useState(() => getComputedStyle(document.documentElement).getPropertyValue('--text-color').trim()); - const [inputBackgroundColor, setInputBackgroundColor] = useState(() => getComputedStyle(document.documentElement).getPropertyValue('--input-background-color').trim()); - const [inputButtonColor, setInputButtonColor] = useState(() => getComputedStyle(document.documentElement).getPropertyValue('--input-button-color').trim()); - const [inputButtonHoverColor, setInputButtonHoverColor] = useState(() => getComputedStyle(document.documentElement).getPropertyValue('--input-button-hover-color').trim()); - const [userMessageBackgroundColor, setUserMessageBackgroundColor] = useState(() => getComputedStyle(document.documentElement).getPropertyValue('--user-message-background-color').trim()); - const [userMessageTextColor, setUserMessageTextColor] = useState(() => getComputedStyle(document.documentElement).getPropertyValue('--user-message-text-color').trim()); - const [aiMessageBackgroundColor, setAiMessageBackgroundColor] = useState(() => getComputedStyle(document.documentElement).getPropertyValue('--ai-message-background-color').trim()); - const [aiMessageTextColor, setAiMessageTextColor] = useState(() => getComputedStyle(document.documentElement).getPropertyValue('--ai-message-text-color').trim()); - const [buttonBackgroundColor, setButtonBackgroundColor] = useState(() => getComputedStyle(document.documentElement).getPropertyValue('--button-background-color').trim()); - const [buttonHoverBackgroundColor, setButtonHoverBackgroundColor] = useState(() => getComputedStyle(document.documentElement).getPropertyValue('--button-hover-background-color').trim()); - const [modelsBackgroundColor, setModelsBackgroundColor] = useState(() => getComputedStyle(document.documentElement).getPropertyValue('--models-background-color').trim()); - const [historyBackgroundColor, setHistoryBackgroundColor] = useState(() => getComputedStyle(document.documentElement).getPropertyValue('--history-background-color').trim()); - const [leftPanelBackgroundColor, setLeftPanelBackgroundColor] = useState(() => getComputedStyle(document.documentElement).getPropertyValue('--left-panel-background-color').trim()); - const [conversationBackgroundColor, setConversationBackgroundColor] = useState(() => getComputedStyle(document.documentElement).getPropertyValue('--conversation-background-color').trim()); - const [popUpTextColor, setPopUpTextColor] = useState(() => getComputedStyle(document.documentElement).getPropertyValue('--pop-up-text').trim()); - const [inputBorderColor, setInputBorderColor] = useState(() => getComputedStyle(document.documentElement).getPropertyValue('--input-border-color').trim()); - const [fontFamily, setFontFamily] = useState(() => getComputedStyle(document.documentElement).getPropertyValue('--font-family').trim()); - const [fontSize, setFontSize] = useState(() => getComputedStyle(document.documentElement).getPropertyValue('--font-size').trim()); - const [burgerMenu, setBurgerMenu] = useState(() => getComputedStyle(document.documentElement).getPropertyValue('--burger-menu-background-color').trim()); - const [faqBackgroundColor, setFaqBackgroundColor] = useState(() => getComputedStyle(document.documentElement).getPropertyValue('--faq-background-color').trim()); - const [faqHeadingColor, setFaqHeadingColor] = useState(() => getComputedStyle(document.documentElement).getPropertyValue('--faq-heading-color').trim()); - const [faqItemBackgroundColor, setFaqItemBackgroundColor] = useState(() => getComputedStyle(document.documentElement).getPropertyValue('--faq-item-background-color').trim()); - const [faqItemHeadingColor, setFaqItemHeadingColor] = useState(() => getComputedStyle(document.documentElement).getPropertyValue('--faq-item-heading-color').trim()); - const [faqItemTextColor, setFaqItemTextColor] = useState(() => getComputedStyle(document.documentElement).getPropertyValue('--faq-item-text-color').trim()); - const [faqItemHoverBackgroundColor, setFaqItemHoverBackgroundColor] = useState(() => getComputedStyle(document.documentElement).getPropertyValue('--faq-item-hover-background-color').trim()); - const [popupBackgroundColor, setPopupBackgroundColor] = useState(() => getComputedStyle(document.documentElement).getPropertyValue('--popup-background-color').trim()); - const [overlayTextColor, setOverlayTextColor] = useState(() => getComputedStyle(document.documentElement).getPropertyValue('--overlay-text-color').trim()); - - - // Theme selection - const [selectedTheme, setSelectedTheme] = useState(''); - - useEffect(() => { - const savedTheme = localStorage.getItem('selectedTheme'); - if (savedTheme) { - setSelectedTheme(savedTheme); - applyTheme(savedTheme); - } - }, []); // Runs only once when the component mounts - - // API Keys - const [mistral, setmistral] = useState(() => getComputedStyle(document.documentElement).getPropertyValue('--online-la-plateforme').trim()); - const [openai, setopenai] = useState(() => getComputedStyle(document.documentElement).getPropertyValue('--online-cheap-openai').trim()); - const [anthropic, setAnthropic] = useState(() => getComputedStyle(document.documentElement).getPropertyValue('--online-cheap-anthropic').trim()); - const [google, setGoogle] = useState(() => getComputedStyle(document.documentElement).getPropertyValue('--online-cheap-google').trim()); - - const [isLoggedIn, setIsLoggedIn] = useState(false); - - const handleLogout = () => { - setIsLoggedIn(false); - localStorage.removeItem('accountName'); - localStorage.removeItem('accountEmail'); - localStorage.removeItem('accountPassword'); - }; - - const settings = { - userPreferences: { - activeSection, - preferredLanguage, - preferredCurrency, - dateFormat, - timeFormat, - timeZone, - selectedOption, - disableChatHistory, - disableAIMemory, - openSourceMode, - newName, - newEmail, - newPassword, - preferredMeasurement, - }, - theme: { - backgroundColor, - textColor, - inputBackgroundColor, - inputButtonColor, - inputButtonHoverColor, - userMessageBackgroundColor, - userMessageTextColor, - aiMessageBackgroundColor, - aiMessageTextColor, - buttonBackgroundColor, - buttonHoverBackgroundColor, - modelsBackgroundColor, - historyBackgroundColor, - leftPanelBackgroundColor, - conversationBackgroundColor, - popUpTextColor, - inputBorderColor, - fontFamily, - fontSize, - selectedTheme, - faqSettings: { - faqBackgroundColor, - faqHeadingColor, - faqItemBackgroundColor, - faqItemHeadingColor, - faqItemTextColor, - faqItemHoverBackgroundColor, - }, - popupSettings: { - popupBackgroundColor, - overlayTextColor, - }, - }, - apiKeys: { - mistral, - openai, - anthropic, - google, - }, - generalSettings: { - burgerMenu, - }, - }; - - // Effect hooks to update localStorage whenever any state changes - useEffect(() => { - // Flatten nested objects - const flattenedSettings = { - ...settings.userPreferences, - ...settings.theme, - ...settings.theme.faqSettings, - ...settings.theme.popupSettings, - ...settings.apiKeys, - ...settings.generalSettings, - }; - // Update localStorage for all settings - for (const [key, value] of Object.entries(flattenedSettings)) { - localStorage.setItem(key, typeof value === 'boolean' ? JSON.stringify(value) : value); - } - }, [ - ...Object.values(settings.userPreferences), - ...Object.values(settings.theme), - ...Object.values(settings.theme.faqSettings), - ...Object.values(settings.theme.popupSettings), - ...Object.values(settings.apiKeys), - ...Object.values(settings.generalSettings), - ]); - - useEffect(() => { - const savedOption = localStorage.getItem('radioSelection'); - if (savedOption) { - setSelectedOption(savedOption); // Set saved selection - } - }, []); - - const handleRadioChange = (newValue: string) => { - setSelectedOption(newValue); // Update the state with the selected option - if (openSourceMode){ - newValue += " (FOSS)" - } - localStorage.setItem('radioSelection', newValue); // Save the selection for persistence - }; - - // Function to handle updating all credentials - const handleUpdateCredentials = async () => { - // Update account information - const newData = { - name: newName || accountName, // Keep old name if new name is not provided - email: newEmail || '', // Optionally use empty string if not provided - }; - - // First change the data - const dataSuccess = await changeData(accountName, currentPassword, newData); - - // Then change the password if a new password is provided - const passwordSuccess = newPassword ? - await changePassword(accountName, currentPassword, newPassword) : - true; // If no new password, treat as success - - if (dataSuccess && passwordSuccess) { - alert('Credentials updated successfully!'); - closeSettings(); // Close settings after updating - } else { - alert('Failed to update credentials. Please check your current password.'); - } - }; - - // Function to handle account deletion - const handleDeleteAccount = async () => { - const success = await deleteAccount(accountName, currentPassword); - if (success) { - alert('Account deleted successfully!'); - closeSettings(); // Close settings after deletion - // Optionally, redirect or reset state here - } else { - alert('Account deletion failed. Please check your password.'); - } - }; - - - // Render settings content based on the active section - const renderSettingsContent = () => { - switch (activeSection) { - case 'general': - return ( -
-

General Settings

- -
- - -
- -
- - -
- -
- - -
- -
- - -
- -
- - -
- - {/* New Preferred Measurement Option */} -
- - -
-
- ); - - case 'privacy': - return ( -
-

Privacy Settings

- {/* AI Mode Radio Options */} -
-

Disable Options:

-
- {/* Offline */} -
handleRadioChange('Offline')} // Allow selection only if not in open-source mode - > - Offline tools{openSourceMode ? ' (FOSS)' : ''} -
- - {/* Online */} -
handleRadioChange('Online')} - > - Online tools{openSourceMode ? ' (FOSS)' : ''} -
- - {/* None */} -
handleRadioChange('None')} - > - None{openSourceMode ? ' (FOSS)' : ''} -
-
-
-

- After changing the preferred settings, please reload the website so it can update itself properly. -

-
- - {/* Disable Chat History Checkbox */} -
- -
- - {/* Disable AI Memory Checkbox */} -
- -
-
- ); - - - case 'theme': - return ( -
-

Theme Settings

- - {/* Dropdown to select theme */} -
-

Select Theme

- -
- {/* Conditionally render theme settings only if "CUSTOM" is selected */} - {selectedTheme === 'CUSTOM' && ( - <> - {/* Font Size */} -
-

Font Size

- { - const newSize = `${e.target.value}px`; - setFontSize(newSize); - document.documentElement.style.setProperty('--font-size', newSize); - }} - /> - {fontSize} -
- - {/* Background Color */} -
-

Background Color

- { - const newColor = e.target.value; - setBackgroundColor(newColor); - document.documentElement.style.setProperty('--background-color', newColor); - }} - /> -
- - {/* Text Color */} -
-

Text Color

- { - const newColor = e.target.value; - setTextColor(newColor); - document.documentElement.style.setProperty('--text-color', newColor); - }} - /> -
- - {/* Input Background Color */} -
-

Input Background Color

- { - const newColor = e.target.value; - setInputBackgroundColor(newColor); - document.documentElement.style.setProperty('--input-background-color', newColor); - }} - /> -
- - {/* Input Button Color */} -
-

Input Button Color

- { - const newColor = e.target.value; - setInputButtonColor(newColor); - document.documentElement.style.setProperty('--input-button-color', newColor); - }} - /> -
- - {/* Input Button Hover Color */} -
-

Input Button Hover Color

- { - const newColor = e.target.value; - setInputButtonHoverColor(newColor); - document.documentElement.style.setProperty('--input-button-hover-color', newColor); - }} - /> -
- - {/* User Message Background Color */} -
-

User Message Background Color

- { - const newColor = e.target.value; - setUserMessageBackgroundColor(newColor); - document.documentElement.style.setProperty('--user-message-background-color', newColor); - }} - /> -
- - {/* User Message Text Color */} -
-

User Message Text Color

- { - const newColor = e.target.value; - setUserMessageTextColor(newColor); - document.documentElement.style.setProperty('--user-message-text-color', newColor); - }} - /> -
- - {/* AI Message Background Color */} -
-

AI Message Background Color

- { - const newColor = e.target.value; - setAiMessageBackgroundColor(newColor); - document.documentElement.style.setProperty('--ai-message-background-color', newColor); - }} - /> -
- - {/* AI Message Text Color */} -
-

AI Message Text Color

- { - const newColor = e.target.value; - setAiMessageTextColor(newColor); - document.documentElement.style.setProperty('--ai-message-text-color', newColor); - }} - /> -
- - {/* Button Background Color */} -
-

Button Background Color

- { - const newColor = e.target.value; - setButtonBackgroundColor(newColor); - document.documentElement.style.setProperty('--button-background-color', newColor); - }} - /> -
- - {/* Button Hover Background Color */} -
-

Button Hover Background Color

- { - const newColor = e.target.value; - setButtonHoverBackgroundColor(newColor); - document.documentElement.style.setProperty('--button-hover-background-color', newColor); - }} - /> -
- - {/* Models Background Color */} -
-

Models Background Color

- { - const newColor = e.target.value; - setModelsBackgroundColor(newColor); - document.documentElement.style.setProperty('--models-background-color', newColor); - }} - /> -
- - {/* History Background Color */} -
-

History Background Color

- { - const newColor = e.target.value; - setHistoryBackgroundColor(newColor); - document.documentElement.style.setProperty('--history-background-color', newColor); - }} - /> -
- - {/* Left Panel Background Color */} -
-

Left Panel Background Color

- { - const newColor = e.target.value; - setLeftPanelBackgroundColor(newColor); - document.documentElement.style.setProperty('--left-panel-background-color', newColor); - }} - /> -
- - {/* Conversation Background Color */} -
-

Conversation Background Color

- { - const newColor = e.target.value; - setConversationBackgroundColor(newColor); - document.documentElement.style.setProperty('--conversation-background-color', newColor); - }} - /> -
- - {/* Pop-up Text Color */} -
-

Pop-up Text Color

- { - const newColor = e.target.value; - setPopUpTextColor(newColor); - document.documentElement.style.setProperty('--pop-up-text', newColor); - }} - /> -
- - {/* Input Border Color */} -
-

Input Border Color

- { - const newColor = e.target.value; - setInputBorderColor(newColor); - document.documentElement.style.setProperty('--input-border-color', newColor); - }} - /> -
- - {/* Font Family */} -
-

Font Family

- -
- - {/* FAQ Background Color */} -
-

FAQ Background Color

- { - const newColor = e.target.value; - setFaqBackgroundColor(newColor); - document.documentElement.style.setProperty('--faq-background-color', newColor); - }} - /> -
- - {/* FAQ Heading Color */} -
-

FAQ Heading Color

- { - const newColor = e.target.value; - setFaqHeadingColor(newColor); - document.documentElement.style.setProperty('--faq-heading-color', newColor); - }} - /> -
- - {/* FAQ Item Background Color */} -
-

FAQ Item Background Color

- { - const newColor = e.target.value; - setFaqItemBackgroundColor(newColor); - document.documentElement.style.setProperty('--faq-item-background-color', newColor); - }} - /> -
- - {/* FAQ Item Heading Color */} -
-

FAQ Item Heading Color

- { - const newColor = e.target.value; - setFaqItemHeadingColor(newColor); - document.documentElement.style.setProperty('--faq-item-heading-color', newColor); - }} - /> -
- - {/* FAQ Item Text Color */} -
-

FAQ Item Text Color

- { - const newColor = e.target.value; - setFaqItemTextColor(newColor); - document.documentElement.style.setProperty('--faq-item-text-color', newColor); - }} - /> -
- - {/* FAQ Item Hover Background Color */} -
-

FAQ Item Hover Background Color

- { - const newColor = e.target.value; - setFaqItemHoverBackgroundColor(newColor); - document.documentElement.style.setProperty('--faq-item-hover-background-color', newColor); - }} - /> -
- - {/* Popup Background Color */} -
-

Popup Background Color

- { - const newColor = e.target.value; - setPopupBackgroundColor(newColor); - document.documentElement.style.setProperty('--popup-background-color', newColor); - }} - /> -
- - {/* Overlay Text Color */} -
-

Overlay Text Color

- { - const newColor = e.target.value; - setOverlayTextColor(newColor); - document.documentElement.style.setProperty('--overlay-text-color', newColor); - }} - /> -
- - )} -
- ); - - - case 'foss': - return ( -
-

Open Source Settings

-
- -
-
- ); - - - case 'account': - return ( -
-

Account Settings

-
- - setNewName(e.target.value)} - /> -
-
- - setNewEmail(e.target.value)} - /> -
-
- - setNewPassword(e.target.value)} - /> -
-
- - setCurrentPassword(e.target.value)} - /> -
-
- -
-
- -
-
- ); - - case 'api': - return ( -
-
- - setmistral(e.target.value)} - /> -
-
- - setopenai(e.target.value)} - /> -
-
- - setAnthropic(e.target.value)} - /> -
-
- - setGoogle(e.target.value)} - /> -
-
- ); - - case 'im/export': - return ( -
-

Import & Export

-
-

Export the settings

- -
-
-

Import the settings

- -
-
- ); - - default: - return null; - } - }; - - // Handle file import - const handleImport = (event: React.ChangeEvent) => { - if (event.target.files && event.target.files.length > 0) { - const file = event.target.files[0]; - importSettings(file, applyCustomTheme); - } - }; - - // Gather all settings into an object - // Gather current settings into an object - const currentSettings = { - ...settings.userPreferences, - ...settings.theme, - ...settings.theme.faqSettings, - ...settings.theme.popupSettings, - ...settings.apiKeys, - ...settings.generalSettings, - }; - - return ( -
-
-
-
-
    -
  • setActiveSection('general')}>General
  • -
  • setActiveSection('privacy')}>Privacy
  • -
  • setActiveSection('theme')}>Theme
  • -
  • setActiveSection('foss')}>FOSS
  • -
  • setActiveSection('account')}>Account
  • -
  • setActiveSection('api')}>API Keys
  • -
  • setActiveSection('im/export')}>Import/Export
  • -
-
-
-

Settings for {accountName}

- {renderSettingsContent()} - - -
-
-
-
- ); -}; - -export default Settings; diff --git a/app/components/settings/ColorSettings.tsx b/app/components/settings/ColorSettings.tsx new file mode 100644 index 0000000..42b69f8 --- /dev/null +++ b/app/components/settings/ColorSettings.tsx @@ -0,0 +1,29 @@ +import React from 'react'; + +interface ColorSettingProps { + name: string; // The name to display in the

tag + value: string; // The current color value + setValue: (newColor: string) => void; // The method to update the state + cssVariable: string; // The CSS variable name to set +} + +const ColorSetting: React.FC = ({ name, value, setValue, cssVariable }) => { + const handleColorChange = (e: React.ChangeEvent) => { + const newColor = e.target.value; + setValue(newColor); + document.documentElement.style.setProperty(cssVariable, newColor); + }; + + return ( +

+

{name}

+ +
+ ); +}; + +export default ColorSetting; diff --git a/app/components/settings/Settings.tsx b/app/components/settings/Settings.tsx new file mode 100644 index 0000000..feb5558 --- /dev/null +++ b/app/components/settings/Settings.tsx @@ -0,0 +1,866 @@ +import React, { useState, useEffect } from 'react'; +import { applyTheme, applyCustomTheme } from './theme'; +import { exportSettings, importSettings } from './settingUtils'; // Import utility functions +import { getAllLocalStorageItems } from '../../backend/GetLocalStorage'; +import ColorSetting from './ColorSettings'; +import { + changePassword, + changeData, + deleteAccount, +} from '../../backend/database'; + + +const Settings: React.FC<{ closeSettings: () => void; accountName: string }> = ({ closeSettings, accountName }) => { + + const getItemFromLocalStorage = (key: string) => { + const item = localStorage.getItem(key); + + if (item) { + try { + return JSON.parse(item); // Attempt to parse the item + } catch (e) { + console.error(`Error parsing JSON for key "${key}":`, e); + return false; // Return false if parsing fails + } + } + + return false; // Default to false if item is null or empty + }; + + interface SettingsProps { + closeSettings: () => void; + accountName: string; + handleLogout: () => void; // Add this line to accept handleLogout as a prop + } + + // Active section + const [activeSection, setActiveSection] = useState(() => localStorage.getItem('activeSection') || 'general'); + + // Language setting + const [preferredLanguage, setPreferredLanguage] = useState(() => localStorage.getItem('preferredLanguage') || 'en'); + + // Currency setting + const [preferredCurrency, setPreferredCurrency] = useState(() => localStorage.getItem('preferredCurrency') || 'usd'); + + // Date and time format settings + const [dateFormat, setDateFormat] = useState(() => localStorage.getItem('dateFormat') || 'mm/dd/yyyy'); + const [timeFormat, setTimeFormat] = useState(() => localStorage.getItem('timeFormat') || '12-hour'); + const [timeZone, setTimeZone] = useState(() => localStorage.getItem('timeZone') || 'GMT'); + + // Online AI and chat history settings + const [selectedOption, setSelectedOption] = useState('Offline'); // Default to 'Offline' + const [disableChatHistory, setDisableChatHistory] = useState(() => getItemFromLocalStorage('disableChatHistory')); + const [disableAIMemory, setDisableAIMemory] = useState(() => getItemFromLocalStorage('disableAIMemory')); + const [openSourceMode, setOpenSourceMode] = useState(() => getItemFromLocalStorage('openSourceMode')); + + // User credentials + const [newName, setNewName] = useState(() => localStorage.getItem('newName') || ''); + const [newEmail, setNewEmail] = useState(() => localStorage.getItem('newEmail') || ''); + const [newPassword, setNewPassword] = useState(() => localStorage.getItem('newPassword') || ''); + const [currentPassword, setCurrentPassword] = useState(''); + + // Measurement setting + const [preferredMeasurement, setPreferredMeasurement] = useState(() => localStorage.getItem('preferredMeasurement') || 'Metric'); + + // Theme settings + const [backgroundColor, setBackgroundColor] = useState(() => getComputedStyle(document.documentElement).getPropertyValue('--background-color').trim()); + const [textColor, setTextColor] = useState(() => getComputedStyle(document.documentElement).getPropertyValue('--text-color').trim()); + const [inputBackgroundColor, setInputBackgroundColor] = useState(() => getComputedStyle(document.documentElement).getPropertyValue('--input-background-color').trim()); + const [inputButtonColor, setInputButtonColor] = useState(() => getComputedStyle(document.documentElement).getPropertyValue('--input-button-color').trim()); + const [inputButtonHoverColor, setInputButtonHoverColor] = useState(() => getComputedStyle(document.documentElement).getPropertyValue('--input-button-hover-color').trim()); + const [userMessageBackgroundColor, setUserMessageBackgroundColor] = useState(() => getComputedStyle(document.documentElement).getPropertyValue('--user-message-background-color').trim()); + const [userMessageTextColor, setUserMessageTextColor] = useState(() => getComputedStyle(document.documentElement).getPropertyValue('--user-message-text-color').trim()); + const [aiMessageBackgroundColor, setAiMessageBackgroundColor] = useState(() => getComputedStyle(document.documentElement).getPropertyValue('--ai-message-background-color').trim()); + const [aiMessageTextColor, setAiMessageTextColor] = useState(() => getComputedStyle(document.documentElement).getPropertyValue('--ai-message-text-color').trim()); + const [buttonBackgroundColor, setButtonBackgroundColor] = useState(() => getComputedStyle(document.documentElement).getPropertyValue('--button-background-color').trim()); + const [buttonHoverBackgroundColor, setButtonHoverBackgroundColor] = useState(() => getComputedStyle(document.documentElement).getPropertyValue('--button-hover-background-color').trim()); + const [modelsBackgroundColor, setModelsBackgroundColor] = useState(() => getComputedStyle(document.documentElement).getPropertyValue('--models-background-color').trim()); + const [historyBackgroundColor, setHistoryBackgroundColor] = useState(() => getComputedStyle(document.documentElement).getPropertyValue('--history-background-color').trim()); + const [leftPanelBackgroundColor, setLeftPanelBackgroundColor] = useState(() => getComputedStyle(document.documentElement).getPropertyValue('--left-panel-background-color').trim()); + const [conversationBackgroundColor, setConversationBackgroundColor] = useState(() => getComputedStyle(document.documentElement).getPropertyValue('--conversation-background-color').trim()); + const [popUpTextColor, setPopUpTextColor] = useState(() => getComputedStyle(document.documentElement).getPropertyValue('--pop-up-text').trim()); + const [inputBorderColor, setInputBorderColor] = useState(() => getComputedStyle(document.documentElement).getPropertyValue('--input-border-color').trim()); + const [fontFamily, setFontFamily] = useState(() => getComputedStyle(document.documentElement).getPropertyValue('--font-family').trim()); + const [fontSize, setFontSize] = useState(() => getComputedStyle(document.documentElement).getPropertyValue('--font-size').trim()); + const [burgerMenu, setBurgerMenu] = useState(() => getComputedStyle(document.documentElement).getPropertyValue('--burger-menu-background-color').trim()); + const [faqBackgroundColor, setFaqBackgroundColor] = useState(() => getComputedStyle(document.documentElement).getPropertyValue('--faq-background-color').trim()); + const [faqHeadingColor, setFaqHeadingColor] = useState(() => getComputedStyle(document.documentElement).getPropertyValue('--faq-heading-color').trim()); + const [faqItemBackgroundColor, setFaqItemBackgroundColor] = useState(() => getComputedStyle(document.documentElement).getPropertyValue('--faq-item-background-color').trim()); + const [faqItemHeadingColor, setFaqItemHeadingColor] = useState(() => getComputedStyle(document.documentElement).getPropertyValue('--faq-item-heading-color').trim()); + const [faqItemTextColor, setFaqItemTextColor] = useState(() => getComputedStyle(document.documentElement).getPropertyValue('--faq-item-text-color').trim()); + const [faqItemHoverBackgroundColor, setFaqItemHoverBackgroundColor] = useState(() => getComputedStyle(document.documentElement).getPropertyValue('--faq-item-hover-background-color').trim()); + const [popupBackgroundColor, setPopupBackgroundColor] = useState(() => getComputedStyle(document.documentElement).getPropertyValue('--popup-background-color').trim()); + const [overlayTextColor, setOverlayTextColor] = useState(() => getComputedStyle(document.documentElement).getPropertyValue('--overlay-text-color').trim()); + + + // Theme selection + const [selectedTheme, setSelectedTheme] = useState(''); + + // API Keys + const [mistral, setmistral] = useState(() => getComputedStyle(document.documentElement).getPropertyValue('--online-la-plateforme').trim()); + const [openai, setopenai] = useState(() => getComputedStyle(document.documentElement).getPropertyValue('--online-cheap-openai').trim()); + const [anthropic, setAnthropic] = useState(() => getComputedStyle(document.documentElement).getPropertyValue('--online-cheap-anthropic').trim()); + const [google, setGoogle] = useState(() => getComputedStyle(document.documentElement).getPropertyValue('--online-cheap-google').trim()); + + const [isLoggedIn, setIsLoggedIn] = useState(false); + + const settings = { + userPreferences: { + activeSection, + preferredLanguage, + preferredCurrency, + dateFormat, + timeFormat, + timeZone, + selectedOption, + disableChatHistory, + disableAIMemory, + openSourceMode, + newName, + newEmail, + newPassword, + preferredMeasurement, + }, + theme: { + backgroundColor, + textColor, + inputBackgroundColor, + inputButtonColor, + inputButtonHoverColor, + userMessageBackgroundColor, + userMessageTextColor, + aiMessageBackgroundColor, + aiMessageTextColor, + buttonBackgroundColor, + buttonHoverBackgroundColor, + modelsBackgroundColor, + historyBackgroundColor, + leftPanelBackgroundColor, + conversationBackgroundColor, + popUpTextColor, + inputBorderColor, + fontFamily, + fontSize, + selectedTheme, + faqSettings: { + faqBackgroundColor, + faqHeadingColor, + faqItemBackgroundColor, + faqItemHeadingColor, + faqItemTextColor, + faqItemHoverBackgroundColor, + }, + popupSettings: { + popupBackgroundColor, + overlayTextColor, + }, + }, + apiKeys: { + mistral, + openai, + anthropic, + google, + }, + generalSettings: { + burgerMenu, + }, + }; + + const handleLogout = () => { + setIsLoggedIn(false); + localStorage.removeItem('accountName'); + localStorage.removeItem('accountEmail'); + localStorage.removeItem('accountPassword'); + }; + + useEffect(() => { + const savedTheme = localStorage.getItem('selectedTheme'); + if (savedTheme) { + setSelectedTheme(savedTheme); + applyTheme(savedTheme); + } + }, []); // Runs only once when the component mounts + + // Effect hooks to update localStorage whenever any state changes + useEffect(() => { + // Flatten nested objects + const flattenedSettings = { + ...settings.userPreferences, + ...settings.theme, + ...settings.theme.faqSettings, + ...settings.theme.popupSettings, + ...settings.apiKeys, + ...settings.generalSettings, + }; + // Update localStorage for all settings + for (const [key, value] of Object.entries(flattenedSettings)) { + localStorage.setItem(key, typeof value === 'boolean' ? JSON.stringify(value) : value); + } + }, [ + ...Object.values(settings.userPreferences), + ...Object.values(settings.theme), + ...Object.values(settings.theme.faqSettings), + ...Object.values(settings.theme.popupSettings), + ...Object.values(settings.apiKeys), + ...Object.values(settings.generalSettings), + ]); + + useEffect(() => { + const savedOption = localStorage.getItem('radioSelection'); + if (savedOption) { + setSelectedOption(savedOption); // Set saved selection + } + }, []); + + const handleRadioChange = (newValue: string) => { + setSelectedOption(newValue); // Update the state with the selected option + if (openSourceMode){ + newValue += " (FOSS)" + } + localStorage.setItem('radioSelection', newValue); // Save the selection for persistence + }; + + // Function to handle updating all credentials + const handleUpdateCredentials = async () => { + // Update account information + const newData = { + name: newName || accountName, // Keep old name if new name is not provided + email: newEmail || '', // Optionally use empty string if not provided + }; + + // First change the data + const dataSuccess = await changeData(accountName, currentPassword, newData); + + // Then change the password if a new password is provided + const passwordSuccess = newPassword ? + await changePassword(accountName, currentPassword, newPassword) : + true; // If no new password, treat as success + + if (dataSuccess && passwordSuccess) { + alert('Credentials updated successfully!'); + closeSettings(); // Close settings after updating + } else { + alert('Failed to update credentials. Please check your current password.'); + } + }; + + // Function to handle account deletion + const handleDeleteAccount = async () => { + const success = await deleteAccount(accountName, currentPassword); + if (success) { + alert('Account deleted successfully!'); + closeSettings(); // Close settings after deletion + // Optionally, redirect or reset state here + } else { + alert('Account deletion failed. Please check your password.'); + } + }; + + + // Render settings content based on the active section + const renderSettingsContent = () => { + switch (activeSection) { + case 'general': + return ( +
+

General Settings

+ +
+ + +
+ +
+ + +
+ +
+ + +
+ +
+ + +
+ +
+ + +
+ + {/* New Preferred Measurement Option */} +
+ + +
+
+ ); + + case 'privacy': + return ( +
+

Privacy Settings

+ {/* AI Mode Radio Options */} +
+

Disable Options:

+
+ {/* Offline */} +
handleRadioChange('Offline')} // Allow selection only if not in open-source mode + > + Offline tools{openSourceMode ? ' (FOSS)' : ''} +
+ + {/* Online */} +
handleRadioChange('Online')} + > + Online tools{openSourceMode ? ' (FOSS)' : ''} +
+ + {/* None */} +
handleRadioChange('None')} + > + None{openSourceMode ? ' (FOSS)' : ''} +
+
+
+

+ After changing the preferred settings, please reload the website so it can update itself properly. +

+
+ + {/* Disable Chat History Checkbox */} +
+ +
+ + {/* Disable AI Memory Checkbox */} +
+ +
+
+ ); + + + case 'theme': + return ( +
+

Theme Settings

+ + {/* Dropdown to select theme */} +
+

Select Theme

+ +
+ {/* Conditionally render theme settings only if "CUSTOM" is selected */} + {selectedTheme === 'CUSTOM' && ( + <> + {/* Font Size */} +
+

Font Size

+ { + const newSize = `${e.target.value}px`; + setFontSize(newSize); + document.documentElement.style.setProperty('--font-size', newSize); + }} + /> + {fontSize} +
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+

Font Family

+ +
+ + )} +
+ ); + + + case 'foss': + return ( +
+

Open Source Settings

+
+ +
+
+ ); + + + case 'account': + return ( +
+

Account Settings

+
+ + setNewName(e.target.value)} + /> +
+
+ + setNewEmail(e.target.value)} + /> +
+
+ + setNewPassword(e.target.value)} + /> +
+
+ + setCurrentPassword(e.target.value)} + /> +
+
+ +
+
+ +
+
+ +
+
+ ); + + case 'api': + return ( +
+
+ + setmistral(e.target.value)} + /> +
+
+ + setopenai(e.target.value)} + /> +
+
+ + setAnthropic(e.target.value)} + /> +
+
+ + setGoogle(e.target.value)} + /> +
+
+ ); + + case 'im/export': + return ( +
+

Import & Export

+
+

Export the settings

+ +
+
+

Import the settings

+ +
+
+ ); + + default: + return null; + } + }; + + // Handle file import + const handleImport = (event: React.ChangeEvent) => { + if (event.target.files && event.target.files.length > 0) { + const file = event.target.files[0]; + importSettings(file, applyCustomTheme); + } + }; + + // Gather all settings into an object + // Gather current settings into an object + const currentSettings = { + ...settings.userPreferences, + ...settings.theme, + ...settings.theme.faqSettings, + ...settings.theme.popupSettings, + ...settings.apiKeys, + ...settings.generalSettings, + }; + + return ( +
+
+
+
+
    +
  • setActiveSection('general')}>General
  • +
  • setActiveSection('privacy')}>Privacy
  • +
  • setActiveSection('theme')}>Theme
  • +
  • setActiveSection('foss')}>FOSS
  • +
  • setActiveSection('account')}>Account
  • +
  • setActiveSection('api')}>API Keys
  • +
  • setActiveSection('im/export')}>Import/Export
  • +
+
+
+

Settings for {accountName}

+ {renderSettingsContent()} + + +
+
+
+
+ ); +}; + +export default Settings; diff --git a/app/components/settingUtils.ts b/app/components/settings/settingUtils.ts similarity index 100% rename from app/components/settingUtils.ts rename to app/components/settings/settingUtils.ts diff --git a/app/components/theme.ts b/app/components/settings/theme.ts similarity index 100% rename from app/components/theme.ts rename to app/components/settings/theme.ts diff --git a/app/page.tsx b/app/page.tsx index b2e6c90..cca5ac1 100644 --- a/app/page.tsx +++ b/app/page.tsx @@ -7,8 +7,8 @@ import Documentation from './components/Documentation'; // Ensure the import pat import History from './components/History'; import Models from './components/Models'; import Credits from './components/Credits'; -import Settings from './components/Settings'; -import { applyIOMarketTheme, applyWhiteTheme, applyBlackTheme, applyCustomTheme } from './components/theme' +import Settings from './components/settings/Settings'; +import { applyIOMarketTheme, applyWhiteTheme, applyBlackTheme, applyCustomTheme } from './components/settings/theme' import Head from 'next/head'; import './styles/master.css';