From f83bd85f1d2c8b31d99062e21a699880540be186 Mon Sep 17 00:00:00 2001 From: gitstart Date: Fri, 25 Nov 2022 16:12:56 +0000 Subject: [PATCH 1/3] fix: rename/remove table filter/sort rules on column rename --- .../SidePanelEditor/SidePanelEditor.tsx | 82 ++++++++++++++++++- 1 file changed, 81 insertions(+), 1 deletion(-) diff --git a/studio/components/interfaces/TableGridEditor/SidePanelEditor/SidePanelEditor.tsx b/studio/components/interfaces/TableGridEditor/SidePanelEditor/SidePanelEditor.tsx index 8a0d3ee57d3..c04a98fff2a 100644 --- a/studio/components/interfaces/TableGridEditor/SidePanelEditor/SidePanelEditor.tsx +++ b/studio/components/interfaces/TableGridEditor/SidePanelEditor/SidePanelEditor.tsx @@ -9,11 +9,13 @@ import { PostgresType, } from '@supabase/postgres-meta' -import { useStore } from 'hooks' +import { useStore, useUrlState } from 'hooks' import { RowEditor, ColumnEditor, TableEditor } from '.' import { ImportContent } from './TableEditor/TableEditor.types' import { ColumnField, CreateColumnPayload, UpdateColumnPayload } from './SidePanelEditor.types' import ConfirmationModal from 'components/ui/ConfirmationModal' +import { onLoadStorage, saveStorageDebounced } from 'components/grid/SupabaseGrid.utils' +import { InitialStateType } from 'components/grid/store/reducers' interface Props { selectedSchema: string @@ -48,9 +50,11 @@ const SidePanelEditor: FC = ({ onColumnSaved = () => {}, }) => { const { meta, ui } = useStore() + const projectRef = ui.selectedProject?.ref const [isEdited, setIsEdited] = useState(false) const [isClosingPanel, setIsClosingPanel] = useState(false) + const [_, setParams] = useUrlState({ arrayKeys: ['filter', 'sort'] }) const tables = meta.tables.list() const enumTypes = meta.types.list( @@ -121,6 +125,65 @@ const SidePanelEditor: FC = ({ } } + /** + * Renames a column's filter and/or sort rules if the related column has been renamed. + */ + const updateSortsAndFiltersAfterColumnRename = (oldColumnName: string, newColumnName: string) => { + setParams((prevParams) => { + const existingFilters = (prevParams?.filter ?? []) as string[] + const existingSorts = (prevParams?.sort ?? []) as string[] + + return { + ...prevParams, + filter: existingFilters.map((filter: string) => { + const [column] = filter.split(':') + return column === oldColumnName ? filter.replace(column, newColumnName) : filter + }), + sort: existingSorts.map((sort: string) => { + const [column] = sort.split(':') + return column === oldColumnName ? sort.replace(column, newColumnName) : sort + }), + } + }) + } + + /** + * Removes tables's column filter and/or sort rules if the related column unavailable + * (resulting from name change or deleted column) + */ + const updateSavedStateAfterTableUpdate = ( + storageRef: string, + tableName: string, + schema: string | null, + tableColumns: ColumnField[] + ) => { + const savedState = onLoadStorage(storageRef, tableName, schema) + const columnNames = tableColumns.map((columnInstance) => columnInstance.name) + const { filters, sorts }: { filters?: string[]; sorts?: string[] } = savedState + const state = { + table: { name: tableName, schema }, + gridColumns: savedState?.gridColumns, + } as InitialStateType + + const newSorts = sorts?.filter((filterQuery) => { + const [column] = filterQuery.split(':') + + return columnNames.includes(column) + }) + const newFilters = filters?.filter((sortrQuery) => { + const [column] = sortrQuery.split(':') + + return columnNames.includes(column) + }) + + saveStorageDebounced( + state, + storageRef, + newSorts?.length ? newSorts : undefined, + newFilters?.length ? newFilters : undefined + ) + } + const saveColumn = async ( payload: CreateColumnPayload | UpdateColumnPayload, foreignKey: Partial | undefined, @@ -144,6 +207,15 @@ const SidePanelEditor: FC = ({ if (response?.error) { ui.setNotification({ category: 'error', message: response.error.message }) } else { + if ( + !isNewRecord && + payload.name && + selectedColumnToEdit && + selectedColumnToEdit.name !== payload.name + ) { + updateSortsAndFiltersAfterColumnRename(selectedColumnToEdit.name, payload.name) + } + await meta.tables.loadById(selectedTable!.id) onColumnSaved() setIsEdited(false) @@ -235,6 +307,14 @@ const SidePanelEditor: FC = ({ message: `Successfully updated ${table.name}!`, }) } + if (projectRef) { + updateSavedStateAfterTableUpdate( + projectRef, + selectedTableToEdit.name, + selectedTableToEdit.schema, + columns + ) + } } } catch (error: any) { saveTableError = true From c2d0825d42b2867a7dea8754bff9040ad1a02f70 Mon Sep 17 00:00:00 2001 From: gitstart Date: Mon, 26 Dec 2022 17:24:31 +0000 Subject: [PATCH 2/3] chore: remove unavailable columns from sorts and filters --- studio/components/grid/SupabaseGrid.tsx | 30 +++++++ studio/components/grid/utils/row.ts | 15 +++- .../SidePanelEditor/SidePanelEditor.tsx | 82 +------------------ 3 files changed, 43 insertions(+), 84 deletions(-) diff --git a/studio/components/grid/SupabaseGrid.tsx b/studio/components/grid/SupabaseGrid.tsx index 75b3abf65b9..c454b675216 100644 --- a/studio/components/grid/SupabaseGrid.tsx +++ b/studio/components/grid/SupabaseGrid.tsx @@ -95,6 +95,36 @@ const SupabaseGridLayout = forwardRef((props }, []) useEffect(() => { + if (state.refreshPageFlag == 0) { + return + } + + const columnNames = state.table?.columns.map((column) => column.name) + + if ( + columnNames && + (sorts.some((sortInstance) => !columnNames.includes(sortInstance.column)) || + filters.some((filterInstance) => !columnNames.includes(filterInstance.column))) + ) { + // Remove unavailable columns from "sort" and "filter" query string parameters + setParams((prevParams) => { + return { + ...prevParams, + filter: filters + .filter((filterInstance) => columnNames.includes(filterInstance.column)) + .map( + (filterInstance) => + `${filterInstance.column}:${filterInstance.operator}:${filterInstance.value}` + ), + sort: sorts + .filter((sortInstance) => columnNames.includes(sortInstance.column)) + .map( + (sortInstance) => `${sortInstance.column}:${sortInstance.ascending ? 'asc' : 'desc'}` + ), + } + }) + } + if (state.refreshPageFlag === REFRESH_PAGE_IMMEDIATELY) { fetchPage(state, dispatch, sorts, filters) } else if (state.refreshPageFlag !== 0) { diff --git a/studio/components/grid/utils/row.ts b/studio/components/grid/utils/row.ts index aabf2c85a78..59f43166ee4 100644 --- a/studio/components/grid/utils/row.ts +++ b/studio/components/grid/utils/row.ts @@ -9,7 +9,11 @@ export async function fetchCount( ) { if (!state.rowService) return - const { data, error } = await state.rowService.count(filters) + // Remove unavailable columns from filters + const columnNames = state.table?.columns.map((column) => column.name) ?? [] + const cleanedFilters = filters.filter((filter) => columnNames.includes(filter.column)) + + const { data, error } = await state.rowService.count(cleanedFilters) if (error) { // TODO: handle fetch rows count error } else { @@ -35,11 +39,16 @@ export async function fetchPage( dispatch({ type: 'SET_IS_LOADING', payload: { isLoading: true } }) + // Remove unavailable columns from sorts & filter + const columnNames = state.table?.columns.map((column) => column.name) ?? [] + const cleanedFilters = filters.filter((filter) => columnNames.includes(filter.column)) + const cleanedSorts = sorts.filter((sort) => columnNames.includes(sort.column)) + const { data, error } = await state.rowService.fetchPage( state.page, state.rowsPerPage, - filters, - sorts + cleanedFilters, + cleanedSorts ) if (error) { // TODO: handle fetch rows data error diff --git a/studio/components/interfaces/TableGridEditor/SidePanelEditor/SidePanelEditor.tsx b/studio/components/interfaces/TableGridEditor/SidePanelEditor/SidePanelEditor.tsx index b0b8efcac56..7e410f7d2ab 100644 --- a/studio/components/interfaces/TableGridEditor/SidePanelEditor/SidePanelEditor.tsx +++ b/studio/components/interfaces/TableGridEditor/SidePanelEditor/SidePanelEditor.tsx @@ -4,13 +4,11 @@ import { Query, Dictionary } from 'components/grid' import { Modal } from 'ui' import { PostgresRelationship, PostgresTable, PostgresColumn } from '@supabase/postgres-meta' -import { useStore, useUrlState } from 'hooks' +import { useStore } from 'hooks' import { RowEditor, ColumnEditor, TableEditor } from '.' import { ImportContent } from './TableEditor/TableEditor.types' import { ColumnField, CreateColumnPayload, UpdateColumnPayload } from './SidePanelEditor.types' import ConfirmationModal from 'components/ui/ConfirmationModal' -import { onLoadStorage, saveStorageDebounced } from 'components/grid/SupabaseGrid.utils' -import { InitialStateType } from 'components/grid/store/reducers' interface Props { selectedSchema: string @@ -45,11 +43,9 @@ const SidePanelEditor: FC = ({ onColumnSaved = () => {}, }) => { const { meta, ui } = useStore() - const projectRef = ui.selectedProject?.ref const [isEdited, setIsEdited] = useState(false) const [isClosingPanel, setIsClosingPanel] = useState(false) - const [_, setParams] = useUrlState({ arrayKeys: ['filter', 'sort'] }) const tables = meta.tables.list() @@ -117,65 +113,6 @@ const SidePanelEditor: FC = ({ } } - /** - * Renames a column's filter and/or sort rules if the related column has been renamed. - */ - const updateSortsAndFiltersAfterColumnRename = (oldColumnName: string, newColumnName: string) => { - setParams((prevParams) => { - const existingFilters = (prevParams?.filter ?? []) as string[] - const existingSorts = (prevParams?.sort ?? []) as string[] - - return { - ...prevParams, - filter: existingFilters.map((filter: string) => { - const [column] = filter.split(':') - return column === oldColumnName ? filter.replace(column, newColumnName) : filter - }), - sort: existingSorts.map((sort: string) => { - const [column] = sort.split(':') - return column === oldColumnName ? sort.replace(column, newColumnName) : sort - }), - } - }) - } - - /** - * Removes tables's column filter and/or sort rules if the related column unavailable - * (resulting from name change or deleted column) - */ - const updateSavedStateAfterTableUpdate = ( - storageRef: string, - tableName: string, - schema: string | null, - tableColumns: ColumnField[] - ) => { - const savedState = onLoadStorage(storageRef, tableName, schema) - const columnNames = tableColumns.map((columnInstance) => columnInstance.name) - const { filters, sorts }: { filters?: string[]; sorts?: string[] } = savedState - const state = { - table: { name: tableName, schema }, - gridColumns: savedState?.gridColumns, - } as InitialStateType - - const newSorts = sorts?.filter((filterQuery) => { - const [column] = filterQuery.split(':') - - return columnNames.includes(column) - }) - const newFilters = filters?.filter((sortrQuery) => { - const [column] = sortrQuery.split(':') - - return columnNames.includes(column) - }) - - saveStorageDebounced( - state, - storageRef, - newSorts?.length ? newSorts : undefined, - newFilters?.length ? newFilters : undefined - ) - } - const saveColumn = async ( payload: CreateColumnPayload | UpdateColumnPayload, foreignKey: Partial | undefined, @@ -201,15 +138,6 @@ const SidePanelEditor: FC = ({ if (response?.error) { ui.setNotification({ category: 'error', message: response.error.message }) } else { - if ( - !isNewRecord && - payload.name && - selectedColumnToEdit && - selectedColumnToEdit.name !== payload.name - ) { - updateSortsAndFiltersAfterColumnRename(selectedColumnToEdit.name, payload.name) - } - await meta.tables.loadById(selectedTable!.id) onColumnSaved(configuration.isEncrypted) setIsEdited(false) @@ -305,14 +233,6 @@ const SidePanelEditor: FC = ({ message: `Successfully updated ${table.name}!`, }) } - if (projectRef) { - updateSavedStateAfterTableUpdate( - projectRef, - selectedTableToEdit.name, - selectedTableToEdit.schema, - columns - ) - } } } catch (error: any) { saveTableError = true From 72b716eb22dbf704d1d412e7f12ec705616dc4aa Mon Sep 17 00:00:00 2001 From: gitstart Date: Thu, 2 Feb 2023 09:46:37 +0000 Subject: [PATCH 3/3] chore: support column name update with active filter --- studio/components/grid/store/reducers/base.ts | 8 ++--- .../SidePanelEditor/SidePanelEditor.tsx | 34 ++++++++++++++++++- 2 files changed, 37 insertions(+), 5 deletions(-) diff --git a/studio/components/grid/store/reducers/base.ts b/studio/components/grid/store/reducers/base.ts index 1307eb23007..9795680c2df 100644 --- a/studio/components/grid/store/reducers/base.ts +++ b/studio/components/grid/store/reducers/base.ts @@ -83,15 +83,15 @@ const BaseReducer = (state: BaseInitialState, action: BASE_ACTIONTYPE) => { case 'UPDATE_FILTERS': { const newState: any = { ...state } newState.page = 1 - // newState.refreshPageFlag = Date.now() - newState.refreshPageFlag = REFRESH_PAGE_IMMEDIATELY + newState.refreshPageFlag = Date.now() + // newState.refreshPageFlag = REFRESH_PAGE_IMMEDIATELY newState.totalRows = TOTAL_ROWS_RESET return newState } case 'UPDATE_SORTS': { const newState: any = { ...state } - // newState.refreshPageFlag = Date.now() - newState.refreshPageFlag = REFRESH_PAGE_IMMEDIATELY + newState.refreshPageFlag = Date.now() + // newState.refreshPageFlag = REFRESH_PAGE_IMMEDIATELY return newState } default: diff --git a/studio/components/interfaces/TableGridEditor/SidePanelEditor/SidePanelEditor.tsx b/studio/components/interfaces/TableGridEditor/SidePanelEditor/SidePanelEditor.tsx index 7d8c40bd8d0..23dc0ebb37c 100644 --- a/studio/components/interfaces/TableGridEditor/SidePanelEditor/SidePanelEditor.tsx +++ b/studio/components/interfaces/TableGridEditor/SidePanelEditor/SidePanelEditor.tsx @@ -4,7 +4,7 @@ import { Query, Dictionary } from 'components/grid' import { Modal } from 'ui' import type { PostgresRelationship, PostgresTable, PostgresColumn } from '@supabase/postgres-meta' -import { useStore } from 'hooks' +import { useStore, useUrlState } from 'hooks' import { RowEditor, ColumnEditor, TableEditor } from '.' import { ImportContent } from './TableEditor/TableEditor.types' import { ColumnField, CreateColumnPayload, UpdateColumnPayload } from './SidePanelEditor.types' @@ -42,6 +42,7 @@ const SidePanelEditor: FC = ({ onTableCreated = () => {}, onColumnSaved = () => {}, }) => { + const [_, setParams] = useUrlState({ arrayKeys: ['filter', 'sort'] }) const { meta, ui } = useStore() const [isEdited, setIsEdited] = useState(false) @@ -139,6 +140,15 @@ const SidePanelEditor: FC = ({ ui.setNotification({ category: 'error', message: response.error.message }) } else { await meta.tables.loadById(selectedTable!.id) + if ( + !isNewRecord && + payload.name && + selectedColumnToEdit && + selectedColumnToEdit.name !== payload.name + ) { + reAddRenamedColumnSortAndFilter(selectedColumnToEdit.name, payload.name) + } + onColumnSaved(configuration.isEncrypted) setIsEdited(false) closePanel() @@ -151,6 +161,28 @@ const SidePanelEditor: FC = ({ resolve() } + /** + * Adds the renamed column's filter and/or sort rules. + */ + const reAddRenamedColumnSortAndFilter = (oldColumnName: string, newColumnName: string) => { + setParams((prevParams) => { + const existingFilters = (prevParams?.filter ?? []) as string[] + const existingSorts = (prevParams?.sort ?? []) as string[] + + return { + ...prevParams, + filter: existingFilters.map((filter: string) => { + const [column] = filter.split(':') + return column === oldColumnName ? filter.replace(column, newColumnName) : filter + }), + sort: existingSorts.map((sort: string) => { + const [column] = sort.split(':') + return column === oldColumnName ? sort.replace(column, newColumnName) : sort + }), + } + }) + } + const saveTable = async ( payload: any, columns: ColumnField[],