From 6f8d424612bbe513cc61702918ba417699063d0d Mon Sep 17 00:00:00 2001 From: Austin Turner Date: Mon, 11 Dec 2023 19:53:14 -0700 Subject: [PATCH 01/10] Allow better rollbar reporting for api server don't minify build and upload sourcemaps to rollbar --- apps/api/project.json | 2 +- scripts/upload-source-maps.ts | 28 +++++++++++++++++++++++++--- 2 files changed, 26 insertions(+), 4 deletions(-) diff --git a/apps/api/project.json b/apps/api/project.json index 9bbd3fcf9..12efc2d3d 100644 --- a/apps/api/project.json +++ b/apps/api/project.json @@ -20,7 +20,7 @@ }, "configurations": { "production": { - "optimization": true, + "optimization": false, "extractLicenses": true, "inspect": false, "sourceMap": true, diff --git a/scripts/upload-source-maps.ts b/scripts/upload-source-maps.ts index ef47537dd..741e362f6 100644 --- a/scripts/upload-source-maps.ts +++ b/scripts/upload-source-maps.ts @@ -14,7 +14,6 @@ void (async function () { } console.log(chalk.blue(`Uploading sourcemaps to Rollbar`)); const distPath = path.join(__dirname, '../dist'); - const sourceMapPaths = [path.join(distPath, 'apps/jetstream'), path.join(distPath, 'apps/download-zip-sw')]; const version = (fs.readFileSync(path.join(distPath, 'VERSION'), 'utf8') || (await $`git describe --always`).stdout).trim(); const url = 'https://api.rollbar.com/api/1/sourcemap'; const accessToken = process.env.ROLLBAR_SERVER_TOKEN; @@ -28,7 +27,10 @@ void (async function () { $.verbose = false; console.time(); - for (const sourceMapPath of sourceMapPaths) { + /** + * CLIENT SOURCEMAPS + */ + for (const sourceMapPath of [path.join(distPath, 'apps/jetstream'), path.join(distPath, 'apps/download-zip-sw')]) { console.log(sourceMapPath); const files = (await fs.readdir(sourceMapPath)).filter((item) => item.endsWith('.js.map')).sort(); @@ -41,7 +43,27 @@ void (async function () { await $`curl ${url} -F access_token=${accessToken} -F version=${version} -F minified_url=${minifiedUrl} -F source_map=@${filePath}`; } catch (ex: any) { - console.error(chalk.redBright('🚫 Error uploading sourcemap', ex.message)); + console.error(chalk.redBright('🚫 Error uploading client sourcemap', ex.message)); + } + } + } + /** + * SERVER SOURCEMAPS + */ + for (const sourceMapPath of [path.join(distPath, 'apps/api')]) { + console.log(sourceMapPath); + const files = (await fs.readdir(sourceMapPath)).filter((item) => item.endsWith('.js.map')).sort(); + + for (const file of files) { + try { + const filePath = path.join(sourceMapPath, file); + const minifiedUrl = `/opt/render/project/src/dist/apps/api/${file.replace('.js.map', '.js')}`; + + console.log(chalk.blue(`- ${file}`)); + + await $`curl ${url} -F access_token=${accessToken} -F version=${version} -F minified_url=${minifiedUrl} -F source_map=@${filePath}`; + } catch (ex: any) { + console.error(chalk.redBright('🚫 Error uploading server sourcemap', ex.message)); } } } From 7f9af23c539146aba77c01c102cfaeaf5fabfdc0 Mon Sep 17 00:00:00 2001 From: Austin Turner Date: Mon, 11 Dec 2023 19:54:32 -0700 Subject: [PATCH 02/10] Sort debug logs by event time work towards #664 --- .../debug-log-viewer/DebugLogViewerTable.tsx | 10 +++++++++- .../app/components/debug-log-viewer/useDebugLogs.tsx | 2 +- libs/ui/src/lib/data-table/DataTable.tsx | 5 ++++- libs/ui/src/lib/data-table/useDataTable.tsx | 4 +++- 4 files changed, 17 insertions(+), 4 deletions(-) diff --git a/apps/jetstream/src/app/components/debug-log-viewer/DebugLogViewerTable.tsx b/apps/jetstream/src/app/components/debug-log-viewer/DebugLogViewerTable.tsx index 4a4e3f8bf..7d8808eeb 100644 --- a/apps/jetstream/src/app/components/debug-log-viewer/DebugLogViewerTable.tsx +++ b/apps/jetstream/src/app/components/debug-log-viewer/DebugLogViewerTable.tsx @@ -22,6 +22,8 @@ export const LogViewedRenderer: FunctionComponent[] = [ { name: '', @@ -101,7 +103,13 @@ export const DebugLogViewerTable: FunctionComponent = return ( - + ); }; diff --git a/apps/jetstream/src/app/components/debug-log-viewer/useDebugLogs.tsx b/apps/jetstream/src/app/components/debug-log-viewer/useDebugLogs.tsx index 20c187116..a4a1a1857 100644 --- a/apps/jetstream/src/app/components/debug-log-viewer/useDebugLogs.tsx +++ b/apps/jetstream/src/app/components/debug-log-viewer/useDebugLogs.tsx @@ -50,7 +50,7 @@ export function useDebugLogs(org: SalesforceOrgUi, { limit, pollInterval, userId setLogs(queryResults.records); } else { setLogs((logs) => - orderBy(Object.values({ ...getMapOf(logs, 'Id'), ...getMapOf(queryResults.records, 'Id') }), ['Id'], ['asc']) + orderBy(Object.values({ ...getMapOf(logs, 'Id'), ...getMapOf(queryResults.records, 'Id') }), ['LastModifiedDate'], ['desc']) ); } setLoading(false); diff --git a/libs/ui/src/lib/data-table/DataTable.tsx b/libs/ui/src/lib/data-table/DataTable.tsx index 9a8441538..311b7fc83 100644 --- a/libs/ui/src/lib/data-table/DataTable.tsx +++ b/libs/ui/src/lib/data-table/DataTable.tsx @@ -1,6 +1,6 @@ import { SalesforceOrgUi } from '@jetstream/types'; import { forwardRef } from 'react'; -import DataGrid, { DataGridProps } from 'react-data-grid'; +import DataGrid, { DataGridProps, SortColumn } from 'react-data-grid'; import 'react-data-grid/lib/styles.css'; import { ContextMenuContext, ContextMenuItem } from '../popover/ContextMenu'; import { DataTableFilterContext, DataTableGenericContext } from './data-table-context'; @@ -19,6 +19,7 @@ export interface DataTableProps> context?: TContext; /** Must be stable to avoid constant re-renders */ contextMenuItems?: ContextMenuItem[]; + initialSortColumns?: SortColumn[]; /** Must be stable to avoid constant re-renders */ contextMenuAction?: (item: ContextMenuItem, data: ContextMenuActionData) => void; getRowKey: (row: T) => string; @@ -39,6 +40,7 @@ export const DataTable = forwardRef>( includeQuickFilter, context, contextMenuItems, + initialSortColumns, contextMenuAction, getRowKey, ignoreRowInSetFilter, @@ -70,6 +72,7 @@ export const DataTable = forwardRef>( quickFilterText, includeQuickFilter, contextMenuItems, + initialSortColumns, ref, contextMenuAction, getRowKey, diff --git a/libs/ui/src/lib/data-table/useDataTable.tsx b/libs/ui/src/lib/data-table/useDataTable.tsx index ac07fb572..09abe5060 100644 --- a/libs/ui/src/lib/data-table/useDataTable.tsx +++ b/libs/ui/src/lib/data-table/useDataTable.tsx @@ -38,6 +38,7 @@ export interface UseDataTableProps { // context?: TContext; /** Must be stable to avoid constant re-renders */ contextMenuItems?: ContextMenuItem[]; + initialSortColumns?: SortColumn[]; ref: any; /** Must be stable to avoid constant re-renders */ contextMenuAction?: (item: ContextMenuItem, data: ContextMenuActionData) => void; @@ -56,6 +57,7 @@ export function useDataTable({ quickFilterText, includeQuickFilter, contextMenuItems, + initialSortColumns, ref, contextMenuAction, getRowKey, @@ -66,7 +68,7 @@ export function useDataTable({ }: UseDataTableProps) { const [gridId] = useState(() => uniqueId('grid-')); const [columns, setColumns] = useState(_columns || []); - const [sortColumns, setSortColumns] = useState([]); + const [sortColumns, setSortColumns] = useState(() => initialSortColumns || []); const [rowFilterText, setRowFilterText] = useState>({}); const [renderers, setRenderers] = useState>({}); const [columnsOrder, setColumnsOrder] = useState((): readonly number[] => columns.map((_, index) => index)); From 78b2a3e26b787e071311deb1c215a802c626f95d Mon Sep 17 00:00:00 2001 From: Austin Turner Date: Mon, 11 Dec 2023 19:55:15 -0700 Subject: [PATCH 03/10] Enable sourcemaps on rollbar --- libs/api-config/src/lib/api-rollbar-config.ts | 1 + 1 file changed, 1 insertion(+) diff --git a/libs/api-config/src/lib/api-rollbar-config.ts b/libs/api-config/src/lib/api-rollbar-config.ts index 89de4ed10..4588e05ec 100644 --- a/libs/api-config/src/lib/api-rollbar-config.ts +++ b/libs/api-config/src/lib/api-rollbar-config.ts @@ -9,4 +9,5 @@ export const rollbarServer = new Rollbar({ captureUncaught: true, captureUnhandledRejections: true, enabled: !!ENV.ROLLBAR_SERVER_TOKEN, + nodeSourceMaps: true, }); From be10842c5d25e97d6e2b8b93ffda1f7ce5aca083 Mon Sep 17 00:00:00 2001 From: Austin Turner Date: Mon, 11 Dec 2023 19:56:15 -0700 Subject: [PATCH 04/10] Add ability to open saved queries directly Added button for accessing saved queries from query builder Added tooltip with keyboard shortcuts --- .../query/QueryBuilder/ExecuteQueryButton.tsx | 48 ++++++---- .../query/QueryBuilder/QueryBuilder.tsx | 2 +- .../query/QueryHistory/QueryHistory.tsx | 91 ++++++++++++++----- .../query/QueryOptions/QueryResetButton.tsx | 5 +- 4 files changed, 99 insertions(+), 47 deletions(-) diff --git a/apps/jetstream/src/app/components/query/QueryBuilder/ExecuteQueryButton.tsx b/apps/jetstream/src/app/components/query/QueryBuilder/ExecuteQueryButton.tsx index f105415b0..62a76cf4d 100644 --- a/apps/jetstream/src/app/components/query/QueryBuilder/ExecuteQueryButton.tsx +++ b/apps/jetstream/src/app/components/query/QueryBuilder/ExecuteQueryButton.tsx @@ -1,7 +1,7 @@ import { Maybe } from '@jetstream/types'; -import { Icon } from '@jetstream/ui'; +import { Icon, KeyboardShortcut, Tooltip, getModifierKey } from '@jetstream/ui'; import type { DescribeGlobalSObjectResult } from 'jsforce'; -import { Fragment, FunctionComponent } from 'react'; +import { FunctionComponent } from 'react'; import { Link } from 'react-router-dom'; interface ExecuteQueryButtonProps { @@ -12,25 +12,33 @@ interface ExecuteQueryButtonProps { export const ExecuteQueryButton: FunctionComponent = ({ soql, isTooling, selectedSObject }) => { return ( - + <> {soql && selectedSObject && ( - + + + } > - - Execute - + + + Execute + + )} {!soql && ( )} - + ); }; diff --git a/apps/jetstream/src/app/components/query/QueryBuilder/QueryBuilder.tsx b/apps/jetstream/src/app/components/query/QueryBuilder/QueryBuilder.tsx index 2cdb8f38e..ad26fc387 100644 --- a/apps/jetstream/src/app/components/query/QueryBuilder/QueryBuilder.tsx +++ b/apps/jetstream/src/app/components/query/QueryBuilder/QueryBuilder.tsx @@ -310,7 +310,7 @@ export const QueryBuilder: FunctionComponent = () => { - + diff --git a/apps/jetstream/src/app/components/query/QueryHistory/QueryHistory.tsx b/apps/jetstream/src/app/components/query/QueryHistory/QueryHistory.tsx index f1bcc8c6a..adfb491b7 100644 --- a/apps/jetstream/src/app/components/query/QueryHistory/QueryHistory.tsx +++ b/apps/jetstream/src/app/components/query/QueryHistory/QueryHistory.tsx @@ -1,23 +1,43 @@ /* eslint-disable @typescript-eslint/no-explicit-any */ +import { css } from '@emotion/react'; import { logger } from '@jetstream/shared/client-logger'; import { ANALYTICS_KEYS } from '@jetstream/shared/constants'; -import { formatNumber, hasModifierKey, isHKey, useGlobalEventHandler, useNonInitialEffect } from '@jetstream/shared/ui-utils'; +import { + formatNumber, + hasModifierKey, + hasShiftModifierKey, + isHKey, + useGlobalEventHandler, + useNonInitialEffect, +} from '@jetstream/shared/ui-utils'; import { multiWordObjectFilter } from '@jetstream/shared/utils'; import { QueryHistoryItem, QueryHistorySelection, SalesforceOrgUi, UpDown } from '@jetstream/types'; -import { EmptyState, Grid, GridCol, Icon, List, Modal, SearchInput, Spinner } from '@jetstream/ui'; +import { + EmptyState, + Grid, + GridCol, + Icon, + KeyboardShortcut, + List, + Modal, + SearchInput, + Spinner, + Tooltip, + getModifierKey, +} from '@jetstream/ui'; import classNames from 'classnames'; import { createRef, forwardRef, useCallback, useEffect, useImperativeHandle, useState } from 'react'; import { ErrorBoundary } from 'react-error-boundary'; import { useLocation } from 'react-router-dom'; import { useRecoilState, useRecoilValue, useResetRecoilState } from 'recoil'; -import { useAmplitude } from '../../core/analytics'; import ErrorBoundaryFallback from '../../core/ErrorBoundaryFallback'; -import * as fromQueryHistoryState from './query-history.state'; +import { useAmplitude } from '../../core/analytics'; import QueryHistoryEmptyState from './QueryHistoryEmptyState'; import QueryHistoryItemCard from './QueryHistoryItemCard'; import QueryHistoryWhichOrg from './QueryHistoryWhichOrg'; import QueryHistoryWhichType from './QueryHistoryWhichType'; +import * as fromQueryHistoryState from './query-history.state'; const SHOWING_STEP = 25; @@ -60,9 +80,7 @@ export const QueryHistory = forwardRef(({ className, sel useImperativeHandle(ref, () => { return { open: (type: fromQueryHistoryState.QueryHistoryType = 'HISTORY') => { - setIsOpen(true); - setWhichType(type); - setWhichOrg('ALL'); + handleOpenModal(type, 'externalAction'); }, }; }); @@ -72,9 +90,10 @@ export const QueryHistory = forwardRef(({ className, sel if (!isOpen && hasModifierKey(event as any) && isHKey(event as any)) { event.stopPropagation(); event.preventDefault(); - setIsOpen(true); + handleOpenModal(hasShiftModifierKey(event as any) ? 'SAVED' : 'HISTORY', 'keyboardShortcut'); } }, + // eslint-disable-next-line react-hooks/exhaustive-deps [isOpen] ); @@ -131,12 +150,6 @@ export const QueryHistory = forwardRef(({ className, sel } }, [filteredQueryHistory, showingUpTo]); - useEffect(() => { - if (isOpen) { - trackEvent(ANALYTICS_KEYS.query_HistoryModalOpened); - } - }, [isOpen, trackEvent]); - useEffect(() => { if (isOpen) { setIsOpen(false); @@ -162,9 +175,11 @@ export const QueryHistory = forwardRef(({ className, sel // eslint-disable-next-line react-hooks/exhaustive-deps }, [selectObjectsList, filterValue]); - function handleOpenModal() { + function handleOpenModal(type: fromQueryHistoryState.QueryHistoryType = 'HISTORY', source = 'buttonClick') { + setWhichType(type); setIsOpen(true); fromQueryHistoryState.initQueryHistory().then((queryHistory) => setQueryHistorySateMap(queryHistory)); + trackEvent(ANALYTICS_KEYS.query_HistoryModalOpened, { source, type }); } function handleExecute({ created, lastRun, runCount, isTooling, isFavorite }: QueryHistoryItem) { @@ -229,15 +244,45 @@ export const QueryHistory = forwardRef(({ className, sel return ( - + + + + View saved queries + + + } + > + + {isOpen && ( = ({ cla return ( + + + + } + > + + } > diff --git a/apps/jetstream/src/app/components/deploy/DeployMetadataDeployment.tsx b/apps/jetstream/src/app/components/deploy/DeployMetadataDeployment.tsx index 0bea6c92c..6852d02f5 100644 --- a/apps/jetstream/src/app/components/deploy/DeployMetadataDeployment.tsx +++ b/apps/jetstream/src/app/components/deploy/DeployMetadataDeployment.tsx @@ -297,6 +297,7 @@ export const DeployMetadataDeployment: FunctionComponent (null); const orgsById = useRecoilValue(fromAppState.salesforceOrgsById); + const onKeydown = useCallback( + (event: KeyboardEvent) => { + if (!isOpen && hasModifierKey(event as any) && isHKey(event as any)) { + event.stopPropagation(); + event.preventDefault(); + handleToggleOpen(true, 'keyboardShortcut'); + } + }, + // eslint-disable-next-line react-hooks/exhaustive-deps + [isOpen] + ); + + useGlobalEventHandler('keydown', onKeydown); + useEffect(() => { if (isOpen) { setIsLoading(true); @@ -77,10 +103,10 @@ export const DeployMetadataHistoryModal = ({ className }: DeployMetadataHistoryM } }, [isOpen]); - function handleToggleOpen(open: boolean) { + function handleToggleOpen(open: boolean, source = 'buttonClick') { setIsOpen(open); if (open) { - trackEvent(ANALYTICS_KEYS.deploy_history_opened); + trackEvent(ANALYTICS_KEYS.deploy_history_opened, source); } } @@ -161,15 +187,24 @@ export const DeployMetadataHistoryModal = ({ className }: DeployMetadataHistoryM return ( - + + {downloadPackageModalState.open && downloadPackageModalState.org && downloadPackageModalState.data && ( actions={ <> - + + } > diff --git a/libs/ui/src/lib/form/dropdown/DropDown.tsx b/libs/ui/src/lib/form/dropdown/DropDown.tsx index 949d5ff8e..451067a46 100644 --- a/libs/ui/src/lib/form/dropdown/DropDown.tsx +++ b/libs/ui/src/lib/form/dropdown/DropDown.tsx @@ -168,8 +168,9 @@ export const DropDown: FunctionComponent = ({ setIsOpen(false)}>
+ } > - - - - - View saved queries - - - } - > - + + + View saved queries + + + } > - - - + + + {isOpen && ( = React.memo(() SOQL Query