diff --git a/client/src/features/book/components/BookFilterBuilder.vue b/client/src/features/book/components/BookFilterBuilder.vue index b846dbba1..b197df550 100644 --- a/client/src/features/book/components/BookFilterBuilder.vue +++ b/client/src/features/book/components/BookFilterBuilder.vue @@ -43,7 +43,8 @@ const emit = defineEmits<{ }>() const MAX_DEPTH = 5 -const NUMERIC_FIELDS: RuleField[] = ['seriesIndex', 'publishedYear', 'pageCount', 'rating', 'communityRating', 'metadataScore'] +const BYTES_PER_MIB = 1024 ** 2 +const NUMERIC_FIELDS: RuleField[] = ['seriesIndex', 'publishedYear', 'pageCount', 'fileSize', 'rating', 'communityRating', 'metadataScore'] const DATE_FIELDS: RuleField[] = ['publishedDate', 'addedAt', 'startedAt', 'finishedAt'] const NO_VALUE_OPERATORS: RuleOperator[] = [ 'isEmpty', @@ -167,13 +168,15 @@ function makeEmptyRule(): LocalNode { function toEditableRule(r: Rule): EditableRule { const usesChips = Array.isArray(r.value) + const value = r.field === 'fileSize' && typeof r.value === 'number' ? String(r.value / BYTES_PER_MIB) : String(r.value ?? '') + const valueTo = r.field === 'fileSize' && typeof r.valueTo === 'number' ? String(r.valueTo / BYTES_PER_MIB) : String(r.valueTo ?? '') return { field: r.field, operator: r.operator, provider: r.field === 'communityRating' ? (r.provider ?? 'any') : 'any', - value: usesChips ? '' : String(r.value ?? ''), + value: usesChips ? '' : value, valueChips: usesChips ? (r.value as string[]) : [], - valueTo: String(r.valueTo ?? ''), + valueTo, valueUnit: 'days', } } @@ -190,7 +193,7 @@ function toLocalNodes(group: GroupRule | undefined): LocalNode[] { function parseValue(field: RuleField, operator: RuleOperator, raw: string, chips: string[], unit: WithinLastUnit): Rule['value'] { if (NO_VALUE_OPERATORS.includes(operator)) return undefined if (COLLECTION_OPERATORS.includes(operator)) return chips - if (isNumericField(field)) return raw === '' ? undefined : Number(raw) + if (isNumericField(field)) return raw === '' ? undefined : field === 'fileSize' ? Math.round(Number(raw) * BYTES_PER_MIB) : Number(raw) if (isDateField(field) && operator === 'withinLast') { if (raw === '') return undefined const n = Number(raw) @@ -248,7 +251,9 @@ function emitUpdate() { valueTo: BETWEEN_OPERATORS.includes(n.rule.operator) && n.rule.valueTo !== '' ? isNumericField(n.rule.field) - ? Number(n.rule.valueTo) + ? n.rule.field === 'fileSize' + ? Math.round(Number(n.rule.valueTo) * BYTES_PER_MIB) + : Number(n.rule.valueTo) : n.rule.valueTo : undefined, } @@ -387,6 +392,7 @@ function valueInputType(field: RuleField, operator: RuleOperator): string { } function numericInputMin(field: RuleField): string | undefined { + if (field === 'fileSize') return '0' if (field === 'communityRating') return '0' return undefined } @@ -398,6 +404,7 @@ function numericInputMax(field: RuleField): string | undefined { } function numericInputStep(field: RuleField): string | undefined { + if (field === 'fileSize') return '0.1' if (field === 'communityRating') return '0.1' return undefined } @@ -577,6 +584,38 @@ function showValueToInput(operator: RuleOperator): boolean { :endpoint="ENDPOINT_BY_FIELD[node.rule.field]!" @update:model-value="emitUpdate" /> + +