@opengeoweb/taf 2.1.0 → 2.1.4

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -1,10 +1,10 @@
1
1
  import * as React from 'react';
2
2
  import React__default from 'react';
3
- import { makeStyles, Grid, TextField, IconButton, Tooltip, Chip, SvgIcon, Typography, Button, Dialog, DialogTitle, DialogContent, Card, Accordion, AccordionSummary, AccordionDetails, Backdrop, CircularProgress } from '@material-ui/core';
4
- import { Skeleton } from '@material-ui/lab';
3
+ import { Grid, TextField, IconButton, Tooltip, Chip, SvgIcon, Typography, Button, Dialog, DialogTitle, DialogContent, Card, Accordion, AccordionSummary, AccordionDetails, Skeleton, Backdrop, CircularProgress } from '@mui/material';
4
+ import makeStyles from '@mui/styles/makeStyles';
5
5
  import moment from 'moment';
6
- import { useIsMounted, useDraftFormHelpers, ToolContainerDraggable, useConfirmationDialog, usePreventBrowserClose, usePrevious, StatusTag, AlertBanner, ConfirmationServiceProvider, usePoller, isAxiosError, getAxiosErrorMessage } from '@opengeoweb/shared';
7
- import { Delete, DragIndicator, Add, Close, InfoOutlined, Create, CheckCircle, Schedule, OpenInNew, ExpandMore } from '@material-ui/icons';
6
+ import { useIsMounted, useDraftFormHelpers, ToolContainerDraggable, useConfirmationDialog, usePreventBrowserClose, StatusTag, AlertBanner, ConfirmationServiceProvider, usePoller, isAxiosError, getAxiosErrorMessage } from '@opengeoweb/shared';
7
+ import { Add, Delete, DragIndicator, Close, InfoOutlined, Create, CheckCircle, Schedule, OpenInNew, ExpandMore } from '@mui/icons-material';
8
8
  import { useApiContext, useApi, createApiInstance } from '@opengeoweb/api';
9
9
  import { getDeepProperty, ReactHookFormHiddenInput, ReactHookFormProvider } from '@opengeoweb/form-fields';
10
10
  import { useFormContext, Controller, useFieldArray } from 'react-hook-form';
