lodash#isNaN TypeScript Examples
The following examples show how to use
lodash#isNaN.
You can vote up the ones you like or vote down the ones you don't like,
and go to the original project or source file by following the links above each example. You may check out the related API usage on the sidebar.
Example #1
Source File: formatter.ts From S2 with MIT License | 6 votes |
auto = (
v: number,
fixed = 2,
formatter = FORMATTERS[getLang()] || FORMATTERS.zh_CN,
): string => {
if (typeof v !== 'number' || isNaN(v)) {
return '';
}
// let n = Math.abs(v); // abs什么鬼。
let n = v;
// 语义化
const [texts, powers] = formatter;
let loop = 0;
let power;
let running = true;
while (running) {
power = powers[loop] as number;
if (n >= power && loop < texts.length) {
n /= power;
} else {
running = false;
}
loop += 1;
}
// parseFloat 解决 toFixed 出现很多 0 结尾。
// 举例:123.toFixed(2) = '123.00',需要返回 '123'
n = parseFloat(n.toFixed(fixed));
// 千分位
const output = n >= 1000 ? n.toLocaleString('en') : `${n}`;
// 加上最后的单位
return loop === 0 ? output : `${output} ${texts[loop - 1]}`;
}
Example #2
Source File: machine-table.tsx From erda-ui with GNU Affero General Public License v3.0 | 6 votes |
filterFunc = {
include: (record: ORG_MACHINE.IMachine, val = '', key: string) => {
return record[key].includes(val);
},
includeMult: (record: ORG_MACHINE.IMachine, val: string[] = [], key: string) => {
if (isEmpty(val)) {
return true;
}
let res = true;
val.forEach((item) => {
if (!record[key].includes(item)) res = false;
});
return res;
},
numberRange: (record: ORG_MACHINE.IMachine, val: string[] = [], key: string) => {
const [more, less] = val[0] || ([] as any);
const curVal = get(record, key);
if (!isNaN(curVal)) {
if (more && Number(curVal) <= Number(more)) {
return false;
}
if (less && Number(curVal) >= Number(less)) {
return false;
}
}
return true;
},
}
Example #3
Source File: in-params-drawer.tsx From erda-ui with GNU Affero General Public License v3.0 | 6 votes |
formDataToYmlData = (data: IFormData[]): PIPELINE.IPipelineInParams[] => {
return compact(
map(data, (item) => {
const { key, required, defaultValue, component, labelTip } = item;
const type = get(find(typeMapping, { component }), 'type') || component;
let _default = defaultValue as any;
if (type === 'int') _default = isNaN(+_default) ? undefined : +_default;
if (type === 'boolean') _default = isBoolean(_default) ? _default : _default === 'true';
if (!key) return null;
const dataItem = {
name: key,
required,
default: _default || undefined,
type,
desc: labelTip,
};
const res = {};
map(dataItem, (val, k) => {
if (val !== undefined) {
res[k] = val;
}
});
return res as PIPELINE.IPipelineInParams;
}),
);
}
Example #4
Source File: in-params-drawer.tsx From erda-ui with GNU Affero General Public License v3.0 | 6 votes |
ymlDataToFormData = (data: PIPELINE.IPipelineInParams[], val: Obj = {}): IFormData[] => {
return map(data, (item) => {
const { name, required = false, default: _default, type, desc, inConfig, value } = item;
let _defaultVal = val[item.name] !== null && val[item.name] !== undefined ? val[item.name] : _default || undefined;
const component = get(find(typeMapping, { type }), 'component') || type;
let labelTip = desc;
if (_defaultVal !== null && _defaultVal !== undefined && value === _defaultVal) {
labelTip = `${inConfig ? i18n.t('dop:default value comes from the environment configuration') : ''}; ${desc}`;
}
if (type === 'boolean' && _defaultVal !== true) _defaultVal = false;
if (type === 'int') _defaultVal = _defaultVal === null ? undefined : isNaN(+_defaultVal) ? undefined : +_defaultVal;
return {
key: name,
label: name,
required,
defaultValue: _defaultVal,
component: componentList.includes(component) ? component : 'input',
labelTip,
};
});
}
Example #5
Source File: alert-worker.ts From prism-frontend with MIT License | 5 votes |
async function processAlert(alert: Alert, alertRepository: Repository<Alert>) {
const { baseUrl, serverLayerName, type } = alert.alertConfig;
const {
id,
alertName,
createdAt,
email,
lastTriggered,
prismUrl,
active,
} = alert;
const availableDates =
type === 'wms'
? await getWMSCapabilities(`${baseUrl}/wms`)
: await getWCSCoverage(`${baseUrl}`);
const layerAvailableDates = availableDates[serverLayerName];
const maxDate = new Date(Math.max(...(layerAvailableDates || [])));
if (
!active ||
isNaN(maxDate.getTime()) ||
(lastTriggered && lastTriggered >= maxDate) ||
createdAt >= maxDate
) {
return;
}
const alertMessage = await calculateBoundsForAlert(maxDate, alert);
// Use the URL API to create the url and perform url encoding on all character
const url = new URL(`/alerts/${id}`, ANALYSIS_API_URL);
url.searchParams.append('deactivate', 'true');
url.searchParams.append('email', email);
if (alertMessage) {
const emailMessage = `
Your alert${alertName ? ` ${alertName}` : ''} has been triggered.
Layer: ${serverLayerName}
Date: ${maxDate}
Go to ${prismUrl} for more information.
Alert: ${alertMessage}`;
const emailHtml = `${emailMessage.replace(
/(\r\n|\r|\n)/g,
'<br>',
)} <br><br>To cancel this alert, click <a href='${url.href}'>here</a>.`;
console.log(
`Alert ${id} - '${alert.alertName}' was triggered on ${maxDate}.`,
);
// TODO - Send an email using WFP SMTP servers.
await sendEmail({
from: '[email protected]',
to: email,
subject: `PRISM Alert Triggered`,
text: emailMessage,
html: emailHtml,
});
console.log(alertMessage);
}
// Update lastTriggered (imnactive during testing)
await alertRepository.update(alert.id, { lastTriggered: maxDate });
}
Example #6
Source File: upload-hobo-data.ts From aqualink-app with MIT License | 5 votes |
getSiteRecords = async (
dataAsJson: Coords[],
siteIds: number[],
regionRepository: Repository<Region>,
) => {
// Group by site
const recordsGroupedBySite = groupBy(dataAsJson, 'site');
// Extract site entities and calculate position of site by averaging all each surveyPoints positions
const sites = await Promise.all(
siteIds.map((siteId) => {
// Filter out NaN values
const filteredSiteCoords = recordsGroupedBySite[siteId].filter(
(record) => !isNaN(record.lat) && !isNaN(record.long),
);
const siteRecord = filteredSiteCoords.reduce(
(previous, record) => {
return {
...previous,
lat: previous.lat + record.lat,
long: previous.long + record.long,
};
},
{ site: siteId, colony: 0, lat: 0, long: 0 },
);
// Calculate site position
const point: Point = createPoint(
siteRecord.long / filteredSiteCoords.length,
siteRecord.lat / filteredSiteCoords.length,
);
// Augment site information
const [longitude, latitude] = point.coordinates;
const timezones = getTimezones(latitude, longitude) as string[];
return Promise.all([
getRegion(longitude, latitude, regionRepository),
getMMM(longitude, latitude),
]).then(([region, maxMonthlyMean]) => ({
name: SITE_PREFIX + siteId,
polygon: point,
region,
maxMonthlyMean,
display: false,
timezone: timezones[0],
status: SiteStatus.Approved,
}));
}),
);
return { recordsGroupedBySite, sites };
}
Example #7
Source File: upload-hobo-data.ts From aqualink-app with MIT License | 5 votes |
createSurveyPoints = async (
siteEntities: Site[],
dbIdToCSVId: Record<number, number>,
recordsGroupedBySite: Dictionary<Coords[]>,
rootPath: string,
poiRepository: Repository<SiteSurveyPoint>,
) => {
// Create site points of interest entities for each imported site
// Final result needs to be flattened since the resulting array is grouped by site
const surveyPoints = siteEntities
.map((site) => {
const currentSiteId = dbIdToCSVId[site.id];
const siteFolder = FOLDER_PREFIX + currentSiteId;
return recordsGroupedBySite[currentSiteId]
.filter((record) => {
const colonyId = record.colony.toString().padStart(3, '0');
const colonyFolder = COLONY_FOLDER_PREFIX + colonyId;
const colonyFolderPath = path.join(
rootPath,
siteFolder,
colonyFolder,
);
return fs.existsSync(colonyFolderPath);
})
.map((record) => {
const point: Point | undefined =
!isNaN(record.long) && !isNaN(record.lat)
? createPoint(record.long, record.lat)
: undefined;
return {
name: COLONY_PREFIX + record.colony,
site,
polygon: point,
};
});
})
.flat();
logger.log('Saving site points of interest');
const poiEntities = await Promise.all(
surveyPoints.map((poi) =>
poiRepository
.save(poi)
.catch(handleEntityDuplicate(poiRepository, poiQuery, poi.polygon)),
),
);
return poiEntities;
}
Example #8
Source File: numberUtils.ts From aqualink-app with MIT License | 5 votes |
export function formatNumber(n?: number | null, decimal = 0) {
return isNumber(n) && !isNaN(n) ? n.toFixed(decimal) : "- -";
}
Example #9
Source File: NumberInput.tsx From react-native-jigsaw with MIT License | 5 votes |
NumberInput: FC<Props> = ({
onChangeText,
value,
defaultValue,
...props
}) => {
const [currentStringNumberValue, setCurrentStringNumberValue] = useState("0");
const formatValueToStringNumber = (valueToFormat?: number | string) => {
if (valueToFormat != null) {
if (isString(valueToFormat) && valueToFormat !== "") {
if (/^0[1-9]$/.test(valueToFormat)) {
return valueToFormat.slice(1);
} else if (/^[+-]?([0-9]+\.?[0-9]*|\.[0-9]+)$/.test(valueToFormat)) {
return valueToFormat;
} else {
return currentStringNumberValue;
}
} else if (isNumber(valueToFormat) && !isNaN(valueToFormat)) {
return valueToFormat.toString();
}
}
return "0";
};
// set currentStringNumberValue as defaultValue prop if there is a differnce on first render only
useEffect(() => {
const defaultStringNumberValue = formatValueToStringNumber(defaultValue);
if (currentStringNumberValue !== defaultStringNumberValue) {
setCurrentStringNumberValue(defaultStringNumberValue);
}
// eslint-disable-next-line react-hooks/exhaustive-deps
}, []);
const handleChangeText = (newValue: string) => {
const newStringNumberValue = formatValueToStringNumber(newValue);
const number = parseFloat(newStringNumberValue);
setCurrentStringNumberValue(newStringNumberValue);
onChangeText?.(number);
};
// run handleChangeText with value prop only when value prop changes (and first render to reset currentStringNumberValue)
useEffect(() => {
const nextStringNumberValue = formatValueToStringNumber(value);
if (currentStringNumberValue !== nextStringNumberValue) {
handleChangeText(nextStringNumberValue);
}
// eslint-disable-next-line react-hooks/exhaustive-deps
}, [value]);
return (
<TextInput
keyboardType="numeric"
value={currentStringNumberValue}
onChangeText={handleChangeText}
{...props}
/>
);
}
Example #10
Source File: upload-sheet-data.ts From aqualink-app with MIT License | 4 votes |
uploadTimeSeriesData = async (
filePath: string,
fileName: string,
siteId: string,
surveyPointId: string | undefined,
sourceType: SourceType,
repositories: Repositories,
failOnWarning?: boolean,
mimetype?: Mimetype,
) => {
// // TODO
// // - Add foreign key constraint to sources on site_id
console.time(`Upload datafile ${fileName}`);
const { site, surveyPoint } = surveyPointId
? await getSiteAndSurveyPoint(
parseInt(siteId, 10),
parseInt(surveyPointId, 10),
repositories.siteRepository,
repositories.surveyPointRepository,
)
: {
site: await getSite(parseInt(siteId, 10), repositories.siteRepository),
surveyPoint: undefined,
};
const existingSourceEntity = await repositories.sourcesRepository.findOne({
relations: ['surveyPoint', 'site'],
where: {
site: { id: siteId },
surveyPoint: surveyPointId || null,
type: sourceType,
},
});
const sourceEntity =
existingSourceEntity ||
(await repositories.sourcesRepository.save({
type: sourceType,
site,
surveyPoint,
}));
if (
sourceType === SourceType.SONDE ||
sourceType === SourceType.METLOG ||
sourceType === SourceType.HOBO
) {
const workSheetsFromFile = xlsx.parse(filePath, { raw: true });
const workSheetData = workSheetsFromFile[0]?.data;
const { ignoredHeaders, importedHeaders } = validateHeaders(
fileName,
workSheetData,
sourceType,
);
if (failOnWarning && ignoredHeaders.length > 0) {
throw new BadRequestException(
`${fileName}: The columns ${ignoredHeaders
.map((header) => `"${header}"`)
.join(
', ',
)} are not configured for import yet and cannot be uploaded.`,
);
}
const signature = await md5Fle(filePath);
const uploadExists = await repositories.dataUploadsRepository.findOne({
where: {
signature,
site,
surveyPoint,
sensorType: sourceType,
},
});
if (uploadExists) {
throw new ConflictException(
`${fileName}: A file upload named '${uploadExists.file}' with the same data already exists`,
);
}
console.time(`Get data from sheet ${fileName}`);
const results = findSheetDataWithHeader(
fileName,
workSheetData,
sourceType,
mimetype,
);
console.timeEnd(`Get data from sheet ${fileName}`);
console.time(`Remove duplicates and empty values ${fileName}`);
const data = uniqBy(
results
.reduce((timeSeriesObjects: any[], object) => {
const { timestamp } = object;
return [
...timeSeriesObjects,
...Object.keys(object)
.filter((k) => k !== 'timestamp')
.map((key) => {
return {
timestamp,
value: parseFloat(object[key]),
metric: key,
source: sourceEntity,
};
}),
];
}, [])
.filter((valueObject) => {
if (!isNaN(parseFloat(valueObject.value))) {
return true;
}
logger.log('Excluding incompatible value:');
logger.log(valueObject);
return false;
}),
({ timestamp, metric, source }) =>
`${timestamp}, ${metric}, ${source.id}`,
);
console.timeEnd(`Remove duplicates and empty values ${fileName}`);
const minDate = get(
minBy(data, (item) => new Date(get(item, 'timestamp')).getTime()),
'timestamp',
);
const maxDate = get(
maxBy(data, (item) => new Date(get(item, 'timestamp')).getTime()),
'timestamp',
);
// Initialize google cloud service, to be used for media upload
const googleCloudService = new GoogleCloudService();
// Note this may fail. It would still return a location, but the file may not have been uploaded
const fileLocation = googleCloudService.uploadFileAsync(
filePath,
sourceType,
'data_uploads',
'data_upload',
);
const dataUploadsFile = await repositories.dataUploadsRepository.save({
file: fileName,
signature,
sensorType: sourceType,
site,
surveyPoint,
minDate,
maxDate,
metrics: importedHeaders,
fileLocation,
});
const dataAsTimeSeries = data.map((x: any) => {
return {
timestamp: x.timestamp,
value: x.value,
metric: x.metric,
source: x.source,
dataUpload: dataUploadsFile,
};
});
// Data is too big to added with one bulk insert so we batch the upload.
console.time(`Loading into DB ${fileName}`);
const batchSize = 100;
logger.log(`Saving time series data in batches of ${batchSize}`);
const inserts = chunk(dataAsTimeSeries, batchSize).map(
async (batch: any[]) => {
try {
await repositories.timeSeriesRepository
.createQueryBuilder('time_series')
.insert()
.values(batch)
// If there's a conflict, replace data with the new value.
// onConflict is deprecated, but updating it is tricky.
// See https://github.com/typeorm/typeorm/issues/8731?fbclid=IwAR2Obg9eObtGNRXaFrtKvkvvVSWfvjtHpFu-VEM47yg89SZcPpxEcZOmcLw
.onConflict(
'ON CONSTRAINT "no_duplicate_data" DO UPDATE SET "value" = excluded.value',
)
.execute();
} catch {
console.warn('The following batch failed to upload:');
console.warn(batch);
}
return true;
},
);
// Return insert promises and print progress updates
const actionsLength = inserts.length;
await Bluebird.Promise.each(inserts, (props, idx) => {
logger.log(`Saved ${idx + 1} out of ${actionsLength} batches`);
});
console.timeEnd(`Loading into DB ${fileName}`);
logger.log('loading complete');
refreshMaterializedView(repositories);
console.timeEnd(`Upload datafile ${fileName}`);
return ignoredHeaders;
}
return [];
}
Example #11
Source File: index.tsx From aqualink-app with MIT License | 4 votes |
MultipleSensorsCharts = ({
site,
pointId,
surveysFiltered,
disableGutters,
displayOceanSenseCharts,
}: MultipleSensorsChartsProps) => {
const classes = useStyles();
const dispatch = useDispatch();
const getQueryParam = useQueryParams();
const [startDateParam, endDateParam, initialChart] = [
"start",
"end",
"chart",
].map(getQueryParam);
const initialStart =
startDateParam && isISODate(startDateParam) ? startDateParam : undefined;
const initialEnd =
endDateParam && isISODate(endDateParam) ? endDateParam : undefined;
const granularDailyData = useSelector(siteGranularDailyDataSelector);
const timeSeriesData = useSelector(siteTimeSeriesDataSelector);
const oceanSenseData = useSelector(siteOceanSenseDataSelector);
const latestData = useSelector(latestDataSelector);
const { bottomTemperature, topTemperature } = timeSeriesData || {};
const { hobo: hoboBottomTemperature } = bottomTemperature || {};
const timeSeriesDataRanges = useSelector(siteTimeSeriesDataRangeSelector);
const { hobo: hoboBottomTemperatureRange } =
timeSeriesDataRanges?.bottomTemperature || {};
const rangesLoading = useSelector(siteTimeSeriesDataRangeLoadingSelector);
const [pickerEndDate, setPickerEndDate] = useState<string>();
const [pickerStartDate, setPickerStartDate] = useState<string>();
const [endDate, setEndDate] = useState<string>();
const [startDate, setStartDate] = useState<string>();
const [pickerErrored, setPickerErrored] = useState(false);
const [range, setRange] = useState<RangeValue>(
initialStart || initialEnd ? "custom" : "one_month"
);
const history = useHistory();
const today = localizedEndOfDay(undefined, site.timezone);
const hasSpotterData = Boolean(
bottomTemperature?.spotter?.data?.[1] || topTemperature?.spotter?.data?.[1]
);
const hasSondeData = Boolean(
latestData?.some((data) => data.source === "sonde")
);
const hasMetlogData = Boolean(
latestData?.some((data) => data.source === "metlog")
);
const chartStartDate = startDate || subtractFromDate(today, "week");
const chartEndDate = moment
.min(
moment(),
moment(endDate)
.tz(site.timezone || "UTC")
.endOf("day")
)
.toISOString();
const hasOceanSenseId = Boolean(oceanSenseConfig?.[site.id]);
const tempAnalysisDatasets = generateTempAnalysisDatasets(
granularDailyData,
timeSeriesData?.bottomTemperature?.spotter?.data,
timeSeriesData?.topTemperature?.spotter?.data,
hoboBottomTemperature?.data,
site.historicalMonthlyMean,
startDate,
endDate,
chartStartDate,
chartEndDate,
site.timezone,
site.depth
);
const spotterMetricDataset = (metric: Metrics) => {
const { unit, convert } = spotterConfig[metric] || {};
return generateMetricDataset(
"SENSOR",
timeSeriesData?.[metric]?.spotter?.data?.map((item) => ({
...item,
value: convert ? convert * item.value : item.value,
})) || [],
unit || "",
SPOTTER_METRIC_DATA_COLOR,
chartStartDate,
chartEndDate,
site.timezone
);
};
const sondeDatasets = () =>
hasSondeData
? sortBy(getPublicSondeMetrics(), (key) => getSondeConfig(key).order)
.filter(
(key) =>
timeSeriesData?.[camelCase(key) as Metrics]?.sonde?.data?.length
)
.map((key) => {
const { data, surveyPoint } =
timeSeriesData?.[camelCase(key) as Metrics]?.sonde || {};
const { title, units } = getSondeConfig(key);
return {
key,
title,
surveyPoint,
dataset: generateMetricDataset(
"SENSOR",
data || [],
units,
SONDE_DATA_COLOR,
chartStartDate,
chartEndDate,
site.timezone
),
};
})
: [];
const metlogDatasets = () =>
hasMetlogData
? sortBy(getPublicMetlogMetrics(), (key) => getMetlogConfig(key).order)
.filter(
(key) =>
timeSeriesData?.[camelCase(key) as Metrics]?.metlog?.data?.length
)
.map((key) => {
const { data, surveyPoint } =
timeSeriesData?.[camelCase(key) as Metrics]?.metlog || {};
const { title, units, convert } = getMetlogConfig(key);
return {
key,
title,
surveyPoint,
dataset: generateMetricDataset(
"SENSOR",
(data || []).map((item) => ({
...item,
value: isNumber(convert) ? item.value * convert : item.value,
})),
units,
METLOG_DATA_COLOR,
chartStartDate,
chartEndDate,
site.timezone
),
};
})
: [];
// Scroll to the chart defined by the initialChart query param.
useEffect(() => {
if (initialChart) {
const chartElement = document.getElementById(initialChart);
chartElement?.scrollIntoView();
}
}, [initialChart]);
// Set pickers initial values once the range request is completed
useEffect(() => {
if (!rangesLoading && !pickerStartDate && !pickerEndDate) {
const { maxDate } = hoboBottomTemperatureRange?.data?.[0] || {};
const localizedMaxDate = localizedEndOfDay(maxDate, site.timezone);
const pastOneMonth = moment(
subtractFromDate(localizedMaxDate || today, "month", 1)
)
.tz(site.timezone || "UTC")
.startOf("day")
.toISOString();
setPickerStartDate(
initialStart
? zonedTimeToUtc(initialStart, site.timezone || "UTC").toISOString()
: utcToZonedTime(pastOneMonth, site.timezone || "UTC").toISOString()
);
setPickerEndDate(
initialEnd
? zonedTimeToUtc(initialEnd, site.timezone || "UTC").toISOString()
: utcToZonedTime(
localizedMaxDate || today,
site.timezone || "UTC"
).toISOString()
);
}
}, [
hoboBottomTemperatureRange,
initialEnd,
initialStart,
pickerStartDate,
pickerEndDate,
rangesLoading,
site.timezone,
today,
]);
// Get time series data
useEffect(() => {
if (
pickerStartDate &&
pickerEndDate &&
isBefore(pickerStartDate, pickerEndDate)
) {
const siteLocalStartDate = setTimeZone(
new Date(pickerStartDate),
site.timezone
);
const siteLocalEndDate = setTimeZone(
new Date(pickerEndDate),
site.timezone
);
dispatch(
siteTimeSeriesDataRequest({
siteId: `${site.id}`,
pointId,
start: siteLocalStartDate,
end: siteLocalEndDate,
metrics: hasSondeData || hasMetlogData ? undefined : DEFAULT_METRICS,
hourly:
moment(siteLocalEndDate).diff(moment(siteLocalStartDate), "days") >
2,
})
);
if (hasOceanSenseId) {
dispatch(
siteOceanSenseDataRequest({
sensorID: oceanSenseConfig[site.id],
startDate: siteLocalStartDate,
endDate: siteLocalEndDate,
latest: false,
})
);
}
}
}, [
dispatch,
hasMetlogData,
hasOceanSenseId,
hasSondeData,
pickerEndDate,
pickerStartDate,
pointId,
site.id,
site.timezone,
]);
// Set chart start/end dates based on data received
useEffect(() => {
const pickerLocalEndDate = new Date(
setTimeZone(
new Date(moment(pickerEndDate).format("MM/DD/YYYY")),
site?.timezone
)
).toISOString();
const pickerLocalStartDate = new Date(
setTimeZone(
new Date(moment(pickerStartDate).format("MM/DD/YYYY")),
site?.timezone
)
).toISOString();
const [minDataDate, maxDataDate] = findDataLimits(
site.historicalMonthlyMean,
granularDailyData,
timeSeriesData,
pickerLocalStartDate,
localizedEndOfDay(pickerLocalEndDate, site.timezone)
);
setStartDate(
minDataDate
? moment
.max(moment(minDataDate), moment(pickerLocalStartDate))
.toISOString()
: pickerLocalStartDate
);
setEndDate(
maxDataDate
? moment
.min(moment(maxDataDate), moment(pickerLocalEndDate).endOf("day"))
.toISOString()
: moment(pickerLocalEndDate).endOf("day").toISOString()
);
}, [granularDailyData, pickerEndDate, pickerStartDate, site, timeSeriesData]);
useEffect(() => {
if (pickerStartDate && pickerEndDate) {
// eslint-disable-next-line fp/no-mutating-methods
history.push({
search: `?start=${pickerStartDate.split("T")[0]}&end=${
pickerEndDate.split("T")[0]
}`,
});
}
// eslint-disable-next-line react-hooks/exhaustive-deps
}, [history, pickerEndDate, pickerStartDate, site.timezone]);
// Set picker error
useEffect(() => {
if (pickerStartDate && pickerEndDate) {
setPickerErrored(!isBefore(pickerStartDate, pickerEndDate));
}
}, [pickerEndDate, pickerStartDate]);
const dataForCsv = [
...tempAnalysisDatasets.map((dataset) => ({
name: `${dataset.metric || "unknownMetric"}_${
dataset.source || "unknownSource"
}`,
values: dataset.data,
})),
...Object.entries(spotterConfig).map(([key]) => {
const dataset = spotterMetricDataset(key as Metrics);
return {
name: `${key}_spotter`,
values: dataset.data,
};
}),
...Object.entries(constructOceanSenseDatasets(oceanSenseData)).map(
([key, item]) => {
const dataset = generateMetricDataset(
key,
item.data,
item.unit,
OCEAN_SENSE_DATA_COLOR,
chartStartDate,
chartEndDate,
site.timezone
);
return {
name: `${camelCase(item.title.split(" ")[0])}`,
values: dataset.data,
};
}
),
...sondeDatasets().map(({ title, dataset }) => ({
name: `${title} ${dataset.label}`,
values: dataset.data,
})),
].filter((x) => x.values.length > 0);
const onRangeChange = (value: RangeValue) => {
const { minDate, maxDate } = hoboBottomTemperatureRange?.data?.[0] || {};
const localizedMinDate = new Date(
moment(minDate)
.tz(site.timezone || "UTC")
.format("MM/DD/YYYY")
).toISOString();
const localizedMaxDate = new Date(
moment(maxDate)
.tz(site.timezone || "UTC")
.format("MM/DD/YYYY")
).toISOString();
setRange(value);
switch (value) {
case "one_month":
setPickerEndDate(moment(localizedMaxDate).endOf("day").toISOString());
setPickerStartDate(subtractFromDate(localizedMaxDate, "month", 1));
break;
case "one_year":
setPickerEndDate(moment(localizedMaxDate).endOf("day").toISOString());
setPickerStartDate(subtractFromDate(localizedMaxDate, "year"));
break;
case "max":
setPickerEndDate(moment(localizedMaxDate).endOf("day").toISOString());
setPickerStartDate(localizedMinDate);
break;
default:
break;
}
};
const onPickerDateChange = (type: "start" | "end") => (date: Date | null) => {
const time = date?.getTime();
if (date && time && !isNaN(time)) {
const dateString = date.toISOString();
setRange("custom");
switch (type) {
case "start":
// Set picker start date only if input date is after zero time
if (
moment(dateString)
.startOf("day")
.isSameOrAfter(moment(0).startOf("day"))
) {
setPickerStartDate(moment(dateString).startOf("day").toISOString());
}
break;
case "end":
// Set picker end date only if input date is before today
if (
moment(dateString)
.endOf("day")
.isSameOrBefore(moment().endOf("day"))
) {
setPickerEndDate(moment(dateString).endOf("day").toISOString());
}
break;
default:
break;
}
}
};
return (
<Container
disableGutters={disableGutters}
className={classes.chartWithRange}
>
<div className={classes.buttonWrapper}>
<DownloadCSVButton
data={dataForCsv}
startDate={pickerStartDate}
endDate={pickerEndDate}
siteId={site.id}
pointId={pointId}
className={classes.button}
/>
</div>
<ChartWithCard
id="temperature"
range={range}
onRangeChange={onRangeChange}
disableMaxRange={!hoboBottomTemperatureRange?.data?.[0]}
chartTitle="TEMPERATURE ANALYSIS"
availableRanges={[
{
name: "Spotter",
data: timeSeriesDataRanges?.bottomTemperature?.spotter?.data,
},
{
name: "HOBO",
data: timeSeriesDataRanges?.bottomTemperature?.hobo?.data,
},
]}
timeZone={site.timezone}
chartWidth={findChartWidth(tempAnalysisDatasets)}
site={site}
datasets={tempAnalysisDatasets}
pointId={pointId ? parseInt(pointId, 10) : undefined}
pickerStartDate={pickerStartDate || subtractFromDate(today, "week")}
pickerEndDate={pickerEndDate || today}
chartStartDate={chartStartDate}
chartEndDate={chartEndDate}
onStartDateChange={onPickerDateChange("start")}
onEndDateChange={onPickerDateChange("end")}
isPickerErrored={pickerErrored}
areSurveysFiltered={surveysFiltered}
/>
{hasSpotterData &&
Object.entries(spotterConfig).map(([key, { title }]) => (
<Box mt={4} key={title}>
<ChartWithCard
datasets={[spotterMetricDataset(key as Metrics)]}
id={key}
range={range}
onRangeChange={onRangeChange}
disableMaxRange={!hoboBottomTemperatureRange?.data?.[0]}
chartTitle={title}
availableRanges={[
{
name: "Spotter",
data: timeSeriesDataRanges?.[key as Metrics]?.spotter?.data,
},
]}
timeZone={site.timezone}
showRangeButtons={false}
chartWidth="large"
site={site}
pickerStartDate={
pickerStartDate || subtractFromDate(today, "week")
}
pickerEndDate={pickerEndDate || today}
chartStartDate={chartStartDate}
chartEndDate={chartEndDate}
onStartDateChange={onPickerDateChange("start")}
onEndDateChange={onPickerDateChange("end")}
isPickerErrored={pickerErrored}
showDatePickers={false}
hideYAxisUnits
cardColumnJustification="flex-start"
/>
</Box>
))}
{displayOceanSenseCharts &&
hasOceanSenseId &&
Object.entries(constructOceanSenseDatasets(oceanSenseData)).map(
([key, item]) => (
<Box mt={4} key={item.title}>
<ChartWithCard
datasets={[
generateMetricDataset(
key,
item.data,
item.unit,
OCEAN_SENSE_DATA_COLOR,
chartStartDate,
chartEndDate,
site.timezone
),
]}
id={item.id}
range={range}
onRangeChange={onRangeChange}
disableMaxRange={!hoboBottomTemperatureRange?.data?.[0]}
chartTitle={item.title}
timeZone={site.timezone}
showRangeButtons={false}
chartWidth="large"
site={site}
pickerStartDate={
pickerStartDate || subtractFromDate(today, "week")
}
pickerEndDate={pickerEndDate || today}
chartStartDate={chartStartDate}
chartEndDate={chartEndDate}
onStartDateChange={onPickerDateChange("start")}
onEndDateChange={onPickerDateChange("end")}
isPickerErrored={pickerErrored}
showDatePickers={false}
hideYAxisUnits
cardColumnJustification="flex-start"
/>
</Box>
)
)}
{sondeDatasets().map(({ key, title, surveyPoint, dataset }) => (
<Box mt={4} key={key}>
<ChartWithCard
datasets={[dataset]}
id={key}
range={range}
onRangeChange={onRangeChange}
disableMaxRange={!hoboBottomTemperatureRange?.data?.[0]}
chartTitle={title}
availableRanges={[
{
name: "Sonde",
data: timeSeriesDataRanges?.[camelCase(key) as Metrics]?.sonde
?.data,
},
]}
timeZone={site.timezone}
showRangeButtons={false}
chartWidth="large"
site={site}
pickerStartDate={pickerStartDate || subtractFromDate(today, "week")}
pickerEndDate={pickerEndDate || today}
chartStartDate={chartStartDate}
chartEndDate={chartEndDate}
onStartDateChange={onPickerDateChange("start")}
onEndDateChange={onPickerDateChange("end")}
isPickerErrored={pickerErrored}
showDatePickers={false}
surveyPoint={surveyPoint}
hideYAxisUnits
cardColumnJustification="flex-start"
/>
</Box>
))}
{metlogDatasets().map(({ key, title, surveyPoint, dataset }) => (
<Box mt={4} key={key}>
<ChartWithCard
datasets={[dataset]}
id={key}
range={range}
onRangeChange={onRangeChange}
disableMaxRange={!hoboBottomTemperatureRange?.data?.[0]}
chartTitle={title}
availableRanges={[
{
name: "Meteorological",
data: timeSeriesDataRanges?.[camelCase(key) as Metrics]?.metlog
?.data,
},
]}
timeZone={site.timezone}
showRangeButtons={false}
chartWidth="large"
site={site}
pickerStartDate={pickerStartDate || subtractFromDate(today, "week")}
pickerEndDate={pickerEndDate || today}
chartStartDate={chartStartDate}
chartEndDate={chartEndDate}
onStartDateChange={onPickerDateChange("start")}
onEndDateChange={onPickerDateChange("end")}
isPickerErrored={pickerErrored}
showDatePickers={false}
surveyPoint={surveyPoint}
hideYAxisUnits
cardColumnJustification="flex-start"
/>
</Box>
))}
</Container>
);
}
Example #12
Source File: index.tsx From erda-ui with GNU Affero General Public License v3.0 | 4 votes |
CaseTable = ({
query: queryProp,
columns,
onClickRow,
scope,
onChange,
testPlanId,
modalQuery = {},
slot,
}: IProps) => {
const isModal = scope === 'caseModal';
const client = layoutStore.useStore((s) => s.client);
const projectInfo = projectStore.useStore((s) => s.info);
const { getCases: oldGetCases } = testCaseStore.effects;
const { updateBreadcrumb } = testSetStore.effects;
const [loading] = useLoading(testCaseStore, ['getCases']);
const routeQuery = queryProp || routeInfoStore.useStore((s) => s.query);
const [metaFields, pageCaseList, pageCaseTotal, modalCaseList, modalCaseTotal] = testCaseStore.useStore((s) => [
s.metaFields,
s.caseList,
s.caseTotal,
s.modalCaseList,
s.modalCaseTotal,
]);
const caseList = isModal ? modalCaseList : pageCaseList;
const caseTotal = (isModal ? modalCaseTotal : pageCaseTotal) as number;
const getCases = useCallback(
(payload: any) => {
oldGetCases({ selected: [], ...payload, scope, testPlanId });
},
[scope, oldGetCases, testPlanId],
);
// 使用弹框里可能传入的参数覆盖url上的参数
const query = { ...routeQuery, ...modalQuery };
const { dataSource, expandedRowKeys } = useMemo(
() => getDataSource(caseList, scope, projectInfo.name),
[caseList, projectInfo.name, scope],
);
useEffect(() => {
if (query.caseId && onClickRow) {
let target: any;
forEach(dataSource, (item) => {
target = target || find(item.children, { id: +query.caseId });
});
if (target) {
updateBreadcrumb({
pathName: target.parent.directory,
testSetID: target.testSetID,
testPlanID: testPlanId,
});
onClickRow(target, dataSource);
}
}
}, [query.caseId, dataSource, onClickRow]);
// 计算是否需要左右滑动
const ref = useRef(null);
const hasMount = !!ref.current;
// 不必去除 react-hooks/exhaustive-deps, 无法识别 clientWidth
// eslint-disable-next-line react-hooks/exhaustive-deps
const tableWidth = useMemo(
() => get(document.querySelector('.case-table'), ['clientWidth'], 0),
[client.width, hasMount],
);
const tempProps: any = {};
const className = 'case-table';
// if (mode !== 'modal') { // 非弹框
const configWidth = reduce(
columns,
(temp, { width }: any) => temp + (isNaN(parseInt(width, 10)) ? 380 : parseInt(width, 10)),
0,
);
// 操作列
const operationWidth = (columns[columns.length - 1].width || 60) as number;
if (tableWidth + operationWidth < configWidth) {
tempProps.scroll = { x: configWidth };
}
// }
const isScroll = !!tempProps.scroll;
const { orderBy, orderRule } = query;
const metaMap = useMemo(() => {
const temp = {};
forEach(metaFields, ({ enums }) => {
forEach(enums, ({ fieldUniqueName, showName, value }) => {
temp[`${fieldUniqueName}-${value}`] = showName;
});
});
return temp;
}, [metaFields]);
const newColumns = useMemo(() => {
// 初始化表格columns
const tempColumns = cloneDeep(columns);
// ID列
const idColumn = find(tempColumns, ({ key }: any) => key === 'id');
if (idColumn) {
Object.assign(idColumn, {
dataIndex: 'id',
className: 'case-table-id',
render: (id: string, record: any) => {
if (id) {
return <span className="text-normal">{`#${record.testCaseID}`}</span>;
}
return {
children: <Ellipsis className="text-desc" title={record.directory} />,
props: {
colSpan: 5,
},
};
},
});
}
// 标题列(只针对当前页操作)
const nameColumn = find(tempColumns, ({ key }: any) => key === 'name');
if (nameColumn) {
Object.assign(nameColumn, {
// title: <ChooseTitle mode={mode} />,
title: <span>{i18n.t('dop:Use case title')}</span>,
render: (name: string, record: any) => {
const obj = {
children: <Ellipsis className="font-bold" title={name} />,
props: {},
};
if (!record.id) {
obj.props.colSpan = 0;
}
return obj;
},
});
}
// 全选列
const checkColumn = find(tempColumns, ({ key }: any) => key === 'checkbox');
if (checkColumn) {
Object.assign(checkColumn, {
title: <AllCheckBox mode={scope} />,
className: 'case-table-checkbox',
render: (_text: any, record: any) => {
if (record.id) {
return <CaseCheckBox mode={scope} id={record.id} />;
}
return <CustomIcon type="wjj1" className="text-warning" />;
},
});
}
// 正在排序的列
if (orderBy) {
const sortColumn = find(tempColumns, ({ key }: any) => key === orderBy);
if (!sortColumn || !biggerSorterMap[orderRule]) return;
Object.assign(sortColumn, {
defaultSortOrder: biggerSorterMap[orderRule],
});
}
// 测试元数据相关列
forEach(tempColumns, (single: any) => {
const { dataIndex, key } = single;
const fieldUniqueName = metaColumnsMap[dataIndex] || metaColumnsMap[key];
if (fieldUniqueName && !single.render) {
// eslint-disable-next-line no-param-reassign
single.render = (value: any) => metaMap[`${fieldUniqueName}-${value}`] || value;
}
});
// 操作列
const operationColumn = filter(tempColumns, ({ fixed }: any) => fixed);
if (!isEmpty(operationColumn)) {
operationColumn.width = 120;
forEach(operationColumn, (single: any) => {
Object.assign(single, {
fixed: isScroll ? single.fixed : undefined,
});
});
}
return tempColumns;
}, [orderBy, orderRule, columns, scope, metaMap, isScroll]);
const handleChange = (pagination: any, _filters: any, sorter: any) => {
let newSorter = {};
if (!sorter?.order) {
delete query.orderBy;
delete query.orderRule;
newSorter = { orderBy: undefined, orderRule: undefined };
} else {
newSorter = { orderBy: sorter.columnKey, orderRule: sorterMap[sorter.order] };
}
const newQuery = { ...query, testPlanId, pageNo: pagination.current, pageSize: pagination.pageSize, ...newSorter };
// 不在弹框里时,不改变url参数
if (scope !== 'caseModal') {
updateSearch(newQuery);
}
getCases(newQuery);
if (onChange) {
onChange(newQuery);
}
};
const handleClickRow = (record: TEST_CASE.CaseTableRecord) => {
if (record.children) {
return;
}
updateBreadcrumb({
pathName: record.parent.directory,
testSetID: record.testSetID,
testPlanID: testPlanId,
});
onClickRow && onClickRow(record, dataSource);
};
return (
<ErdaTable
ref={ref}
loading={loading}
className={className}
indentSize={0}
expandedRowKeys={expandedRowKeys}
dataSource={dataSource}
columns={newColumns}
rowKey={(record: any) => `${record.testSetID}-${record.id || ''}`}
onChange={handleChange}
expandIcon={() => null}
onRow={(record: TEST_CASE.CaseTableRecord) => ({
onClick: () => handleClickRow(record),
})}
slot={slot}
{...tempProps}
pagination={{
total: caseTotal,
current: parseInt(query.pageNo, 10) || defaultPageNo,
pageSize: parseInt(query.pageSize, 10) || defaultPageSize,
showSizeChanger: true,
}}
/>
);
}