Copilot commented on code in PR #17878: URL: https://github.com/apache/pinot/pull/17878#discussion_r2933816546
########## pinot-controller/src/main/resources/app/pages/LogicalTableDetails.tsx: ########## @@ -0,0 +1,346 @@ +/** + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +import React, { useState, useEffect } from 'react'; +import { makeStyles } from '@material-ui/core/styles'; +import { Grid } from '@material-ui/core'; +import { RouteComponentProps, useHistory } from 'react-router-dom'; +import { UnControlled as CodeMirror } from 'react-codemirror2'; +import { Link } from 'react-router-dom'; +import AppLoader from '../components/AppLoader'; +import CustomizedTables from '../components/Table'; +import TableToolbar from '../components/TableToolbar'; +import SimpleAccordion from '../components/SimpleAccordion'; +import PinotMethodUtils from '../utils/PinotMethodUtils'; +import CustomButton from '../components/CustomButton'; +import EditConfigOp from '../components/Homepage/Operations/EditConfigOp'; +import Confirm from '../components/Confirm'; +import { NotificationContext } from '../components/Notification/NotificationContext'; +import NotFound from '../components/NotFound'; +import 'codemirror/lib/codemirror.css'; +import 'codemirror/theme/material.css'; +import 'codemirror/mode/javascript/javascript'; + +const useStyles = makeStyles((theme) => ({ + highlightBackground: { + border: '1px #4285f4 solid', + backgroundColor: 'rgba(66, 133, 244, 0.05)', + borderRadius: 4, + marginBottom: '20px', + }, + body: { + borderTop: '1px solid #BDCCD9', + fontSize: '16px', + lineHeight: '3rem', + paddingLeft: '15px', + }, + queryOutput: { + border: '1px solid #BDCCD9', + '& .CodeMirror': { height: 532 }, + }, + sqlDiv: { + border: '1px #BDCCD9 solid', + borderRadius: 4, + marginBottom: '20px', + }, + operationDiv: { + border: '1px #BDCCD9 solid', + borderRadius: 4, + marginBottom: 20, + }, + link: { + color: '#4285f4', + textDecoration: 'none', + '&:hover': { + textDecoration: 'underline', + }, + }, +})); + +const jsonoptions = { + lineNumbers: true, + mode: 'application/json', + styleActiveLine: true, + gutters: ['CodeMirror-lint-markers'], + theme: 'default', + readOnly: true, +}; + +type Props = { + logicalTableName: string; +}; + +const ConfigSection = ({ title, data, classes }: { title: string; data: any; classes: any }) => { + if (!data) return null; + return ( + <div className={classes.sqlDiv}> + <SimpleAccordion headerTitle={title} showSearchBox={false}> + <CodeMirror + options={jsonoptions} + value={JSON.stringify(data, null, 2)} + className={classes.queryOutput} + autoCursor={false} + /> + </SimpleAccordion> + </div> + ); +}; + +const LogicalTableDetails = ({ match }: RouteComponentProps<Props>) => { + const { logicalTableName } = match.params; + const classes = useStyles(); + const history = useHistory(); + const [fetching, setFetching] = useState(true); + const [notFound, setNotFound] = useState(false); + const [logicalTableConfig, setLogicalTableConfig] = useState<any>(null); + const [configJSON, setConfigJSON] = useState(''); + + const [showEditConfig, setShowEditConfig] = useState(false); + const [config, setConfig] = useState('{}'); + const [confirmDialog, setConfirmDialog] = useState(false); + const [dialogDetails, setDialogDetails] = useState(null); + const { dispatch } = React.useContext(NotificationContext); + + const fetchData = async () => { + setFetching(true); + try { + const result = await PinotMethodUtils.getLogicalTableConfig(logicalTableName); + if (result.error || !result) { + setNotFound(true); + } else { + const parsed = typeof result === 'string' ? JSON.parse(result) : result; + setLogicalTableConfig(parsed); + setConfigJSON(JSON.stringify(parsed, null, 2)); + } + } catch (e) { + setNotFound(true); + } + setFetching(false); + }; Review Comment: `notFound` is set to `true` on failures but never reset to `false` on subsequent successful fetches (or when starting a new fetch). If a user navigates from a missing logical table to an existing one without unmounting this component (same route, different `logicalTableName`), the page will keep rendering the NotFound state. Reset `notFound` to `false` at the start of `fetchData()` (or explicitly on the success path). ########## pinot-controller/src/main/resources/app/utils/PinotMethodUtils.ts: ########## @@ -1458,6 +1461,29 @@ const getPackageVersionsData = () => { }); }; +const getLogicalTablesList = async () => { + const { data } = await getLogicalTables(); + return { + columns: ['Logical Table Name'], + records: data.map((name) => [name]) + }; +}; + Review Comment: `getLogicalTablesList()` duplicates the same endpoint + mapping logic already implemented in `getQueryLogicalTablesList()` (both call `/logicalTables` and convert `string[]` into `{ columns, records }`). Consider consolidating into a single helper (e.g., a shared formatter that takes the column header label) to avoid the two implementations drifting over time. ########## pinot-controller/src/main/resources/app/components/Breadcrumbs.tsx: ########## @@ -98,7 +99,12 @@ const BreadcrumbsComponent = ({ ...props }) => { const breadcrumbs = [getClickableLabel(breadcrumbNameMap['/'], '/')]; const paramsKeys = keys(props.match.params); if(paramsKeys.length){ - const {tenantName, tableName, segmentName, instanceName, schemaName, query, taskType, queueTableName, taskID, subTaskID} = props.match.params; + const {tenantName, tableName, segmentName, instanceName, schemaName, query, taskType, queueTableName, taskID, subTaskID, logicalTableName} = props.match.params; + if (logicalTableName) { + breadcrumbs.push( + getClickableLabel('Tables', '/tables'), Review Comment: For logical table details routes, the breadcrumb builder currently inserts a clickable "Tables" crumb when `logicalTableName` is present, but never inserts a "Logical Tables" crumb (even though `breadcrumbNameMap` defines `/logical-tables`). This makes the breadcrumb trail misleading for the new logical-table flow. Consider adding a "Logical Tables" breadcrumb (likely linking back to `/tables` where the logical tables list is shown, or to a dedicated `/logical-tables` listing route if one is added). -- This is an automated message from the Apache Git Service. To respond to the message, please log on to GitHub and use the URL above to go to the specific comment. To unsubscribe, e-mail: [email protected] For queries about this service, please contact Infrastructure at: [email protected] --------------------------------------------------------------------- To unsubscribe, e-mail: [email protected] For additional commands, e-mail: [email protected]