@@ -284,20 +284,20 @@ function __awaiter(thisArg, _arguments, P, generator) {
284
284
  * Copyright 2021 - Finnish Meteorological Institute (FMI)
285
285
  * */
286
286
  var FORMAT_DAYS_HOURS = 'DDHH';
287
- var FORMAT_DAYS_HOURS_MINUTS = 'DDHHmm'; // formatters
287
+ var FORMAT_DAYS_HOURS_MINUTES = 'DDHHmm'; // formatters
288
288
 
289
289
  var formatValidToString = function formatValidToString(valid) {
290
290
  // Ensure end of valid period if midnight is shown as 24 and not 00
291
291
  var endHours = moment.utc(valid.end).format('kk');
292
292
  var endDate = endHours === '24' ? moment.utc(valid.end).add(-1, 'day').format('DD') : moment.utc(valid.end).format('DD');
293
- return valid.end ? "".concat(moment.utc(valid.start).format(FORMAT_DAYS_HOURS), "/").concat(endDate).concat(endHours) : moment.utc(valid.start).format(FORMAT_DAYS_HOURS_MINUTS);
293
+ return valid.end ? "".concat(moment.utc(valid.start).format(FORMAT_DAYS_HOURS), "/").concat(endDate).concat(endHours) : moment.utc(valid.start).format(FORMAT_DAYS_HOURS_MINUTES);
294
294
  };
295
295
 
296
296
  var getSections = function getSections(value, format) {
297
297
  return {
298
298
  day: parseInt(value.substring(0, 2), 10),
299
299
  hour: parseInt(value.substring(2, 4), 10),
300
- min: format === FORMAT_DAYS_HOURS_MINUTS ? parseInt(value.substring(4), 10) : 0
300
+ min: format === FORMAT_DAYS_HOURS_MINUTES ? parseInt(value.substring(4), 10) : 0
301
301
  };
302
302
  };
303
303
 
@@ -327,7 +327,7 @@ var parseValidToObject = function parseValidToObject(value, tafStart, tafEnd) {
327
327
  var tafStartMoment = moment.utc(tafStart);
328
328
  var tafEndMoment = moment.utc(tafEnd);
329
329
  var startEnd = trimmedValue.split('/');
330
- var format = trimmedValue.indexOf('/') === -1 ? FORMAT_DAYS_HOURS_MINUTS : FORMAT_DAYS_HOURS;
330
+ var format = trimmedValue.indexOf('/') === -1 ? FORMAT_DAYS_HOURS_MINUTES : FORMAT_DAYS_HOURS;
331
331
  var startSections = getSections(startEnd[0], format);
332
332
  var start = getValidityTimeStamp('start', startSections, tafStartMoment, tafEndMoment).format('YYYY-MM-DDTHH:mm:ss[Z]'); // If we only have start value - return that otherwise, also get end time
333
333
 
@@ -352,7 +352,8 @@ var invalidValidityStartTimeMessage = 'Invalid validity start time, change group
352
352
  var invalidValidityPeriodMessage = 'Invalid validity period, validity end time has to be after validity start time';
353
353
  var invalidValidityEndTimeMessage = 'Invalid validity end time, change group validity must lie within the TAF validity period';
354
354
  var invalidChangeValidInterFieldMessage = 'Invalid validity period, expected DDHH/DDHH for BECMG and TEMPO or DDHHmm for FM';
355
- var invalidBECMGDurationValidInterFieldMessage = 'Invalid validity period, for BECMG only validity periods with a duration of 1, 2, 3 or 4 hours are allowed'; // Validations
355
+ var invalidBECMGDurationValidInterFieldMessage = 'Invalid validity period, for BECMG only validity periods with a duration of 1, 2, 3 or 4 hours are allowed';
356
+ var invalidChangeGroupOverlapsWithFM = 'The valid time of a change group cannot overlap with the valid time of a following FM group. This change group should end before or at the hour of the start of the FM group'; // Validations
356
357
  // Ensure all individual sections are correct (DD HH mm etc)
357
358
 
358
359
  var validateValidSections = function validateValidSections(sections, isStartEnd) {
@@ -376,7 +377,7 @@ var validateIsValidDates = function validateIsValidDates(value, tafStart, tafEnd
376
377
  var tafStartMoment = moment.utc(tafStart);
377
378
  var tafEndMoment = moment.utc(tafEnd);
378
379
  var startEnd = value.split('/');
379
- var format = value.indexOf('/') === -1 ? FORMAT_DAYS_HOURS_MINUTS : FORMAT_DAYS_HOURS;
380
+ var format = value.indexOf('/') === -1 ? FORMAT_DAYS_HOURS_MINUTES : FORMAT_DAYS_HOURS;
380
381
  var validateMsg = []; // Validate start time
381
382
 
382
383
  var startSections = getSections(startEnd[0], format); // Validate individual sections for start
@@ -454,7 +455,7 @@ var validateValidChangeGroupInterField = function validateValidChangeGroupInterF
454
455
  var tafStartMoment = moment.utc(tafStart);
455
456
  var tafEndMoment = moment.utc(tafEnd);
456
457
  var startEnd = trimmedValue.split('/');
457
- var format = trimmedValue.indexOf('/') === -1 ? FORMAT_DAYS_HOURS_MINUTS : FORMAT_DAYS_HOURS; // Validate start time
458
+ var format = trimmedValue.indexOf('/') === -1 ? FORMAT_DAYS_HOURS_MINUTES : FORMAT_DAYS_HOURS; // Validate start time
458
459
 
459
460
  var startSections = getSections(startEnd[0], format);
460
461
  var startTimestamp = getValidityTimeStamp('start', startSections, tafStartMoment, tafEndMoment);
@@ -474,18 +475,18 @@ var validProbChangeOrder = ['FM', 'BECMG', 'TEMPO', 'PROB40', 'PROB30', 'PROB40
474
475
  var invalidPreviousChangeGroupIsBeforeMessage = 'Valid time should be after previous valid time';
475
476
  var invalidPreviousChangeGroupOrderMessage = "When valid times are equal, the change/probability order should be: ".concat(validProbChangeOrder.join(', '));
476
477
  var invalidPreviousChangeGroupHasSameValue = "When valid times are equal, probability/change can't have the same value as previous row value";
477
- var validatePreviousChangeGroup = function validatePreviousChangeGroup(currentChangeGroup, nextChangeGroup, tafStart, tafEnd) {
478
+ var validatePreviousChangeGroup = function validatePreviousChangeGroup(currentChangeGroup, previousChangeGroup, tafStart, tafEnd) {
478
479
  // extract the needed values from changeGroups
479
480
  var valid = currentChangeGroup.valid,
480
481
  _currentChangeGroup$c = currentChangeGroup.change,
481
482
  change = _currentChangeGroup$c === void 0 ? '' : _currentChangeGroup$c,
482
483
  _currentChangeGroup$p = currentChangeGroup.probability,
483
484
  probability = _currentChangeGroup$p === void 0 ? '' : _currentChangeGroup$p;
484
- var previousValid = nextChangeGroup.valid,
485
- _nextChangeGroup$chan = nextChangeGroup.change,
486
- previousChange = _nextChangeGroup$chan === void 0 ? '' : _nextChangeGroup$chan,
487
- _nextChangeGroup$prob = nextChangeGroup.probability,
488
- previousProbability = _nextChangeGroup$prob === void 0 ? '' : _nextChangeGroup$prob; // get the parsed values
485
+ var previousValid = previousChangeGroup.valid,
486
+ _previousChangeGroup$ = previousChangeGroup.change,
487
+ previousChange = _previousChangeGroup$ === void 0 ? '' : _previousChangeGroup$,
488
+ _previousChangeGroup$2 = previousChangeGroup.probability,
489
+ previousProbability = _previousChangeGroup$2 === void 0 ? '' : _previousChangeGroup$2; // get the parsed values
489
490
 
490
491
  var validValue = parseValidToObject(valid, tafStart, tafEnd);
491
492
  var previousValidValue = parseValidToObject(previousValid, tafStart, tafEnd);
@@ -518,6 +519,27 @@ var validatePreviousChangeGroup = function validatePreviousChangeGroup(currentCh
518
519
  }
519
520
  }
520
521
 
522
+ return true;
523
+ }; // validate overlapping validity when one of the following changegroups is FM
524
+
525
+ var validateOverlapWithFM = function validateOverlapWithFM(currentChangeGroup, nextChangeGroups, tafStart, tafEnd) {
526
+ var valid = currentChangeGroup.valid;
527
+ if (!valid || !nextChangeGroups.length) return true;
528
+ var validValue = parseValidToObject(valid, tafStart, tafEnd);
529
+ var currentRowEndTime = moment(validValue.end);
530
+ var firstFMChangeGroup = nextChangeGroups.find(function (nextChangeGroup) {
531
+ return nextChangeGroup.change.trim() === 'FM';
532
+ });
533
+
534
+ if (firstFMChangeGroup && firstFMChangeGroup.valid) {
535
+ var FMValidValue = parseValidToObject(firstFMChangeGroup.valid, tafStart, tafEnd);
536
+ var FMStartTime = moment(FMValidValue.start);
537
+
538
+ if (currentRowEndTime.isAfter(FMStartTime)) {
539
+ return invalidChangeGroupOverlapsWithFM;
540
+ }
541
+ }
542
+
521
543
  return true;
522
544
  };
523
545
 
@@ -1446,8 +1468,12 @@ var invalidWindDirectionMinMaxMessage = 'Invalid wind direction, the direction m
1446
1468
  var invalidWindDirectionStepMessage = 'Invalid wind direction, the direction must be rounded to the nearest step of 10 degrees';
1447
1469
  var invalidWindSpeedMessage = 'Invalid wind speed, expected a speed equal to 2 digits or P99';
1448
1470
  var invalidWindGustsMessage = 'Invalid wind gusts, expected gusts equal to 2 digits or P99';
1449
- var invalidWindGustSpeedCombinationMessage = 'Invalid wind, wind gusts must at least be 10 knots greater than the wind speed';
1450
- var invalidGustForWindP99Message = 'Invalid wind speed and gust combination, if wind is P99, gusts can only be empty or P99'; // validations
1471
+ var invalidWindGustSpeedCombinationMessage = 'Invalid wind gusts, gusts must at least be 10 knots greater than the wind speed';
1472
+ var invalidWindGustSpeedBelow15Message = 'Invalid wind gusts, gusts are only allowed for a minimum wind speed of 15 knots';
1473
+ var invalidGustForWindP99Message = 'Invalid wind speed and gust combination, if wind is P99, gusts can only be empty or P99';
1474
+ var invalidWindSpeedForDirectionZeroMessage = 'Invalid wind speed, if direction is 000, speed can only be 00';
1475
+ var invalidWindSpeedForDirectionNonZeroMessage = 'Invalid wind speed, if direction is not 000, speed can not be 00';
1476
+ var invalidWindVRBWindDirectionMessage = 'Invalid wind direction, VRB can only be used in combination with windspeeds smaller than 4 or in combination with gusts and showers'; // validations
1451
1477
 
1452
1478
  var validateDirection = function validateDirection(value) {
1453
1479
  // if not a number
@@ -1495,8 +1521,29 @@ var validateUnit = function validateUnit(value) {
1495
1521
  return value === 'KT' || value === '' || invalidWindMessage;
1496
1522
  };
1497
1523
 
1498
- var validateWindInterSection = function validateWindInterSection(speed, gust) {
1499
- if (gust === '' || gust === 'P99') {
1524
+ var validateWindInterSection = function validateWindInterSection(speed, gust, direction, weather1Value, weather2Value, weather3Value) {
1525
+ if (direction === '000' && speed !== '00') {
1526
+ return invalidWindSpeedForDirectionZeroMessage;
1527
+ }
1528
+
1529
+ if (direction !== '000' && speed === '00') {
1530
+ return invalidWindSpeedForDirectionNonZeroMessage;
1531
+ }
1532
+
1533
+ if (direction === 'VRB' && (speed === 'P99' || parseInt(speed, 10) > 3)) {
1534
+ // Only allow VRB for speeds smaller than 4 or in combination with gusts and showers (SH, TS, SQ)
1535
+ var allowedWeatherPhenom = ['SH', 'TS', 'SQ'];
1536
+ if (gust === '' || !allowedWeatherPhenom.some(function (allowedPhenom) {
1537
+ return weather1Value.includes(allowedPhenom);
1538
+ }) && !allowedWeatherPhenom.some(function (allowedPhenom) {
1539
+ return weather2Value.includes(allowedPhenom);
1540
+ }) && !allowedWeatherPhenom.some(function (allowedPhenom) {
1541
+ return weather3Value.includes(allowedPhenom);
1542
+ })) return invalidWindVRBWindDirectionMessage;
1543
+ } // Validate gust
1544
+
1545
+
1546
+ if (gust === '') {
1500
1547
  return true;
1501
1548
  }
1502
1549
 
@@ -1504,24 +1551,35 @@ var validateWindInterSection = function validateWindInterSection(speed, gust) {
1504
1551
  return invalidGustForWindP99Message;
1505
1552
  }
1506
1553
 
1507
- return parseInt(gust, 10) - parseInt(speed, 10) >= 10 || invalidWindGustSpeedCombinationMessage;
1554
+ if (parseInt(speed, 10) < 15) {
1555
+ return invalidWindGustSpeedBelow15Message;
1556
+ }
1557
+
1558
+ if (gust !== 'P99' && parseInt(gust, 10) - parseInt(speed, 10) < 10) {
1559
+ return invalidWindGustSpeedCombinationMessage;
1560
+ }
1561
+
1562
+ return true;
1508
1563
  }; // Main validation function
1509
1564
 
1510
1565
 
1511
- var validateWindField = function validateWindField(value) {
1512
- var trimmedValue = value.trim();
1566
+ var validateWindField = function validateWindField(value, weatherfield1, weatherfield2, weatherfield3) {
1567
+ var trimmedWindValue = value.trim();
1568
+ var trimmedWeather1Value = weatherfield1.trim();
1569
+ var trimmedWeather2Value = weatherfield2.trim();
1570
+ var trimmedWeather3Value = weatherfield3.trim();
1513
1571
 
1514
- if (!trimmedValue.length) {
1572
+ if (!trimmedWindValue.length) {
1515
1573
  return true;
1516
1574
  } // Validate length and ensure it doesn't contain any other characters than numbers and VRB P KT and G
1517
1575
 
1518
1576
 
1519
- if (trimmedValue.length < 5 || trimmedValue.length > 12 || !trimmedValue.match(/^[0-9VRBPGKT]+$/)) {
1577
+ if (trimmedWindValue.length < 5 || trimmedWindValue.length > 12 || !trimmedWindValue.match(/^[0-9VRBPGKT]+$/)) {
1520
1578
  return invalidWindMessage;
1521
1579
  } // First validate individual sections
1522
1580
 
1523
1581
 
1524
- var windValidationIndv = extractValidateWindSections(trimmedValue, true);
1582
+ var windValidationIndv = extractValidateWindSections(trimmedWindValue, true);
1525
1583
 
1526
1584
  if (windValidationIndv.validations.length !== 0) {
1527
1585
  return windValidationIndv.validations.find(function (error) {
@@ -1530,7 +1588,7 @@ var validateWindField = function validateWindField(value) {
1530
1588
  } // Then do any intersection validation
1531
1589
 
1532
1590
 
1533
- return validateWindInterSection(windValidationIndv.speed, windValidationIndv.gust);
1591
+ return validateWindInterSection(windValidationIndv.speed, windValidationIndv.gust, windValidationIndv.direction, trimmedWeather1Value, trimmedWeather2Value, trimmedWeather3Value);
1534
1592
  };
1535
1593
 
1536
1594
  var formatSingleCloud = function formatSingleCloud(value) {
@@ -1577,10 +1635,10 @@ var formatCloudsToString = function formatCloudsToString(clouds) {
1577
1635
  var parseSingleCloud = function parseSingleCloud(value) {
1578
1636
  if (!value) return null;
1579
1637
  var trimmedCloud = value.trim();
1580
- var cloudType = trimmedCloud.substr(6, 3);
1638
+ var cloudType = trimmedCloud.substring(6, 9);
1581
1639
  return Object.assign({
1582
- coverage: trimmedCloud.substr(0, 3),
1583
- height: parseInt(trimmedCloud.substr(3, 3), 10)
1640
+ coverage: trimmedCloud.substring(0, 3),
1641
+ height: parseInt(trimmedCloud.substring(3, 6), 10)
1584
1642
  }, cloudType && {
1585
1643
  type: cloudType
1586
1644
  });
@@ -1596,20 +1654,22 @@ var parseCloudsToObject = function parseCloudsToObject(clouds) {
1596
1654
  switch (key) {
1597
1655
  case 'cloud1':
1598
1656
  {
1599
- if (isVerticalVisibility(clouds[key])) {
1657
+ var trimmedValue = clouds[key].trim();
1658
+
1659
+ if (isVerticalVisibility(trimmedValue)) {
1600
1660
  // Parse Vertical Visibility
1601
1661
  return Object.assign(Object.assign({}, list), _defineProperty({}, key, {
1602
- verticalVisibility: parseInt(clouds[key].substr(2, 3), 10)
1662
+ verticalVisibility: parseInt(trimmedValue.substring(2, 5), 10)
1603
1663
  }));
1604
1664
  }
1605
1665
 
1606
- if (clouds[key] === 'NSC') {
1666
+ if (trimmedValue === 'NSC') {
1607
1667
  // Parse No Significant Clouds
1608
- return Object.assign(Object.assign({}, list), _defineProperty({}, key, clouds[key]));
1668
+ return Object.assign(Object.assign({}, list), _defineProperty({}, key, trimmedValue));
1609
1669
  } // Parse Cloud
1610
1670
 
1611
1671
 
1612
- return Object.assign(Object.assign({}, list), _defineProperty({}, key, parseSingleCloud(clouds[key])));
1672
+ return Object.assign(Object.assign({}, list), _defineProperty({}, key, parseSingleCloud(trimmedValue)));
1613
1673
  }
1614
1674
 
1615
1675
  default:
@@ -1637,16 +1697,19 @@ var invalidCloudHeightMessage = 'Invalid cloud height, expected a height consist
1637
1697
  var invalidCloudHeightStepsMessage = 'Invalid cloud height, a height above 050 must be rounded to the nearest 1000ft';
1638
1698
  var invalidCLoudTypeMessage = 'Invalid cloud type, expected CB or TCU';
1639
1699
  var invalidHeightTypeCombinationMessage = 'Invalid cloud input, when cloud height is 050 or higher, CB or TCU is required';
1640
- var invalidVerticalVisibilityMessage = 'Invalid vertical visibility, expected VV<value> with value consisting of 3 digits between 000 and 010';
1700
+ var invalidVerticalVisibilityMessage = 'Invalid vertical visibility, expected VV<value> with value consisting of 3 digits';
1701
+ var invalidVerticalVisibilityHeightMessage = 'Invalid vertical visibility, visibility should lie between 001 and 010';
1702
+ var invalidVerticalVisibilityHeightFGMessage = 'Invalid vertical visibility, in case of fog visibility should lie between 001 and 005';
1641
1703
  var invalidCloudHeightComparedToPreviousMessage = 'Invalid cloud height, height must be greater than previous cloud group height';
1642
- var invalidCloud2CoverageTypeCombinationMessage = 'Invalid cloud input, cloud type is required when coverage is FEW';
1704
+ var invalidCloud2CoverageTypeCombinationMessage = 'Invalid cloud input, for the second cloud group (excluding any significant convective cloud groups with CB/TCU) a coverage of FEW is not allowed';
1643
1705
  var invalidCloud2WithVerticalVisibilityMessage = 'Invalid cloud input, cloud type is required when Vertical Visibility is given';
1644
- var invalidCloud3CoverageTypeCombinationMessage = 'Invalid cloud input, cloud type is required when coverage is FEW or SCT';
1706
+ var invalidCloud3CoverageTypeCombinationMessage = 'Invalid cloud input, for the third cloud group (excluding any significant convective cloud groups with CB/TCU) a coverage of FEW or SCT is not allowed';
1645
1707
  var invalidCloudWithVerticalVisibilityMessage = 'Invalid cloud input, third or fourth cloud group is not allowed when Vertical Visibility is given';
1646
1708
  var invalidCloudWithPreviousEmptyCloud = 'Invalid cloud input, value is not allowed when previous cloud group is empty';
1647
1709
  var invalidCloudWithNSCMessage = 'Invalid cloud input, other cloud groups are not allowed when No Significant Clouds is given';
1648
- var invalidCloud4WithPreviousType = 'Invalid cloud input, value is not allowed when previous cloud group has a type';
1649
- var invalidCloudCAVOKMessage = 'Invalid cloud input, when CAVOK is used, cloud groups must be empty'; // validations
1710
+ var invalidCloudWithPreviousType = 'Invalid cloud input, only one significant convective cloud group with CB/TCU is allowed';
1711
+ var invalidCloudCAVOKMessage = 'Invalid cloud input, when CAVOK is used, cloud groups must be empty';
1712
+ var invalidCloud4TypeMissingMessage = 'Invalid cloud input for fourth group, CB or TCU required if no other significant convective cloud group present'; // validations
1650
1713
 
1651
1714
  var isLengthValid = function isLengthValid(value) {
1652
1715
  var onlyDigitsOrSpecificLetters = RegExp(/^[0-9FEWSCTBKNOVU]+$/);
@@ -1693,25 +1756,32 @@ var isHeightAllowedWithType = function isHeightAllowedWithType(height, type) {
1693
1756
  };
1694
1757
 
1695
1758
  var isVerticalVisibility = function isVerticalVisibility(value) {
1696
- return value.substr(0, 2) === 'VV';
1759
+ return value.substring(0, 2) === 'VV';
1697
1760
  };
1698
1761
 
1699
- var isVerticalVisibilityValid = function isVerticalVisibilityValid(value) {
1762
+ var isVerticalVisibilityValid = function isVerticalVisibilityValid(value, weather1, weather2, weather3) {
1700
1763
  // validate if its a number
1701
1764
  if (!value.match(/^[0-9]+$/)) {
1702
- return false;
1765
+ return invalidVerticalVisibilityMessage;
1703
1766
  } // check if its 3 digits
1704
1767
 
1705
1768
 
1706
1769
  if (value.length !== 3) {
1707
- return false;
1708
- } // value should not be above 10
1709
-
1770
+ return invalidVerticalVisibilityMessage;
1771
+ }
1710
1772
 
1711
1773
  var intValue = parseInt(value, 10);
1712
1774
 
1713
- if (intValue > 10) {
1714
- return false;
1775
+ if (weather1 !== 'FG' && weather2 !== 'FG' && weather3 !== 'FG') {
1776
+ // value should not be above 10
1777
+ if (intValue < 1 || intValue > 10) {
1778
+ return invalidVerticalVisibilityHeightMessage;
1779
+ }
1780
+ } else if (intValue < 1 || intValue > 5) {
1781
+ // Apply Dutch rule as national exception
1782
+ // In case of fog (FG) the vertical visibility can only be between VV001 up to and including VV005
1783
+ // HEIGHT_VV is: (001-(1)-005). Deze maxima zijn officiële Nederlandse afwijkingen van de WMO/ICAO regelgeving
1784
+ return invalidVerticalVisibilityHeightFGMessage;
1715
1785
  }
1716
1786
 
1717
1787
  return true;
@@ -1728,14 +1798,14 @@ var sharedCloudValidations = function sharedCloudValidations(_ref) {
1728
1798
  } // Validate coverage
1729
1799
 
1730
1800
 
1731
- var coverage = value.substr(0, 3);
1801
+ var coverage = value.substring(0, 3);
1732
1802
 
1733
1803
  if (!isCoverageValid(coverage)) {
1734
1804
  errorMessages.push(invalidCloudCoverageMessage);
1735
1805
  } // Validate height
1736
1806
 
1737
1807
 
1738
- var height = value.substr(3, 3);
1808
+ var height = value.substring(3, 6);
1739
1809
  var isValidHeight = isHeightValid(height);
1740
1810
 
1741
1811
  if (isValidHeight !== true) {
@@ -1744,7 +1814,7 @@ var sharedCloudValidations = function sharedCloudValidations(_ref) {
1744
1814
  } // Validate type
1745
1815
 
1746
1816
 
1747
- var type = value.substr(6, 3);
1817
+ var type = value.substring(6, 9);
1748
1818
 
1749
1819
  if (!isTypeValid(type)) {
1750
1820
  errorMessages.push(invalidCLoudTypeMessage);
@@ -1763,41 +1833,40 @@ var sharedCloudValidations = function sharedCloudValidations(_ref) {
1763
1833
  };
1764
1834
  };
1765
1835
 
1766
- var validateFirstCloud = function validateFirstCloud(value, visibilityValue) {
1767
- var trimmedValue = value.trim(); // Field can be left empty
1836
+ var validateFirstCloud = function validateFirstCloud(value, visibilityValue, weatherfield1, weatherfield2, weatherfield3) {
1837
+ var trimmedValueFirstCloud = value.trim();
1838
+ var trimmedValueVisibility = visibilityValue.trim();
1839
+ var trimmedWeather1Value = weatherfield1.trim();
1840
+ var trimmedWeather2Value = weatherfield2.trim();
1841
+ var trimmedWeather3Value = weatherfield3.trim(); // Field can be left empty
1768
1842
 
1769
- if (!trimmedValue.length) {
1843
+ if (!trimmedValueFirstCloud.length) {
1770
1844
  return true;
1771
1845
  }
1772
1846
 
1773
- if (trimmedValue && visibilityValue === 'CAVOK') {
1847
+ if (trimmedValueFirstCloud && trimmedValueVisibility === 'CAVOK') {
1774
1848
  return invalidCloudCAVOKMessage;
1775
1849
  } // Check for No Signicant Clouds
1776
1850
 
1777
1851
 
1778
- if (trimmedValue === 'NSC') {
1852
+ if (trimmedValueFirstCloud === 'NSC') {
1779
1853
  return true;
1780
1854
  } // Validate Vertical Visibility
1781
1855
 
1782
1856
 
1783
- if (isVerticalVisibility(trimmedValue)) {
1857
+ if (isVerticalVisibility(trimmedValueFirstCloud)) {
1784
1858
  if ( // Validate length of 5
1785
- trimmedValue.length !== 5 || // Ensure it only contains digits or VV
1786
- !trimmedValue.match(/^[0-9V]+$/)) {
1787
- return invalidVerticalVisibilityMessage;
1788
- }
1789
-
1790
- var verticalVisibilityValue = trimmedValue.substr(2, 3);
1791
-
1792
- if (!isVerticalVisibilityValid(verticalVisibilityValue)) {
1859
+ trimmedValueFirstCloud.length !== 5 || // Ensure it only contains digits or VV
1860
+ !trimmedValueFirstCloud.match(/^[0-9V]+$/)) {
1793
1861
  return invalidVerticalVisibilityMessage;
1794
1862
  }
1795
1863
 
1796
- return true;
1864
+ var verticalVisibilityValue = trimmedValueFirstCloud.substring(2, 5);
1865
+ return isVerticalVisibilityValid(verticalVisibilityValue, trimmedWeather1Value, trimmedWeather2Value, trimmedWeather3Value);
1797
1866
  }
1798
1867
 
1799
1868
  var _sharedCloudValidatio = sharedCloudValidations({
1800
- value: trimmedValue,
1869
+ value: trimmedValueFirstCloud,
1801
1870
  canBeNSC: true
1802
1871
  }),
1803
1872
  errorMessages = _sharedCloudValidatio.errorMessages; // Return first error if any
@@ -1806,15 +1875,26 @@ var validateFirstCloud = function validateFirstCloud(value, visibilityValue) {
1806
1875
  return errorMessages.length ? errorMessages[0] : true;
1807
1876
  };
1808
1877
  var validateSecondCloud = function validateSecondCloud(firstCloudValue, secondCloudValue, visibilityValue) {
1809
- var trimmedValue = secondCloudValue.trim(); // Field can be left empty
1878
+ var trimmedValueFirstCloud = firstCloudValue.trim();
1879
+ var trimmedValueSecondCloud = secondCloudValue.trim(); // Field can be left empty
1810
1880
 
1811
- if (!trimmedValue.length) {
1881
+ if (!trimmedValueSecondCloud.length) {
1812
1882
  return true;
1883
+ } // If Cloud1 is NSC, Cloud2 has to be empty
1884
+
1885
+
1886
+ if (trimmedValueFirstCloud === 'NSC' && trimmedValueSecondCloud !== '') {
1887
+ return invalidCloudWithNSCMessage;
1888
+ } // If Cloud1 is empty Cloud2 should be empty
1889
+
1890
+
1891
+ if (trimmedValueFirstCloud === '') {
1892
+ return invalidCloudWithPreviousEmptyCloud;
1813
1893
  } // Generic validations
1814
1894
 
1815
1895
 
1816
1896
  var _sharedCloudValidatio2 = sharedCloudValidations({
1817
- value: trimmedValue
1897
+ value: trimmedValueSecondCloud
1818
1898
  }),
1819
1899
  coverage = _sharedCloudValidatio2.coverage,
1820
1900
  height = _sharedCloudValidatio2.height,
@@ -1824,32 +1904,32 @@ var validateSecondCloud = function validateSecondCloud(firstCloudValue, secondCl
1824
1904
 
1825
1905
  if (errorMessages.length) {
1826
1906
  return errorMessages[0];
1827
- } // Cloud2 specific validations
1828
- // Without type, coverage can not be FEW
1907
+ } // Cloud2 specific validations - only to be applied when cloud1 does not contain type CB/TCU
1829
1908
 
1830
1909
 
1831
- if (type === '' && coverage === 'FEW') {
1832
- return invalidCloud2CoverageTypeCombinationMessage;
1910
+ var typeCloud1 = trimmedValueFirstCloud.substring(6, 9);
1911
+
1912
+ if (typeCloud1 !== 'CB' && typeCloud1 !== 'TCU') {
1913
+ // Without a type specified in cloud group 1, this group counts as #2 and coverage can not be FEW
1914
+ if (type === '' && coverage === 'FEW') {
1915
+ return invalidCloud2CoverageTypeCombinationMessage;
1916
+ }
1833
1917
  } // If Cloud1 is not valid, skip further validation of Cloud2
1918
+ // No need to pass the weatherfields as their content does not influence what happens below
1834
1919
 
1835
1920
 
1836
- if (validateFirstCloud(firstCloudValue, visibilityValue) !== true) {
1921
+ if (validateFirstCloud(trimmedValueFirstCloud, visibilityValue, '', '', '') !== true) {
1837
1922
  return true;
1838
- } // If Cloud1 is NSC, Cloud2 has to be empty
1923
+ } // If Cloud1 contains a type, Cloud2 cannot contain a type
1839
1924
 
1840
1925
 
1841
- if (firstCloudValue === 'NSC' && trimmedValue !== '') {
1842
- return invalidCloudWithNSCMessage;
1843
- } // If Cloud1 is empty Cloud2 should be empty
1844
-
1845
-
1846
- if (firstCloudValue === '') {
1847
- return invalidCloudWithPreviousEmptyCloud;
1926
+ if (type !== '' && typeCloud1 !== '') {
1927
+ return invalidCloudWithPreviousType;
1848
1928
  } // Cloud2 height has to be greater than Cloud1 height
1849
1929
 
1850
1930
 
1851
- if (!isVerticalVisibility(firstCloudValue)) {
1852
- var cloud1Height = firstCloudValue.substr(3, 3);
1931
+ if (!isVerticalVisibility(trimmedValueFirstCloud)) {
1932
+ var cloud1Height = trimmedValueFirstCloud.substring(3, 6);
1853
1933
 
1854
1934
  if (height <= cloud1Height) {
1855
1935
  return invalidCloudHeightComparedToPreviousMessage;
@@ -1857,22 +1937,34 @@ var validateSecondCloud = function validateSecondCloud(firstCloudValue, secondCl
1857
1937
  } // If Cloud1 contains Vertical Visibility then Cloud2 type is required
1858
1938
 
1859
1939
 
1860
- if (isVerticalVisibility(firstCloudValue) && type === '') {
1940
+ if (isVerticalVisibility(trimmedValueFirstCloud) && type === '') {
1861
1941
  return invalidCloud2WithVerticalVisibilityMessage;
1862
1942
  }
1863
1943
 
1864
1944
  return true;
1865
1945
  };
1866
1946
  var validateThirdCloud = function validateThirdCloud(firstCloudValue, secondCloudValue, thirdCloudValue, visibilityValue) {
1867
- var trimmedValue = thirdCloudValue.trim(); // Field can be left empty
1947
+ var trimmedValueFirstCloud = firstCloudValue.trim();
1948
+ var trimmedValueSecondCloud = secondCloudValue.trim();
1949
+ var trimmedValueThirdCloud = thirdCloudValue.trim(); // Field can be left empty
1868
1950
 
1869
- if (!trimmedValue.length) {
1951
+ if (!trimmedValueThirdCloud.length) {
1870
1952
  return true;
1953
+ } // If Cloud1 is NSC, Cloud3 has to be empty
1954
+
1955
+
1956
+ if (trimmedValueFirstCloud === 'NSC' && trimmedValueThirdCloud !== '') {
1957
+ return invalidCloudWithNSCMessage;
1958
+ } // If Cloud1 or Cloud2 is empty Cloud3 should be empty
1959
+
1960
+
1961
+ if (trimmedValueFirstCloud === '' || trimmedValueSecondCloud === '') {
1962
+ return invalidCloudWithPreviousEmptyCloud;
1871
1963
  } // Generic validations
1872
1964
 
1873
1965
 
1874
1966
  var _sharedCloudValidatio3 = sharedCloudValidations({
1875
- value: trimmedValue
1967
+ value: trimmedValueThirdCloud
1876
1968
  }),
1877
1969
  coverage = _sharedCloudValidatio3.coverage,
1878
1970
  height = _sharedCloudValidatio3.height,
@@ -1883,45 +1975,45 @@ var validateThirdCloud = function validateThirdCloud(firstCloudValue, secondClou
1883
1975
  if (errorMessages.length) {
1884
1976
  return errorMessages[0];
1885
1977
  } // Cloud3 specific validations
1886
- // Without type, coverage can not be FEW or SCT
1978
+ // Without a type specified in cloud group 1 or 2, this group counts as #3 and coverage can not be FEW or SCT
1887
1979
 
1888
1980
 
1889
- if (type === '' && (coverage === 'FEW' || coverage === 'SCT')) {
1890
- return invalidCloud3CoverageTypeCombinationMessage;
1981
+ var typeCloud1 = trimmedValueFirstCloud.substring(6, 9);
1982
+ var typeCloud2 = trimmedValueSecondCloud.substring(6, 9);
1983
+
1984
+ if (typeCloud1 !== 'CB' && typeCloud1 !== 'TCU' && typeCloud2 !== 'CB' && typeCloud2 !== 'TCU') {
1985
+ if (type === '' && (coverage === 'FEW' || coverage === 'SCT')) {
1986
+ return invalidCloud3CoverageTypeCombinationMessage;
1987
+ }
1988
+ } else if (type === '' && coverage === 'FEW') {
1989
+ // In case we do have a CB/TCU group in one of the previous clouf groups. This cloudgroup should be evaluated as if it was the second group
1990
+ return invalidCloud2CoverageTypeCombinationMessage;
1891
1991
  } // If Cloud1 is not valid, skip further validation of Cloud3
1992
+ // No need to pass the weatherfields as their content does not influence what happens below
1993
+ // Since in case of VV the third field should be empty anyways
1892
1994
 
1893
1995
 
1894
- if (validateFirstCloud(firstCloudValue, visibilityValue) !== true) {
1996
+ if (validateFirstCloud(trimmedValueFirstCloud, visibilityValue, '', '', '') !== true) {
1895
1997
  return true;
1896
- } // If Cloud1 is empty Cloud3 should be empty
1897
-
1898
-
1899
- if (firstCloudValue === '') {
1900
- return invalidCloudWithPreviousEmptyCloud;
1901
1998
  } // If Cloud1 contains Vertical Visibility then Cloud3 should be empty
1902
1999
 
1903
2000
 
1904
- if (isVerticalVisibility(firstCloudValue)) {
2001
+ if (isVerticalVisibility(trimmedValueFirstCloud)) {
1905
2002
  return invalidCloudWithVerticalVisibilityMessage;
1906
- } // If Cloud1 is NSC, Cloud3 has to be empty
1907
-
1908
-
1909
- if (firstCloudValue === 'NSC' && trimmedValue !== '') {
1910
- return invalidCloudWithNSCMessage;
1911
2003
  } // If Cloud2 is not valid, skip further validation of Cloud3
1912
2004
 
1913
2005
 
1914
- if (validateSecondCloud(firstCloudValue, secondCloudValue, visibilityValue) !== true) {
2006
+ if (validateSecondCloud(trimmedValueFirstCloud, trimmedValueSecondCloud, visibilityValue) !== true) {
1915
2007
  return true;
1916
- } // If Cloud2 is empty Cloud3 should be empty
2008
+ } // If one of the previous groups contains a type, Cloud3 cannot contain a type
1917
2009
 
1918
2010
 
1919
- if (secondCloudValue === '') {
1920
- return invalidCloudWithPreviousEmptyCloud;
2011
+ if (type !== '' && (typeCloud1 !== '' || typeCloud2 !== '')) {
2012
+ return invalidCloudWithPreviousType;
1921
2013
  } // Cloud3 height has to be greater than Cloud2 height
1922
2014
 
1923
2015
 
1924
- var cloud2Height = secondCloudValue.substr(3, 3);
2016
+ var cloud2Height = trimmedValueSecondCloud.substring(3, 6);
1925
2017
 
1926
2018
  if (height <= cloud2Height) {
1927
2019
  return invalidCloudHeightComparedToPreviousMessage;
@@ -1930,17 +2022,31 @@ var validateThirdCloud = function validateThirdCloud(firstCloudValue, secondClou
1930
2022
  return true;
1931
2023
  };
1932
2024
  var validateFourthCloud = function validateFourthCloud(firstCloudValue, secondCloudValue, thirdCloudValue, fourthCloudValue, visibilityValue) {
1933
- var trimmedValue = fourthCloudValue.trim(); // Field can be left empty
2025
+ var trimmedValueFirstCloud = firstCloudValue.trim();
2026
+ var trimmedValueSecondCloud = secondCloudValue.trim();
2027
+ var trimmedValueThirdCloud = thirdCloudValue.trim();
2028
+ var trimmedValueFourthCloud = fourthCloudValue.trim(); // Field can be left empty
1934
2029
 
1935
- if (!trimmedValue.length) {
2030
+ if (!trimmedValueFourthCloud.length) {
1936
2031
  return true;
2032
+ } // If Cloud1 is NSC, Cloud4 has to be empty
2033
+
2034
+
2035
+ if (trimmedValueFirstCloud === 'NSC' && trimmedValueFourthCloud !== '') {
2036
+ return invalidCloudWithNSCMessage;
2037
+ } // If Cloud 1, 2 or 3 is empty Cloud4 should be empty
2038
+
2039
+
2040
+ if (trimmedValueFirstCloud === '' || trimmedValueSecondCloud === '' || trimmedValueThirdCloud === '') {
2041
+ return invalidCloudWithPreviousEmptyCloud;
1937
2042
  } // Generic validations
1938
2043
 
1939
2044
 
1940
2045
  var _sharedCloudValidatio4 = sharedCloudValidations({
1941
- value: trimmedValue
2046
+ value: trimmedValueFourthCloud
1942
2047
  }),
1943
2048
  height = _sharedCloudValidatio4.height,
2049
+ coverage = _sharedCloudValidatio4.coverage,
1944
2050
  type = _sharedCloudValidatio4.type,
1945
2051
  errorMessages = _sharedCloudValidatio4.errorMessages; // Return generic errors if any
1946
2052
 
@@ -1948,50 +2054,48 @@ var validateFourthCloud = function validateFourthCloud(firstCloudValue, secondCl
1948
2054
  if (errorMessages.length) {
1949
2055
  return errorMessages[0];
1950
2056
  } // Cloud4 specific validations
1951
- // Type is required
2057
+ // If none of the previous cloud groups contains a type, the fourth group must contain a type if not empty
2058
+
1952
2059
 
2060
+ var typeCloud1 = trimmedValueFirstCloud.substring(6, 9);
2061
+ var typeCloud2 = trimmedValueSecondCloud.substring(6, 9);
2062
+ var typeCloud3 = trimmedValueThirdCloud.substring(6, 9);
1953
2063
 
1954
- if (type === '') {
1955
- return invalidCLoudTypeMessage;
2064
+ if (typeCloud1 !== 'CB' && typeCloud1 !== 'TCU' && typeCloud2 !== 'CB' && typeCloud2 !== 'TCU' && typeCloud3 !== 'CB' && typeCloud3 !== 'TCU') {
2065
+ if (type === '') {
2066
+ return invalidCloud4TypeMissingMessage;
2067
+ }
2068
+ } else if (coverage === 'FEW' || coverage === 'SCT') {
2069
+ // We have a CB/TCU group in one of the previous cloud groups. This group should be evaluated as if it was the third group
2070
+ return invalidCloud3CoverageTypeCombinationMessage;
1956
2071
  } // If Cloud1 is not valid, skip further validation of Cloud4
2072
+ // No need to pass the weatherfields as their content does not influence what happens below
2073
+ // Since in case of VV the fourth field should be empty anyways
1957
2074
 
1958
2075
 
1959
- if (validateFirstCloud(firstCloudValue, visibilityValue) !== true) {
2076
+ if (validateFirstCloud(trimmedValueFirstCloud, visibilityValue, '', '', '') !== true) {
1960
2077
  return true;
1961
2078
  } // If Cloud1 contains Vertical Visibility then Cloud4 should be empty
1962
2079
 
1963
2080
 
1964
- if (isVerticalVisibility(firstCloudValue)) {
2081
+ if (isVerticalVisibility(trimmedValueFirstCloud)) {
1965
2082
  return invalidCloudWithVerticalVisibilityMessage;
1966
- } // If Cloud1 is NSC, Cloud4 has to be empty
1967
-
1968
-
1969
- if (firstCloudValue === 'NSC' && trimmedValue !== '') {
1970
- return invalidCloudWithNSCMessage;
1971
- } // If Cloud 1, 2 or 3 is empty Cloud4 should be empty
1972
-
1973
-
1974
- if (firstCloudValue === '' || secondCloudValue === '' || thirdCloudValue === '') {
1975
- return invalidCloudWithPreviousEmptyCloud;
1976
2083
  } // If Cloud3 is not valid, skip further validation of Cloud4
1977
2084
 
1978
2085
 
1979
- if (validateThirdCloud(firstCloudValue, secondCloudValue, thirdCloudValue, visibilityValue) !== true) {
2086
+ if (validateThirdCloud(trimmedValueFirstCloud, trimmedValueSecondCloud, trimmedValueThirdCloud, visibilityValue) !== true) {
1980
2087
  return true;
1981
- } // If Cloud3 contains a type, Cloud4 should be empty
1982
-
2088
+ } // If one of the previous groups contains a type, Cloud4 cannot contain a type
1983
2089
 
1984
- var cloud3Type = thirdCloudValue.substr(6, 3);
1985
2090
 
1986
- if (cloud3Type !== '') {
1987
- return invalidCloud4WithPreviousType;
2091
+ if (type !== '' && (typeCloud1 !== '' || typeCloud2 !== '' || typeCloud3 !== '')) {
2092
+ return invalidCloudWithPreviousType;
1988
2093
  } // Cloud4 height has to be greater than Cloud3 height
1989
2094
 
1990
2095
 
1991
- var cloud3Height = thirdCloudValue.substr(3, 3);
2096
+ var cloud3Height = trimmedValueThirdCloud.substring(3, 6);
1992
2097
 
1993
2098
  if (height <= cloud3Height) {
1994
- // Because of the type validations this is automatically enforced for Cloud4, so impossible to end up here but leaving the check in just in case
1995
2099
  return invalidCloudHeightComparedToPreviousMessage;
1996
2100
  }
1997
2101
 
@@ -2399,11 +2503,22 @@ var getConfirmationDialogTitle = function getConfirmationDialogTitle(action) {
2399
2503
  return 'Cancel TAF';
2400
2504
  };
2401
2505
  var getConfirmationDialogContent = function getConfirmationDialogContent(action) {
2402
- if (action === 'DISCARD') return 'Are you sure you would like to discard this TAF? Any changes made will be lost';
2403
- if (action === 'PUBLISH') return 'Are you sure you want to publish this TAF?';
2404
- if (action === 'AMEND') return 'Do you want to publish it?';
2405
- if (action === 'CORRECT') return 'Do you want to publish it?';
2406
- return 'Are you sure you want to cancel this TAF?';
2506
+ var isFormDirty = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : true;
2507
+
2508
+ switch (action) {
2509
+ case 'DISCARD':
2510
+ return 'Are you sure you would like to discard this TAF? Any changes made will be lost';
2511
+
2512
+ case 'PUBLISH':
2513
+ return 'Are you sure you want to publish this TAF?';
2514
+
2515
+ case 'AMEND':
2516
+ case 'CORRECT':
2517
+ return isFormDirty ? 'Do you want to publish it?' : 'Are you sure you want to publish the TAF? You did not make any changes!';
2518
+
2519
+ default:
2520
+ return 'Are you sure you want to cancel this TAF?';
2521
+ }
2407
2522
  };
2408
2523
  var getConfirmationDialogButtonLabel = function getConfirmationDialogButtonLabel(action) {
2409
2524
  switch (action) {
@@ -2419,34 +2534,28 @@ var getConfirmationDialogButtonLabel = function getConfirmationDialogButtonLabel
2419
2534
  }
2420
2535
  };
2421
2536
 
2422
- /* *
2423
- * Licensed under the Apache License, Version 2.0 (the "License");
2424
- * you may not use this file except in compliance with the License.
2425
- * You may obtain a copy of the License at
2426
- *
2427
- * http://www.apache.org/licenses/LICENSE-2.0
2428
- *
2429
- * Unless required by applicable law or agreed to in writing, software
2430
- * distributed under the License is distributed on an "AS IS" BASIS,
2431
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
2432
- * See the License for the specific language governing permissions and
2433
- * limitations under the License.
2434
- *
2435
- * Copyright 2021 - Koninklijk Nederlands Meteorologisch Instituut (KNMI)
2436
- * Copyright 2021 - Finnish Meteorological Institute (FMI)
2437
- * */
2438
2537
  var useStyles$9 = makeStyles(function (theme) {
2439
2538
  return {
2440
2539
  textFieldGridItem: {
2441
2540
  marginRight: 4
2442
2541
  },
2542
+ textFieldRoot: {
2543
+ '&.Mui-disabled::before': {
2544
+ borderBottom: 'none'
2545
+ },
2546
+ '&:after': {
2547
+ borderBottomColor: '#3f51b5'
2548
+ }
2549
+ },
2443
2550
  textFieldInput: {
2444
2551
  fontSize: '14px',
2445
2552
  paddingLeft: 1,
2446
2553
  paddingRight: 1,
2447
2554
  color: '#051039',
2448
- WebkitTextFillColor: '#051039' // safari text color fix
2449
-
2555
+ WebkitTextFillColor: '#051039',
2556
+ '&.Mui-disabled': {
2557
+ backgroundColor: 'white'
2558
+ }
2450
2559
  },
2451
2560
  textFieldLabel: {
2452
2561
  fontSize: '14px'
@@ -2480,7 +2589,7 @@ var useStyles$9 = makeStyles(function (theme) {
2480
2589
  borderRadius: '3px'
2481
2590
  },
2482
2591
  addButton: {
2483
- marginRight: '38px'
2592
+ marginRight: '48px'
2484
2593
  },
2485
2594
  lifecycleButtons: {
2486
2595
  marginTop: '40px',
@@ -2495,6 +2604,9 @@ var useStyles$9 = makeStyles(function (theme) {
2495
2604
  backdrop: {
2496
2605
  zIndex: 1500,
2497
2606
  color: '#fff'
2607
+ },
2608
+ emptyButtonContainer: {
2609
+ minWidth: 96
2498
2610
  }
2499
2611
  };
2500
2612
  });
@@ -2526,7 +2638,8 @@ var BaseTextField = /*#__PURE__*/React__default.memo(function (props) {
2526
2638
  variant: "filled",
2527
2639
  InputProps: {
2528
2640
  classes: {
2529
- input: classes.textFieldInput
2641
+ input: classes.textFieldInput,
2642
+ root: classes.textFieldRoot
2530
2643
  }
2531
2644
  },
2532
2645
  InputLabelProps: {
@@ -2739,8 +2852,10 @@ var TafFormRow = /*#__PURE__*/React__default.forwardRef(function (_a, ref) {
2739
2852
  field = _a$field === void 0 ? null : _a$field,
2740
2853
  _a$dragHandle = _a.dragHandle,
2741
2854
  dragHandle = _a$dragHandle === void 0 ? null : _a$dragHandle,
2742
- props = __rest(_a, ["isChangeGroup", "disabled", "index", "lastRow", "onRemoveChangeGroup", "field", "dragHandle"]);
2855
+ onAddChangeGroup = _a.onAddChangeGroup,
2856
+ props = __rest(_a, ["isChangeGroup", "disabled", "index", "lastRow", "onRemoveChangeGroup", "field", "dragHandle", "onAddChangeGroup"]);
2743
2857
 
2858
+ var classes = useStyles$9();
2744
2859
  var namePrefix = !isChangeGroup ? 'baseForecast' : "changeGroups[".concat(index, "]");
2745
2860
  var fieldNames = getFieldNames(isChangeGroup, index);
2746
2861
 
@@ -2820,13 +2935,45 @@ var TafFormRow = /*#__PURE__*/React__default.forwardRef(function (_a, ref) {
2820
2935
  return true;
2821
2936
  }, [index, isChangeGroup, getValues]);
2822
2937
 
2823
- var getFieldFromNextRow = function getFieldFromNextRow() {
2824
- var fieldname = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : 'valid';
2825
- var nextValidFieldName = "changeGroups[".concat(index + 1, "].").concat(fieldname);
2826
- var nextRowValidValue = getValues(nextValidFieldName);
2827
- return nextRowValidValue ? [nextValidFieldName] : [];
2938
+ var getFieldFromRow = function getFieldFromRow(fieldname, rowIndex) {
2939
+ var indexedFieldName = "changeGroups[".concat(rowIndex, "].").concat(fieldname);
2940
+ var rowValue = getValues(indexedFieldName);
2941
+ return rowValue ? [indexedFieldName] : [];
2942
+ };
2943
+
2944
+ var getValidFieldFromNextRow = function getValidFieldFromNextRow() {
2945
+ return getFieldFromRow('valid', index + 1);
2946
+ };
2947
+
2948
+ var validateNextRows = React__default.useCallback(function () {
2949
+ if (isChangeGroup) {
2950
+ var _getValues2 = getValues(),
2951
+ validDateEnd = _getValues2.validDateEnd,
2952
+ validDateStart = _getValues2.validDateStart,
2953
+ changeGroups = _getValues2.changeGroups;
2954
+
2955
+ var currentChangeGroup = changeGroups[index];
2956
+ var nextChangeGroups = changeGroups.slice(index + 1);
2957
+ return validateOverlapWithFM(currentChangeGroup, nextChangeGroups, validDateStart, validDateEnd);
2958
+ }
2959
+
2960
+ return true;
2961
+ }, [index, isChangeGroup, getValues]);
2962
+
2963
+ var getPreviousValidFields = function getPreviousValidFields(rowIndex) {
2964
+ var _getValues3 = getValues(),
2965
+ changeGroups = _getValues3.changeGroups;
2966
+
2967
+ var previousChangeGroups = changeGroups.slice(0, rowIndex);
2968
+ var validFields = previousChangeGroups.map(function (_element, index) {
2969
+ return getFieldFromRow('valid', index)[0];
2970
+ });
2971
+ return validFields;
2828
2972
  };
2829
2973
 
2974
+ var addChangeGroup = React__default.useCallback(function () {
2975
+ onAddChangeGroup(index);
2976
+ }, [onAddChangeGroup, index]);
2830
2977
  return /*#__PURE__*/React__default.createElement(Grid, Object.assign({
2831
2978
  "data-testid": "row-".concat(namePrefix),
2832
2979
  container: true,
@@ -2870,7 +3017,7 @@ var TafFormRow = /*#__PURE__*/React__default.forwardRef(function (_a, ref) {
2870
3017
  }],
2871
3018
  onBlur: function onBlur() {
2872
3019
  if (isChangeGroup) {
2873
- var nextValidFieldName = getFieldFromNextRow();
3020
+ var nextValidFieldName = getValidFieldFromNextRow();
2874
3021
  trigger([].concat(_toConsumableArray(subFields), _toConsumableArray(mainOptionalFields), _toConsumableArray(mainFields), _toConsumableArray(nextValidFieldName)));
2875
3022
  }
2876
3023
  }
@@ -2900,8 +3047,9 @@ var TafFormRow = /*#__PURE__*/React__default.forwardRef(function (_a, ref) {
2900
3047
  }],
2901
3048
  onBlur: function onBlur() {
2902
3049
  if (isChangeGroup) {
2903
- var nextValidFieldName = getFieldFromNextRow();
2904
- trigger([].concat(_toConsumableArray(subFields), _toConsumableArray(mainOptionalFields), _toConsumableArray(mainFields), _toConsumableArray(nextValidFieldName)));
3050
+ var nextValidFieldName = getValidFieldFromNextRow();
3051
+ var previousValidFieldNames = getPreviousValidFields(index);
3052
+ trigger([].concat(_toConsumableArray(subFields), _toConsumableArray(mainOptionalFields), _toConsumableArray(mainFields), _toConsumableArray(nextValidFieldName), _toConsumableArray(previousValidFieldNames)));
2905
3053
  }
2906
3054
  }
2907
3055
  })), /*#__PURE__*/React__default.createElement(TafFormTextField, {
@@ -2919,13 +3067,15 @@ var TafFormRow = /*#__PURE__*/React__default.forwardRef(function (_a, ref) {
2919
3067
  validateValidChangeGroupInterField: function validateValidChangeGroupInterField$1(value) {
2920
3068
  return isChangeGroup ? validateValidChangeGroupInterField(value, getValues(fieldNames.change), getValues('validDateStart'), getValues('validDateEnd')) : true;
2921
3069
  },
2922
- validatePreviousRow: validatePreviousRow
3070
+ validatePreviousRow: validatePreviousRow,
3071
+ validateNextRows: validateNextRows
2923
3072
  }
2924
3073
  },
2925
3074
  onBlur: function onBlur() {
2926
3075
  if (isChangeGroup) {
2927
- var nextValidFieldName = getFieldFromNextRow();
2928
- trigger([].concat(_toConsumableArray(subFields), _toConsumableArray(mainOptionalFields), _toConsumableArray(mainFields), _toConsumableArray(nextValidFieldName)));
3076
+ var nextValidFieldName = getValidFieldFromNextRow();
3077
+ var previousValidFieldNames = getPreviousValidFields(index);
3078
+ trigger([].concat(_toConsumableArray(subFields), _toConsumableArray(mainOptionalFields), _toConsumableArray(mainFields), _toConsumableArray(nextValidFieldName), _toConsumableArray(previousValidFieldNames)));
2929
3079
  }
2930
3080
  }
2931
3081
  }), /*#__PURE__*/React__default.createElement(TafFormTextField, {
@@ -2939,7 +3089,9 @@ var TafFormRow = /*#__PURE__*/React__default.forwardRef(function (_a, ref) {
2939
3089
  return _validateRequiredMainFields(value, 'Wind');
2940
3090
  },
2941
3091
  validateRequiredSub: validateRequiredSub,
2942
- validateWindField: validateWindField
3092
+ validateWindField: function validateWindField$1(value) {
3093
+ return validateWindField(value, getValues(fieldNames.weather1), getValues(fieldNames.weather2), getValues(fieldNames.weather3));
3094
+ }
2943
3095
  }
2944
3096
  },
2945
3097
  onBlur: function onBlur() {
@@ -2988,7 +3140,7 @@ var TafFormRow = /*#__PURE__*/React__default.forwardRef(function (_a, ref) {
2988
3140
  },
2989
3141
  onBlur: function onBlur() {
2990
3142
  var fields = isChangeGroup ? [].concat(_toConsumableArray(subFields), _toConsumableArray(mainFields)) : [];
2991
- return trigger([].concat(_toConsumableArray(fields), [fieldNames.weather2, fieldNames.weather3]));
3143
+ return trigger([].concat(_toConsumableArray(fields), [fieldNames.weather2, fieldNames.weather3, fieldNames.wind, fieldNames.cloud1]));
2992
3144
  }
2993
3145
  }), /*#__PURE__*/React__default.createElement(TafFormTextField, {
2994
3146
  label: "Weather",
@@ -3004,7 +3156,7 @@ var TafFormRow = /*#__PURE__*/React__default.forwardRef(function (_a, ref) {
3004
3156
  },
3005
3157
  onBlur: function onBlur() {
3006
3158
  var fields = isChangeGroup ? [].concat(_toConsumableArray(subFields), _toConsumableArray(mainFields)) : [];
3007
- return trigger([].concat(_toConsumableArray(fields), [fieldNames.weather3]));
3159
+ return trigger([].concat(_toConsumableArray(fields), [fieldNames.weather3, fieldNames.wind, fieldNames.cloud1]));
3008
3160
  }
3009
3161
  }), /*#__PURE__*/React__default.createElement(TafFormTextField, {
3010
3162
  label: "Weather",
@@ -3017,6 +3169,9 @@ var TafFormRow = /*#__PURE__*/React__default.forwardRef(function (_a, ref) {
3017
3169
  return validateThirdWeatherField(getValues(fieldNames.weather1), getValues(fieldNames.weather2), value, getValues(fieldNames.visibility));
3018
3170
  }
3019
3171
  }
3172
+ },
3173
+ onBlur: function onBlur() {
3174
+ return trigger([fieldNames.wind, fieldNames.cloud1]);
3020
3175
  }
3021
3176
  }), /*#__PURE__*/React__default.createElement(TafFormTextField, {
3022
3177
  label: "Cloud",
@@ -3030,7 +3185,7 @@ var TafFormRow = /*#__PURE__*/React__default.forwardRef(function (_a, ref) {
3030
3185
  },
3031
3186
  validateRequiredSub: validateRequiredSub,
3032
3187
  validateFirstCloud: function validateFirstCloud$1(value) {
3033
- return validateFirstCloud(value, getValues(fieldNames.visibility));
3188
+ return validateFirstCloud(value, getValues(fieldNames.visibility), getValues(fieldNames.weather1), getValues(fieldNames.weather2), getValues(fieldNames.weather3));
3034
3189
  }
3035
3190
  }
3036
3191
  },
@@ -3081,20 +3236,32 @@ var TafFormRow = /*#__PURE__*/React__default.forwardRef(function (_a, ref) {
3081
3236
  }
3082
3237
  }
3083
3238
  }), (!isChangeGroup || disabled) && /*#__PURE__*/React__default.createElement(Grid, {
3239
+ className: classes.emptyButtonContainer,
3084
3240
  item: true,
3085
3241
  xs: true
3086
- }), isChangeGroup && !disabled && /*#__PURE__*/React__default.createElement(Grid, {
3087
- item: true,
3088
- xs: true
3089
- }, /*#__PURE__*/React__default.createElement(IconButton, Object.assign({
3242
+ }, !disabled && /*#__PURE__*/React__default.createElement(IconButton, {
3243
+ "aria-label": "add",
3244
+ "data-testid": "add-changeGroup",
3245
+ onClick: addChangeGroup,
3246
+ size: "large"
3247
+ }, /*#__PURE__*/React__default.createElement(Add, null))), isChangeGroup && !disabled && /*#__PURE__*/React__default.createElement(Grid, {
3248
+ item: true
3249
+ }, /*#__PURE__*/React__default.createElement(IconButton, {
3250
+ "aria-label": "add",
3251
+ "data-testid": "add-changeGroup",
3252
+ onClick: addChangeGroup,
3253
+ size: "large"
3254
+ }, /*#__PURE__*/React__default.createElement(Add, null)), /*#__PURE__*/React__default.createElement(IconButton, Object.assign({
3090
3255
  "aria-label": "delete",
3091
3256
  "data-testid": "remove-changeGroup",
3092
3257
  onClick: function onClick() {
3093
3258
  return onRemoveChangeGroup(index);
3094
3259
  }
3095
3260
  }, lastRow ? {
3096
- tabIndex: '-1'
3097
- } : {}), /*#__PURE__*/React__default.createElement(Delete, null))));
3261
+ tabIndex: -1
3262
+ } : {}, {
3263
+ size: "large"
3264
+ }), /*#__PURE__*/React__default.createElement(Delete, null))));
3098
3265
  });
3099
3266
 
3100
3267
  /* *
@@ -3413,7 +3580,7 @@ var TafFormButtons = function TafFormButtons(_ref) {
3413
3580
 
3414
3581
  return /*#__PURE__*/React__default.createElement(React__default.Fragment, null, /*#__PURE__*/React__default.createElement(Grid, {
3415
3582
  container: true,
3416
- justify: "space-between",
3583
+ justifyContent: "space-between",
3417
3584
  alignItems: "center",
3418
3585
  className: classes.lifecycleButtons
3419
3586
  }, /*#__PURE__*/React__default.createElement(Grid, {
@@ -3541,7 +3708,8 @@ var DragHandle = function DragHandle(_ref) {
3541
3708
  className: "".concat(classes.dragHandle, " handle"),
3542
3709
  "data-testid": "dragHandle-".concat(index),
3543
3710
  disableRipple: true,
3544
- disabled: isDisabled
3711
+ disabled: isDisabled,
3712
+ size: "large"
3545
3713
  }, /*#__PURE__*/React__default.createElement(DragIndicator, null));
3546
3714
  };
3547
3715
 
@@ -3640,7 +3808,7 @@ var TafForm = function TafForm(_ref) {
3640
3808
  var showConfirmDialog = function showConfirmDialog(action) {
3641
3809
  return confirmDialog({
3642
3810
  title: getConfirmationDialogTitle(action),
3643
- description: getConfirmationDialogContent(action),
3811
+ description: getConfirmationDialogContent(action, isFormDirty),
3644
3812
  confirmLabel: getConfirmationDialogButtonLabel(action)
3645
3813
  });
3646
3814
  };
@@ -3689,32 +3857,58 @@ var TafForm = function TafForm(_ref) {
3689
3857
  name: 'changeGroups'
3690
3858
  }),
3691
3859
  fields = _useFieldArray.fields,
3692
- append = _useFieldArray.append,
3860
+ insert = _useFieldArray.insert,
3693
3861
  remove = _useFieldArray.remove,
3694
3862
  move = _useFieldArray.move;
3695
3863
 
3696
- var lastTotalFields = usePrevious(fields.length);
3697
-
3698
3864
  var _useDragOrder = useDragOrder(trigger, move, onFormBlur),
3699
3865
  onChangeOrder = _useDragOrder.onChangeOrder,
3700
3866
  onStartDrag = _useDragOrder.onStartDrag,
3701
3867
  activeDraggingIndex = _useDragOrder.activeDraggingIndex;
3702
3868
 
3703
3869
  var onRemoverow = function onRemoverow(index) {
3704
- remove(index);
3870
+ confirmDialog({
3871
+ title: 'Delete row',
3872
+ description: 'Are you sure you want to delete this row?',
3873
+ confirmLabel: 'Yes'
3874
+ }).then(function () {
3875
+ remove(index);
3876
+ trigger(); // trigger all fields validation
3877
+
3878
+ onFormBlur();
3879
+ });
3880
+ }; // TODO: https://gitlab.com/opengeoweb/opengeoweb/-/issues/726 insert (react-hook-form) does not work well with focus (only last row will get focus), so added this dirty solution. This can be fixed in a better way in react-hook-form v7
3881
+
3882
+
3883
+ var focusFirstInput = function focusFirstInput(rowIndex) {
3884
+ var nextRowFirstInput = document.querySelector("[name=\"changeGroups[".concat(rowIndex, "].probability\"]"));
3885
+
3886
+ if (nextRowFirstInput) {
3887
+ nextRowFirstInput.focus();
3888
+ }
3705
3889
  };
3706
3890
 
3707
3891
  var onAddRow = function onAddRow() {
3708
- append(emptyChangegroup, true);
3892
+ var rowIndex = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : -1;
3893
+ var newRowIndex = rowIndex + 1;
3894
+ insert(newRowIndex, emptyChangegroup, false); // adding rows and validation don't work well combined, with always skipping validation of fields of rows above the one the inserted row. A setTimeout prevents this issue
3895
+
3896
+ setTimeout(function () {
3897
+ focusFirstInput(newRowIndex);
3898
+ trigger(); // trigger all fields validation
3899
+ }, 0);
3709
3900
  };
3710
3901
 
3711
3902
  React__default.useEffect(function () {
3712
- if (isFormDirty && fields.length !== lastTotalFields) {
3713
- onFormBlur();
3714
- trigger(); // trigger all field validation,
3903
+ if (!isDisabled) {
3904
+ // trigger validation on all changegroup validity times after initial load since they could be outside the new validity period
3905
+ var validFields = fields.map(function (_field, index) {
3906
+ return "changeGroups[".concat(index, "].valid");
3907
+ });
3908
+ trigger(validFields);
3715
3909
  } // eslint-disable-next-line react-hooks/exhaustive-deps
3716
3910
 
3717
- }, [fields.length, isFormDirty]);
3911
+ }, []);
3718
3912
  return /*#__PURE__*/React__default.createElement("form", {
3719
3913
  onSubmit: handleSubmit(onSubmit),
3720
3914
  onBlur: onFormBlur
@@ -3730,7 +3924,8 @@ var TafForm = function TafForm(_ref) {
3730
3924
  "data-testid": "tafform-tac-message-line"
3731
3925
  }, line);
3732
3926
  })), /*#__PURE__*/React__default.createElement(TafFormRow, {
3733
- disabled: isDisabled
3927
+ disabled: isDisabled,
3928
+ onAddChangeGroup: onAddRow
3734
3929
  }), /*#__PURE__*/React__default.createElement(ReactSortable, {
3735
3930
  tag: "div",
3736
3931
  list: fields,
@@ -3754,6 +3949,7 @@ var TafForm = function TafForm(_ref) {
3754
3949
  lastRow: fields.length === index + 1,
3755
3950
  disabled: isDisabled,
3756
3951
  onRemoveChangeGroup: onRemoverow,
3952
+ onAddChangeGroup: onAddRow,
3757
3953
  isChangeGroup: true,
3758
3954
  dragHandle: /*#__PURE__*/React__default.createElement(DragHandle, {
3759
3955
  isDisabled: fields.length === 1 || isDisabled,
@@ -3761,20 +3957,10 @@ var TafForm = function TafForm(_ref) {
3761
3957
  index: index
3762
3958
  })
3763
3959
  });
3764
- })), !isDisabled && /*#__PURE__*/React__default.createElement(React__default.Fragment, null, /*#__PURE__*/React__default.createElement(Grid, {
3765
- container: true,
3766
- item: true,
3767
- xs: 12,
3768
- justify: "flex-end"
3769
- }, /*#__PURE__*/React__default.createElement(IconButton, {
3770
- "aria-label": "add",
3771
- "data-testid": "add-changeGroup",
3772
- onClick: onAddRow,
3773
- className: classes.addButton
3774
- }, /*#__PURE__*/React__default.createElement(Add, null))), /*#__PURE__*/React__default.createElement(TafFormButtons, {
3960
+ })), !isDisabled && /*#__PURE__*/React__default.createElement(TafFormButtons, {
3775
3961
  tafAction: tafAction,
3776
3962
  onTafFormButtonPress: onTafButtonPress
3777
- })), hiddenInputFields.map(function (field) {
3963
+ }), hiddenInputFields.map(function (field) {
3778
3964
  return /*#__PURE__*/React__default.createElement(ReactHookFormHiddenInput, {
3779
3965
  name: field,
3780
3966
  key: field
@@ -3920,14 +4106,12 @@ var ContentDialog = function ContentDialog(_a) {
3920
4106
  return onFormAction('CLOSE');
3921
4107
  },
3922
4108
  maxWidth: other.maxWidth ? other.maxWidth : 'lg',
3923
- disableBackdropClick: true,
3924
4109
  PaperProps: {
3925
4110
  style: {
3926
4111
  position: 'inherit'
3927
4112
  }
3928
4113
  }
3929
4114
  }, other), /*#__PURE__*/React.createElement(DialogTitle, {
3930
- disableTypography: true,
3931
4115
  className: classes.dialogHeader
3932
4116
  }, /*#__PURE__*/React.createElement(Grid, {
3933
4117
  container: true
@@ -4113,13 +4297,13 @@ var TafListRow = function TafListRow(_ref) {
4113
4297
  md: 3
4114
4298
  }, /*#__PURE__*/React__default.createElement(Grid, {
4115
4299
  container: true,
4116
- justify: "flex-start",
4300
+ justifyContent: "flex-start",
4117
4301
  spacing: 2
4118
4302
  }, /*#__PURE__*/React__default.createElement(Grid, {
4119
4303
  item: true,
4120
4304
  xs: 8,
4121
4305
  container: true,
4122
- justify: "flex-end"
4306
+ justifyContent: "flex-end"
4123
4307
  }, taf.status === 'DRAFT' || taf.status === 'DRAFT_CORRECTED' || taf.status === 'DRAFT_AMENDED' ? /*#__PURE__*/React__default.createElement(Button, {
4124
4308
  color: "secondary",
4125
4309
  size: "small",
@@ -4242,7 +4426,7 @@ var TafListButtons = function TafListButtons(_ref) {
4242
4426
  return !canBe.includes('CANCELLED') && !canBe.includes('CORRECTED') && !canBe.includes('AMENDED') ? null : /*#__PURE__*/React__default.createElement(Grid, {
4243
4427
  container: true,
4244
4428
  alignItems: "center",
4245
- justify: "flex-end",
4429
+ justifyContent: "flex-end",
4246
4430
  "data-testid": "taflist-canbebuttons",
4247
4431
  spacing: 1,
4248
4432
  className: classes.actionButtons
@@ -4282,6 +4466,9 @@ var useStyles$3 = makeStyles({
4282
4466
  },
4283
4467
  formPadding: {
4284
4468
  padding: '8px'
4469
+ },
4470
+ expandIcon: {
4471
+ color: '#0075a9'
4285
4472
  }
4286
4473
  });
4287
4474
 
@@ -4343,11 +4530,13 @@ var TafListAccordion = function TafListAccordion(_ref) {
4343
4530
  }), /*#__PURE__*/React__default.createElement(AccordionSummary, {
4344
4531
  "data-testid": "tafListSectionAccordionSummary",
4345
4532
  expandIcon: popOut ? /*#__PURE__*/React__default.createElement(Tooltip, {
4346
- title: "Popout to edit"
4533
+ title: "Popout to edit",
4534
+ className: classes.expandIcon
4347
4535
  }, /*#__PURE__*/React__default.createElement(OpenInNew, {
4348
4536
  "data-testid": "popOutIcon",
4349
4537
  fontSize: "small"
4350
4538
  })) : /*#__PURE__*/React__default.createElement(ExpandMore, {
4539
+ className: classes.expandIcon,
4351
4540
  "data-testid": "expandIcon"
4352
4541
  })
4353
4542
  }, /*#__PURE__*/React__default.createElement(TafListRow, {
@@ -4521,7 +4710,7 @@ var TafList = function TafList(_ref) {
4521
4710
  onClickTafActionButton: onClickTafActionButton
4522
4711
  })), isLoading && /*#__PURE__*/React__default.createElement(Skeleton, {
4523
4712
  "data-testid": "tafList-skeleton",
4524
- variant: "rect",
4713
+ variant: "rectangular",
4525
4714
  height: "660px"
4526
4715
  })));
4527
4716
  };