Skip to content

Commit

Permalink
Merge pull request #107 from bcgov/feature/frontend-validator
Browse files Browse the repository at this point in the history
Feature / Frontend File-upload/Input-param Validations and Validators
  • Loading branch information
vividroyjeong authored Jan 18, 2025
2 parents 3b5c38f + d47408d commit 7e985db
Show file tree
Hide file tree
Showing 18 changed files with 392 additions and 157 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -71,7 +71,7 @@
import { ref, onBeforeUnmount, watch, type PropType } from 'vue'
import { CONSTANTS, MESSAGE } from '@/constants'
import type { SpeciesList } from '@/interfaces/interfaces'
import { SpeciesInfoValidation } from '@/validation/speciesInfoValidation'
import { SpeciesInfoValidator } from '@/validation/speciesInfoValidator'
import { Util } from '@/utils/util'
import { cloneDeep } from 'lodash'
Expand Down Expand Up @@ -106,7 +106,7 @@ const emit = defineEmits(['update:speciesList'])
const localSpeciesList = ref(cloneDeep(props.speciesList))
const speciesInfoValidator = new SpeciesInfoValidation()
const speciesInfoValidator = new SpeciesInfoValidator()
// Watch for external speciesList changes
watch(
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -68,11 +68,11 @@ import {
} from '@/components'
import { CONSTANTS, DEFAULTS, MESSAGE } from '@/constants'
import type { MessageDialog } from '@/interfaces/interfaces'
import { ReportInfoValidation } from '@/validation/reportInfoValidation'
import { ReportInfoValidator } from '@/validation/reportInfoValidator'
const form = ref<HTMLFormElement>()
const reportInfoValidator = new ReportInfoValidation()
const reportInfoValidator = new ReportInfoValidator()
const modelParameterStore = useModelParameterStore()
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -188,12 +188,12 @@ import { useModelParameterStore } from '@/stores/modelParameterStore'
import { AppMessageDialog, AppPanelActions, AppSpinField } from '@/components'
import type { SpeciesGroup, MessageDialog } from '@/interfaces/interfaces'
import { CONSTANTS, OPTIONS, DEFAULTS, MESSAGE } from '@/constants'
import { SiteInfoValidation } from '@/validation/siteInfoValidation'
import { SiteInfoValidator } from '@/validation/siteInfoValidator'
import { Util } from '@/utils/util'
const form = ref<HTMLFormElement>()
const siteInfoValidator = new SiteInfoValidation()
const siteInfoValidator = new SiteInfoValidator()
const modelParameterStore = useModelParameterStore()
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -197,12 +197,12 @@ import {
} from '@/components'
import { CONSTANTS, DEFAULTS, MAPPINGS, MESSAGE, OPTIONS } from '@/constants'
import type { SpeciesList, MessageDialog } from '@/interfaces/interfaces'
import { SpeciesInfoValidation } from '@/validation/speciesInfoValidation'
import { SpeciesInfoValidator } from '@/validation/speciesInfoValidator'
import { cloneDeep } from 'lodash'
const form = ref<HTMLFormElement>()
const speciesInfoValidator = new SpeciesInfoValidation()
const speciesInfoValidator = new SpeciesInfoValidator()
const modelParameterStore = useModelParameterStore()
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -83,11 +83,11 @@ import {
MDL_PRM_INPUT_HINT,
} from '@/constants/message'
import type { MessageDialog } from '@/interfaces/interfaces'
import { StandDensityValidation } from '@/validation/standDensityValidation'
import { StandDensityValidator } from '@/validation/standDensityValidator'
const form = ref<HTMLFormElement>()
const standDensityValidator = new StandDensityValidation()
const standDensityValidator = new StandDensityValidator()
const modelParameterStore = useModelParameterStore()
Expand Down
17 changes: 17 additions & 0 deletions frontend/src/utils/util.ts
Original file line number Diff line number Diff line change
Expand Up @@ -164,6 +164,23 @@ export class Util {
return value
}

/**
* Parses the input value and converts it to a number or null.
* If the input is an empty string, it returns null.
* If the input is a valid number, it returns the number.
* @param value - The value to be parsed (string | number | null)
* @returns number | null
*/
static parseNumberOrNull(value: string | number | null): number | null {
if (value === '' || value === null) {
return null
}

const parsedValue = Number(value)

return isNaN(parsedValue) ? null : parsedValue
}

/**
* Converts the input to a number if it's a string and returns it,
* otherwise returns the input unchanged if it's a number.
Expand Down
125 changes: 77 additions & 48 deletions frontend/src/validation/fileUploadValidation.ts
Original file line number Diff line number Diff line change
@@ -1,66 +1,95 @@
import { ValidationBase } from './validationBase'
import { NUM_INPUT_LIMITS } from '@/constants/constants'
import Papa from 'papaparse'
import { FileUploadValidator } from './fileUploadValidator'

export class FileUploadValidation extends ValidationBase {
validateRequiredFields(
startingAge: number | null,
finishingAge: number | null,
ageIncrement: number | null,
): boolean {
return (
startingAge !== null && finishingAge !== null && ageIncrement !== null
const fileUploadValidator = new FileUploadValidator()

export const validateComparison = (
startingAge: number | null,
finishingAge: number | null,
) => {
if (!fileUploadValidator.validateAgeComparison(startingAge, finishingAge)) {
return { isValid: false }
}

return { isValid: true }
}

export const validateRequiredFields = (
startingAge: number | null,
finishingAge: number | null,
ageIncrement: number | null,
) => {
if (
!fileUploadValidator.validateRequiredFields(
startingAge,
finishingAge,
ageIncrement,
)
) {
return { isValid: false }
}
return { isValid: true }
}

export const validateRange = (
startingAge: number | null,
finishingAge: number | null,
ageIncrement: number | null,
) => {
if (!fileUploadValidator.validateStartingAgeRange(startingAge)) {
return {
isValid: false,
errorType: 'startingAge',
}
}

if (!fileUploadValidator.validateFinishingAgeRange(finishingAge)) {
return {
isValid: false,
errorType: 'finishingAge',
}
}

validateAgeComparison(
finishingAge: number | null,
startingAge: number | null,
): boolean {
if (finishingAge !== null && startingAge !== null) {
return finishingAge >= startingAge
if (!fileUploadValidator.validateAgeIncrementRange(ageIncrement)) {
return {
isValid: false,
errorType: 'ageIncrement',
}
return true
}

validateStartingAgeRange(startingAge: number | null): boolean {
if (startingAge !== null) {
return (
startingAge >= NUM_INPUT_LIMITS.STARTING_AGE_MIN &&
startingAge <= NUM_INPUT_LIMITS.STARTING_AGE_MAX
)
return { isValid: true }
}

export const validateFiles = async (
layerFile: File | null,
polygonFile: File | null,
) => {
if (!layerFile) {
return {
isValid: false,
errorType: 'layerFileMissing',
}
return true
}

validateFinishingAgeRange(finishingAge: number | null): boolean {
if (finishingAge !== null) {
return (
finishingAge >= NUM_INPUT_LIMITS.FINISHING_AGE_MIN &&
finishingAge <= NUM_INPUT_LIMITS.FINISHING_AGE_MAX
)
if (!polygonFile) {
return {
isValid: false,
errorType: 'polygonFileMissing',
}
return true
}

validateAgeIncrementRange(ageIncrement: number | null): boolean {
if (ageIncrement !== null) {
return (
ageIncrement >= NUM_INPUT_LIMITS.AGE_INC_MIN &&
ageIncrement <= NUM_INPUT_LIMITS.AGE_INC_MAX
)
if (!(await fileUploadValidator.isCSVFile(layerFile))) {
return {
isValid: false,
errorType: 'layerFileNotCSVFormat',
}
return true
}

async isCSVFile(file: File): Promise<boolean> {
return new Promise((resolve) => {
Papa.parse(file, {
complete: (results: any) => {
resolve(results.errors.length === 0)
},
error: () => resolve(false),
})
})
if (!(await fileUploadValidator.isCSVFile(polygonFile))) {
return {
isValid: false,
errorType: 'polygonFileNotCSVFormat',
}
}

return { isValid: true }
}
70 changes: 70 additions & 0 deletions frontend/src/validation/fileUploadValidator.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,70 @@
import { ValidationBase } from './validationBase'
import { NUM_INPUT_LIMITS } from '@/constants/constants'

export class FileUploadValidator extends ValidationBase {
validateRequiredFields(
startingAge: number | null,
finishingAge: number | null,
ageIncrement: number | null,
): boolean {
return (
startingAge !== null && finishingAge !== null && ageIncrement !== null
)
}

validateAgeComparison(
startingAge: number | null,
finishingAge: number | null,
): boolean {
if (startingAge !== null && finishingAge !== null) {
return finishingAge >= startingAge
}
return true
}

validateStartingAgeRange(startingAge: number | null): boolean {
if (startingAge !== null) {
return (
startingAge >= NUM_INPUT_LIMITS.STARTING_AGE_MIN &&
startingAge <= NUM_INPUT_LIMITS.STARTING_AGE_MAX
)
}
return true
}

validateFinishingAgeRange(finishingAge: number | null): boolean {
if (finishingAge !== null) {
return (
finishingAge >= NUM_INPUT_LIMITS.FINISHING_AGE_MIN &&
finishingAge <= NUM_INPUT_LIMITS.FINISHING_AGE_MAX
)
}
return true
}

validateAgeIncrementRange(ageIncrement: number | null): boolean {
if (ageIncrement !== null) {
return (
ageIncrement >= NUM_INPUT_LIMITS.AGE_INC_MIN &&
ageIncrement <= NUM_INPUT_LIMITS.AGE_INC_MAX
)
}
return true
}

async isCSVFile(file: File): Promise<boolean> {
// Check file extension
const fileExtension = file.name.split('.').pop()?.toLowerCase()
if (fileExtension !== 'csv') {
return false
}

// Check MIME type
const validMimeType = 'text/csv'
if (file.type !== validMimeType) {
return false
}

return true
}
}
5 changes: 5 additions & 0 deletions frontend/src/validation/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
export * as speciesInfoValidation from './speciesInfoValidation'
export * as siteInfoValidation from './siteInfoValidation'
export * as standDensityValidation from './standDensityValidation'
export * as reportInfoValidation from './reportInfoValidation'
export * as fileUploadValidation from './fileUploadValidation'
63 changes: 31 additions & 32 deletions frontend/src/validation/reportInfoValidation.ts
Original file line number Diff line number Diff line change
@@ -1,44 +1,43 @@
import { ValidationBase } from './validationBase'
import { NUM_INPUT_LIMITS } from '@/constants/constants'
import { ReportInfoValidator } from '@/validation/reportInfoValidator'

export class ReportInfoValidation extends ValidationBase {
validateAgeComparison(
finishingAge: number | null,
startingAge: number | null,
): boolean {
if (finishingAge !== null && startingAge !== null) {
return finishingAge >= startingAge
}
return true
const reportInfoValidator = new ReportInfoValidator()

export const validateComparison = (
startingAge: number | null,
finishingAge: number | null,
) => {
if (!reportInfoValidator.validateAgeComparison(startingAge, finishingAge)) {
return { isValid: false }
}

validateStartingAgeRange(startingAge: number | null): boolean {
if (startingAge !== null) {
return (
startingAge >= NUM_INPUT_LIMITS.STARTING_AGE_MIN &&
startingAge <= NUM_INPUT_LIMITS.STARTING_AGE_MAX
)
return { isValid: true }
}

export const validateRange = (
startingAge: number | null,
finishingAge: number | null,
ageIncrement: number | null,
) => {
if (!reportInfoValidator.validateStartingAgeRange(startingAge)) {
return {
isValid: false,
errorType: 'startingAge',
}
return true
}

validateFinishingAgeRange(finishingAge: number | null): boolean {
if (finishingAge !== null) {
return (
finishingAge >= NUM_INPUT_LIMITS.FINISHING_AGE_MIN &&
finishingAge <= NUM_INPUT_LIMITS.FINISHING_AGE_MAX
)
if (!reportInfoValidator.validateFinishingAgeRange(finishingAge)) {
return {
isValid: false,
errorType: 'finishingAge',
}
return true
}

validateAgeIncrementRange(ageIncrement: number | null): boolean {
if (ageIncrement !== null) {
return (
ageIncrement >= NUM_INPUT_LIMITS.AGE_INC_MIN &&
ageIncrement <= NUM_INPUT_LIMITS.AGE_INC_MAX
)
if (!reportInfoValidator.validateAgeIncrementRange(ageIncrement)) {
return {
isValid: false,
errorType: 'ageIncrement',
}
return true
}

return { isValid: true }
}
Loading

0 comments on commit 7e985db

Please sign in to comment.