korbit-ai[bot] commented on code in PR #33348:
URL: https://github.com/apache/superset/pull/33348#discussion_r2072307159


##########
superset-frontend/src/explore/components/controls/NumberControl/index.tsx:
##########
@@ -0,0 +1,77 @@
+/**
+ * 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 { styled } from '@superset-ui/core';
+import { InputNumber } from 'src/components/Input';
+import ControlHeader, { ControlHeaderProps } from '../../ControlHeader';
+
+type NumberValueType = number | undefined;
+
+export interface NumberControlProps extends ControlHeaderProps {
+  onChange?: (value: NumberValueType) => void;
+  value?: NumberValueType;
+  label?: string;
+  description?: string;
+  min?: number;
+  max?: number;
+  step?: number;
+  placeholder?: string;
+  disabled?: boolean;
+}
+
+const FullWidthDiv = styled.div`
+  width: 100%;
+`;
+
+const FullWidthInputNumber = styled(InputNumber)`
+  width: 100%;
+`;
+
+function parseValue(value: string | number | null | undefined) {
+  if (value === null || value === undefined || value === '') {
+    return undefined;
+  }
+  return Number(value);
+}
+
+export default function NumberControl({
+  min,
+  max,
+  step,
+  placeholder,
+  value,
+  onChange,
+  disabled,
+  ...rest
+}: NumberControlProps) {
+  return (
+    <FullWidthDiv>
+      <ControlHeader {...rest} />
+      <FullWidthInputNumber
+        min={min}
+        max={max}
+        step={step}
+        placeholder={placeholder}
+        value={value}
+        onChange={value => onChange?.(parseValue(value))}
+        disabled={disabled}
+        aria-label={rest.label}
+      />

Review Comment:
   ### Missing min/max validation <sub>![category 
Functionality](https://img.shields.io/badge/Functionality-0284c7)</sub>
   
   <details>
     <summary>Tell me more</summary>
   
   ###### What is the issue?
   The component doesn't validate that max is greater than min when both are 
provided, which could create an unusable threshold input.
   
   ###### Why this matters
   Users won't be able to input values if max is less than min, breaking the 
threshold functionality.
   
   ###### Suggested change ∙ *Feature Preview*
   Add validation check and adjust max if needed:
   ```typescript
   export default function NumberControl({
     min,
     max,
     step,
     placeholder,
     value,
     onChange,
     disabled,
     ...rest
   }: NumberControlProps) {
     const validatedMax = max != null && min != null ? Math.max(max, min) : max;
     return (
       <FullWidthDiv>
         <ControlHeader {...rest} />
         <FullWidthInputNumber
           min={min}
           max={validatedMax}
           step={step}
           placeholder={placeholder}
           value={value}
           onChange={value => onChange?.(parseValue(value))}
           disabled={disabled}
           aria-label={rest.label}
         />
       </FullWidthDiv>
     );
   }
   ```
   
   
   ###### Provide feedback to improve future suggestions
   [![Nice 
Catch](https://img.shields.io/badge/👍%20Nice%20Catch-71BC78)](https://app.korbit.ai/feedback/aa91ff46-6083-4491-9416-b83dd1994b51/52c16c42-ba56-4663-9f37-8f6532f2ddd4/upvote)
 
[![Incorrect](https://img.shields.io/badge/👎%20Incorrect-white)](https://app.korbit.ai/feedback/aa91ff46-6083-4491-9416-b83dd1994b51/52c16c42-ba56-4663-9f37-8f6532f2ddd4?what_not_true=true)
  [![Not in 
Scope](https://img.shields.io/badge/👎%20Out%20of%20PR%20scope-white)](https://app.korbit.ai/feedback/aa91ff46-6083-4491-9416-b83dd1994b51/52c16c42-ba56-4663-9f37-8f6532f2ddd4?what_out_of_scope=true)
 [![Not in coding 
standard](https://img.shields.io/badge/👎%20Not%20in%20our%20standards-white)](https://app.korbit.ai/feedback/aa91ff46-6083-4491-9416-b83dd1994b51/52c16c42-ba56-4663-9f37-8f6532f2ddd4?what_not_in_standard=true)
 
[![Other](https://img.shields.io/badge/👎%20Other-white)](https://app.korbit.ai/feedback/aa91ff46-6083-4491-9416-b83dd1994b51/52c16c42-ba56-4663-9f37-8f6532f2ddd4)
   </details>
   
   <sub>
   
   💬 Looking for more details? Reply to this comment to chat with Korbit.
   </sub>
   
   <!--- korbi internal id:79916287-6388-4829-af94-3c3b5cea2931 -->
   
   
   [](79916287-6388-4829-af94-3c3b5cea2931)



##########
superset-frontend/plugins/plugin-chart-echarts/src/Pie/buildQuery.ts:
##########
@@ -16,14 +16,30 @@
  * specific language governing permissions and limitations
  * under the License.
  */
