kgabryje commented on code in PR #26202:
URL: https://github.com/apache/superset/pull/26202#discussion_r1473407080


##########
package-lock.json:
##########
@@ -0,0 +1,6 @@
+{

Review Comment:
   Delete this file too, I suppose it got created when you accidentally run 
`npm install` in the root superset dir :)



##########
superset-frontend/src/components/CronPicker/CronPicker.tsx:
##########
@@ -110,22 +110,81 @@ export const CronPicker = styled((props: CronProps) => (
     <ReactCronPicker locale={LOCALE} {...props} />
   </ConfigProvider>
 ))`
-  .react-js-cron-field {
-    margin-bottom: 0px;
+:has(.react-js-cron-months){

Review Comment:
   Suggestion - if you wrap the whole css code in `${({ theme }) => ...}`, you 
won't need to call it in every line. For example `grid-column-gap: ${({ theme 
}) => theme.gridUnit}px;` will become `grid-column-gap: ${theme.gridUnit}px;`
   You can see an example in `DashboardWrapper.tsx` and many other files
   
   <img width="589" alt="image" 
src="https://github.com/apache/superset/assets/15073128/73a826ea-f8c0-4d73-af1e-b240230d5797";>
   



##########
superset-frontend/src/components/Button/index.tsx:
##########
@@ -209,7 +209,7 @@ export default function Button(props: ButtonProps) {
     return (
       <Tooltip
         placement={placement}
-        id={`${kebabCase(tooltip)}-tooltip`}
+        // id={`${kebabCase(tooltip)}-tooltip`}

Review Comment:
   I don't think this change is relevant, could you uncomment?



##########
superset-frontend/src/features/alerts/AlertReportModal.tsx:
##########
@@ -385,47 +354,83 @@ interface NotificationMethodAddProps {
 }
 
 export const TRANSLATIONS = {
-  ADD_NOTIFICATION_METHOD_TEXT: t('Add notification method'),
-  ADD_DELIVERY_METHOD_TEXT: t('Add delivery method'),
-  SAVE_TEXT: t('Save'),
-  ADD_TEXT: t('Add'),
+  // Modal header
   EDIT_REPORT_TEXT: t('Edit Report'),
   EDIT_ALERT_TEXT: t('Edit Alert'),
   ADD_REPORT_TEXT: t('Add Report'),
   ADD_ALERT_TEXT: t('Add Alert'),
+  // Panel titles
+  GENERAL_TITLE: t('General information'),
+  ALERT_CONDITION_TITLE: t('Alert condition'),
+  ALERT_CONTENTS_TITLE: t('Alert contents'),
+  REPORT_CONTENTS_TITLE: t('Report contents'),
+  SCHEDULE_TITLE: t('Schedule'),
+  NOTIFICATION_TITLE: t('Notification method'),
+  // Panel subtitles
+  GENERAL_SUBTITLE: t('Set up basic details, such as name and description.'),
+  ALERT_CONDITION_SUBTITLE: t(
+    'Define the database, SQL query, and triggering conditions for alert.',
+  ),
+  CONTENTS_SUBTITLE: t('Customize data source, filters, and layout.'),
+  SCHEDULE_SUBTITLE: t(
+    'Define delivery schedule, timezone, and frequency settings.',
+  ),
+  NOTIFICATION_SUBTITLE: t('Choose notification method and recipients.'),
+  // General info panel inputs
   REPORT_NAME_TEXT: t('Report name'),
+  REPORT_NAME_PLACEHOLDER: t('Enter report name'),
   ALERT_NAME_TEXT: t('Alert name'),
+  ALERT_NAME_PLACEHOLDER: t('Enter alert name'),
   OWNERS_TEXT: t('Owners'),
+  OWNERS_PLACEHOLDER: t('Select owners'),
   DESCRIPTION_TEXT: t('Description'),
-  ACTIVE_TEXT: t('Active'),
-  ALERT_CONDITION_TEXT: t('Alert condition'),
+  REPORT_DESCRIPTION_PLACEHOLDER: t(
+    'Include description to be sent with report',
+  ),
+  ALERT_DESCRIPTION_PLACEHOLDER: t('Include description to be sent with 
alert'),
+  ACTIVE_REPORT_TEXT: t('Report is active'),
+  ACTIVE_ALERT_TEXT: t('Alert is active'),
+  // Alert condition panel inputs
   DATABASE_TEXT: t('Database'),
+  DATABASE_PLACEHOLDER: t('Select database'),
   SQL_QUERY_TEXT: t('SQL Query'),
   SQL_QUERY_TOOLTIP: t(
-    'The result of this query should be a numeric-esque value',
+    'The result of this query must be a value capable of numeric 
interpretation e.g. 1, 1.0, or "1" (compatible with Python\'s float() 
function).',
   ),
   TRIGGER_ALERT_IF_TEXT: t('Trigger Alert If...'),
   CONDITION_TEXT: t('Condition'),
+  CONDITION_PLACEHOLDER: t('Condition'),
   VALUE_TEXT: t('Value'),
-  REPORT_SCHEDULE_TEXT: t('Report schedule'),
-  ALERT_CONDITION_SCHEDULE_TEXT: t('Alert condition schedule'),
+  // Contents panel inputs
+  CONTENT_TYPE_LABEL: t('Content type'),
+  CONTENT_TYPE_PLACEHOLDER: t('Select content type'),
+  SELECT_DASHBOARD_LABEL: t('Select dashboard'),
+  SELECT_DASHBOARD_PLACEHOLDER: t('Select dashboard to use'),
+  SELECT_CHART_LABEL: t('Select chart'),
+  SELECT_CHART_PLACEHOLDER: t('Select chart to use'),
+  DASHBOARD_TEXT: t('Dashboard'),
+  CHART_TEXT: t('Chart'),
+  FORMAT_TYPE_LABEL: t('Content Format'),

Review Comment:
   nit - lowercase `format`



##########
superset-frontend/src/features/alerts/components/AlertReportCronScheduler.test.tsx:
##########
@@ -32,7 +32,7 @@ const createProps = (props: 
Partial<AlertReportCronSchedulerProps> = {}) => ({
   ...props,
 });
 
-test('should render', () => {
+test.skip('should render', () => {

Review Comment:
   Can we unskip/fix those tests?



##########
superset-frontend/src/features/alerts/AlertReportModal.tsx:
##########
@@ -1190,367 +1313,456 @@ const AlertReportModal: 
FunctionComponent<AlertReportModalProps> = ({
         </h4>
       }
     >
-      <StyledSectionContainer>
-        <div className="header-section">
-          <StyledInputContainer>
-            <div className="control-label">
-              {isReport
-                ? TRANSLATIONS.REPORT_NAME_TEXT
-                : TRANSLATIONS.ALERT_NAME_TEXT}
-              <span className="required">*</span>
-            </div>
-            <div className="input-container">
-              <input
-                type="text"
-                name="name"
-                value={currentAlert ? currentAlert.name : ''}
-                placeholder={
-                  isReport
-                    ? TRANSLATIONS.REPORT_NAME_TEXT
-                    : TRANSLATIONS.ALERT_NAME_TEXT
-                }
-                onChange={onInputChange}
-                css={inputSpacer}
-              />
-            </div>
-          </StyledInputContainer>
-          <StyledInputContainer>
-            <div className="control-label">
-              {TRANSLATIONS.OWNERS_TEXT}
-              <span className="required">*</span>
-            </div>
-            <div data-test="owners-select" className="input-container">
-              <AsyncSelect
-                ariaLabel={TRANSLATIONS.OWNERS_TEXT}
-                allowClear
-                name="owners"
-                mode="multiple"
-                value={
-                  (currentAlert?.owners as {
-                    label: string;
-                    value: number;
-                  }[]) || []
-                }
-                options={loadOwnerOptions}
-                onChange={onOwnersChange}
-                css={inputSpacer}
-              />
-            </div>
-          </StyledInputContainer>
-          <StyledInputContainer>
-            <div 
className="control-label">{TRANSLATIONS.DESCRIPTION_TEXT}</div>
-            <div className="input-container">
-              <input
-                type="text"
-                name="description"
-                value={currentAlert ? currentAlert.description || '' : ''}
-                placeholder={TRANSLATIONS.DESCRIPTION_TEXT}
-                onChange={onInputChange}
-                css={inputSpacer}
-              />
-            </div>
-          </StyledInputContainer>
-          <StyledSwitchContainer>
-            <Switch
-              onChange={onActiveSwitch}
-              checked={currentAlert ? currentAlert.active : true}
+      <Collapse
+        expandIconPosition="right"
+        defaultActiveKey="1"
+        accordion
+        style={{ border: 'none' }}
+      >
+        <StyledPanel
+          header={
+            <ValidatedPanelHeader
+              title={TRANSLATIONS.GENERAL_TITLE}
+              subtitle={TRANSLATIONS.GENERAL_SUBTITLE}
+              required
+              validateCheckStatus={validationStatus[Sections.GENERAL].status}
+              testId="general-information-panel"
             />
-            <div className="switch-label">{TRANSLATIONS.ACTIVE_TEXT}</div>
-          </StyledSwitchContainer>
-        </div>
-        <div className="column-section">
-          {!isReport && (
-            <div className="column condition">
-              <StyledSectionTitle>
-                <h4>{TRANSLATIONS.ALERT_CONDITION_TEXT}</h4>
-              </StyledSectionTitle>
-              <StyledInputContainer>
-                <div className="control-label">
-                  {TRANSLATIONS.DATABASE_TEXT}
-                  <span className="required">*</span>
-                </div>
-                <div className="input-container">
-                  <AsyncSelect
-                    ariaLabel={TRANSLATIONS.DATABASE_TEXT}
-                    name="source"
-                    value={
-                      currentAlert?.database?.label &&
-                      currentAlert?.database?.value
-                        ? {
-                            value: currentAlert.database.value,
-                            label: currentAlert.database.label,
-                          }
-                        : undefined
-                    }
-                    options={loadSourceOptions}
-                    onChange={onSourceChange}
-                  />
-                </div>
-              </StyledInputContainer>
-              <StyledInputContainer>
-                <div className="control-label">
-                  {TRANSLATIONS.SQL_QUERY_TEXT}
-                  <StyledTooltip tooltip={TRANSLATIONS.SQL_QUERY_TOOLTIP} />
-                  <span className="required">*</span>
-                </div>
-                <TextAreaControl
-                  name="sql"
-                  language="sql"
-                  offerEditInModal={false}
-                  minLines={15}
-                  maxLines={15}
-                  onChange={onSQLChange}
-                  readOnly={false}
-                  initialValue={resource?.sql}
-                  key={currentAlert?.id}
+          }
+          key="1"
+          style={panelBorder}
+        >
+          <div className="header-section">
+            <StyledInputContainer>
+              <div className="control-label">
+                {isReport
+                  ? TRANSLATIONS.REPORT_NAME_TEXT
+                  : TRANSLATIONS.ALERT_NAME_TEXT}
+                <span className="required">*</span>
+              </div>
+              <div className="input-container">
+                <input
+                  type="text"
+                  name="name"
+                  value={currentAlert ? currentAlert.name : ''}
+                  placeholder={
+                    isReport
+                      ? TRANSLATIONS.REPORT_NAME_PLACEHOLDER
+                      : TRANSLATIONS.ALERT_NAME_PLACEHOLDER
+                  }
+                  onChange={onInputChange}
                 />
-              </StyledInputContainer>
-              <div className="inline-container wrap">
-                <StyledInputContainer>
-                  <div className="control-label" css={inputSpacer}>
-                    {TRANSLATIONS.TRIGGER_ALERT_IF_TEXT}
-                    <span className="required">*</span>
-                  </div>
-                  <div className="input-container">
-                    <Select
-                      ariaLabel={TRANSLATIONS.CONDITION_TEXT}
-                      onChange={onConditionChange}
-                      placeholder="Condition"
-                      value={
-                        currentAlert?.validator_config_json?.op || undefined
-                      }
-                      options={CONDITIONS}
-                      css={inputSpacer}
-                    />
-                  </div>
-                </StyledInputContainer>
-                <StyledInputContainer>
-                  <div className="control-label">
-                    {TRANSLATIONS.VALUE_TEXT}
-                    <span className="required">*</span>
-                  </div>
-                  <div className="input-container">
-                    <input
-                      type="number"
-                      name="threshold"
-                      disabled={conditionNotNull}
-                      value={
-                        currentAlert?.validator_config_json?.threshold !==
-                        undefined
-                          ? currentAlert.validator_config_json.threshold
-                          : ''
-                      }
-                      placeholder={TRANSLATIONS.VALUE_TEXT}
-                      onChange={onThresholdChange}
-                    />
-                  </div>
-                </StyledInputContainer>
               </div>
-            </div>
-          )}
-          <div className="column schedule">
-            <StyledSectionTitle>
-              <h4>
-                {isReport
-                  ? TRANSLATIONS.REPORT_SCHEDULE_TEXT
-                  : TRANSLATIONS.ALERT_CONDITION_SCHEDULE_TEXT}
-              </h4>
-              <span className="required">*</span>
-            </StyledSectionTitle>
-            <AlertReportCronScheduler
-              value={currentAlert?.crontab || ALERT_REPORTS_DEFAULT_CRON_VALUE}
-              onChange={newVal => updateAlertState('crontab', newVal)}
-            />
-            <div className="control-label">{TRANSLATIONS.TIMEZONE_TEXT}</div>
-            <div
-              className="input-container"
-              css={(theme: SupersetTheme) => timezoneHeaderStyle(theme)}
-            >
-              <TimezoneSelector
-                onTimezoneChange={onTimezoneChange}
-                timezone={currentAlert?.timezone}
-                minWidth="100%"
-              />
-            </div>
-            <StyledSectionTitle>
-              <h4>{TRANSLATIONS.SCHEDULE_SETTINGS_TEXT}</h4>
-            </StyledSectionTitle>
+            </StyledInputContainer>
             <StyledInputContainer>
               <div className="control-label">
-                {TRANSLATIONS.LOG_RETENTION_TEXT}
+                {TRANSLATIONS.OWNERS_TEXT}
                 <span className="required">*</span>
               </div>
-              <div className="input-container">
-                <Select
-                  ariaLabel={TRANSLATIONS.LOG_RETENTION_TEXT}
-                  placeholder={TRANSLATIONS.LOG_RETENTION_TEXT}
-                  onChange={onLogRetentionChange}
+              <div data-test="owners-select" className="input-container">
+                <AsyncSelect
+                  ariaLabel={TRANSLATIONS.OWNERS_TEXT}
+                  allowClear
+                  name="owners"
+                  mode="multiple"
+                  placeholder={TRANSLATIONS.OWNERS_PLACEHOLDER}
                   value={
-                    typeof currentAlert?.log_retention === 'number'
-                      ? currentAlert?.log_retention
-                      : ALERT_REPORTS_DEFAULT_RETENTION
+                    (currentAlert?.owners as {
+                      label: string;
+                      value: number;
+                    }[]) || []
                   }
-                  options={RETENTION_OPTIONS}
-                  sortComparator={propertyComparator('value')}
+                  options={loadOwnerOptions}
+                  onChange={onOwnersChange}
                 />
               </div>
             </StyledInputContainer>
             <StyledInputContainer>
               <div className="control-label">
-                {TRANSLATIONS.WORKING_TIMEOUT_TEXT}
-                <span className="required">*</span>
+                {TRANSLATIONS.DESCRIPTION_TEXT}
               </div>
               <div className="input-container">
                 <input
-                  type="number"
-                  min="1"
-                  name="working_timeout"
-                  value={currentAlert?.working_timeout || ''}
-                  placeholder={TRANSLATIONS.TIME_IN_SECONDS_TEXT}
-                  onChange={onTimeoutVerifyChange}
+                  type="text"
+                  name="description"
+                  value={currentAlert ? currentAlert.description || '' : ''}
+                  placeholder={
+                    isReport
+                      ? TRANSLATIONS.REPORT_DESCRIPTION_PLACEHOLDER
+                      : TRANSLATIONS.ALERT_DESCRIPTION_PLACEHOLDER
+                  }
+                  onChange={onInputChange}
                 />
-                <span 
className="input-label">{TRANSLATIONS.SECONDS_TEXT}</span>
               </div>
             </StyledInputContainer>
-            {!isReport && (
-              <StyledInputContainer>
+            <StyledSwitchContainer>
+              <Switch
+                checked={currentAlert ? currentAlert.active : false}
+                defaultChecked
+                onChange={onActiveSwitch}
+              />
+              <div className="switch-label">
+                {isReport
+                  ? TRANSLATIONS.ACTIVE_REPORT_TEXT
+                  : TRANSLATIONS.ACTIVE_ALERT_TEXT}
+              </div>
+            </StyledSwitchContainer>
+          </div>
+        </StyledPanel>
+        {!isReport && (
+          <StyledPanel
+            header={
+              <ValidatedPanelHeader
+                title={TRANSLATIONS.ALERT_CONDITION_TITLE}
+                subtitle={TRANSLATIONS.ALERT_CONDITION_SUBTITLE}
+                required={false}
+                validateCheckStatus={validationStatus[Sections.ALERT].status}
+                testId="alert-condition-panel"
+              />
+            }
+            key="2"
+            style={{ borderBottom: 'none' }}
+          >
+            <StyledInputContainer>
+              <div className="control-label">
+                {TRANSLATIONS.DATABASE_TEXT}
+                <span className="required">*</span>
+              </div>
+              <div className="input-container">
+                <AsyncSelect
+                  ariaLabel={TRANSLATIONS.DATABASE_TEXT}
+                  name="source"
+                  placeholder={TRANSLATIONS.DATABASE_PLACEHOLDER}
+                  value={
+                    currentAlert?.database?.label &&
+                    currentAlert?.database?.value
+                      ? {
+                          value: currentAlert.database.value,
+                          label: currentAlert.database.label,
+                        }
+                      : undefined
+                  }
+                  options={loadSourceOptions}
+                  onChange={onSourceChange}
+                />
+              </div>
+            </StyledInputContainer>
+            <StyledInputContainer>
+              <div className="control-label">
+                {TRANSLATIONS.SQL_QUERY_TEXT}
+                <StyledTooltip tooltip={TRANSLATIONS.SQL_QUERY_TOOLTIP} />
+                <span className="required">*</span>
+              </div>
+              <TextAreaControl
+                name="sql"
+                language="sql"
+                offerEditInModal={false}
+                minLines={15}
+                maxLines={15}
+                onChange={onSQLChange}
+                readOnly={false}
+                initialValue={resource?.sql}
+                key={currentAlert?.id}
+              />
+            </StyledInputContainer>
+            <div className="inline-container wrap">
+              <StyledInputContainer css={no_margin_bottom}>
+                <div className="control-label" css={inputSpacer}>
+                  {TRANSLATIONS.TRIGGER_ALERT_IF_TEXT}
+                  <span className="required">*</span>
+                </div>
+                <div className="input-container">
+                  <Select
+                    ariaLabel={TRANSLATIONS.CONDITION_TEXT}
+                    onChange={onConditionChange}
+                    placeholder={TRANSLATIONS.CONDITION_PLACEHOLDER}
+                    value={currentAlert?.validator_config_json?.op || 
undefined}
+                    options={CONDITIONS}
+                    css={inputSpacer}
+                  />
+                </div>
+              </StyledInputContainer>
+              <StyledInputContainer css={no_margin_bottom}>
                 <div className="control-label">
-                  {TRANSLATIONS.GRACE_PERIOD_TEXT}
+                  {TRANSLATIONS.VALUE_TEXT} <span className="required">*</span>
                 </div>
                 <div className="input-container">
                   <input
                     type="number"
-                    min="1"
-                    name="grace_period"
-                    value={currentAlert?.grace_period || ''}
-                    placeholder={TRANSLATIONS.TIME_IN_SECONDS_TEXT}
-                    onChange={onTimeoutVerifyChange}
+                    name="threshold"
+                    disabled={conditionNotNull}
+                    value={
+                      currentAlert?.validator_config_json?.threshold !==
+                      undefined
+                        ? currentAlert.validator_config_json.threshold
+                        : ''
+                    }
+                    placeholder={TRANSLATIONS.VALUE_TEXT}
+                    onChange={onThresholdChange}
                   />
-                  <span className="input-label">
-                    {TRANSLATIONS.SECONDS_TEXT}
-                  </span>
                 </div>
               </StyledInputContainer>
-            )}
-          </div>
-          <div className="column message">
-            <StyledSectionTitle>
-              <h4>{TRANSLATIONS.MESSAGE_CONTENT_TEXT}</h4>
+            </div>
+          </StyledPanel>
+        )}
+        <StyledPanel
+          header={
+            <ValidatedPanelHeader
+              title={
+                isReport
+                  ? TRANSLATIONS.REPORT_CONTENTS_TITLE
+                  : TRANSLATIONS.ALERT_CONTENTS_TITLE
+              }
+              subtitle={TRANSLATIONS.CONTENTS_SUBTITLE}
+              required
+              validateCheckStatus={validationStatus[Sections.CONTENT].status}
+              testId="contents-panel"

Review Comment:
   `data-test`



##########
Pipfile.lock:
##########
@@ -0,0 +1,20 @@
+{

Review Comment:
   Could you also delete this file?



##########
superset-frontend/src/features/alerts/AlertReportModal.tsx:
##########
@@ -966,18 +1016,14 @@ const AlertReportModal: 
FunctionComponent<AlertReportModalProps> = ({
     updateAlertState('timezone', timezone);
   };
 
-  const onContentTypeChange = (event: any) => {
-    const { target } = event;
+  const onContentTypeChange = (value: any) => {

Review Comment:
   Could we use appropriate type here instead of `any`?



##########
superset-frontend/src/features/alerts/AlertReportModal.tsx:
##########
@@ -143,62 +148,60 @@ const RETENTION_OPTIONS = [
   },
 ];
 
-const StyledModal = styled(Modal)`
-  max-width: 1200px;
-  width: 100%;
+const CONTENT_TYPE_OPTIONS = [
+  {
+    label: t('Dashboard'),
+    value: 'dashboard',
+  },
+  {
+    label: t('Chart'),
+    value: 'chart',
+  },
+];
+const FORMAT_OPTIONS = {
+  png: {
+    label: t('Send as PNG'),
+    value: 'PNG',
+  },
+  csv: {
+    label: t('Send as CSV'),
+    value: 'CSV',
+  },
+  txt: {
+    label: t('Send as text'),
+    value: 'TEXT',
+  },
+};
 
