import { differenceInCalendarDays, format, isValid, parse, } from "date-fns"; import { useI18n } from "vue-i18n"; export const useInputRules = () => { const { t } = useI18n(); const hasValue = (value) => { if (value === null || value === undefined) return false; if (typeof value === "string") return value.trim().length > 0; if (Array.isArray(value)) return value.length > 0; return true; }; const cepPattern = /^[0-9]{5}-[0-9]{3}$/; const emailPattern = /^(([^<>()[\]\\.,;:\s@"]+(\.[^<>()[\]\\.,;:\s@"]+)*)|(".+"))@((\[[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}])|(([a-zA-Z\-0-9]+\.)+[a-zA-Z]{2,}))$/; const passwordPattern = /^(?=.*\d)(?=.*[a-z])(?=.*[A-Z])(?=.*[a-zA-Z]).{8,}$/; const inputRules = { afterTime: (getStartTime) => (value) => { const startTime = typeof getStartTime === "function" ? getStartTime() : getStartTime; return ( !hasValue(startTime) || !hasValue(value) || value > startTime || t("validation.rules.after_time") ); }, cep: (value) => { if (!value) return true; return cepPattern.test(value) || t("validation.rules.cep"); }, cnpj: (value) => !value || isValidCNPJ(value) || t("validation.rules.cnpj"), cpf: (value) => !value || isValidCPF(value) || t("validation.rules.cpf"), date: (value) => { if (!hasValue(value)) return true; return parseInputDate(value) || t("validation.rules.date"); }, dateMaxDaysFromToday: (maxDays = 365) => (value) => { if (!hasValue(value)) return true; const date = parseInputDate(value); if (!date) return true; return ( differenceInCalendarDays(date, new Date()) <= maxDays || t("validation.rules.date_max_days_from_today", { maxDays }) ); }, dateNotBefore: (getMinimumDate) => (value) => { if (!hasValue(value)) return true; const minimumValue = typeof getMinimumDate === "function" ? getMinimumDate() : getMinimumDate; if (!hasValue(minimumValue)) return true; const date = parseInputDate(value); const minimumDate = parseInputDate(minimumValue); if (!date || !minimumDate) return true; return ( date >= minimumDate || t("validation.rules.date_not_before", { minimumDate: minimumValue, }) ); }, dateRangeDays: (getStartDate, maxDays = 365) => (value) => { if (!hasValue(value)) return true; const startValue = typeof getStartDate === "function" ? getStartDate() : getStartDate; if (!hasValue(startValue)) return true; const startDate = parseInputDate(startValue); const endDate = parseInputDate(value); if (!startDate || !endDate) return true; if (endDate < startDate) { return t("validation.rules.date_before_start"); } return ( differenceInCalendarDays(endDate, startDate) <= maxDays || t("validation.rules.date_range_days", { maxDays }) ); }, durationHours: (value) => { const minutes = Number(value) * 60; return ( (Number.isInteger(minutes) && minutes >= 1 && minutes <= 1440) || t("validation.rules.duration_hours") ); }, email: (value) => !value || emailPattern.test(value) || t("validation.rules.email"), emails: (value) => { if (!value) return true; const emails = value.split(";").map((email) => email.trim()); return ( emails.every((email) => inputRules.email(email) === true) || t("validation.rules.email") ); }, forValue: (rules, getValue) => rules.map((rule) => { const wrapped = (...args) => rule(getValue(), ...args); if (rule?.$id) wrapped.$id = rule.$id; return wrapped; }), integer: (value) => !hasValue(value) || Number.isInteger(Number(value)) || t("validation.rules.integer"), integerRange: (min, max = null) => (value) => { if (!hasValue(value)) return true; const number = Number(value); const withinRange = number >= min && (max == null || number <= max); if (Number.isInteger(number) && withinRange) return true; return max == null ? t("validation.rules.integer_min", { min }) : t("validation.rules.integer_range", { min, max }); }, max: (max) => (value) => !hasValue(value) || String(value).length <= max || `${t("validation.rules.max")} ${max} ${t("validation.rules.characters")}`, maxValue: (max) => (value) => value <= max || `${t("validation.rules.max")} ${max}`, min: (min) => (value) => !hasValue(value) || String(value).length >= min || `${t("validation.rules.min")} ${min} ${t("validation.rules.characters")}`, minValue: (min) => (value) => value >= min || `${t("validation.rules.min")} ${min}`, notSameDocument: (allDocuments) => (value) => { if (!value) return true; let found = 0; for (const doc of allDocuments) { if (doc == value) { found++; } if (found > 1) { return t("validation.rules.not_same_document"); } } return true; }, oneOf: (allowedValues) => (value) => !hasValue(value) || allowedValues.includes(value) || t("validation.rules.one_of"), password: (value) => !value || passwordPattern.test(value) || t("validation.rules.password"), positive: (value) => Number(value) > 0 || t("validation.rules.positive"), samePassword: (otherValue) => (value) => value === otherValue || t("validation.rules.same_password"), slug: (value) => !hasValue(value) || /^[A-Z0-9_]+$/.test(value) || t("validation.rules.slug"), time: (value) => { if (!hasValue(value)) return true; const match = /^(\d{2}):(\d{2})$/.exec(value); return ( (!!match && Number(match[1]) <= 23 && Number(match[2]) <= 59) || t("validation.rules.time") ); }, whenValue: (getDependency, rule) => (value) => !hasValue(getDependency()) || rule(value), // required: (value) => hasValue(value) || t("validation.rules.required"), requiredHideMessage: (value) => hasValue(value), requiredNumber: (value) => (value !== null && value !== undefined && value !== "" && !Number.isNaN(Number(value))) || t("validation.rules.required"), }; inputRules.required.$id = "required"; inputRules.requiredHideMessage.$id = "required"; inputRules.requiredNumber.$id = "required"; return { inputRules, }; }; const parseInputDate = (value) => { const formats = ["dd/MM/yyyy", "yyyy-MM-dd"]; for (const dateFormat of formats) { const parsedDate = parse(value, dateFormat, new Date()); if (isValid(parsedDate) && format(parsedDate, dateFormat) === value) { return parsedDate; } } return null; }; const isValidCNPJ = (cnpj) => { if (!cnpj) return false; cnpj = cnpj.replace(/[^\d]+/g, ""); if (cnpj.length !== 14) return false; if (/^(\d)\1+$/.test(cnpj)) return false; let length = cnpj.length - 2; let numbers = cnpj.substring(0, length); let digits = cnpj.substring(length); let sum = 0; let pos = length - 7; for (let i = length; i >= 1; i--) { sum += parseInt(numbers.charAt(length - i)) * pos--; if (pos < 2) pos = 9; } let result = sum % 11 < 2 ? 0 : 11 - (sum % 11); if (result !== parseInt(digits.charAt(0))) return false; length = length + 1; numbers = cnpj.substring(0, length); sum = 0; pos = length - 7; for (let i = length; i >= 1; i--) { sum += parseInt(numbers.charAt(length - i)) * pos--; if (pos < 2) pos = 9; } result = sum % 11 < 2 ? 0 : 11 - (sum % 11); if (result !== parseInt(digits.charAt(1))) return false; return true; }; const isValidCPF = (cpf) => { if (!cpf) return false; cpf = cpf.replace(/[^\d]+/g, ""); if (cpf.length !== 11) return false; if (/^(\d)\1+$/.test(cpf)) return false; let sum = 0; for (let i = 0; i < 9; i++) { sum += parseInt(cpf.charAt(i)) * (10 - i); } let rev = 11 - (sum % 11); if (rev === 10 || rev === 11) rev = 0; if (rev !== parseInt(cpf.charAt(9))) return false; sum = 0; for (let i = 0; i < 10; i++) { sum += parseInt(cpf.charAt(i)) * (11 - i); } rev = 11 - (sum % 11); if (rev === 10 || rev === 11) rev = 0; if (rev !== parseInt(cpf.charAt(10))) return false; return true; };