diff --git a/.changeset/fair-buttons-camp.md b/.changeset/fair-buttons-camp.md new file mode 100644 index 00000000..5acb4e1d --- /dev/null +++ b/.changeset/fair-buttons-camp.md @@ -0,0 +1,5 @@ +--- +'@hyperdx/app': patch +--- + +Add User Preferences modal diff --git a/.vscode/settings.json b/.vscode/settings.json index b101dbb1..9e1325d5 100644 --- a/.vscode/settings.json +++ b/.vscode/settings.json @@ -1,10 +1,10 @@ { "editor.formatOnSave.eslint": true, "[typescript]": { - "editor.defaultFormatter": "esbenp.prettier-vscode" + "editor.defaultFormatter": "dbaeumer.vscode-eslint" }, "[typescriptreact]": { - "editor.defaultFormatter": "esbenp.prettier-vscode" + "editor.defaultFormatter": "dbaeumer.vscode-eslint" }, "cssVariables.lookupFiles": [ "**/*.css", diff --git a/packages/app/pages/_app.tsx b/packages/app/pages/_app.tsx index b8118d68..e8507dfb 100644 --- a/packages/app/pages/_app.tsx +++ b/packages/app/pages/_app.tsx @@ -18,7 +18,7 @@ import { apiConfigs } from '../src/api'; import * as config from '../src/config'; import { useConfirmModal } from '../src/useConfirm'; import { QueryParamProvider as HDXQueryParamProvider } from '../src/useQueryParam'; -import { UserPreferencesProvider } from '../src/useUserPreferences'; +import { useBackground, useUserPreferences } from '../src/useUserPreferences'; import '@mantine/core/styles.css'; import '@mantine/notifications/styles.css'; @@ -29,12 +29,17 @@ import '../src/LandingPage.scss'; const queryClient = new QueryClient(); import HyperDX from '@hyperdx/browser'; -const mantineTheme: MantineThemeOverride = { - fontFamily: 'IBM Plex Mono, sans-serif', +const makeTheme = ({ + fontFamily, +}: { + fontFamily: string; +}): MantineThemeOverride => ({ + fontFamily, primaryColor: 'green', primaryShade: 8, white: '#fff', fontSizes: { + xxs: '11px', xs: '12px', sm: '13px', md: '15px', @@ -68,13 +73,13 @@ const mantineTheme: MantineThemeOverride = { ], }, headings: { - fontFamily: 'IBM Plex Mono, sans-serif', + fontFamily, }, components: { Modal: { styles: { header: { - fontFamily: 'IBM Plex Mono, sans-serif', + fontFamily, fontWeight: 'bold', }, }, @@ -108,7 +113,7 @@ const mantineTheme: MantineThemeOverride = { }, }, }, -}; +}); export type NextPageWithLayout

= NextPage & { getLayout?: (page: React.ReactElement) => React.ReactNode; @@ -119,7 +124,9 @@ type AppPropsWithLayout = AppProps & { }; export default function MyApp({ Component, pageProps }: AppPropsWithLayout) { + const { userPreferences } = useUserPreferences(); const confirmModal = useConfirmModal(); + const background = useBackground(userPreferences); // port to react query ? (needs to wrap with QueryClientProvider) useEffect(() => { @@ -162,6 +169,20 @@ export default function MyApp({ Component, pageProps }: AppPropsWithLayout) { }); }, []); + useEffect(() => { + document.documentElement.className = + userPreferences.theme === 'dark' ? 'hdx-theme-dark' : 'hdx-theme-light'; + // TODO: Remove after migration to Mantine + document.body.style.fontFamily = userPreferences.font + ? `"${userPreferences.font}", sans-serif` + : '"IBM Plex Mono"'; + }, [userPreferences.theme, userPreferences.font]); + + const mantineTheme = React.useMemo( + () => makeTheme({ fontFamily: userPreferences.font }), + [userPreferences.font], + ); + const getLayout = Component.getLayout ?? (page => page); return ( @@ -195,14 +216,13 @@ export default function MyApp({ Component, pageProps }: AppPropsWithLayout) { - - - - {getLayout()} - - - {confirmModal} - + + + {getLayout()} + + + {confirmModal} + {background} diff --git a/packages/app/src/AppNav.tsx b/packages/app/src/AppNav.tsx index c0102dd3..747f6361 100644 --- a/packages/app/src/AppNav.tsx +++ b/packages/app/src/AppNav.tsx @@ -21,6 +21,7 @@ import { Loader, ScrollArea, } from '@mantine/core'; +import { useDisclosure } from '@mantine/hooks'; import { version } from '../package.json'; @@ -31,6 +32,7 @@ import Icon from './Icon'; import Logo from './Logo'; import { KubernetesFlatIcon } from './SVGIcons'; import type { Dashboard, LogView } from './types'; +import { UserPreferencesModal } from './UserPreferencesModal'; import { useLocalStorage, useWindowSize } from './utils'; import styles from '../styles/AppNav.module.scss'; @@ -902,6 +904,11 @@ export default function AppNav({ fixed = false }: { fixed?: boolean }) { [dashboards, refetchDashboards, updateDashboard], ); + const [ + UserPreferencesOpen, + { close: closeUserPreferences, open: openUserPreferences }, + ] = useDisclosure(false); + return ( <> @@ -1348,6 +1355,19 @@ export default function AppNav({ fixed = false }: { fixed?: boolean }) { +

+ + + {' '} + {!isCollapsed && User Preferences} + + +
)} + ); } diff --git a/packages/app/src/ChartPage.tsx b/packages/app/src/ChartPage.tsx index 9860e6ba..ae7ccf04 100644 --- a/packages/app/src/ChartPage.tsx +++ b/packages/app/src/ChartPage.tsx @@ -137,7 +137,6 @@ function GraphPage() { const { isReady, searchedTimeRange, displayedTimeInputValue, onSearch } = useNewTimeQuery({ - isUTC: false, initialDisplayValue: 'Past 1h', initialTimeRange: defaultTimeRange, }); diff --git a/packages/app/src/DBQuerySidePanel.tsx b/packages/app/src/DBQuerySidePanel.tsx index 22e24cdf..b07e1c51 100644 --- a/packages/app/src/DBQuerySidePanel.tsx +++ b/packages/app/src/DBQuerySidePanel.tsx @@ -34,7 +34,6 @@ export default function DBQuerySidePanel() { ); const { searchedTimeRange: dateRange } = useTimeQuery({ - isUTC: false, defaultValue: 'Past 1h', defaultTimeRange: [ defaultTimeRange?.[0]?.getTime() ?? -1, diff --git a/packages/app/src/DashboardPage.tsx b/packages/app/src/DashboardPage.tsx index 3bf8c8cc..5e92e24a 100644 --- a/packages/app/src/DashboardPage.tsx +++ b/packages/app/src/DashboardPage.tsx @@ -351,8 +351,6 @@ const Tile = forwardRef( {}} onPropertySearchClick={() => {}} onSettled={onSettled} /> @@ -692,7 +690,6 @@ export default function DashboardPage() { const { searchedTimeRange, displayedTimeInputValue, onSearch } = useNewTimeQuery({ - isUTC: false, initialDisplayValue: 'Past 1h', initialTimeRange: defaultTimeRange, }); diff --git a/packages/app/src/EditChartForm.tsx b/packages/app/src/EditChartForm.tsx index 8152bb88..ee0217ba 100644 --- a/packages/app/src/EditChartForm.tsx +++ b/packages/app/src/EditChartForm.tsx @@ -329,8 +329,6 @@ export const EditSearchChartForm = ({ where: previewConfig.where, }} isLive={false} - isUTC={false} - setIsUTC={() => {}} onPropertySearchClick={() => {}} />
@@ -486,8 +484,6 @@ export const EditNumberChartForm = ({ }`, }} isLive={false} - isUTC={false} - setIsUTC={() => {}} onPropertySearchClick={() => {}} /> @@ -691,8 +687,6 @@ export const EditTableChartForm = ({ }`, }} isLive={false} - isUTC={false} - setIsUTC={() => {}} onPropertySearchClick={() => {}} /> @@ -887,8 +881,6 @@ export const EditHistogramChartForm = ({ }`, }} isLive={false} - isUTC={false} - setIsUTC={() => {}} onPropertySearchClick={() => {}} /> @@ -1426,8 +1418,6 @@ export const EditLineChartForm = ({ })}`, }} isLive={false} - isUTC={false} - setIsUTC={() => {}} onPropertySearchClick={() => {}} /> diff --git a/packages/app/src/EndpointSidePanel.tsx b/packages/app/src/EndpointSidePanel.tsx index c8544e91..9434390a 100644 --- a/packages/app/src/EndpointSidePanel.tsx +++ b/packages/app/src/EndpointSidePanel.tsx @@ -34,7 +34,6 @@ export default function EndpointSidePanel() { ); const { searchedTimeRange: dateRange } = useTimeQuery({ - isUTC: false, defaultValue: 'Past 1h', defaultTimeRange: [ defaultTimeRange?.[0]?.getTime() ?? -1, diff --git a/packages/app/src/HDXMultiSeriesTimeChart.tsx b/packages/app/src/HDXMultiSeriesTimeChart.tsx index 59d48156..525c5070 100644 --- a/packages/app/src/HDXMultiSeriesTimeChart.tsx +++ b/packages/app/src/HDXMultiSeriesTimeChart.tsx @@ -28,7 +28,7 @@ import { seriesToUrlSearchQueryParam, } from './ChartUtils'; import type { ChartSeries, NumberFormat } from './types'; -import useUserPreferences, { TimeFormat } from './useUserPreferences'; +import { useUserPreferences } from './useUserPreferences'; import { formatNumber } from './utils'; import { semanticKeyedColor, TIME_TOKENS, truncateMiddle } from './utils'; @@ -38,7 +38,9 @@ const MAX_LEGEND_ITEMS = 4; const HDXLineChartTooltip = withErrorBoundary( memo((props: any) => { - const timeFormat: TimeFormat = useUserPreferences().timeFormat; + const { + userPreferences: { timeFormat }, + } = useUserPreferences(); const tsFormat = TIME_TOKENS[timeFormat]; const { active, payload, label, numberFormat } = props; if (active && payload && payload.length) { @@ -217,7 +219,9 @@ const MemoChart = memo(function MemoChart({ }, [groupKeys, displayType, lineNames, graphResults]); const sizeRef = useRef<[number, number]>([0, 0]); - const timeFormat: TimeFormat = useUserPreferences().timeFormat; + const { + userPreferences: { timeFormat }, + } = useUserPreferences(); const tsFormat = TIME_TOKENS[timeFormat]; // Gets the preffered time format from User Preferences, then converts it to a formattable token diff --git a/packages/app/src/KubernetesDashboardPage.tsx b/packages/app/src/KubernetesDashboardPage.tsx index 5ce9cc49..a14aba1e 100644 --- a/packages/app/src/KubernetesDashboardPage.tsx +++ b/packages/app/src/KubernetesDashboardPage.tsx @@ -753,7 +753,6 @@ export default function KubernetesDashboardPage() { setDisplayedTimeInputValue, onSearch, } = useTimeQuery({ - isUTC: false, defaultValue: 'Past 1h', defaultTimeRange: [ defaultTimeRange?.[0]?.getTime() ?? -1, @@ -991,8 +990,6 @@ export default function KubernetesDashboardPage() { 'object.regarding.name': 'Name', }} isLive={false} - isUTC={false} - setIsUTC={() => {}} onPropertySearchClick={() => {}} showServiceColumn={false} /> diff --git a/packages/app/src/LogTable.tsx b/packages/app/src/LogTable.tsx index bd4f0242..b2254672 100644 --- a/packages/app/src/LogTable.tsx +++ b/packages/app/src/LogTable.tsx @@ -28,8 +28,7 @@ import InstallInstructionsModal from './InstallInstructionsModal'; import LogLevel from './LogLevel'; import { useSearchEventStream } from './search'; import { UNDEFINED_WIDTH } from './tableUtils'; -import type { TimeFormat } from './useUserPreferences'; -import useUserPreferences from './useUserPreferences'; +import { useUserPreferences } from './useUserPreferences'; import { useLocalStorage, usePrevious, useWindowSize } from './utils'; import { TIME_TOKENS } from './utils'; @@ -142,19 +141,16 @@ function LogTableSettingsModal({ onHide, onDone, initialAdditionalColumns, - initialIsUTC, initialWrapLines, downloadCSVButton, }: { initialAdditionalColumns: string[]; - initialIsUTC: boolean; initialWrapLines: boolean; show: boolean; onHide: () => void; onDone: (settings: { additionalColumns: string[]; wrapLines: boolean; - isUTC: boolean; }) => void; downloadCSVButton: JSX.Element; }) { @@ -162,7 +158,6 @@ function LogTableSettingsModal({ initialAdditionalColumns, ); const [wrapLines, setWrapLines] = useState(initialWrapLines); - const [isUTC, setIsUTC] = useState(initialIsUTC); return ( setWrapLines(!wrapLines)} label="Wrap Lines" /> - setIsUTC(!isUTC)} - label="Use UTC time instead of local time" - /> +
+ UTC setting moved to User Preferences +
Download Search Results
{downloadCSVButton} @@ -205,7 +195,7 @@ function LogTableSettingsModal({ variant="outline-success" className="fs-7 text-muted-hover" onClick={() => { - onDone({ additionalColumns, wrapLines, isUTC }); + onDone({ additionalColumns, wrapLines }); onHide(); }} > @@ -225,7 +215,6 @@ export const RawLogTable = memo( tableId, displayedColumns, fetchNextPage, - formatUTC, hasNextPage, highlightedLineId, isLive, @@ -261,7 +250,6 @@ export const RawLogTable = memo( // value: string | number | boolean, // ) => void; hasNextPage: boolean; - formatUTC: boolean; highlightedLineId: string | undefined; onScroll: (scrollTop: number) => void; isLive: boolean; @@ -283,7 +271,9 @@ export const RawLogTable = memo( const { width } = useWindowSize(); const isSmallScreen = (width ?? 1000) < 900; - const timeFormat: TimeFormat = useUserPreferences().timeFormat; + const { + userPreferences: { timeFormat, isUTC }, + } = useUserPreferences(); const tsFormat = TIME_TOKENS[timeFormat]; const [columnSizeStorage, setColumnSizeStorage] = useLocalStorage< @@ -340,13 +330,13 @@ export const RawLogTable = memo( header: () => isSmallScreen ? 'Time' - : `Timestamp${formatUTC ? ' (UTC)' : ' (Local)'}`, + : `Timestamp${isUTC ? ' (UTC)' : ' (Local)'}`, cell: info => { // FIXME: since original timestamp doesn't come with timezone info const date = new Date(info.getValue()); return ( - {formatUTC + {isUTC ? formatInTimeZone( date, 'Etc/UTC', @@ -436,7 +426,7 @@ export const RawLogTable = memo( }, ], [ - formatUTC, + isUTC, highlightedLineId, onRowExpandClick, displayedColumns, @@ -795,10 +785,8 @@ export default function LogTable({ highlightedLineId, onPropertySearchClick, onRowExpandClick, - formatUTC, isLive, onScroll, - setIsUTC, onEnd, onShowPatternsClick, tableId, @@ -817,10 +805,8 @@ export default function LogTable({ value: string | number | boolean, ) => void; onRowExpandClick: (logId: string, sortKey: string) => void; - formatUTC: boolean; onScroll: (scrollTop: number) => void; isLive: boolean; - setIsUTC: (isUTC: boolean) => void; onEnd?: () => void; onShowPatternsClick?: () => void; tableId?: string; @@ -837,6 +823,10 @@ export default function LogTable({ const resultsKey = [searchedQuery, displayedColumns, isLive].join(':'); + const { + userPreferences: { isUTC }, + } = useUserPreferences(); + const { results: searchResults, resultsKey: searchResultsKey, @@ -891,16 +881,14 @@ export default function LogTable({ onHide={() => setInstructionsOpen(false)} /> setSettingsOpen(false)} - onDone={({ additionalColumns, wrapLines, isUTC }) => { + onDone={({ additionalColumns, wrapLines }) => { setDisplayedColumns(additionalColumns); setWrapLines(wrapLines); - setIsUTC(isUTC); }} downloadCSVButton={ ; showServiceColumn?: boolean; @@ -34,7 +31,6 @@ export function LogTableWithSidePanel({ property: string, value: string | number | boolean, ) => void; - setIsUTC: (isUTC: boolean) => void; onPropertyAddClick?: (name: string, value: string | boolean | number) => void; onRowExpandClick?: (logId: string, sortKey: string) => void; @@ -111,12 +107,10 @@ export function LogTableWithSidePanel({ ) : null} { setOpenedLog({ id, sortKey }); diff --git a/packages/app/src/NamespaceDetailsSidePanel.tsx b/packages/app/src/NamespaceDetailsSidePanel.tsx index d84695b6..2f073a30 100644 --- a/packages/app/src/NamespaceDetailsSidePanel.tsx +++ b/packages/app/src/NamespaceDetailsSidePanel.tsx @@ -166,8 +166,6 @@ function NamespaceLogs({ where: _where, }} isLive={false} - isUTC={false} - setIsUTC={() => {}} onPropertySearchClick={() => {}} /> @@ -192,7 +190,6 @@ export default function NamespaceDetailsSidePanel() { }, [namespaceName]); const { searchedTimeRange: dateRange } = useTimeQuery({ - isUTC: false, defaultValue: 'Past 1h', defaultTimeRange: [ defaultTimeRange?.[0]?.getTime() ?? -1, diff --git a/packages/app/src/NodeDetailsSidePanel.tsx b/packages/app/src/NodeDetailsSidePanel.tsx index 62f7dd01..7b35a4f4 100644 --- a/packages/app/src/NodeDetailsSidePanel.tsx +++ b/packages/app/src/NodeDetailsSidePanel.tsx @@ -181,8 +181,6 @@ function NodeLogs({ where: _where, }} isLive={false} - isUTC={false} - setIsUTC={() => {}} onPropertySearchClick={() => {}} /> @@ -207,7 +205,6 @@ export default function NodeDetailsSidePanel() { }, [nodeName]); const { searchedTimeRange: dateRange } = useTimeQuery({ - isUTC: false, defaultValue: 'Past 1h', defaultTimeRange: [ defaultTimeRange?.[0]?.getTime() ?? -1, diff --git a/packages/app/src/PatternSidePanel.tsx b/packages/app/src/PatternSidePanel.tsx index 0a450647..aaa3eb29 100644 --- a/packages/app/src/PatternSidePanel.tsx +++ b/packages/app/src/PatternSidePanel.tsx @@ -160,7 +160,6 @@ export default function PatternSidePanel({ isLoading={false} hasNextPage={false} wrapLines={false} - formatUTC={false} fetchNextPage={useCallback(() => {}, [])} onScroll={useCallback(() => {}, [])} /> diff --git a/packages/app/src/PatternTable.tsx b/packages/app/src/PatternTable.tsx index 5f0d3f49..03d12f4b 100644 --- a/packages/app/src/PatternTable.tsx +++ b/packages/app/src/PatternTable.tsx @@ -132,7 +132,6 @@ const MemoPatternTable = memo( ({ dateRange, patterns, - formatUTC, highlightedPatternId, isLoading, onRowExpandClick, @@ -144,7 +143,6 @@ const MemoPatternTable = memo( wrapLines: boolean; isLoading: boolean; onRowExpandClick: (pattern: Pattern) => void; - formatUTC: boolean; highlightedPatternId: string | undefined; onShowEventsClick?: () => void; }) => { @@ -254,7 +252,6 @@ const MemoPatternTable = memo( }, ], [ - // formatUTC, highlightedPatternId, onRowExpandClick, isSmallScreen, @@ -437,7 +434,6 @@ const MemoPatternTable = memo( export default function PatternTable({ config: { where, dateRange }, onRowExpandClick, - isUTC, onShowEventsClick, highlightedPatternId, }: { @@ -447,7 +443,6 @@ export default function PatternTable({ }; highlightedPatternId: undefined | string; onRowExpandClick: (pattern: Pattern) => void; - isUTC: boolean; onShowEventsClick?: () => void; }) { const { data: histogramResults, isLoading: isHistogramResultsLoading } = @@ -495,7 +490,6 @@ export default function PatternTable({ highlightedPatternId={highlightedPatternId} patterns={patterns?.data ?? []} isLoading={isLoading} - formatUTC={isUTC} onRowExpandClick={onRowExpandClick} onShowEventsClick={onShowEventsClick} /> diff --git a/packages/app/src/PatternTableWithSidePanel.tsx b/packages/app/src/PatternTableWithSidePanel.tsx index f4f519c8..41ebe3db 100644 --- a/packages/app/src/PatternTableWithSidePanel.tsx +++ b/packages/app/src/PatternTableWithSidePanel.tsx @@ -7,15 +7,12 @@ import PatternTable from './PatternTable'; function PatternTableWithSidePanel({ config, - isUTC, onShowEventsClick, }: { config: { where: string; dateRange: [Date, Date]; }; - isUTC: boolean; - onShowEventsClick?: () => void; }) { const [openedPattern, setOpenedPattern] = useState(); @@ -38,7 +35,6 @@ function PatternTableWithSidePanel({ ) : null} {}} onPropertySearchClick={() => {}} columnNameMap={{ 'k8s.container.name': 'Container', @@ -179,7 +177,6 @@ export default function PodDetailsSidePanel() { }, [podName]); const { searchedTimeRange: dateRange } = useTimeQuery({ - isUTC: false, defaultValue: 'Past 1h', defaultTimeRange: [ defaultTimeRange?.[0]?.getTime() ?? -1, diff --git a/packages/app/src/SearchPage.tsx b/packages/app/src/SearchPage.tsx index 346679e0..d0e3fb60 100644 --- a/packages/app/src/SearchPage.tsx +++ b/packages/app/src/SearchPage.tsx @@ -48,6 +48,7 @@ import SearchTimeRangePicker from './SearchTimeRangePicker'; import { Tags } from './Tags'; import { useTimeQuery } from './timeQuery'; import { useDisplayedColumns } from './useDisplayedColumns'; +import { useUserPreferences } from './useUserPreferences'; import 'react-modern-drawer/dist/index.css'; import styles from '../styles/SearchPage.module.scss'; @@ -90,7 +91,6 @@ const HDXHistogram = memo( config: { dateRange, where }, onTimeRangeSelect, isLive, - isUTC, }: { config: { dateRange: [Date, Date]; @@ -98,8 +98,11 @@ const HDXHistogram = memo( }; onTimeRangeSelect: (start: Date, end: Date) => void; isLive: boolean; - isUTC: boolean; }) => { + const { + userPreferences: { isUTC }, + } = useUserPreferences(); + const { data: histogramResults, isLoading: isHistogramResultsLoading } = api.useLogHistogram( where, @@ -216,7 +219,7 @@ const HDXHistogram = memo( } tick={{ fontSize: 12, fontFamily: 'IBM Plex Mono, monospace' }} /> - } /> + } /> {highlightStart && highlightEnd ? ( @@ -272,8 +275,6 @@ const LogViewerContainer = memo(function LogViewerContainer({ generateChartUrl, isLive, setIsLive, - isUTC, - setIsUTC, onShowPatternsClick, }: { config: { @@ -293,8 +294,6 @@ const LogViewerContainer = memo(function LogViewerContainer({ onPropertyAddClick: (name: string, value: string | boolean | number) => void; isLive: boolean; setIsLive: (isLive: boolean) => void; - isUTC: boolean; - setIsUTC: (isUTC: boolean) => void; onShowPatternsClick: () => void; }) { const [openedLogQuery, setOpenedLogQuery] = useQueryParams( @@ -362,7 +361,6 @@ const LogViewerContainer = memo(function LogViewerContainer({ { // If the user scrolls a bit down, kick out of live mode @@ -375,7 +373,6 @@ const LogViewerContainer = memo(function LogViewerContainer({ highlightedLineId={openedLog?.id} config={config} onPropertySearchClick={onPropertySearchClick} - formatUTC={isUTC} onRowExpandClick={useCallback( (id: string, sortKey: string) => { setOpenedLog({ id, sortKey }); @@ -399,7 +396,6 @@ function SearchPage() { 'search', ); - const [isUTC, setIsUTC] = useState(false); const { isReady, isLive, @@ -409,7 +405,7 @@ function SearchPage() { onSearch, setIsLive, onTimeRangeSelect, - } = useTimeQuery({ isUTC }); + } = useTimeQuery({}); const [isFirstLoad, setIsFirstLoad] = useState(true); useEffect(() => { @@ -462,6 +458,10 @@ function SearchPage() { [searchInput], ); + const { + userPreferences: { isUTC }, + } = useUserPreferences(); + const [saveSearchModalMode, setSaveSearchModalMode] = useState< 'update' | 'save' | 'hidden' >('hidden'); @@ -975,7 +975,6 @@ function SearchPage() { config={chartsConfig} onTimeRangeSelect={onTimeRangeSelect} isLive={isLive} - isUTC={isUTC} /> ) : null}
@@ -1008,8 +1007,6 @@ function SearchPage() { onPropertySearchClick={onPropertySearchClick} isLive={isLive} setIsLive={setIsLive} - isUTC={isUTC} - setIsUTC={setIsUTC} onShowPatternsClick={() => { setIsLive(false); setResultsMode('patterns'); @@ -1017,7 +1014,6 @@ function SearchPage() { /> ) : ( diff --git a/packages/app/src/SearchTimeRangePicker.tsx b/packages/app/src/SearchTimeRangePicker.tsx index 9500eac5..25cb1922 100644 --- a/packages/app/src/SearchTimeRangePicker.tsx +++ b/packages/app/src/SearchTimeRangePicker.tsx @@ -7,7 +7,7 @@ import OverlayTrigger from 'react-bootstrap/OverlayTrigger'; import DatePicker from 'react-datepicker'; import { useHotkeys } from 'react-hotkeys-hook'; -import { TimeFormat } from './useUserPreferences'; +import { useUserPreferences } from './useUserPreferences'; import 'react-datepicker/dist/react-datepicker.css'; @@ -39,14 +39,12 @@ export default function SearchTimeRangePicker({ onSearch, onSubmit, showLive = false, - timeFormat = '12h', }: { inputValue: string; setInputValue: (str: string) => any; onSearch: (rangeStr: string) => void; onSubmit?: (rangeStr: string) => void; showLive?: boolean; - timeFormat?: TimeFormat; }) { const inputRef = useRef(null); @@ -71,6 +69,10 @@ export default function SearchTimeRangePicker({ } }, [isDatePickerOpen]); + const { + userPreferences: { timeFormat }, + } = useUserPreferences(); + return ( <> {}} onPropertySearchClick={() => {}} showServiceColumn={false} /> @@ -639,7 +636,6 @@ export default function ServiceDashboardPage() { {}} onPropertySearchClick={() => {}} /> )} diff --git a/packages/app/src/TeamPage.tsx b/packages/app/src/TeamPage.tsx index 022adad0..36f3f62a 100644 --- a/packages/app/src/TeamPage.tsx +++ b/packages/app/src/TeamPage.tsx @@ -42,7 +42,6 @@ import api from './api'; import { withAppNav } from './layout'; import { WebhookFlatIcon } from './SVGIcons'; import { WebhookService } from './types'; -import useUserPreferences, { TimeFormat } from './useUserPreferences'; import { truncateMiddle } from './utils'; import { isValidJson, isValidUrl } from './utils'; @@ -167,9 +166,6 @@ export default function TeamPage() { const deleteTeamInvitation = api.useDeleteTeamInvitation(); const saveWebhook = api.useSaveWebhook(); const deleteWebhook = api.useDeleteWebhook(); - const setTimeFormat = useUserPreferences().setTimeFormat; - const timeFormat = useUserPreferences().timeFormat; - const handleTimeButtonClick = (val: TimeFormat) => setTimeFormat(val); const hasAdminAccess = true; @@ -1112,43 +1108,6 @@ export default function TeamPage() { isSubmitting={saveTeamInvitation.isLoading} /> - -
Time Format
-
-
- Note: Only affects your own view and does not propagate to - other team members. -
- - - 24h - - - 12h - - -
)} diff --git a/packages/app/src/UserPreferencesModal.tsx b/packages/app/src/UserPreferencesModal.tsx new file mode 100644 index 00000000..6f20831f --- /dev/null +++ b/packages/app/src/UserPreferencesModal.tsx @@ -0,0 +1,283 @@ +import * as React from 'react'; +import { + Autocomplete, + Badge, + Button, + Divider, + Group, + Input, + Modal, + Select, + Slider, + Stack, + Switch, + Text, +} from '@mantine/core'; + +import { UserPreferences, useUserPreferences } from './useUserPreferences'; + +const OPTIONS_FONTS = [ + 'IBM Plex Mono', + 'Roboto Mono', + 'Inter', + { value: 'or use your own font', disabled: true }, +]; + +const OPTIONS_THEMES = [ + { label: 'Dark', value: 'dark' }, + { label: 'Light', value: 'light' }, +]; + +const OPTIONS_MIX_BLEND_MODE = [ + 'normal', + 'multiply', + 'screen', + 'overlay', + 'darken', + 'lighten', + 'color-dodge', + 'color-burn', + 'hard-light', + 'soft-light', + 'difference', + 'exclusion', + 'hue', + 'saturation', + 'color', + 'luminosity', + 'plus-darker', + 'plus-lighter', +]; + +const SettingContainer = ({ + label, + description, + children, +}: { + label: React.ReactNode; + description?: React.ReactNode; + children: React.ReactNode; +}) => { + return ( + +
+ {label} + {description && ( + + {description} + + )} +
+
{children}
+
+ ); +}; + +export const UserPreferencesModal = ({ + opened, + onClose, +}: { + opened: boolean; + onClose: () => void; +}) => { + const { userPreferences, setUserPreference } = useUserPreferences(); + + return ( + + Preferences + + Customize your experience + + + } + size="lg" + padding="lg" + keepMounted={false} + opened={opened} + onClose={onClose} + > + + + + + value && + setUserPreference({ + theme: value as UserPreferences['theme'], + }) + } + data={OPTIONS_THEMES} + allowDeselect={false} + /> + + + + options} + onChange={value => + setUserPreference({ + font: value as UserPreferences['font'], + }) + } + data={OPTIONS_FONTS} + /> + + + + + setUserPreference({ + backgroundEnabled: !userPreferences.backgroundEnabled, + }) + } + checked={userPreferences.backgroundEnabled} + /> + + + {userPreferences.backgroundEnabled && ( + <> + Background} labelPosition="left" /> + + + + + } + > + } + onChange={e => + setUserPreference({ + backgroundUrl: e.currentTarget.value, + }) + } + /> + + + + setUserPreference({ + backgroundOpacity: value, + }) + } + /> + + + + setUserPreference({ + backgroundBlur: value, + }) + } + /> + + +