Path: blob/master/src/packages/frontend/account/settings-index.tsx
5961 views
/*1* This file is part of CoCalc: Copyright © 2025 Sagemath, Inc.2* License: MS-RSL – see LICENSE.md for details3*/45// cSpell:ignore payg67import { Card, Divider, Flex } from "antd";8import { defineMessages, useIntl } from "react-intl";910import { redux, useTypedRedux } from "@cocalc/frontend/app-framework";11import { Icon } from "@cocalc/frontend/components";12import AIAvatar from "@cocalc/frontend/components/ai-avatar";13import { cloudFilesystemsEnabled } from "@cocalc/frontend/compute";14import { labels } from "@cocalc/frontend/i18n";15import { KUCALC_COCALC_COM } from "@cocalc/util/db-schema/site-defaults";16import { COLORS } from "@cocalc/util/theme";17import {18VALID_PREFERENCES_SUB_TYPES,19type NavigatePath,20type PreferencesSubTabType,21} from "@cocalc/util/types/settings";22import { APPEARANCE_ICON_NAME } from "./account-preferences-appearance";23import { COMMUNICATION_ICON_NAME } from "./account-preferences-communication";24import { EDITOR_ICON_NAME } from "./account-preferences-editor";25import { KEYBOARD_ICON_NAME } from "./account-preferences-keyboard";26import { OTHER_ICON_NAME } from "./account-preferences-other";27import { ACCOUNT_PROFILE_ICON_NAME } from "./account-preferences-profile";28import { KEYS_ICON_NAME } from "./account-preferences-security";2930const MESSAGES = defineMessages({31title: {32id: "account.settings.overview.title",33defaultMessage: "Settings Overview",34},35profile: {36id: "account.settings.overview.profile",37defaultMessage:38"Manage your personal information, avatar, and account details.",39},40appearance: {41id: "account.settings.overview.appearance",42defaultMessage:43"Customize the visual experience via color themes, {dark_mode}, language, and visual settings.",44},45editor: {46id: "account.settings.overview.editor",47defaultMessage:48"Customize code editor behavior, indentation, and content options.",49},50keyboard: {51id: "account.settings.overview.keyboard",52defaultMessage: "Keyboard shortcuts.",53},54ai: {55id: "account.settings.overview.ai",56defaultMessage: "Configure AI assistant settings and integrations.",57},58communication: {59id: "account.settings.overview.communication",60defaultMessage: "Notification preferences and communication settings.",61},62keys: {63id: "account.settings.overview.keys",64defaultMessage: "Manage API keys and setup SSH keys.",65},66other: {67id: "account.settings.overview.other",68defaultMessage: "Miscellaneous settings and options.",69},70billing: {71id: "account.settings.overview.billing",72defaultMessage: "Manage subscriptions, licenses, and billing information.",73},74subscriptions: {75id: "account.settings.overview.subscriptions",76defaultMessage: "View and manage your active subscriptions.",77},78licenses: {79id: "account.settings.overview.licenses",80defaultMessage: "Manage software licenses and access permissions.",81},82payg: {83id: "account.settings.overview.payg",84defaultMessage: "Configure pay-as-you-go usage and billing.",85},86upgrades: {87id: "account.settings.overview.upgrades",88defaultMessage: "Manage your legacy quota upgrades.",89},90purchases: {91id: "account.settings.overview.purchases",92defaultMessage: "View purchase history and receipts.",93},94payments: {95id: "account.settings.overview.payments",96defaultMessage: "Manage payment methods and transaction history.",97},98paymentMethods: {99id: "account.settings.overview.payment_methods",100defaultMessage: "Manage your saved payment methods or add new ones.",101},102statements: {103id: "account.settings.overview.statements",104defaultMessage: "View detailed billing statements and invoices.",105},106files: {107id: "account.settings.overview.files",108defaultMessage: "Manage published files and public sharing.",109},110cloud: {111id: "account.settings.overview.cloud",112defaultMessage: "Manage cloud file system storage.",113},114support: {115id: "account.settings.overview.support",116defaultMessage: "Access support tickets and help resources.",117},118});119120const CARD_PROPS = {121size: "small" as const,122hoverable: true,123style: { width: 300, minWidth: 250 },124} as const;125126// TODO: highlighting a few cards is a good idea, but better to do this later...127const HIGHLIGHTED_CARD_PROPS = CARD_PROPS; // = {128// ...CARD_PROPS,129// style: {130// ...CARD_PROPS.style,131// border: `2px solid ${COLORS.BLUE_LLL}`,132// },133// } as const;134135const FLEX_PROPS = {136wrap: true as const,137gap: "15px",138style: { marginBottom: "40px" },139} as const;140141export function SettingsOverview() {142const intl = useIntl();143const is_commercial = useTypedRedux("customize", "is_commercial");144const kucalc = useTypedRedux("customize", "kucalc");145146function handleNavigate(path: NavigatePath) {147// Use the same navigation pattern as the account page148const segments = path.split("/").filter(Boolean);149if (segments[0] === "settings") {150if (segments[1] === "profile") {151redux.getActions("account").setState({152active_page: "profile",153active_sub_tab: undefined,154});155redux.getActions("account").push_state(`/profile`);156} else if (segments[1] === "preferences" && segments[2]) {157// Handle preferences sub-tabs158const subTab = segments[2] as PreferencesSubTabType;159if (VALID_PREFERENCES_SUB_TYPES.includes(subTab)) {160const subTabKey = `preferences-${subTab}` as const;161redux.getActions("account").setState({162active_page: "preferences",163active_sub_tab: subTabKey,164});165redux.getActions("account").push_state(`/preferences/${subTab}`);166}167} else {168// Handle other settings pages169redux.getActions("account").set_active_tab(segments[1]);170redux.getActions("account").push_state(`/${segments[1]}`);171}172}173}174175return (176<div style={{ padding: "20px" }}>177<Flex {...FLEX_PROPS}>178<Card179{...HIGHLIGHTED_CARD_PROPS}180onClick={() => handleNavigate("settings/profile")}181>182<Card.Meta183avatar={<Icon name={ACCOUNT_PROFILE_ICON_NAME} />}184title={intl.formatMessage(labels.profile)}185description={intl.formatMessage(MESSAGES.profile)}186/>187</Card>188<Card189{...HIGHLIGHTED_CARD_PROPS}190onClick={() => handleNavigate("settings/preferences/appearance")}191>192<Card.Meta193avatar={<Icon name={APPEARANCE_ICON_NAME} />}194title={intl.formatMessage(labels.appearance)}195description={intl.formatMessage(MESSAGES.appearance, {196dark_mode: (197<span198style={{199backgroundColor: COLORS.GRAY_DD,200color: COLORS.GRAY_LLL,201paddingLeft: "3px",202paddingRight: "3px",203}}204>205dark mode206</span>207),208})}209/>210</Card>211<Card212{...CARD_PROPS}213onClick={() => handleNavigate("settings/preferences/editor")}214>215<Card.Meta216avatar={<Icon name={EDITOR_ICON_NAME} />}217title={intl.formatMessage(labels.editor)}218description={intl.formatMessage(MESSAGES.editor)}219/>220</Card>221<Card222{...CARD_PROPS}223onClick={() => handleNavigate("settings/preferences/keyboard")}224>225<Card.Meta226avatar={<Icon name={KEYBOARD_ICON_NAME} />}227title={intl.formatMessage(labels.keyboard)}228description={intl.formatMessage(MESSAGES.keyboard)}229/>230</Card>231<Card232{...CARD_PROPS}233onClick={() => handleNavigate("settings/preferences/ai")}234>235<Card.Meta236avatar={<AIAvatar size={24} />}237title={intl.formatMessage(labels.ai)}238description={intl.formatMessage(MESSAGES.ai)}239/>240</Card>241<Card242{...CARD_PROPS}243onClick={() => handleNavigate("settings/preferences/communication")}244>245<Card.Meta246avatar={<Icon name={COMMUNICATION_ICON_NAME} />}247title={intl.formatMessage(labels.communication)}248description={intl.formatMessage(MESSAGES.communication)}249/>250</Card>251<Card252{...CARD_PROPS}253onClick={() => handleNavigate("settings/preferences/keys")}254>255<Card.Meta256avatar={<Icon name={KEYS_ICON_NAME} />}257title={intl.formatMessage(labels.ssh_and_api_keys)}258description={intl.formatMessage(MESSAGES.keys)}259/>260</Card>261<Card262{...CARD_PROPS}263onClick={() => handleNavigate("settings/preferences/other")}264>265<Card.Meta266avatar={<Icon name={OTHER_ICON_NAME} />}267title={intl.formatMessage(labels.other)}268description={intl.formatMessage(MESSAGES.other)}269/>270</Card>271</Flex>272273{is_commercial && (274<>275<Divider plain>276<Icon name="money-check" /> {intl.formatMessage(labels.billing)}277</Divider>278<Flex {...FLEX_PROPS}>279<Card280{...HIGHLIGHTED_CARD_PROPS}281onClick={() => handleNavigate("settings/subscriptions")}282>283<Card.Meta284avatar={<Icon name="calendar" />}285title={intl.formatMessage(labels.subscriptions)}286description={intl.formatMessage(MESSAGES.subscriptions)}287/>288</Card>289<Card290{...HIGHLIGHTED_CARD_PROPS}291onClick={() => handleNavigate("settings/licenses")}292>293<Card.Meta294avatar={<Icon name="key" />}295title={intl.formatMessage(labels.licenses)}296description={intl.formatMessage(MESSAGES.licenses)}297/>298</Card>299<Card300{...CARD_PROPS}301onClick={() => handleNavigate("settings/payg")}302>303<Card.Meta304avatar={<Icon name="line-chart" />}305title={intl.formatMessage(labels.pay_as_you_go)}306description={intl.formatMessage(MESSAGES.payg)}307/>308</Card>309{kucalc === KUCALC_COCALC_COM && (310<Card311{...CARD_PROPS}312onClick={() => handleNavigate("settings/upgrades")}313>314<Card.Meta315avatar={<Icon name="arrow-circle-up" />}316title={intl.formatMessage(labels.upgrades)}317description={intl.formatMessage(MESSAGES.upgrades)}318/>319</Card>320)}321<Card322{...CARD_PROPS}323onClick={() => handleNavigate("settings/purchases")}324>325<Card.Meta326avatar={<Icon name="money-check" />}327title={intl.formatMessage(labels.purchases)}328description={intl.formatMessage(MESSAGES.purchases)}329/>330</Card>331<Card332{...CARD_PROPS}333onClick={() => handleNavigate("settings/payments")}334>335<Card.Meta336avatar={<Icon name="credit-card" />}337title={intl.formatMessage(labels.payments)}338description={intl.formatMessage(MESSAGES.payments)}339/>340</Card>341<Card342{...CARD_PROPS}343onClick={() => handleNavigate("settings/payment-methods")}344>345<Card.Meta346avatar={<Icon name="credit-card" />}347title={intl.formatMessage(labels.payment_methods)}348description={intl.formatMessage(MESSAGES.paymentMethods)}349/>350</Card>351<Card352{...CARD_PROPS}353onClick={() => handleNavigate("settings/statements")}354>355<Card.Meta356avatar={<Icon name="calendar-week" />}357title={intl.formatMessage(labels.statements)}358description={intl.formatMessage(MESSAGES.statements)}359/>360</Card>361</Flex>362</>363)}364365<Divider plain>366<Icon name="files" /> {intl.formatMessage(labels.files)}367</Divider>368<Flex {...FLEX_PROPS}>369<Card370{...CARD_PROPS}371onClick={() => handleNavigate("settings/public-files")}372>373<Card.Meta374avatar={<Icon name="share-square" />}375title={intl.formatMessage(labels.published_files)}376description={intl.formatMessage(MESSAGES.files)}377/>378</Card>379380{cloudFilesystemsEnabled() && (381<Card382{...CARD_PROPS}383onClick={() => handleNavigate("settings/cloud-filesystems")}384>385<Card.Meta386avatar={<Icon name="server" />}387title={intl.formatMessage(labels.cloud_file_system)}388description={intl.formatMessage(MESSAGES.cloud)}389/>390</Card>391)}392</Flex>393394{is_commercial && (395<>396<Divider plain>397<Icon name="medkit" /> {intl.formatMessage(labels.support)}398</Divider>399<Flex {...FLEX_PROPS}>400<Card401{...CARD_PROPS}402onClick={() => handleNavigate("settings/support")}403>404<Card.Meta405avatar={<Icon name="medkit" />}406title={intl.formatMessage(labels.support)}407description={intl.formatMessage(MESSAGES.support)}408/>409</Card>410</Flex>411</>412)}413</div>414);415}416417418