-  .ant-modal-body {
-    overflow: initial;
-  }
-`;
+// Style Constants
+const MODAL_BODY_HEIGHT = 180.5;
 
-const StyledTooltip = styled(InfoTooltipWithTrigger)`
-  margin-left: ${({ theme }) => theme.gridUnit}px;
-`;
+// Apply to collapse panels as 'style' prop value
+const panelBorder = {
+  borderBottom: 'none',
+};
 
-const StyledIcon = (theme: SupersetTheme) => css`
-  margin: auto ${theme.gridUnit * 2}px auto 0;
-  color: ${theme.colors.grayscale.base};
+// Apply to final text input components of each collapse panel
+const no_margin_bottom = css`
+  margin-bottom: 0;
 `;
 
-const StyledSectionContainer = styled.div`
-  display: flex;
-  flex-direction: column;
+// Styled Components
 
-  .control-label {
-    margin-top: ${({ theme }) => theme.gridUnit}px;
-  }
+/*
+Height of modal body defined here, total width defined at component invocation 
as antd prop.
 
-  .header-section {
-    display: flex;
-    flex: 0 0 auto;
-    align-items: center;
-    width: 100%;
-    padding: ${({ theme }) => theme.gridUnit * 4}px;
-    border-bottom: 1px solid ${({ theme }) => theme.colors.grayscale.light2};
+.inline-container applies to Alert Condition panel <div> containing trigger
+and value inputs.
+ */
+const StyledModal = styled(Modal)`
+  .ant-modal-body {
+    height: ${({ theme }) => theme.gridUnit * MODAL_BODY_HEIGHT}px;
+    height: ${({ theme }) => theme.gridUnit * MODAL_BODY_HEIGHT}px;

Review Comment:
   1. height is duplicated
   2. That value is not really related to grid units. I _think_ it's fine to 
just hardcode it to 720px. @rusackas @geido wdyt?



##########
superset-frontend/src/features/alerts/AlertReportModal.tsx:
##########
@@ -88,7 +93,7 @@ interface AlertReportModalProps {
 }
 
 const DEFAULT_WORKING_TIMEOUT = 3600;
-const DEFAULT_CRON_VALUE = '0 * * * *'; // every hour
+const DEFAULT_CRON_VALUE = '0 0 * * *'; // every day

Review Comment:
   I don't see the default value in CRON expression control - instead it's empty



##########
superset-frontend/src/components/CronPicker/CronPicker.tsx:
##########
@@ -44,7 +44,7 @@ export const LOCALE: Locale = {
   prefixMonths: t('in'),
   prefixMonthDays: t('on'),
   prefixWeekDays: t('on'),
-  prefixWeekDaysForMonthAndYearPeriod: t('and'),
+  prefixWeekDaysForMonthAndYearPeriod: t('or'),

Review Comment:
   What is this change related to? I can't find it 



##########
superset-frontend/src/features/alerts/AlertReportModal.tsx:
##########
@@ -255,7 +230,10 @@ const StyledSwitchContainer = styled.div`
 
 export const StyledInputContainer = styled.div`
   flex: 1;
-  margin-top: 0;
+  margin-top: 0px;
+  margin-bottom: ${({ theme }) => theme.gridUnit * 4}px;
+  margin-top: 0px;
+  margin-bottom: ${({ theme }) => theme.gridUnit * 4}px;

Review Comment:
   duplicated styles



##########
superset-frontend/src/features/alerts/AlertReportModal.tsx:
##########
@@ -497,7 +504,51 @@ const AlertReportModal: 
FunctionComponent<AlertReportModalProps> = ({
   const [sourceOptions, setSourceOptions] = useState<MetaObject[]>([]);
   const [dashboardOptions, setDashboardOptions] = useState<MetaObject[]>([]);
   const [chartOptions, setChartOptions] = useState<MetaObject[]>([]);
+  // Validation
+  const [validationStatus, setValidationStatus] = useState<ValidationObject>({
+    generalSection: { status: false, name: 'General information', errors: [] },

Review Comment:
   1. Could we change the name `status` to something more explicit, like 
`hasErrors`?
   2. I think `name` should be translated. We probably should use 
`TRANSLATIONS` const for this
   3. The keys names are duplicated from `Sections` enum. You can use that enum 
to define keys in objects like this: 
   ```
   {
   [Sections.GENERAL]: { ... },
   [Sections.CONTENT]: { ... },
   ...
   }
   
   ```



##########
superset-frontend/src/features/alerts/AlertReportModal.tsx:
##########
@@ -966,18 +1016,14 @@ const AlertReportModal: 
FunctionComponent<AlertReportModalProps> = ({
     updateAlertState('timezone', timezone);
   };
 
-  const onContentTypeChange = (event: any) => {
-    const { target } = event;
+  const onContentTypeChange = (value: any) => {
     // When switch content type, reset force_screenshot to false
     setForceScreenshot(false);
-    // Gives time to close the select before changing the type
-    setTimeout(() => setContentType(target.value), 200);
+    setContentType(value);
   };
 
-  const onFormatChange = (event: any) => {
-    const { target } = event;
-
-    setReportFormat(target.value);
+  const onFormatChange = (value: any) => {

Review Comment:
   Same here - `any`



##########
superset-frontend/src/components/CronPicker/CronPicker.tsx:
##########
@@ -110,22 +110,81 @@ export const CronPicker = styled((props: CronProps) => (
     <ReactCronPicker locale={LOCALE} {...props} />
   </ConfigProvider>
 ))`
-  .react-js-cron-field {
-    margin-bottom: 0px;
+:has(.react-js-cron-months){
+  display: grid !important;
+  grid-template-columns: repeat(2, 50%);
+  grid-column-gap: ${({ theme }) => theme.gridUnit}px;
+  grid-row-gap: ${({ theme }) => theme.gridUnit * 2}px;
+  div:has(.react-js-cron-hours) {
+    grid-column: span 2;
+    display: flex;
+    justify-content: space-between;
+    .react-js-cron-field {
+      width:50%;
+    }
   }
-  .react-js-cron-select:not(.react-js-cron-custom-select) > div:first-of-type,
-  .react-js-cron-custom-select {
-    border-radius: ${({ theme }) => theme.gridUnit}px;
-    background-color: ${({ theme }) =>
-      theme.colors.grayscale.light4} !important;
+}
+
+:not(:has(.react-js-cron-months)) {
+  display: grid;
+  grid-template-columns: repeat(2, 50%);
+  grid-column-gap: ${({ theme }) => theme.gridUnit}px;
+  grid-row-gap: ${({ theme }) => theme.gridUnit * 2}px;
+  .react-js-cron-period {
+    grid-column: span 2;
   }
-  .react-js-cron-custom-select > div:first-of-type {
-    border-radius: ${({ theme }) => theme.gridUnit}px;
+  div:has(.react-js-cron-hours) {
+    grid-column: span 2;
+    display: flex;
+    justify-content: space-between;
+    .react-js-cron-field {
+      width: 50%;
+    }
   }
+}
+
+:not(:has(.react-js-cron-month-days)) {
+  .react-js-cron-week-days {
+    grid-column: span 2;
+  }
+}
+
+.react-js-cron-minutes > span {
+  padding-left: ${({ theme }) => theme.gridUnit}px;
+}
+grid-template-columns: repeat(1, 100%);
+grid-column-gap: ${({ theme }) => theme.gridUnit}px;
+grid-row-gap: ${({ theme }) => theme.gridUnit * 2}px;
+div:has(.react-js-cron-hours) {
+  width: 100%;
+}
+:not(div:has(.react-js-cron-hours)) {
+  display: flex;
+  flex-wrap: nowrap;
+}
+.react-js-cron-select {
+  width: 100%;
+  .ant-select-selector {
+    flex-wrap: nowrap !important;

Review Comment:
   Instead of using `!important` we could try to increase specificity of the 
css selector (for example `&.ant-select-selector` or `&&.ant-select-selector` 
which translates to 
`.react-js-cron-select.react-js-cron-select.ant-select-selector`)



##########
superset-frontend/src/features/alerts/AlertReportModal.tsx:
##########
@@ -1190,367 +1313,456 @@ const AlertReportModal: 
FunctionComponent<AlertReportModalProps> = ({
         </h4>
       }
     >
-      <StyledSectionContainer>
-        <div className="header-section">
-          <StyledInputContainer>
-            <div className="control-label">
-              {isReport
-                ? TRANSLATIONS.REPORT_NAME_TEXT
-                : TRANSLATIONS.ALERT_NAME_TEXT}
-              <span className="required">*</span>
-            </div>
-            <div className="input-container">
-              <input
-                type="text"
-                name="name"
-                value={currentAlert ? currentAlert.name : ''}
-                placeholder={
-                  isReport
-                    ? TRANSLATIONS.REPORT_NAME_TEXT
-                    : TRANSLATIONS.ALERT_NAME_TEXT
-                }
-                onChange={onInputChange}
-                css={inputSpacer}
-              />
-            </div>
-          </StyledInputContainer>
-          <StyledInputContainer>
-            <div className="control-label">
-              {TRANSLATIONS.OWNERS_TEXT}
-              <span className="required">*</span>
-            </div>
-            <div data-test="owners-select" className="input-container">
-              <AsyncSelect
-                ariaLabel={TRANSLATIONS.OWNERS_TEXT}
-                allowClear
-                name="owners"
-                mode="multiple"
-                value={
-                  (currentAlert?.owners as {
-                    label: string;
-                    value: number;
-                  }[]) || []
-                }
-                options={loadOwnerOptions}
-                onChange={onOwnersChange}
-                css={inputSpacer}
-              />
-            </div>
-          </StyledInputContainer>
-          <StyledInputContainer>
-            <div 
className="control-label">{TRANSLATIONS.DESCRIPTION_TEXT}</div>
-            <div className="input-container">
-              <input
-                type="text"
-                name="description"
-                value={currentAlert ? currentAlert.description || '' : ''}
-                placeholder={TRANSLATIONS.DESCRIPTION_TEXT}
-                onChange={onInputChange}
-                css={inputSpacer}
-              />
-            </div>
-          </StyledInputContainer>
-          <StyledSwitchContainer>
-            <Switch
-              onChange={onActiveSwitch}
-              checked={currentAlert ? currentAlert.active : true}
+      <Collapse
+        expandIconPosition="right"
+        defaultActiveKey="1"
+        accordion
+        style={{ border: 'none' }}
+      >
+        <StyledPanel
+          header={
+            <ValidatedPanelHeader
+              title={TRANSLATIONS.GENERAL_TITLE}
+              subtitle={TRANSLATIONS.GENERAL_SUBTITLE}
+              required
+              validateCheckStatus={validationStatus[Sections.GENERAL].status}
+              testId="general-information-panel"
             />
-            <div className="switch-label">{TRANSLATIONS.ACTIVE_TEXT}</div>
-          </StyledSwitchContainer>
-        </div>
-        <div className="column-section">
-          {!isReport && (
-            <div className="column condition">
-              <StyledSectionTitle>
-                <h4>{TRANSLATIONS.ALERT_CONDITION_TEXT}</h4>
-              </StyledSectionTitle>
-              <StyledInputContainer>
-                <div className="control-label">
-                  {TRANSLATIONS.DATABASE_TEXT}
-                  <span className="required">*</span>
-                </div>
-                <div className="input-container">
-                  <AsyncSelect
-                    ariaLabel={TRANSLATIONS.DATABASE_TEXT}
-                    name="source"
-                    value={
-                      currentAlert?.database?.label &&
-                      currentAlert?.database?.value
-                        ? {
-                            value: currentAlert.database.value,
-                            label: currentAlert.database.label,
-                          }
-                        : undefined
-                    }
-                    options={loadSourceOptions}
-                    onChange={onSourceChange}
-                  />
-                </div>
-              </StyledInputContainer>
-              <StyledInputContainer>
-                <div className="control-label">
-                  {TRANSLATIONS.SQL_QUERY_TEXT}
-                  <StyledTooltip tooltip={TRANSLATIONS.SQL_QUERY_TOOLTIP} />
-                  <span className="required">*</span>
-                </div>
-                <TextAreaControl
-                  name="sql"
-                  language="sql"
-                  offerEditInModal={false}
-                  minLines={15}
-                  maxLines={15}
-                  onChange={onSQLChange}
-                  readOnly={false}
-                  initialValue={resource?.sql}
-                  key={currentAlert?.id}
+          }
+          key="1"

Review Comment:
   Could you change keys from "1,2,3" to more explicit names?



##########
superset-frontend/src/features/alerts/AlertReportModal.tsx:
##########
@@ -1001,30 +1047,102 @@ const AlertReportModal: 
FunctionComponent<AlertReportModalProps> = ({
     return hasInfo;
   };
 
-  const validate = () => {
+  const validateGeneralSection = () => {
+    const errors = [];
+    if (!currentAlert?.name?.length) {
+      errors.push('name');

Review Comment:
   I think errors should be translated too



##########
superset-frontend/src/features/alerts/types.ts:
##########
@@ -123,3 +123,25 @@ export interface AlertsReportsConfig {
   ALERT_REPORTS_DEFAULT_RETENTION: number;
   ALERT_REPORTS_DEFAULT_CRON_VALUE: string;
 }
+
+export type SectionValidationObject = {
+  status: boolean;
+  errors: string[];
+  name: string;
+};
+
+export interface ValidationObject {
+  generalSection: SectionValidationObject;
+  contentSection: SectionValidationObject;
+  alertConditionSection: SectionValidationObject;
+  scheduleSection: SectionValidationObject;
+  notificationSection: SectionValidationObject;
+}
+
+export enum Sections {
+  GENERAL = 'generalSection',

Review Comment:
   We're very close to merging a 
[PR](https://github.com/apache/superset/pull/26875) which enforces PascalCase 
for enums. Could you rename those here so that we prevent conflicts?



##########
superset-frontend/src/features/alerts/AlertReportModal.tsx:
##########
@@ -1190,367 +1313,456 @@ const AlertReportModal: 
FunctionComponent<AlertReportModalProps> = ({
         </h4>
       }
     >
-      <StyledSectionContainer>
-        <div className="header-section">
-          <StyledInputContainer>
-            <div className="control-label">
-              {isReport
-                ? TRANSLATIONS.REPORT_NAME_TEXT
-                : TRANSLATIONS.ALERT_NAME_TEXT}
-              <span className="required">*</span>
-            </div>
-            <div className="input-container">
-              <input
-                type="text"
-                name="name"
-                value={currentAlert ? currentAlert.name : ''}
-                placeholder={
-                  isReport
-                    ? TRANSLATIONS.REPORT_NAME_TEXT
-                    : TRANSLATIONS.ALERT_NAME_TEXT
-                }
-                onChange={onInputChange}
-                css={inputSpacer}
-              />
-            </div>
-          </StyledInputContainer>
-          <StyledInputContainer>
-            <div className="control-label">
-              {TRANSLATIONS.OWNERS_TEXT}
-              <span className="required">*</span>
-            </div>
-            <div data-test="owners-select" className="input-container">
-              <AsyncSelect
-                ariaLabel={TRANSLATIONS.OWNERS_TEXT}
-                allowClear
-                name="owners"
-                mode="multiple"
-                value={
-                  (currentAlert?.owners as {
-                    label: string;
-                    value: number;
-                  }[]) || []
-                }
-                options={loadOwnerOptions}
-                onChange={onOwnersChange}
-                css={inputSpacer}
-              />
-            </div>
-          </StyledInputContainer>
-          <StyledInputContainer>
-            <div 
className="control-label">{TRANSLATIONS.DESCRIPTION_TEXT}</div>
-            <div className="input-container">
-              <input
-                type="text"
-                name="description"
-                value={currentAlert ? currentAlert.description || '' : ''}
-                placeholder={TRANSLATIONS.DESCRIPTION_TEXT}
-                onChange={onInputChange}
-                css={inputSpacer}
-              />
-            </div>
-          </StyledInputContainer>
-          <StyledSwitchContainer>
-            <Switch
-              onChange={onActiveSwitch}
-              checked={currentAlert ? currentAlert.active : true}
+      <Collapse
+        expandIconPosition="right"
+        defaultActiveKey="1"
+        accordion
+        style={{ border: 'none' }}
+      >
+        <StyledPanel
+          header={
+            <ValidatedPanelHeader
+              title={TRANSLATIONS.GENERAL_TITLE}
+              subtitle={TRANSLATIONS.GENERAL_SUBTITLE}
+              required
+              validateCheckStatus={validationStatus[Sections.GENERAL].status}
+              testId="general-information-panel"
             />
-            <div className="switch-label">{TRANSLATIONS.ACTIVE_TEXT}</div>
-          </StyledSwitchContainer>
-        </div>
-        <div className="column-section">
-          {!isReport && (
-            <div className="column condition">
-              <StyledSectionTitle>
-                <h4>{TRANSLATIONS.ALERT_CONDITION_TEXT}</h4>
-              </StyledSectionTitle>
-              <StyledInputContainer>
-                <div className="control-label">
-                  {TRANSLATIONS.DATABASE_TEXT}
-                  <span className="required">*</span>
-                </div>
-                <div className="input-container">
-                  <AsyncSelect
-                    ariaLabel={TRANSLATIONS.DATABASE_TEXT}
-                    name="source"
-                    value={
-                      currentAlert?.database?.label &&
-                      currentAlert?.database?.value
-                        ? {
-                            value: currentAlert.database.value,
-                            label: currentAlert.database.label,
-                          }
-                        : undefined
-                    }
-                    options={loadSourceOptions}
-                    onChange={onSourceChange}
-                  />
-                </div>
-              </StyledInputContainer>
-              <StyledInputContainer>
-                <div className="control-label">
-                  {TRANSLATIONS.SQL_QUERY_TEXT}
-                  <StyledTooltip tooltip={TRANSLATIONS.SQL_QUERY_TOOLTIP} />
-                  <span className="required">*</span>
-                </div>
-                <TextAreaControl
-                  name="sql"
-                  language="sql"
-                  offerEditInModal={false}
-                  minLines={15}
-                  maxLines={15}
-                  onChange={onSQLChange}
-                  readOnly={false}
-                  initialValue={resource?.sql}
-                  key={currentAlert?.id}
+          }
+          key="1"
+          style={panelBorder}
+        >
+          <div className="header-section">
+            <StyledInputContainer>
+              <div className="control-label">
+                {isReport
+                  ? TRANSLATIONS.REPORT_NAME_TEXT
+                  : TRANSLATIONS.ALERT_NAME_TEXT}
+                <span className="required">*</span>
+              </div>
+              <div className="input-container">
+                <input
+                  type="text"
+                  name="name"
+                  value={currentAlert ? currentAlert.name : ''}
+                  placeholder={
+                    isReport
+                      ? TRANSLATIONS.REPORT_NAME_PLACEHOLDER
+                      : TRANSLATIONS.ALERT_NAME_PLACEHOLDER
+                  }
+                  onChange={onInputChange}
                 />
-              </StyledInputContainer>
-              <div className="inline-container wrap">
-                <StyledInputContainer>
-                  <div className="control-label" css={inputSpacer}>
-                    {TRANSLATIONS.TRIGGER_ALERT_IF_TEXT}
-                    <span className="required">*</span>
-                  </div>
-                  <div className="input-container">
-                    <Select
-                      ariaLabel={TRANSLATIONS.CONDITION_TEXT}
-                      onChange={onConditionChange}
-                      placeholder="Condition"
-                      value={
-                        currentAlert?.validator_config_json?.op || undefined
-                      }
-                      options={CONDITIONS}
-                      css={inputSpacer}
-                    />
-                  </div>
-                </StyledInputContainer>
-                <StyledInputContainer>
-                  <div className="control-label">
-                    {TRANSLATIONS.VALUE_TEXT}
-                    <span className="required">*</span>
-                  </div>
-                  <div className="input-container">
-                    <input
-                      type="number"
-                      name="threshold"
-                      disabled={conditionNotNull}
-                      value={
-                        currentAlert?.validator_config_json?.threshold !==
-                        undefined
-                          ? currentAlert.validator_config_json.threshold
-                          : ''
-                      }
-                      placeholder={TRANSLATIONS.VALUE_TEXT}
-                      onChange={onThresholdChange}
-                    />
-                  </div>
-                </StyledInputContainer>
               </div>
-            </div>
-          )}
-          <div className="column schedule">
-            <StyledSectionTitle>
-              <h4>
-                {isReport
-                  ? TRANSLATIONS.REPORT_SCHEDULE_TEXT
-                  : TRANSLATIONS.ALERT_CONDITION_SCHEDULE_TEXT}
-              </h4>
-              <span className="required">*</span>
-            </StyledSectionTitle>
-            <AlertReportCronScheduler
-              value={currentAlert?.crontab || ALERT_REPORTS_DEFAULT_CRON_VALUE}
-              onChange={newVal => updateAlertState('crontab', newVal)}
-            />
-            <div className="control-label">{TRANSLATIONS.TIMEZONE_TEXT}</div>
-            <div
-              className="input-container"
-              css={(theme: SupersetTheme) => timezoneHeaderStyle(theme)}
-            >
-              <TimezoneSelector
-                onTimezoneChange={onTimezoneChange}
-                timezone={currentAlert?.timezone}
-                minWidth="100%"
-              />
-            </div>
-            <StyledSectionTitle>
-              <h4>{TRANSLATIONS.SCHEDULE_SETTINGS_TEXT}</h4>
-            </StyledSectionTitle>
+            </StyledInputContainer>
             <StyledInputContainer>
               <div className="control-label">
-                {TRANSLATIONS.LOG_RETENTION_TEXT}
+                {TRANSLATIONS.OWNERS_TEXT}
                 <span className="required">*</span>
               </div>
-              <div className="input-container">
-                <Select
-                  ariaLabel={TRANSLATIONS.LOG_RETENTION_TEXT}
-                  placeholder={TRANSLATIONS.LOG_RETENTION_TEXT}
-                  onChange={onLogRetentionChange}
+              <div data-test="owners-select" className="input-container">
+                <AsyncSelect
+                  ariaLabel={TRANSLATIONS.OWNERS_TEXT}
+                  allowClear
+                  name="owners"
+                  mode="multiple"
+                  placeholder={TRANSLATIONS.OWNERS_PLACEHOLDER}
                   value={
-                    typeof currentAlert?.log_retention === 'number'
-                      ? currentAlert?.log_retention
-                      : ALERT_REPORTS_DEFAULT_RETENTION
+                    (currentAlert?.owners as {
+                      label: string;
+                      value: number;
+                    }[]) || []
                   }
-                  options={RETENTION_OPTIONS}
-                  sortComparator={propertyComparator('value')}
+                  options={loadOwnerOptions}
+                  onChange={onOwnersChange}
                 />
               </div>
             </StyledInputContainer>
             <StyledInputContainer>
               <div className="control-label">
-                {TRANSLATIONS.WORKING_TIMEOUT_TEXT}
-                <span className="required">*</span>
+                {TRANSLATIONS.DESCRIPTION_TEXT}
               </div>
               <div className="input-container">
                 <input
-                  type="number"
-                  min="1"
-                  name="working_timeout"
-                  value={currentAlert?.working_timeout || ''}
-                  placeholder={TRANSLATIONS.TIME_IN_SECONDS_TEXT}
-                  onChange={onTimeoutVerifyChange}
+                  type="text"
+                  name="description"
+                  value={currentAlert ? currentAlert.description || '' : ''}
+                  placeholder={
+                    isReport
+                      ? TRANSLATIONS.REPORT_DESCRIPTION_PLACEHOLDER
+                      : TRANSLATIONS.ALERT_DESCRIPTION_PLACEHOLDER
+                  }
+                  onChange={onInputChange}
                 />
-                <span 
className="input-label">{TRANSLATIONS.SECONDS_TEXT}</span>
               </div>
             </StyledInputContainer>
-            {!isReport && (
-              <StyledInputContainer>
+            <StyledSwitchContainer>
+              <Switch
+                checked={currentAlert ? currentAlert.active : false}
+                defaultChecked
+                onChange={onActiveSwitch}
+              />
+              <div className="switch-label">
+                {isReport
+                  ? TRANSLATIONS.ACTIVE_REPORT_TEXT
+                  : TRANSLATIONS.ACTIVE_ALERT_TEXT}
+              </div>
+            </StyledSwitchContainer>
+          </div>
+        </StyledPanel>
+        {!isReport && (
+          <StyledPanel
+            header={
+              <ValidatedPanelHeader
+                title={TRANSLATIONS.ALERT_CONDITION_TITLE}
+                subtitle={TRANSLATIONS.ALERT_CONDITION_SUBTITLE}
+                required={false}
+                validateCheckStatus={validationStatus[Sections.ALERT].status}
+                testId="alert-condition-panel"
+              />
+            }
+            key="2"
+            style={{ borderBottom: 'none' }}

Review Comment:
   We should avoid using `style`. Instead you could use `css` prop, like this:
   ```
   <Styled Panel
   ...
   css={css`
   border-bottom: none;
   `}
   />
   ```
   (`css` can be imported from `@superset-core/ui`)



##########
superset-frontend/src/features/alerts/AlertReportModal.tsx:
##########
@@ -1190,367 +1313,456 @@ const AlertReportModal: 
FunctionComponent<AlertReportModalProps> = ({
         </h4>
       }
     >
-      <StyledSectionContainer>
-        <div className="header-section">
-          <StyledInputContainer>
-            <div className="control-label">
-              {isReport
-                ? TRANSLATIONS.REPORT_NAME_TEXT
-                : TRANSLATIONS.ALERT_NAME_TEXT}
-              <span className="required">*</span>
-            </div>
-            <div className="input-container">
-              <input
-                type="text"
-                name="name"
-                value={currentAlert ? currentAlert.name : ''}
-                placeholder={
-                  isReport
-                    ? TRANSLATIONS.REPORT_NAME_TEXT
-                    : TRANSLATIONS.ALERT_NAME_TEXT
-                }
-                onChange={onInputChange}
-                css={inputSpacer}
-              />
-            </div>
-          </StyledInputContainer>
-          <StyledInputContainer>
-            <div className="control-label">
-              {TRANSLATIONS.OWNERS_TEXT}
-              <span className="required">*</span>
-            </div>
-            <div data-test="owners-select" className="input-container">
-              <AsyncSelect
-                ariaLabel={TRANSLATIONS.OWNERS_TEXT}
-                allowClear
-                name="owners"
-                mode="multiple"
-                value={
-                  (currentAlert?.owners as {
-                    label: string;
-                    value: number;
-                  }[]) || []
-                }
-                options={loadOwnerOptions}
-                onChange={onOwnersChange}
-                css={inputSpacer}
-              />
-            </div>
-          </StyledInputContainer>
-          <StyledInputContainer>
-            <div 
className="control-label">{TRANSLATIONS.DESCRIPTION_TEXT}</div>
-            <div className="input-container">
-              <input
-                type="text"
-                name="description"
-                value={currentAlert ? currentAlert.description || '' : ''}
-                placeholder={TRANSLATIONS.DESCRIPTION_TEXT}
-                onChange={onInputChange}
-                css={inputSpacer}
-              />
-            </div>
-          </StyledInputContainer>
-          <StyledSwitchContainer>
-            <Switch
-              onChange={onActiveSwitch}
-              checked={currentAlert ? currentAlert.active : true}
+      <Collapse
+        expandIconPosition="right"
+        defaultActiveKey="1"
+        accordion
+        style={{ border: 'none' }}
+      >
+        <StyledPanel
+          header={
+            <ValidatedPanelHeader
+              title={TRANSLATIONS.GENERAL_TITLE}
+              subtitle={TRANSLATIONS.GENERAL_SUBTITLE}
+              required
+              validateCheckStatus={validationStatus[Sections.GENERAL].status}
+              testId="general-information-panel"
             />
-            <div className="switch-label">{TRANSLATIONS.ACTIVE_TEXT}</div>
-          </StyledSwitchContainer>
-        </div>
-        <div className="column-section">
-          {!isReport && (
-            <div className="column condition">
-              <StyledSectionTitle>
-                <h4>{TRANSLATIONS.ALERT_CONDITION_TEXT}</h4>
-              </StyledSectionTitle>
-              <StyledInputContainer>
-                <div className="control-label">
-                  {TRANSLATIONS.DATABASE_TEXT}
-                  <span className="required">*</span>
-                </div>
-                <div className="input-container">
-                  <AsyncSelect
-                    ariaLabel={TRANSLATIONS.DATABASE_TEXT}
-                    name="source"
-                    value={
-                      currentAlert?.database?.label &&
-                      currentAlert?.database?.value
-                        ? {
-                            value: currentAlert.database.value,
-                            label: currentAlert.database.label,
-                          }
-                        : undefined
-                    }
-                    options={loadSourceOptions}
-                    onChange={onSourceChange}
-                  />
-                </div>
-              </StyledInputContainer>
-              <StyledInputContainer>
-                <div className="control-label">
-                  {TRANSLATIONS.SQL_QUERY_TEXT}
-                  <StyledTooltip tooltip={TRANSLATIONS.SQL_QUERY_TOOLTIP} />
-                  <span className="required">*</span>
-                </div>
-                <TextAreaControl
-                  name="sql"
-                  language="sql"
-                  offerEditInModal={false}
-                  minLines={15}
-                  maxLines={15}
-                  onChange={onSQLChange}
-                  readOnly={false}
-                  initialValue={resource?.sql}
-                  key={currentAlert?.id}
+          }
+          key="1"
+          style={panelBorder}
+        >
+          <div className="header-section">
+            <StyledInputContainer>
+              <div className="control-label">
+                {isReport
+                  ? TRANSLATIONS.REPORT_NAME_TEXT
+                  : TRANSLATIONS.ALERT_NAME_TEXT}
+                <span className="required">*</span>
+              </div>
+              <div className="input-container">
+                <input
+                  type="text"
+                  name="name"
+                  value={currentAlert ? currentAlert.name : ''}
+                  placeholder={
+                    isReport
+                      ? TRANSLATIONS.REPORT_NAME_PLACEHOLDER
+                      : TRANSLATIONS.ALERT_NAME_PLACEHOLDER
+                  }
+                  onChange={onInputChange}
                 />
-              </StyledInputContainer>
-              <div className="inline-container wrap">
-                <StyledInputContainer>
-                  <div className="control-label" css={inputSpacer}>
-                    {TRANSLATIONS.TRIGGER_ALERT_IF_TEXT}
-                    <span className="required">*</span>
-                  </div>
-                  <div className="input-container">
-                    <Select
-                      ariaLabel={TRANSLATIONS.CONDITION_TEXT}
-                      onChange={onConditionChange}
-                      placeholder="Condition"
-                      value={
-                        currentAlert?.validator_config_json?.op || undefined
-                      }
-                      options={CONDITIONS}
-                      css={inputSpacer}
-                    />
-                  </div>
-                </StyledInputContainer>
-                <StyledInputContainer>
-                  <div className="control-label">
-                    {TRANSLATIONS.VALUE_TEXT}
-                    <span className="required">*</span>
-                  </div>
-                  <div className="input-container">
-                    <input
-                      type="number"
-                      name="threshold"
-                      disabled={conditionNotNull}
-                      value={
-                        currentAlert?.validator_config_json?.threshold !==
-                        undefined
-                          ? currentAlert.validator_config_json.threshold
-                          : ''
-                      }
-                      placeholder={TRANSLATIONS.VALUE_TEXT}
-                      onChange={onThresholdChange}
-                    />
-                  </div>
-                </StyledInputContainer>
               </div>
-            </div>
-          )}
-          <div className="column schedule">
-            <StyledSectionTitle>
-              <h4>
-                {isReport
-                  ? TRANSLATIONS.REPORT_SCHEDULE_TEXT
-                  : TRANSLATIONS.ALERT_CONDITION_SCHEDULE_TEXT}
-              </h4>
-              <span className="required">*</span>
-            </StyledSectionTitle>
-            <AlertReportCronScheduler
-              value={currentAlert?.crontab || ALERT_REPORTS_DEFAULT_CRON_VALUE}
-              onChange={newVal => updateAlertState('crontab', newVal)}
-            />
-            <div className="control-label">{TRANSLATIONS.TIMEZONE_TEXT}</div>
-            <div
-              className="input-container"
-              css={(theme: SupersetTheme) => timezoneHeaderStyle(theme)}
-            >
-              <TimezoneSelector
-                onTimezoneChange={onTimezoneChange}
-                timezone={currentAlert?.timezone}
-                minWidth="100%"
-              />
-            </div>
-            <StyledSectionTitle>
-              <h4>{TRANSLATIONS.SCHEDULE_SETTINGS_TEXT}</h4>
-            </StyledSectionTitle>
+            </StyledInputContainer>
             <StyledInputContainer>
               <div className="control-label">
-                {TRANSLATIONS.LOG_RETENTION_TEXT}
+                {TRANSLATIONS.OWNERS_TEXT}
                 <span className="required">*</span>
               </div>
-              <div className="input-container">
-                <Select
-                  ariaLabel={TRANSLATIONS.LOG_RETENTION_TEXT}
-                  placeholder={TRANSLATIONS.LOG_RETENTION_TEXT}
-                  onChange={onLogRetentionChange}
+              <div data-test="owners-select" className="input-container">
+                <AsyncSelect
+                  ariaLabel={TRANSLATIONS.OWNERS_TEXT}
+                  allowClear
+                  name="owners"
+                  mode="multiple"
+                  placeholder={TRANSLATIONS.OWNERS_PLACEHOLDER}
                   value={
-                    typeof currentAlert?.log_retention === 'number'
-                      ? currentAlert?.log_retention
-                      : ALERT_REPORTS_DEFAULT_RETENTION
+                    (currentAlert?.owners as {
+                      label: string;
+                      value: number;
+                    }[]) || []
                   }
-                  options={RETENTION_OPTIONS}
-                  sortComparator={propertyComparator('value')}
+                  options={loadOwnerOptions}
+                  onChange={onOwnersChange}
                 />
               </div>
             </StyledInputContainer>
             <StyledInputContainer>
               <div className="control-label">
-                {TRANSLATIONS.WORKING_TIMEOUT_TEXT}
-                <span className="required">*</span>
+                {TRANSLATIONS.DESCRIPTION_TEXT}
               </div>
               <div className="input-container">
                 <input
-                  type="number"
-                  min="1"
-                  name="working_timeout"
-                  value={currentAlert?.working_timeout || ''}
-                  placeholder={TRANSLATIONS.TIME_IN_SECONDS_TEXT}
-                  onChange={onTimeoutVerifyChange}
+                  type="text"
+                  name="description"
+                  value={currentAlert ? currentAlert.description || '' : ''}
+                  placeholder={
+                    isReport
+                      ? TRANSLATIONS.REPORT_DESCRIPTION_PLACEHOLDER
+                      : TRANSLATIONS.ALERT_DESCRIPTION_PLACEHOLDER
+                  }
+                  onChange={onInputChange}
                 />
-                <span 
className="input-label">{TRANSLATIONS.SECONDS_TEXT}</span>
               </div>
             </StyledInputContainer>
-            {!isReport && (
-              <StyledInputContainer>
+            <StyledSwitchContainer>
+              <Switch
+                checked={currentAlert ? currentAlert.active : false}
+                defaultChecked
+                onChange={onActiveSwitch}
+              />
+              <div className="switch-label">
+                {isReport
+                  ? TRANSLATIONS.ACTIVE_REPORT_TEXT
+                  : TRANSLATIONS.ACTIVE_ALERT_TEXT}
+              </div>
+            </StyledSwitchContainer>
+          </div>
+        </StyledPanel>
+        {!isReport && (
+          <StyledPanel
+            header={
+              <ValidatedPanelHeader
+                title={TRANSLATIONS.ALERT_CONDITION_TITLE}
+                subtitle={TRANSLATIONS.ALERT_CONDITION_SUBTITLE}
+                required={false}
+                validateCheckStatus={validationStatus[Sections.ALERT].status}
+                testId="alert-condition-panel"

Review Comment:
   here `data-test` too



##########
superset-frontend/src/features/alerts/AlertReportModal.tsx:
##########
@@ -1190,367 +1313,456 @@ const AlertReportModal: 
FunctionComponent<AlertReportModalProps> = ({
         </h4>
       }
     >
-      <StyledSectionContainer>
-        <div className="header-section">
-          <StyledInputContainer>
-            <div className="control-label">
-              {isReport
-                ? TRANSLATIONS.REPORT_NAME_TEXT
-                : TRANSLATIONS.ALERT_NAME_TEXT}
-              <span className="required">*</span>
-            </div>
-            <div className="input-container">
-              <input
-                type="text"
-                name="name"
-                value={currentAlert ? currentAlert.name : ''}
-                placeholder={
-                  isReport
-                    ? TRANSLATIONS.REPORT_NAME_TEXT
-                    : TRANSLATIONS.ALERT_NAME_TEXT
-                }
-                onChange={onInputChange}
-                css={inputSpacer}
-              />
-            </div>
-          </StyledInputContainer>
-          <StyledInputContainer>
-            <div className="control-label">
-              {TRANSLATIONS.OWNERS_TEXT}
-              <span className="required">*</span>
-            </div>
-            <div data-test="owners-select" className="input-container">
-              <AsyncSelect
-                ariaLabel={TRANSLATIONS.OWNERS_TEXT}
-                allowClear
-                name="owners"
-                mode="multiple"
-                value={
-                  (currentAlert?.owners as {
-                    label: string;
-                    value: number;
-                  }[]) || []
-                }
-                options={loadOwnerOptions}
-                onChange={onOwnersChange}
-                css={inputSpacer}
-              />
-            </div>
-          </StyledInputContainer>
-          <StyledInputContainer>
-            <div 
className="control-label">{TRANSLATIONS.DESCRIPTION_TEXT}</div>
-            <div className="input-container">
-              <input
-                type="text"
-                name="description"
-                value={currentAlert ? currentAlert.description || '' : ''}
-                placeholder={TRANSLATIONS.DESCRIPTION_TEXT}
-                onChange={onInputChange}
-                css={inputSpacer}
-              />
-            </div>
-          </StyledInputContainer>
-          <StyledSwitchContainer>
-            <Switch
-              onChange={onActiveSwitch}
-              checked={currentAlert ? currentAlert.active : true}
+      <Collapse
+        expandIconPosition="right"
+        defaultActiveKey="1"
+        accordion
+        style={{ border: 'none' }}
+      >
+        <StyledPanel
+          header={
+            <ValidatedPanelHeader
+              title={TRANSLATIONS.GENERAL_TITLE}
+              subtitle={TRANSLATIONS.GENERAL_SUBTITLE}
+              required
+              validateCheckStatus={validationStatus[Sections.GENERAL].status}
+              testId="general-information-panel"

Review Comment:
   we're using `data-test` to mark test ids



##########
superset-frontend/src/features/alerts/components/NotificationMethod.tsx:
##########
@@ -133,25 +136,29 @@ export const NotificationMethod: 
FunctionComponent<NotificationMethodProps> = ({
               )}
               value={method}
             />
+            {method !== undefined && index !== 0 && !!onRemove ? (

Review Comment:
   1. `index !== 0` why do we prevent user from deleting the first item?
   2. I don't see "Add notification method" button anymore 🤔 
   
   <img width="756" alt="image" 
src="https://github.com/apache/superset/assets/15073128/80444de7-435e-4d20-a09c-c68c16332441";>
   



##########
superset-frontend/src/features/alerts/buildErrorTooltipMessage.tsx:
##########
@@ -0,0 +1,57 @@
+/**
+ * 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 from 'react';
+import { styled } from '@superset-ui/core';
+import { ValidationObject } from './types';
+
+const StyledList = styled.ul`
+  margin: 0;
+  padding: 0;
+  li {
+    padding-left: 12px;
+  }
+`;
+
+export const buildErrorTooltipMessage = (
+  build = true,
+  setErrorTooltipMessage: Function,
+  validationStatus: ValidationObject,
+) => {
+  if (build) {
+    const sectionErrors: string[] = [];
+    Object.values(validationStatus).forEach(validationData => {
+      if (!validationData.status) {
+        const sectionTitle = `${validationData.name}: `;
+        sectionErrors.push(sectionTitle + validationData.errors.join(', '));
+      }
+    });
+    setErrorTooltipMessage(
+      <div>
+        Not all required fields are complete. Please provide the following:
+        <StyledList>

Review Comment:
   Dots in the list are too far left
   
   <img width="286" alt="image" 
src="https://github.com/apache/superset/assets/15073128/cadc7fe1-1d97-422c-8ced-854acc657ebd";>
   



##########
superset-frontend/src/features/alerts/buildErrorTooltipMessage.tsx:
##########
@@ -0,0 +1,57 @@
+/**
+ * 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 from 'react';
+import { styled } from '@superset-ui/core';
+import { ValidationObject } from './types';
+
+const StyledList = styled.ul`
+  margin: 0;
+  padding: 0;
+  li {
+    padding-left: 12px;
+  }
+`;
+
+export const buildErrorTooltipMessage = (
+  build = true,
+  setErrorTooltipMessage: Function,
+  validationStatus: ValidationObject,
+) => {
+  if (build) {
+    const sectionErrors: string[] = [];
+    Object.values(validationStatus).forEach(validationData => {
+      if (!validationData.status) {
+        const sectionTitle = `${validationData.name}: `;
+        sectionErrors.push(sectionTitle + validationData.errors.join(', '));
+      }
+    });
+    setErrorTooltipMessage(
+      <div>
+        Not all required fields are complete. Please provide the following:

Review Comment:
   Translation



##########
superset-frontend/src/features/alerts/AlertReportModal.tsx:
##########
@@ -143,62 +148,60 @@ const RETENTION_OPTIONS = [
   },
 ];
 
-const StyledModal = styled(Modal)`
-  max-width: 1200px;
-  width: 100%;
+const CONTENT_TYPE_OPTIONS = [
+  {
+    label: t('Dashboard'),
+    value: 'dashboard',
+  },
+  {
+    label: t('Chart'),
+    value: 'chart',
+  },
+];
+const FORMAT_OPTIONS = {
+  png: {
+    label: t('Send as PNG'),
+    value: 'PNG',
+  },
+  csv: {
+    label: t('Send as CSV'),
+    value: 'CSV',
+  },
+  txt: {
+    label: t('Send as text'),
+    value: 'TEXT',
+  },
+};
 
-  .ant-modal-body {
-    overflow: initial;
-  }
-`;
+// Style Constants
+const MODAL_BODY_HEIGHT = 180.5;
 
-const StyledTooltip = styled(InfoTooltipWithTrigger)`
-  margin-left: ${({ theme }) => theme.gridUnit}px;
-`;
+// Apply to collapse panels as 'style' prop value
+const panelBorder = {
+  borderBottom: 'none',
+};
 
-const StyledIcon = (theme: SupersetTheme) => css`
-  margin: auto ${theme.gridUnit * 2}px auto 0;
-  color: ${theme.colors.grayscale.base};
+// Apply to final text input components of each collapse panel
+const no_margin_bottom = css`

Review Comment:
   We should use consistent naming conventions (though that's something that we 
fail to do in so many cases in Superset 😄). I'd suggest renaming it to 
`noMarginBottom`



##########
superset-frontend/src/features/alerts/components/ValidatedPanelHeader.tsx:
##########
@@ -0,0 +1,57 @@
+/**
+ * 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 from 'react';
+import { t } from '@superset-ui/core';
+import { CheckCircleOutlined } from '@ant-design/icons';
+
+const ValidatedPanelHeader = ({
+  title,
+  subtitle,
+  // eslint-disable-next-line @typescript-eslint/no-unused-vars
+  required,
+  validateCheckStatus,
+  testId,
+}: {
+  title: string;
+  subtitle: string;
+  required: boolean;
+  validateCheckStatus: boolean;
+  testId?: string;
+}): JSX.Element => {
+  const asterisk = ' *';
+  const checkmark = <CheckCircleOutlined />;

Review Comment:
   I don't think we need to extract asterisk and checkmark to constants 



##########
superset-frontend/src/features/alerts/buildErrorTooltipMessage.tsx:
##########
@@ -0,0 +1,57 @@
+/**
+ * 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 from 'react';
+import { styled } from '@superset-ui/core';
+import { ValidationObject } from './types';
+
+const StyledList = styled.ul`
+  margin: 0;
+  padding: 0;
+  li {
+    padding-left: 12px;

Review Comment:
   `theme.gridUnit * 3`



##########
superset-frontend/src/features/alerts/AlertReportModal.tsx:
##########
@@ -1190,367 +1313,456 @@ const AlertReportModal: 
FunctionComponent<AlertReportModalProps> = ({
         </h4>
       }
     >
-      <StyledSectionContainer>
-        <div className="header-section">
-          <StyledInputContainer>
-            <div className="control-label">
-              {isReport
-                ? TRANSLATIONS.REPORT_NAME_TEXT
-                : TRANSLATIONS.ALERT_NAME_TEXT}
-              <span className="required">*</span>
-            </div>
-            <div className="input-container">
-              <input
-                type="text"
-                name="name"
-                value={currentAlert ? currentAlert.name : ''}
-                placeholder={
-                  isReport
-                    ? TRANSLATIONS.REPORT_NAME_TEXT
-                    : TRANSLATIONS.ALERT_NAME_TEXT
-                }
-                onChange={onInputChange}
-                css={inputSpacer}
-              />
-            </div>
-          </StyledInputContainer>
-          <StyledInputContainer>
-            <div className="control-label">
-              {TRANSLATIONS.OWNERS_TEXT}
-              <span className="required">*</span>
-            </div>
-            <div data-test="owners-select" className="input-container">
-              <AsyncSelect
-                ariaLabel={TRANSLATIONS.OWNERS_TEXT}
-                allowClear
-                name="owners"
-                mode="multiple"
-                value={
-                  (currentAlert?.owners as {
-                    label: string;
-                    value: number;
-                  }[]) || []
-                }
-                options={loadOwnerOptions}
-                onChange={onOwnersChange}
-                css={inputSpacer}
-              />
-            </div>
-          </StyledInputContainer>
-          <StyledInputContainer>
-            <div 
className="control-label">{TRANSLATIONS.DESCRIPTION_TEXT}</div>
-            <div className="input-container">
-              <input
-                type="text"
-                name="description"
-                value={currentAlert ? currentAlert.description || '' : ''}
-                placeholder={TRANSLATIONS.DESCRIPTION_TEXT}
-                onChange={onInputChange}
-                css={inputSpacer}
-              />
-            </div>
-          </StyledInputContainer>
-          <StyledSwitchContainer>
-            <Switch
-              onChange={onActiveSwitch}
-              checked={currentAlert ? currentAlert.active : true}
+      <Collapse
+        expandIconPosition="right"
+        defaultActiveKey="1"
+        accordion
+        style={{ border: 'none' }}
+      >
+        <StyledPanel
+          header={
+            <ValidatedPanelHeader
+              title={TRANSLATIONS.GENERAL_TITLE}
+              subtitle={TRANSLATIONS.GENERAL_SUBTITLE}
+              required
+              validateCheckStatus={validationStatus[Sections.GENERAL].status}
+              testId="general-information-panel"
             />
-            <div className="switch-label">{TRANSLATIONS.ACTIVE_TEXT}</div>
-          </StyledSwitchContainer>
-        </div>
-        <div className="column-section">
-          {!isReport && (
-            <div className="column condition">
-              <StyledSectionTitle>
-                <h4>{TRANSLATIONS.ALERT_CONDITION_TEXT}</h4>
-              </StyledSectionTitle>
-              <StyledInputContainer>
-                <div className="control-label">
-                  {TRANSLATIONS.DATABASE_TEXT}
-                  <span className="required">*</span>
-                </div>
-                <div className="input-container">
-                  <AsyncSelect
-                    ariaLabel={TRANSLATIONS.DATABASE_TEXT}
-                    name="source"
-                    value={
-                      currentAlert?.database?.label &&
-                      currentAlert?.database?.value
-                        ? {
-                            value: currentAlert.database.value,
-                            label: currentAlert.database.label,
-                          }
-                        : undefined
-                    }
-                    options={loadSourceOptions}
-                    onChange={onSourceChange}
-                  />
-                </div>
-              </StyledInputContainer>
-              <StyledInputContainer>
-                <div className="control-label">
-                  {TRANSLATIONS.SQL_QUERY_TEXT}
-                  <StyledTooltip tooltip={TRANSLATIONS.SQL_QUERY_TOOLTIP} />
-                  <span className="required">*</span>
-                </div>
-                <TextAreaControl
-                  name="sql"
-                  language="sql"
-                  offerEditInModal={false}
-                  minLines={15}
-                  maxLines={15}
-                  onChange={onSQLChange}
-                  readOnly={false}
-                  initialValue={resource?.sql}
-                  key={currentAlert?.id}
+          }
+          key="1"
+          style={panelBorder}
+        >
+          <div className="header-section">
+            <StyledInputContainer>
+              <div className="control-label">
+                {isReport
+                  ? TRANSLATIONS.REPORT_NAME_TEXT
+                  : TRANSLATIONS.ALERT_NAME_TEXT}
+                <span className="required">*</span>
+              </div>
+              <div className="input-container">
+                <input
+                  type="text"
+                  name="name"
+                  value={currentAlert ? currentAlert.name : ''}
+                  placeholder={
+                    isReport
+                      ? TRANSLATIONS.REPORT_NAME_PLACEHOLDER
+                      : TRANSLATIONS.ALERT_NAME_PLACEHOLDER
+                  }
+                  onChange={onInputChange}
                 />
-              </StyledInputContainer>
-              <div className="inline-container wrap">
-                <StyledInputContainer>
-                  <div className="control-label" css={inputSpacer}>
-                    {TRANSLATIONS.TRIGGER_ALERT_IF_TEXT}
-                    <span className="required">*</span>
-                  </div>
-                  <div className="input-container">
-                    <Select
-                      ariaLabel={TRANSLATIONS.CONDITION_TEXT}
-                      onChange={onConditionChange}
-                      placeholder="Condition"
-                      value={
-                        currentAlert?.validator_config_json?.op || undefined
-                      }
-                      options={CONDITIONS}
-                      css={inputSpacer}
-                    />
-                  </div>
-                </StyledInputContainer>
-                <StyledInputContainer>
-                  <div className="control-label">
-                    {TRANSLATIONS.VALUE_TEXT}
-                    <span className="required">*</span>
-                  </div>
-                  <div className="input-container">
-                    <input
-                      type="number"
-                      name="threshold"
-                      disabled={conditionNotNull}
-                      value={
-                        currentAlert?.validator_config_json?.threshold !==
-                        undefined
-                          ? currentAlert.validator_config_json.threshold
-                          : ''
-                      }
-                      placeholder={TRANSLATIONS.VALUE_TEXT}
-                      onChange={onThresholdChange}
-                    />
-                  </div>
-                </StyledInputContainer>
               </div>
-            </div>
-          )}
-          <div className="column schedule">
-            <StyledSectionTitle>
-              <h4>
-                {isReport
-                  ? TRANSLATIONS.REPORT_SCHEDULE_TEXT
-                  : TRANSLATIONS.ALERT_CONDITION_SCHEDULE_TEXT}
-              </h4>
-              <span className="required">*</span>
-            </StyledSectionTitle>
-            <AlertReportCronScheduler
-              value={currentAlert?.crontab || ALERT_REPORTS_DEFAULT_CRON_VALUE}
-              onChange={newVal => updateAlertState('crontab', newVal)}
-            />
-            <div className="control-label">{TRANSLATIONS.TIMEZONE_TEXT}</div>
-            <div
-              className="input-container"
-              css={(theme: SupersetTheme) => timezoneHeaderStyle(theme)}
-            >
-              <TimezoneSelector
-                onTimezoneChange={onTimezoneChange}
-                timezone={currentAlert?.timezone}
-                minWidth="100%"
-              />
-            </div>
-            <StyledSectionTitle>
-              <h4>{TRANSLATIONS.SCHEDULE_SETTINGS_TEXT}</h4>
-            </StyledSectionTitle>
+            </StyledInputContainer>
             <StyledInputContainer>
               <div className="control-label">
-                {TRANSLATIONS.LOG_RETENTION_TEXT}
+                {TRANSLATIONS.OWNERS_TEXT}
                 <span className="required">*</span>
               </div>
-              <div className="input-container">
-                <Select
-                  ariaLabel={TRANSLATIONS.LOG_RETENTION_TEXT}
-                  placeholder={TRANSLATIONS.LOG_RETENTION_TEXT}
-                  onChange={onLogRetentionChange}
+              <div data-test="owners-select" className="input-container">
+                <AsyncSelect
+                  ariaLabel={TRANSLATIONS.OWNERS_TEXT}
+                  allowClear
+                  name="owners"
+                  mode="multiple"
+                  placeholder={TRANSLATIONS.OWNERS_PLACEHOLDER}
                   value={
-                    typeof currentAlert?.log_retention === 'number'
-                      ? currentAlert?.log_retention
-                      : ALERT_REPORTS_DEFAULT_RETENTION
+                    (currentAlert?.owners as {
+                      label: string;
+                      value: number;
+                    }[]) || []
                   }
-                  options={RETENTION_OPTIONS}
-                  sortComparator={propertyComparator('value')}
+                  options={loadOwnerOptions}
+                  onChange={onOwnersChange}
                 />
               </div>
             </StyledInputContainer>
             <StyledInputContainer>
               <div className="control-label">
-                {TRANSLATIONS.WORKING_TIMEOUT_TEXT}
-                <span className="required">*</span>
+                {TRANSLATIONS.DESCRIPTION_TEXT}
               </div>
               <div className="input-container">
                 <input
-                  type="number"
-                  min="1"
-                  name="working_timeout"
-                  value={currentAlert?.working_timeout || ''}
-                  placeholder={TRANSLATIONS.TIME_IN_SECONDS_TEXT}
-                  onChange={onTimeoutVerifyChange}
+                  type="text"
+                  name="description"
+                  value={currentAlert ? currentAlert.description || '' : ''}
+                  placeholder={
+                    isReport
+                      ? TRANSLATIONS.REPORT_DESCRIPTION_PLACEHOLDER
+                      : TRANSLATIONS.ALERT_DESCRIPTION_PLACEHOLDER
+                  }
+                  onChange={onInputChange}
                 />
-                <span 
className="input-label">{TRANSLATIONS.SECONDS_TEXT}</span>
               </div>
             </StyledInputContainer>
-            {!isReport && (
-              <StyledInputContainer>
+            <StyledSwitchContainer>
+              <Switch
+                checked={currentAlert ? currentAlert.active : false}
+                defaultChecked
+                onChange={onActiveSwitch}
+              />
+              <div className="switch-label">
+                {isReport
+                  ? TRANSLATIONS.ACTIVE_REPORT_TEXT
+                  : TRANSLATIONS.ACTIVE_ALERT_TEXT}
+              </div>
+            </StyledSwitchContainer>
+          </div>
+        </StyledPanel>
+        {!isReport && (
+          <StyledPanel
+            header={
+              <ValidatedPanelHeader
+                title={TRANSLATIONS.ALERT_CONDITION_TITLE}
+                subtitle={TRANSLATIONS.ALERT_CONDITION_SUBTITLE}
+                required={false}
+                validateCheckStatus={validationStatus[Sections.ALERT].status}
+                testId="alert-condition-panel"
+              />
+            }
+            key="2"
+            style={{ borderBottom: 'none' }}
+          >
+            <StyledInputContainer>
+              <div className="control-label">
+                {TRANSLATIONS.DATABASE_TEXT}
+                <span className="required">*</span>
+              </div>
+              <div className="input-container">
+                <AsyncSelect
+                  ariaLabel={TRANSLATIONS.DATABASE_TEXT}
+                  name="source"
+                  placeholder={TRANSLATIONS.DATABASE_PLACEHOLDER}
+                  value={
+                    currentAlert?.database?.label &&
+                    currentAlert?.database?.value
+                      ? {
+                          value: currentAlert.database.value,
+                          label: currentAlert.database.label,
+                        }
+                      : undefined
+                  }
+                  options={loadSourceOptions}
+                  onChange={onSourceChange}
+                />
+              </div>
+            </StyledInputContainer>
+            <StyledInputContainer>
+              <div className="control-label">
+                {TRANSLATIONS.SQL_QUERY_TEXT}
+                <StyledTooltip tooltip={TRANSLATIONS.SQL_QUERY_TOOLTIP} />
+                <span className="required">*</span>
+              </div>
+              <TextAreaControl
+                name="sql"
+                language="sql"
+                offerEditInModal={false}
+                minLines={15}
+                maxLines={15}
+                onChange={onSQLChange}
+                readOnly={false}
+                initialValue={resource?.sql}
+                key={currentAlert?.id}
+              />
+            </StyledInputContainer>
+            <div className="inline-container wrap">
+              <StyledInputContainer css={no_margin_bottom}>
+                <div className="control-label" css={inputSpacer}>
+                  {TRANSLATIONS.TRIGGER_ALERT_IF_TEXT}
+                  <span className="required">*</span>
+                </div>
+                <div className="input-container">
+                  <Select
+                    ariaLabel={TRANSLATIONS.CONDITION_TEXT}
+                    onChange={onConditionChange}
+                    placeholder={TRANSLATIONS.CONDITION_PLACEHOLDER}
+                    value={currentAlert?.validator_config_json?.op || 
undefined}
+                    options={CONDITIONS}
+                    css={inputSpacer}
+                  />
+                </div>
+              </StyledInputContainer>
+              <StyledInputContainer css={no_margin_bottom}>
                 <div className="control-label">
-                  {TRANSLATIONS.GRACE_PERIOD_TEXT}
+                  {TRANSLATIONS.VALUE_TEXT} <span className="required">*</span>
                 </div>
                 <div className="input-container">
                   <input
                     type="number"
-                    min="1"
-                    name="grace_period"
-                    value={currentAlert?.grace_period || ''}
-                    placeholder={TRANSLATIONS.TIME_IN_SECONDS_TEXT}
-                    onChange={onTimeoutVerifyChange}
+                    name="threshold"
+                    disabled={conditionNotNull}
+                    value={
+                      currentAlert?.validator_config_json?.threshold !==
+                      undefined
+                        ? currentAlert.validator_config_json.threshold
+                        : ''
+                    }
+                    placeholder={TRANSLATIONS.VALUE_TEXT}
+                    onChange={onThresholdChange}
                   />
-                  <span className="input-label">
-                    {TRANSLATIONS.SECONDS_TEXT}
-                  </span>
                 </div>
               </StyledInputContainer>
-            )}
-          </div>
-          <div className="column message">
-            <StyledSectionTitle>
-              <h4>{TRANSLATIONS.MESSAGE_CONTENT_TEXT}</h4>
+            </div>
+          </StyledPanel>
+        )}
+        <StyledPanel
+          header={
+            <ValidatedPanelHeader
+              title={
+                isReport
+                  ? TRANSLATIONS.REPORT_CONTENTS_TITLE
+                  : TRANSLATIONS.ALERT_CONTENTS_TITLE
+              }
+              subtitle={TRANSLATIONS.CONTENTS_SUBTITLE}
+              required
+              validateCheckStatus={validationStatus[Sections.CONTENT].status}
+              testId="contents-panel"
+            />
+          }
+          key="3"
+          style={{ borderBottom: 'none' }}
+        >
+          <StyledInputContainer>
+            <div className="control-label">
+              {TRANSLATIONS.CONTENT_TYPE_LABEL}
               <span className="required">*</span>
-            </StyledSectionTitle>
-            <Radio.Group onChange={onContentTypeChange} value={contentType}>
-              <StyledRadio value="dashboard">
-                {TRANSLATIONS.DASHBOARD_TEXT}
-              </StyledRadio>
-              <StyledRadio 
value="chart">{TRANSLATIONS.CHART_TEXT}</StyledRadio>
-            </Radio.Group>
+            </div>
+            <Select
+              ariaLabel={TRANSLATIONS.CONTENT_TYPE_PLACEHOLDER}
+              onChange={onContentTypeChange}
+              value={contentType}
+              options={CONTENT_TYPE_OPTIONS}
+              placeholder={TRANSLATIONS.CONTENT_TYPE_PLACEHOLDER}
+            />
+          </StyledInputContainer>
+          <StyledInputContainer>
             {contentType === 'chart' ? (
-              <AsyncSelect
-                ariaLabel={TRANSLATIONS.CHART_TEXT}
-                name="chart"
-                value={
-                  currentAlert?.chart?.label && currentAlert?.chart?.value
-                    ? {
-                        value: currentAlert.chart.value,
-                        label: currentAlert.chart.label,
-                      }
-                    : undefined
-                }
-                options={loadChartOptions}
-                onChange={onChartChange}
-              />
+              <>
+                <div className="control-label">
+                  {TRANSLATIONS.SELECT_CHART_LABEL}
+                  <span className="required">*</span>
+                </div>
+                <AsyncSelect
+                  ariaLabel={TRANSLATIONS.CHART_TEXT}
+                  name="chart"
+                  value={
+                    currentAlert?.chart?.label && currentAlert?.chart?.value
+                      ? {
+                          value: currentAlert.chart.value,
+                          label: currentAlert.chart.label,
+                        }
+                      : undefined
+                  }
+                  options={loadChartOptions}
+                  onChange={onChartChange}
+                  placeholder={TRANSLATIONS.SELECT_CHART_PLACEHOLDER}
+                />
+              </>
             ) : (
-              <AsyncSelect
-                ariaLabel={TRANSLATIONS.DASHBOARD_TEXT}
-                name="dashboard"
-                value={
-                  currentAlert?.dashboard?.label &&
-                  currentAlert?.dashboard?.value
-                    ? {
-                        value: currentAlert.dashboard.value,
-                        label: currentAlert.dashboard.label,
-                      }
-                    : undefined
-                }
-                options={loadDashboardOptions}
-                onChange={onDashboardChange}
-              />
+              <>
+                <div className="control-label">
+                  {TRANSLATIONS.SELECT_DASHBOARD_LABEL}
+                  <span className="required">*</span>
+                </div>
+                <AsyncSelect
+                  ariaLabel={TRANSLATIONS.DASHBOARD_TEXT}
+                  name="dashboard"
+                  value={
+                    currentAlert?.dashboard?.label &&
+                    currentAlert?.dashboard?.value
+                      ? {
+                          value: currentAlert.dashboard.value,
+                          label: currentAlert.dashboard.label,
+                        }
+                      : undefined
+                  }
+                  options={loadDashboardOptions}
+                  onChange={onDashboardChange}
+                  placeholder={TRANSLATIONS.SELECT_DASHBOARD_PLACEHOLDER}
+                />
+              </>
             )}
+          </StyledInputContainer>
+          <StyledInputContainer
+            css={['TEXT', 'CSV'].includes(reportFormat) && no_margin_bottom}
+          >
             {formatOptionEnabled && (
               <>
-                <div className="inline-container">
-                  <StyledRadioGroup
-                    onChange={onFormatChange}
-                    value={reportFormat}
-                  >
-                    <StyledRadio value="PNG">
-                      {TRANSLATIONS.SEND_AS_PNG_TEXT}
-                    </StyledRadio>
-                    <StyledRadio value="CSV">
-                      {TRANSLATIONS.SEND_AS_CSV_TEXT}
-                    </StyledRadio>
-                    {TEXT_BASED_VISUALIZATION_TYPES.includes(chartVizType) && (
-                      <StyledRadio value="TEXT">
-                        {TRANSLATIONS.SEND_AS_TEXT}
-                      </StyledRadio>
-                    )}
-                  </StyledRadioGroup>
+                <div className="control-label">
+                  {TRANSLATIONS.FORMAT_TYPE_LABEL}
+                  <span className="required">*</span>
                 </div>
+                <Select
+                  ariaLabel={TRANSLATIONS.FORMAT_TYPE_PLACEHOLDER}
+                  onChange={onFormatChange}
+                  value={reportFormat}
+                  options={
+                    /* If chart is of text based viz type: show text
+                  format option */
+                    TEXT_BASED_VISUALIZATION_TYPES.includes(chartVizType)
+                      ? Object.values(FORMAT_OPTIONS)
+                      : ['png', 'csv'].map(key => FORMAT_OPTIONS[key])
+                  }
+                  placeholder={TRANSLATIONS.FORMAT_TYPE_PLACEHOLDER}
+                />
               </>
             )}
-            {isScreenshot && (
-              <StyledInputContainer>
-                <div className="control-label" css={CustomWidthHeaderStyle}>
-                  {TRANSLATIONS.CUSTOM_SCREENSHOT_WIDTH_TEXT}
+          </StyledInputContainer>
+          {isScreenshot && (
+            <StyledInputContainer
+              css={!isReport && contentType === 'chart' && no_margin_bottom}
+            >
+              <div className="control-label">
+                {TRANSLATIONS.CUSTOM_SCREENSHOT_WIDTH_TEXT}
+              </div>
+              <div className="input-container">
+                <Input
+                  type="number"
+                  name="custom_width"
+                  value={currentAlert?.custom_width || ''}
+                  placeholder={
+                    TRANSLATIONS.CUSTOM_SCREENSHOT_WIDTH_PLACEHOLDER_TEXT
+                  }
+                  onChange={onInputChange}
+                />
+              </div>
+            </StyledInputContainer>
+          )}
+          {(isReport || contentType === 'dashboard') && (
+            <div className="inline-container">
+              <StyledCheckbox
+                data-test="bypass-cache"
+                className="checkbox"
+                checked={forceScreenshot}
+                onChange={onForceScreenshotChange}
+              >
+                {TRANSLATIONS.IGNORE_CACHE_TEXT}
+              </StyledCheckbox>
+            </div>
+          )}
+        </StyledPanel>
+        <StyledPanel
+          header={
+            <ValidatedPanelHeader
+              title={TRANSLATIONS.SCHEDULE_TITLE}
+              subtitle={TRANSLATIONS.SCHEDULE_SUBTITLE}
+              required
+              validateCheckStatus={validationStatus[Sections.SCHEDULE].status}
+              testId="schedule-panel"
+            />
+          }
+          key="4"
+          style={{ borderBottom: 'none' }}
+        >
+          <AlertReportCronScheduler
+            value={currentAlert?.crontab || ALERT_REPORTS_DEFAULT_CRON_VALUE}
+            onChange={newVal => updateAlertState('crontab', newVal)}
+          />
+          <StyledInputContainer>
+            <div className="control-label">
+              {TRANSLATIONS.TIMEZONE_TEXT} <span className="required">*</span>
+            </div>
+            <TimezoneSelector
+              onTimezoneChange={onTimezoneChange}
+              timezone={currentAlert?.timezone}
+              minWidth="100%"
+            />
+          </StyledInputContainer>
+          <StyledInputContainer>
+            <div className="control-label">
+              {TRANSLATIONS.LOG_RETENTION_TEXT}
+              <span className="required">*</span>
+            </div>
+            <div className="input-container">
+              <Select
+                ariaLabel={TRANSLATIONS.LOG_RETENTION_TEXT}
+                placeholder={TRANSLATIONS.LOG_RETENTION_TEXT}
+                onChange={onLogRetentionChange}
+                value={
+                  typeof currentAlert?.log_retention === 'number'
+                    ? currentAlert?.log_retention
+                    : ALERT_REPORTS_DEFAULT_RETENTION
+                }
+                options={RETENTION_OPTIONS}
+                sortComparator={propertyComparator('value')}
+              />
+            </div>
+          </StyledInputContainer>
+          <StyledInputContainer css={no_margin_bottom}>
+            {isReport ? (
+              <>
+                <div className="control-label">
+                  {TRANSLATIONS.WORKING_TIMEOUT_TEXT}
+                  <span className="required">*</span>
                 </div>
                 <div className="input-container">
-                  <Input
-                    type="number"
-                    name="custom_width"
-                    value={currentAlert?.custom_width || ''}
-                    placeholder={
-                      TRANSLATIONS.CUSTOM_SCREENSHOT_WIDTH_PLACEHOLDER_TEXT
-                    }
-                    onChange={onInputChange}
+                  <NumberInput
+                    min={1}
+                    name="working_timeout"
+                    value={currentAlert?.working_timeout || ''}
+                    placeholder={TRANSLATIONS.TIME_IN_SECONDS_TEXT}
+                    onChange={onTimeoutVerifyChange}
+                    timeUnit="seconds"
                   />
                 </div>
-              </StyledInputContainer>
-            )}
-            {(isReport || contentType === 'dashboard') && (
-              <div className="inline-container">
-                <StyledCheckbox
-                  data-test="bypass-cache"
-                  className="checkbox"
-                  checked={forceScreenshot}
-                  onChange={onForceScreenshotChange}
-                >
-                  {TRANSLATIONS.IGNORE_CACHE_TEXT}
-                </StyledCheckbox>
-              </div>
+              </>
+            ) : (
+              <>
+                <div className="control-label">
+                  {TRANSLATIONS.GRACE_PERIOD_TEXT}
+                </div>
+                <div className="input-container">
+                  <NumberInput
+                    min={1}
+                    name="grace_period"
+                    value={currentAlert?.grace_period || ''}
+                    placeholder={TRANSLATIONS.TIME_IN_SECONDS_TEXT}
+                    onChange={onTimeoutVerifyChange}
+                    timeUnit="seconds"

Review Comment:
   translation here too



##########
superset-frontend/src/features/alerts/AlertReportModal.tsx:
##########
@@ -1190,367 +1313,456 @@ const AlertReportModal: 
FunctionComponent<AlertReportModalProps> = ({
         </h4>
       }
     >
-      <StyledSectionContainer>
-        <div className="header-section">
-          <StyledInputContainer>
-            <div className="control-label">
-              {isReport
-                ? TRANSLATIONS.REPORT_NAME_TEXT
-                : TRANSLATIONS.ALERT_NAME_TEXT}
-              <span className="required">*</span>
-            </div>
-            <div className="input-container">
-              <input
-                type="text"
-                name="name"
-                value={currentAlert ? currentAlert.name : ''}
-                placeholder={
-                  isReport
-                    ? TRANSLATIONS.REPORT_NAME_TEXT
-                    : TRANSLATIONS.ALERT_NAME_TEXT
-                }
-                onChange={onInputChange}
-                css={inputSpacer}
-              />
-            </div>
-          </StyledInputContainer>
-          <StyledInputContainer>
-            <div className="control-label">
-              {TRANSLATIONS.OWNERS_TEXT}
-              <span className="required">*</span>
-            </div>
-            <div data-test="owners-select" className="input-container">
-              <AsyncSelect
-                ariaLabel={TRANSLATIONS.OWNERS_TEXT}
-                allowClear
-                name="owners"
-                mode="multiple"
-                value={
-                  (currentAlert?.owners as {
-                    label: string;
-                    value: number;
-                  }[]) || []
-                }
-                options={loadOwnerOptions}
-                onChange={onOwnersChange}
-                css={inputSpacer}
-              />
-            </div>
-          </StyledInputContainer>
-          <StyledInputContainer>
-            <div 
className="control-label">{TRANSLATIONS.DESCRIPTION_TEXT}</div>
-            <div className="input-container">
-              <input
-                type="text"
-                name="description"
-                value={currentAlert ? currentAlert.description || '' : ''}
-                placeholder={TRANSLATIONS.DESCRIPTION_TEXT}
-                onChange={onInputChange}
-                css={inputSpacer}
-              />
-            </div>
-          </StyledInputContainer>
-          <StyledSwitchContainer>
-            <Switch
-              onChange={onActiveSwitch}
-              checked={currentAlert ? currentAlert.active : true}
+      <Collapse
+        expandIconPosition="right"
+        defaultActiveKey="1"
+        accordion
+        style={{ border: 'none' }}
+      >
+        <StyledPanel
+          header={
+            <ValidatedPanelHeader
+              title={TRANSLATIONS.GENERAL_TITLE}
+              subtitle={TRANSLATIONS.GENERAL_SUBTITLE}
+              required
+              validateCheckStatus={validationStatus[Sections.GENERAL].status}
+              testId="general-information-panel"
             />
-            <div className="switch-label">{TRANSLATIONS.ACTIVE_TEXT}</div>
-          </StyledSwitchContainer>
-        </div>
-        <div className="column-section">
-          {!isReport && (
-            <div className="column condition">
-              <StyledSectionTitle>
-                <h4>{TRANSLATIONS.ALERT_CONDITION_TEXT}</h4>
-              </StyledSectionTitle>
-              <StyledInputContainer>
-                <div className="control-label">
-                  {TRANSLATIONS.DATABASE_TEXT}
-                  <span className="required">*</span>
-                </div>
-                <div className="input-container">
-                  <AsyncSelect
-                    ariaLabel={TRANSLATIONS.DATABASE_TEXT}
-                    name="source"
-                    value={
-                      currentAlert?.database?.label &&
-                      currentAlert?.database?.value
-                        ? {
-                            value: currentAlert.database.value,
-                            label: currentAlert.database.label,
-                          }
-                        : undefined
-                    }
-                    options={loadSourceOptions}
-                    onChange={onSourceChange}
-                  />
-                </div>
-              </StyledInputContainer>
-              <StyledInputContainer>
-                <div className="control-label">
-                  {TRANSLATIONS.SQL_QUERY_TEXT}
-                  <StyledTooltip tooltip={TRANSLATIONS.SQL_QUERY_TOOLTIP} />
-                  <span className="required">*</span>
-                </div>
-                <TextAreaControl
-                  name="sql"
-                  language="sql"
-                  offerEditInModal={false}
-                  minLines={15}
-                  maxLines={15}
-                  onChange={onSQLChange}
-                  readOnly={false}
-                  initialValue={resource?.sql}
-                  key={currentAlert?.id}
+          }
+          key="1"
+          style={panelBorder}
+        >
+          <div className="header-section">
+            <StyledInputContainer>
+              <div className="control-label">
+                {isReport
+                  ? TRANSLATIONS.REPORT_NAME_TEXT
+                  : TRANSLATIONS.ALERT_NAME_TEXT}
+                <span className="required">*</span>
+              </div>
+              <div className="input-container">
+                <input
+                  type="text"
+                  name="name"
+                  value={currentAlert ? currentAlert.name : ''}
+                  placeholder={
+                    isReport
+                      ? TRANSLATIONS.REPORT_NAME_PLACEHOLDER
+                      : TRANSLATIONS.ALERT_NAME_PLACEHOLDER
+                  }
+                  onChange={onInputChange}
                 />
-              </StyledInputContainer>
-              <div className="inline-container wrap">
-                <StyledInputContainer>
-                  <div className="control-label" css={inputSpacer}>
-                    {TRANSLATIONS.TRIGGER_ALERT_IF_TEXT}
-                    <span className="required">*</span>
-                  </div>
-                  <div className="input-container">
-                    <Select
-                      ariaLabel={TRANSLATIONS.CONDITION_TEXT}
-                      onChange={onConditionChange}
-                      placeholder="Condition"
-                      value={
-                        currentAlert?.validator_config_json?.op || undefined
-                      }
-                      options={CONDITIONS}
-                      css={inputSpacer}
-                    />
-                  </div>
-                </StyledInputContainer>
-                <StyledInputContainer>
-                  <div className="control-label">
-                    {TRANSLATIONS.VALUE_TEXT}
-                    <span className="required">*</span>
-                  </div>
-                  <div className="input-container">
-                    <input
-                      type="number"
-                      name="threshold"
-                      disabled={conditionNotNull}
-                      value={
-                        currentAlert?.validator_config_json?.threshold !==
-                        undefined
-                          ? currentAlert.validator_config_json.threshold
-                          : ''
-                      }
-                      placeholder={TRANSLATIONS.VALUE_TEXT}
-                      onChange={onThresholdChange}
-                    />
-                  </div>
-                </StyledInputContainer>
               </div>
-            </div>
-          )}
-          <div className="column schedule">
-            <StyledSectionTitle>
-              <h4>
-                {isReport
-                  ? TRANSLATIONS.REPORT_SCHEDULE_TEXT
-                  : TRANSLATIONS.ALERT_CONDITION_SCHEDULE_TEXT}
-              </h4>
-              <span className="required">*</span>
-            </StyledSectionTitle>
-            <AlertReportCronScheduler
-              value={currentAlert?.crontab || ALERT_REPORTS_DEFAULT_CRON_VALUE}
-              onChange={newVal => updateAlertState('crontab', newVal)}
-            />
-            <div className="control-label">{TRANSLATIONS.TIMEZONE_TEXT}</div>
-            <div
-              className="input-container"
-              css={(theme: SupersetTheme) => timezoneHeaderStyle(theme)}
-            >
-              <TimezoneSelector
-                onTimezoneChange={onTimezoneChange}
-                timezone={currentAlert?.timezone}
-                minWidth="100%"
-              />
-            </div>
-            <StyledSectionTitle>
-              <h4>{TRANSLATIONS.SCHEDULE_SETTINGS_TEXT}</h4>
-            </StyledSectionTitle>
+            </StyledInputContainer>
             <StyledInputContainer>
               <div className="control-label">
-                {TRANSLATIONS.LOG_RETENTION_TEXT}
+                {TRANSLATIONS.OWNERS_TEXT}
                 <span className="required">*</span>
               </div>
-              <div className="input-container">
-                <Select
-                  ariaLabel={TRANSLATIONS.LOG_RETENTION_TEXT}
-                  placeholder={TRANSLATIONS.LOG_RETENTION_TEXT}
-                  onChange={onLogRetentionChange}
+              <div data-test="owners-select" className="input-container">
+                <AsyncSelect
+                  ariaLabel={TRANSLATIONS.OWNERS_TEXT}
+                  allowClear
+                  name="owners"
+                  mode="multiple"
+                  placeholder={TRANSLATIONS.OWNERS_PLACEHOLDER}
                   value={
-                    typeof currentAlert?.log_retention === 'number'
-                      ? currentAlert?.log_retention
-                      : ALERT_REPORTS_DEFAULT_RETENTION
+                    (currentAlert?.owners as {
+                      label: string;
+                      value: number;
+                    }[]) || []
                   }
-                  options={RETENTION_OPTIONS}
-                  sortComparator={propertyComparator('value')}
+                  options={loadOwnerOptions}
+                  onChange={onOwnersChange}
                 />
               </div>
             </StyledInputContainer>
             <StyledInputContainer>
               <div className="control-label">
-                {TRANSLATIONS.WORKING_TIMEOUT_TEXT}
-                <span className="required">*</span>
+                {TRANSLATIONS.DESCRIPTION_TEXT}
               </div>
               <div className="input-container">
                 <input
-                  type="number"
-                  min="1"
-                  name="working_timeout"
-                  value={currentAlert?.working_timeout || ''}
-                  placeholder={TRANSLATIONS.TIME_IN_SECONDS_TEXT}
-                  onChange={onTimeoutVerifyChange}
+                  type="text"
+                  name="description"
+                  value={currentAlert ? currentAlert.description || '' : ''}
+                  placeholder={
+                    isReport
+                      ? TRANSLATIONS.REPORT_DESCRIPTION_PLACEHOLDER
+                      : TRANSLATIONS.ALERT_DESCRIPTION_PLACEHOLDER
+                  }
+                  onChange={onInputChange}
                 />
-                <span 
className="input-label">{TRANSLATIONS.SECONDS_TEXT}</span>
               </div>
             </StyledInputContainer>
-            {!isReport && (
-              <StyledInputContainer>
+            <StyledSwitchContainer>
+              <Switch
+                checked={currentAlert ? currentAlert.active : false}
+                defaultChecked
+                onChange={onActiveSwitch}
+              />
+              <div className="switch-label">
+                {isReport
+                  ? TRANSLATIONS.ACTIVE_REPORT_TEXT
+                  : TRANSLATIONS.ACTIVE_ALERT_TEXT}
+              </div>
+            </StyledSwitchContainer>
+          </div>
+        </StyledPanel>
+        {!isReport && (
+          <StyledPanel
+            header={
+              <ValidatedPanelHeader
+                title={TRANSLATIONS.ALERT_CONDITION_TITLE}
+                subtitle={TRANSLATIONS.ALERT_CONDITION_SUBTITLE}
+                required={false}
+                validateCheckStatus={validationStatus[Sections.ALERT].status}
+                testId="alert-condition-panel"
+              />
+            }
+            key="2"
+            style={{ borderBottom: 'none' }}
+          >
+            <StyledInputContainer>
+              <div className="control-label">
+                {TRANSLATIONS.DATABASE_TEXT}
+                <span className="required">*</span>
+              </div>
+              <div className="input-container">
+                <AsyncSelect
+                  ariaLabel={TRANSLATIONS.DATABASE_TEXT}
+                  name="source"
+                  placeholder={TRANSLATIONS.DATABASE_PLACEHOLDER}
+                  value={
+                    currentAlert?.database?.label &&
+                    currentAlert?.database?.value
+                      ? {
+                          value: currentAlert.database.value,
+                          label: currentAlert.database.label,
+                        }
+                      : undefined
+                  }
+                  options={loadSourceOptions}
+                  onChange={onSourceChange}
+                />
+              </div>
+            </StyledInputContainer>
+            <StyledInputContainer>
+              <div className="control-label">
+                {TRANSLATIONS.SQL_QUERY_TEXT}
+                <StyledTooltip tooltip={TRANSLATIONS.SQL_QUERY_TOOLTIP} />
+                <span className="required">*</span>
+              </div>
+              <TextAreaControl
+                name="sql"
+                language="sql"
+                offerEditInModal={false}
+                minLines={15}
+                maxLines={15}
+                onChange={onSQLChange}
+                readOnly={false}
+                initialValue={resource?.sql}
+                key={currentAlert?.id}
+              />
+            </StyledInputContainer>
+            <div className="inline-container wrap">
+              <StyledInputContainer css={no_margin_bottom}>
+                <div className="control-label" css={inputSpacer}>
+                  {TRANSLATIONS.TRIGGER_ALERT_IF_TEXT}
+                  <span className="required">*</span>
+                </div>
+                <div className="input-container">
+                  <Select
+                    ariaLabel={TRANSLATIONS.CONDITION_TEXT}
+                    onChange={onConditionChange}
+                    placeholder={TRANSLATIONS.CONDITION_PLACEHOLDER}
+                    value={currentAlert?.validator_config_json?.op || 
undefined}
+                    options={CONDITIONS}
+                    css={inputSpacer}
+                  />
+                </div>
+              </StyledInputContainer>
+              <StyledInputContainer css={no_margin_bottom}>
                 <div className="control-label">
-                  {TRANSLATIONS.GRACE_PERIOD_TEXT}
+                  {TRANSLATIONS.VALUE_TEXT} <span className="required">*</span>
                 </div>
                 <div className="input-container">
                   <input
                     type="number"
-                    min="1"
-                    name="grace_period"
-                    value={currentAlert?.grace_period || ''}
-                    placeholder={TRANSLATIONS.TIME_IN_SECONDS_TEXT}
-                    onChange={onTimeoutVerifyChange}
+                    name="threshold"
+                    disabled={conditionNotNull}
+                    value={
+                      currentAlert?.validator_config_json?.threshold !==
+                      undefined
+                        ? currentAlert.validator_config_json.threshold
+                        : ''
+                    }
+                    placeholder={TRANSLATIONS.VALUE_TEXT}
+                    onChange={onThresholdChange}
                   />
-                  <span className="input-label">
-                    {TRANSLATIONS.SECONDS_TEXT}
-                  </span>
                 </div>
               </StyledInputContainer>
-            )}
-          </div>
-          <div className="column message">
-            <StyledSectionTitle>
-              <h4>{TRANSLATIONS.MESSAGE_CONTENT_TEXT}</h4>
+            </div>
+          </StyledPanel>
+        )}
+        <StyledPanel
+          header={
+            <ValidatedPanelHeader
+              title={
+                isReport
+                  ? TRANSLATIONS.REPORT_CONTENTS_TITLE
+                  : TRANSLATIONS.ALERT_CONTENTS_TITLE
+              }
+              subtitle={TRANSLATIONS.CONTENTS_SUBTITLE}
+              required
+              validateCheckStatus={validationStatus[Sections.CONTENT].status}
+              testId="contents-panel"
+            />
+          }
+          key="3"
+          style={{ borderBottom: 'none' }}
+        >
+          <StyledInputContainer>
+            <div className="control-label">
+              {TRANSLATIONS.CONTENT_TYPE_LABEL}
               <span className="required">*</span>
-            </StyledSectionTitle>
-            <Radio.Group onChange={onContentTypeChange} value={contentType}>
-              <StyledRadio value="dashboard">
-                {TRANSLATIONS.DASHBOARD_TEXT}
-              </StyledRadio>
-              <StyledRadio 
value="chart">{TRANSLATIONS.CHART_TEXT}</StyledRadio>
-            </Radio.Group>
+            </div>
+            <Select
+              ariaLabel={TRANSLATIONS.CONTENT_TYPE_PLACEHOLDER}
+              onChange={onContentTypeChange}
+              value={contentType}
+              options={CONTENT_TYPE_OPTIONS}
+              placeholder={TRANSLATIONS.CONTENT_TYPE_PLACEHOLDER}
+            />
+          </StyledInputContainer>
+          <StyledInputContainer>
             {contentType === 'chart' ? (
-              <AsyncSelect
-                ariaLabel={TRANSLATIONS.CHART_TEXT}
-                name="chart"
-                value={
-                  currentAlert?.chart?.label && currentAlert?.chart?.value
-                    ? {
-                        value: currentAlert.chart.value,
-                        label: currentAlert.chart.label,
-                      }
-                    : undefined
-                }
-                options={loadChartOptions}
-                onChange={onChartChange}
-              />
+              <>
+                <div className="control-label">
+                  {TRANSLATIONS.SELECT_CHART_LABEL}
+                  <span className="required">*</span>
+                </div>
+                <AsyncSelect
+                  ariaLabel={TRANSLATIONS.CHART_TEXT}
+                  name="chart"
+                  value={
+                    currentAlert?.chart?.label && currentAlert?.chart?.value
+                      ? {
+                          value: currentAlert.chart.value,
+                          label: currentAlert.chart.label,
+                        }
+                      : undefined
+                  }
+                  options={loadChartOptions}
+                  onChange={onChartChange}
+                  placeholder={TRANSLATIONS.SELECT_CHART_PLACEHOLDER}
+                />
+              </>
             ) : (
-              <AsyncSelect
-                ariaLabel={TRANSLATIONS.DASHBOARD_TEXT}
-                name="dashboard"
-                value={
-                  currentAlert?.dashboard?.label &&
-                  currentAlert?.dashboard?.value
-                    ? {
-                        value: currentAlert.dashboard.value,
-                        label: currentAlert.dashboard.label,
-                      }
-                    : undefined
-                }
-                options={loadDashboardOptions}
-                onChange={onDashboardChange}
-              />
+              <>
+                <div className="control-label">
+                  {TRANSLATIONS.SELECT_DASHBOARD_LABEL}
+                  <span className="required">*</span>
+                </div>
+                <AsyncSelect
+                  ariaLabel={TRANSLATIONS.DASHBOARD_TEXT}
+                  name="dashboard"
+                  value={
+                    currentAlert?.dashboard?.label &&
+                    currentAlert?.dashboard?.value
+                      ? {
+                          value: currentAlert.dashboard.value,
+                          label: currentAlert.dashboard.label,
+                        }
+                      : undefined
+                  }
+                  options={loadDashboardOptions}
+                  onChange={onDashboardChange}
+                  placeholder={TRANSLATIONS.SELECT_DASHBOARD_PLACEHOLDER}
+                />
+              </>
             )}
+          </StyledInputContainer>
+          <StyledInputContainer
+            css={['TEXT', 'CSV'].includes(reportFormat) && no_margin_bottom}
+          >
             {formatOptionEnabled && (
               <>
-                <div className="inline-container">
-                  <StyledRadioGroup
-                    onChange={onFormatChange}
-                    value={reportFormat}
-                  >
-                    <StyledRadio value="PNG">
-                      {TRANSLATIONS.SEND_AS_PNG_TEXT}
-                    </StyledRadio>
-                    <StyledRadio value="CSV">
-                      {TRANSLATIONS.SEND_AS_CSV_TEXT}
-                    </StyledRadio>
-                    {TEXT_BASED_VISUALIZATION_TYPES.includes(chartVizType) && (
-                      <StyledRadio value="TEXT">
-                        {TRANSLATIONS.SEND_AS_TEXT}
-                      </StyledRadio>
-                    )}
-                  </StyledRadioGroup>
+                <div className="control-label">
+                  {TRANSLATIONS.FORMAT_TYPE_LABEL}
+                  <span className="required">*</span>
                 </div>
+                <Select
+                  ariaLabel={TRANSLATIONS.FORMAT_TYPE_PLACEHOLDER}
+                  onChange={onFormatChange}
+                  value={reportFormat}
+                  options={
+                    /* If chart is of text based viz type: show text
+                  format option */
+                    TEXT_BASED_VISUALIZATION_TYPES.includes(chartVizType)
+                      ? Object.values(FORMAT_OPTIONS)
+                      : ['png', 'csv'].map(key => FORMAT_OPTIONS[key])
+                  }
+                  placeholder={TRANSLATIONS.FORMAT_TYPE_PLACEHOLDER}
+                />
               </>
             )}
-            {isScreenshot && (
-              <StyledInputContainer>
-                <div className="control-label" css={CustomWidthHeaderStyle}>
-                  {TRANSLATIONS.CUSTOM_SCREENSHOT_WIDTH_TEXT}
+          </StyledInputContainer>
+          {isScreenshot && (
+            <StyledInputContainer
+              css={!isReport && contentType === 'chart' && no_margin_bottom}
+            >
+              <div className="control-label">
+                {TRANSLATIONS.CUSTOM_SCREENSHOT_WIDTH_TEXT}
+              </div>
+              <div className="input-container">
+                <Input
+                  type="number"
+                  name="custom_width"
+                  value={currentAlert?.custom_width || ''}
+                  placeholder={
+                    TRANSLATIONS.CUSTOM_SCREENSHOT_WIDTH_PLACEHOLDER_TEXT
+                  }
+                  onChange={onInputChange}
+                />
+              </div>
+            </StyledInputContainer>
+          )}
+          {(isReport || contentType === 'dashboard') && (
+            <div className="inline-container">
+              <StyledCheckbox
+                data-test="bypass-cache"
+                className="checkbox"
+                checked={forceScreenshot}
+                onChange={onForceScreenshotChange}
+              >
+                {TRANSLATIONS.IGNORE_CACHE_TEXT}
+              </StyledCheckbox>
+            </div>
+          )}
+        </StyledPanel>
+        <StyledPanel
+          header={
+            <ValidatedPanelHeader
+              title={TRANSLATIONS.SCHEDULE_TITLE}
+              subtitle={TRANSLATIONS.SCHEDULE_SUBTITLE}
+              required
+              validateCheckStatus={validationStatus[Sections.SCHEDULE].status}
+              testId="schedule-panel"
+            />
+          }
+          key="4"
+          style={{ borderBottom: 'none' }}
+        >
+          <AlertReportCronScheduler
+            value={currentAlert?.crontab || ALERT_REPORTS_DEFAULT_CRON_VALUE}
+            onChange={newVal => updateAlertState('crontab', newVal)}
+          />
+          <StyledInputContainer>
+            <div className="control-label">
+              {TRANSLATIONS.TIMEZONE_TEXT} <span className="required">*</span>
+            </div>
+            <TimezoneSelector
+              onTimezoneChange={onTimezoneChange}
+              timezone={currentAlert?.timezone}
+              minWidth="100%"
+            />
+          </StyledInputContainer>
+          <StyledInputContainer>
+            <div className="control-label">
+              {TRANSLATIONS.LOG_RETENTION_TEXT}
+              <span className="required">*</span>
+            </div>
+            <div className="input-container">
+              <Select
+                ariaLabel={TRANSLATIONS.LOG_RETENTION_TEXT}
+                placeholder={TRANSLATIONS.LOG_RETENTION_TEXT}
+                onChange={onLogRetentionChange}
+                value={
+                  typeof currentAlert?.log_retention === 'number'
+                    ? currentAlert?.log_retention
+                    : ALERT_REPORTS_DEFAULT_RETENTION
+                }
+                options={RETENTION_OPTIONS}
+                sortComparator={propertyComparator('value')}
+              />
+            </div>
+          </StyledInputContainer>
+          <StyledInputContainer css={no_margin_bottom}>
+            {isReport ? (
+              <>
+                <div className="control-label">
+                  {TRANSLATIONS.WORKING_TIMEOUT_TEXT}
+                  <span className="required">*</span>
                 </div>
                 <div className="input-container">
-                  <Input
-                    type="number"
-                    name="custom_width"
-                    value={currentAlert?.custom_width || ''}
-                    placeholder={
-                      TRANSLATIONS.CUSTOM_SCREENSHOT_WIDTH_PLACEHOLDER_TEXT
-                    }
-                    onChange={onInputChange}
+                  <NumberInput
+                    min={1}
+                    name="working_timeout"
+                    value={currentAlert?.working_timeout || ''}
+                    placeholder={TRANSLATIONS.TIME_IN_SECONDS_TEXT}
+                    onChange={onTimeoutVerifyChange}
+                    timeUnit="seconds"

Review Comment:
   translation for seconds?



##########
superset-frontend/src/features/alerts/components/NotificationMethod.tsx:
##########
@@ -36,7 +36,7 @@ const StyledNotificationMethod = styled.div`
     margin-bottom: 10px;
 
     .input-container {
-      margin-left: 10px;
+      // margin-left: 10px;

Review Comment:
   Uncomment or delete



-- 
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