-import { buildQueryContext, QueryFormData } from '@superset-ui/core';
+import {
+  buildQueryContext,
+  getMetricLabel,
+  QueryFormData,
+} from '@superset-ui/core';
+import { getContributionLabel } from './utils';
 
 export default function buildQuery(formData: QueryFormData) {
   const { metric, sort_by_metric } = formData;
+  const metricLabel = getMetricLabel(metric);
+
   return buildQueryContext(formData, baseQueryObject => [
     {
       ...baseQueryObject,
       ...(sort_by_metric && { orderby: [[metric, false]] }),
+      post_processing: [
+        {
+          operation: 'contribution',
+          options: {
+            columns: [metricLabel],
+            rename_columns: [getContributionLabel(metricLabel)],
+          },
+        },
+      ],
     },
   ]);
 }

Review Comment:
   ### Function with Multiple Responsibilities <sub>![category 
Design](https://img.shields.io/badge/Design-0d9488)</sub>
   
   <details>
     <summary>Tell me more</summary>
   
   ###### What is the issue?
   The buildQuery function handles multiple responsibilities: query 
construction, sorting logic, and post-processing configuration. This violates 
the Single Responsibility Principle.
   
   ###### Why this matters
   Having multiple responsibilities in a single function makes it harder to 
maintain, test, and modify individual aspects of the query building process 
independently.
   
   ###### Suggested change ∙ *Feature Preview*
   Split the function into smaller, focused functions:
   ```typescript
   const buildSortingConfig = (metric: string, sortByMetric: boolean) => 
     sortByMetric ? { orderby: [[metric, false]] } : {};
   
   const buildPostProcessingConfig = (metricLabel: string) => ({
     post_processing: [{
       operation: 'contribution',
       options: {
         columns: [metricLabel],
         rename_columns: [getContributionLabel(metricLabel)]
       }
     }]
   });
   
   export default function buildQuery(formData: QueryFormData) {
     const { metric, sort_by_metric } = formData;
     const metricLabel = getMetricLabel(metric);
     
     return buildQueryContext(formData, baseQueryObject => [{
       ...baseQueryObject,
       ...buildSortingConfig(metric, sort_by_metric),
       ...buildPostProcessingConfig(metricLabel)
     }]);
   }
   ```
   
   
   ###### Provide feedback to improve future suggestions
   [![Nice 
Catch](https://img.shields.io/badge/👍%20Nice%20Catch-71BC78)](https://app.korbit.ai/feedback/aa91ff46-6083-4491-9416-b83dd1994b51/5b253641-be7a-4a0b-aa42-dabbd651a9e2/upvote)
 
[![Incorrect](https://img.shields.io/badge/👎%20Incorrect-white)](https://app.korbit.ai/feedback/aa91ff46-6083-4491-9416-b83dd1994b51/5b253641-be7a-4a0b-aa42-dabbd651a9e2?what_not_true=true)
  [![Not in 
Scope](https://img.shields.io/badge/👎%20Out%20of%20PR%20scope-white)](https://app.korbit.ai/feedback/aa91ff46-6083-4491-9416-b83dd1994b51/5b253641-be7a-4a0b-aa42-dabbd651a9e2?what_out_of_scope=true)
 [![Not in coding 
standard](https://img.shields.io/badge/👎%20Not%20in%20our%20standards-white)](https://app.korbit.ai/feedback/aa91ff46-6083-4491-9416-b83dd1994b51/5b253641-be7a-4a0b-aa42-dabbd651a9e2?what_not_in_standard=true)
 
[![Other](https://img.shields.io/badge/👎%20Other-white)](https://app.korbit.ai/feedback/aa91ff46-6083-4491-9416-b83dd1994b51/5b253641-be7a-4a0b-aa42-dabbd651a9e2)
   </details>
   
   <sub>
   
   💬 Looking for more details? Reply to this comment to chat with Korbit.
   </sub>
   
   <!--- korbi internal id:95b63b48-b984-4510-aef3-eb6c88920025 -->
   
   
   [](95b63b48-b984-4510-aef3-eb6c88920025)



##########
superset-frontend/src/explore/components/controls/NumberControl/index.tsx:
##########
@@ -0,0 +1,77 @@
+/**
+ * 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 { styled } from '@superset-ui/core';
+import { InputNumber } from 'src/components/Input';
+import ControlHeader, { ControlHeaderProps } from '../../ControlHeader';
+
+type NumberValueType = number | undefined;
+
+export interface NumberControlProps extends ControlHeaderProps {
+  onChange?: (value: NumberValueType) => void;
+  value?: NumberValueType;
+  label?: string;
+  description?: string;
+  min?: number;
+  max?: number;
+  step?: number;
+  placeholder?: string;
+  disabled?: boolean;
+}
+
+const FullWidthDiv = styled.div`
+  width: 100%;
+`;
+
+const FullWidthInputNumber = styled(InputNumber)`
+  width: 100%;
+`;
+
+function parseValue(value: string | number | null | undefined) {
+  if (value === null || value === undefined || value === '') {
+    return undefined;
+  }
+  return Number(value);
+}

Review Comment:
   ### Missing NaN value validation <sub>![category Error 
Handling](https://img.shields.io/badge/Error%20Handling-ea580c)</sub>
   
   <details>
     <summary>Tell me more</summary>
   
   ###### What is the issue?
   The parseValue function doesn't handle invalid number conversions, which 
could lead to NaN values being silently passed through.
   
   ###### Why this matters
   When invalid numeric strings are converted using Number(), it returns NaN. 
This could cause unexpected behavior in the pie chart threshold calculations.
   
   ###### Suggested change ∙ *Feature Preview*
   Add NaN check to return undefined for invalid numbers:
   ```typescript
   function parseValue(value: string | number | null | undefined) {
     if (value === null || value === undefined || value === '') {
       return undefined;
     }
     const num = Number(value);
     return Number.isNaN(num) ? undefined : num;
   }
   ```
   
   
   ###### Provide feedback to improve future suggestions
   [![Nice 
Catch](https://img.shields.io/badge/👍%20Nice%20Catch-71BC78)](https://app.korbit.ai/feedback/aa91ff46-6083-4491-9416-b83dd1994b51/270132db-5412-4a54-80d9-11bb02d7ca86/upvote)
 
[![Incorrect](https://img.shields.io/badge/👎%20Incorrect-white)](https://app.korbit.ai/feedback/aa91ff46-6083-4491-9416-b83dd1994b51/270132db-5412-4a54-80d9-11bb02d7ca86?what_not_true=true)
  [![Not in 
Scope](https://img.shields.io/badge/👎%20Out%20of%20PR%20scope-white)](https://app.korbit.ai/feedback/aa91ff46-6083-4491-9416-b83dd1994b51/270132db-5412-4a54-80d9-11bb02d7ca86?what_out_of_scope=true)
 [![Not in coding 
standard](https://img.shields.io/badge/👎%20Not%20in%20our%20standards-white)](https://app.korbit.ai/feedback/aa91ff46-6083-4491-9416-b83dd1994b51/270132db-5412-4a54-80d9-11bb02d7ca86?what_not_in_standard=true)
 
[![Other](https://img.shields.io/badge/👎%20Other-white)](https://app.korbit.ai/feedback/aa91ff46-6083-4491-9416-b83dd1994b51/270132db-5412-4a54-80d9-11bb02d7ca86)
   </details>
   
   <sub>
   
   💬 Looking for more details? Reply to this comment to chat with Korbit.
   </sub>
   
   <!--- korbi internal id:f9d2b4b8-740f-4d7e-abd1-5f953728801b -->
   
   
   [](f9d2b4b8-740f-4d7e-abd1-5f953728801b)



##########
superset-frontend/plugins/plugin-chart-echarts/src/Pie/types.ts:
##########
@@ -82,9 +83,20 @@ export const DEFAULT_FORM_DATA: EchartsPieFormData = {
   showLabelsThreshold: 5,
   dateFormat: 'smart_date',
   roseType: null,
+  thresholdForOther: 0,
 };
 
 export type PieChartTransformedProps =
   BaseTransformedProps<EchartsPieFormData> &
     ContextMenuTransformedProps &
     CrossFilterTransformedProps;
+
+export interface PieChartDataItem {
+  name: string;
+  value: number;
+  itemStyle: {
+    color: string;
+    opacity: number;
+  };
+  [key: string]: unknown;
+}

Review Comment:
   ### Over-permissive interface definition <sub>![category 
Design](https://img.shields.io/badge/Design-0d9488)</sub>
   
   <details>
     <summary>Tell me more</summary>
   
   ###### What is the issue?
   The PieChartDataItem interface includes an index signature [key: string]: 
unknown which makes the interface overly permissive and violates Interface 
Segregation Principle.
   
   ###### Why this matters
   Using an index signature allows adding any string-keyed properties with 
unknown types, reducing type safety and making the interface harder to 
maintain. This can lead to runtime errors and makes refactoring more difficult.
   
   ###### Suggested change ∙ *Feature Preview*
   ```typescript
   export interface PieChartDataItem {
     name: string;
     value: number;
     itemStyle: {
       color: string;
       opacity: number;
     };
     // Add specific optional properties if needed instead of [key: string]: 
unknown
   }
   ```
   
   
   ###### Provide feedback to improve future suggestions
   [![Nice 
Catch](https://img.shields.io/badge/👍%20Nice%20Catch-71BC78)](https://app.korbit.ai/feedback/aa91ff46-6083-4491-9416-b83dd1994b51/694a2057-4dd7-4241-bf1c-7ca854c88ff2/upvote)
 
[![Incorrect](https://img.shields.io/badge/👎%20Incorrect-white)](https://app.korbit.ai/feedback/aa91ff46-6083-4491-9416-b83dd1994b51/694a2057-4dd7-4241-bf1c-7ca854c88ff2?what_not_true=true)
  [![Not in 
Scope](https://img.shields.io/badge/👎%20Out%20of%20PR%20scope-white)](https://app.korbit.ai/feedback/aa91ff46-6083-4491-9416-b83dd1994b51/694a2057-4dd7-4241-bf1c-7ca854c88ff2?what_out_of_scope=true)
 [![Not in coding 
standard](https://img.shields.io/badge/👎%20Not%20in%20our%20standards-white)](https://app.korbit.ai/feedback/aa91ff46-6083-4491-9416-b83dd1994b51/694a2057-4dd7-4241-bf1c-7ca854c88ff2?what_not_in_standard=true)
 
[![Other](https://img.shields.io/badge/👎%20Other-white)](https://app.korbit.ai/feedback/aa91ff46-6083-4491-9416-b83dd1994b51/694a2057-4dd7-4241-bf1c-7ca854c88ff2)
   </details>
   
   <sub>
   
   💬 Looking for more details? Reply to this comment to chat with Korbit.
   </sub>
   
   <!--- korbi internal id:50bbdd57-5c3c-48ca-9d54-40581c65f9ab -->
   
   
   [](50bbdd57-5c3c-48ca-9d54-40581c65f9ab)



##########
superset-frontend/plugins/plugin-chart-echarts/src/Pie/buildQuery.ts:
##########
@@ -16,14 +16,30 @@
  * specific language governing permissions and limitations
  * under the License.
  */
-import { buildQueryContext, QueryFormData } from '@superset-ui/core';
+import {
+  buildQueryContext,
+  getMetricLabel,
+  QueryFormData,
+} from '@superset-ui/core';
+import { getContributionLabel } from './utils';
 
 export default function buildQuery(formData: QueryFormData) {
   const { metric, sort_by_metric } = formData;
+  const metricLabel = getMetricLabel(metric);
+
   return buildQueryContext(formData, baseQueryObject => [
     {
       ...baseQueryObject,
       ...(sort_by_metric && { orderby: [[metric, false]] }),
+      post_processing: [
+        {
+          operation: 'contribution',
+          options: {
+            columns: [metricLabel],
+            rename_columns: [getContributionLabel(metricLabel)],
+          },
+        },
+      ],

Review Comment:
   ### Inefficient Post-Processing of Large Datasets <sub>![category 
Performance](https://img.shields.io/badge/Performance-4f46e5)</sub>
   
   <details>
     <summary>Tell me more</summary>
   
   ###### What is the issue?
   The contribution calculation is performed as a post-processing step on the 
entire dataset, which could be inefficient for large datasets.
   
   ###### Why this matters
   Processing the entire dataset after retrieval can cause unnecessary memory 
usage and CPU overhead on the client side, especially when dealing with large 
datasets.
   
   ###### Suggested change ∙ *Feature Preview*
   Consider pushing the contribution calculation to the database level by 
modifying the SQL query. This can be done by using database-specific window 
functions or aggregate functions to calculate percentages directly in the 
query. Example structure:
   ```typescript
   return buildQueryContext(formData, baseQueryObject => [
     {
       ...baseQueryObject,
       metrics: [
         {
           aggregate: 'RATIO_TO_REPORT',
           column: metric,
           label: getContributionLabel(metricLabel)
         }
       ]
     }
   ]);
   ```
   
   
   ###### Provide feedback to improve future suggestions
   [![Nice 
Catch](https://img.shields.io/badge/👍%20Nice%20Catch-71BC78)](https://app.korbit.ai/feedback/aa91ff46-6083-4491-9416-b83dd1994b51/abdd3977-7acd-43ce-95e9-84b5317ad33f/upvote)
 
[![Incorrect](https://img.shields.io/badge/👎%20Incorrect-white)](https://app.korbit.ai/feedback/aa91ff46-6083-4491-9416-b83dd1994b51/abdd3977-7acd-43ce-95e9-84b5317ad33f?what_not_true=true)
  [![Not in 
Scope](https://img.shields.io/badge/👎%20Out%20of%20PR%20scope-white)](https://app.korbit.ai/feedback/aa91ff46-6083-4491-9416-b83dd1994b51/abdd3977-7acd-43ce-95e9-84b5317ad33f?what_out_of_scope=true)
 [![Not in coding 
standard](https://img.shields.io/badge/👎%20Not%20in%20our%20standards-white)](https://app.korbit.ai/feedback/aa91ff46-6083-4491-9416-b83dd1994b51/abdd3977-7acd-43ce-95e9-84b5317ad33f?what_not_in_standard=true)
 
[![Other](https://img.shields.io/badge/👎%20Other-white)](https://app.korbit.ai/feedback/aa91ff46-6083-4491-9416-b83dd1994b51/abdd3977-7acd-43ce-95e9-84b5317ad33f)
   </details>
   
   <sub>
   
   💬 Looking for more details? Reply to this comment to chat with Korbit.
   </sub>
   
   <!--- korbi internal id:56467f41-ae9e-4559-a94f-fede0fd05f02 -->
   
   
   [](56467f41-ae9e-4559-a94f-fede0fd05f02)



-- 
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: notifications-unsubscr...@superset.apache.org

For queries about this service, please contact Infrastructure at:
us...@infra.apache.org


---------------------------------------------------------------------
To unsubscribe, e-mail: notifications-unsubscr...@superset.apache.org
For additional commands, e-mail: notifications-h...@superset.apache.org

Reply via email to