Next.js website for Rocky Mountain Vending company featuring: - Product catalog with Stripe integration - Service areas and parts pages - Admin dashboard with Clerk authentication - SEO optimized pages with JSON-LD structured data Co-authored-by: Cursor <cursoragent@cursor.com>
233 lines
5.2 KiB
Text
Executable file
233 lines
5.2 KiB
Text
Executable file
'use strict';
|
|
|
|
const { assert } = require('@hapi/hoek');
|
|
|
|
const Any = require('./any');
|
|
const Common = require('../common');
|
|
const Template = require('../template');
|
|
|
|
|
|
const internals = {};
|
|
|
|
|
|
internals.isDate = function (value) {
|
|
|
|
return value instanceof Date;
|
|
};
|
|
|
|
|
|
module.exports = Any.extend({
|
|
|
|
type: 'date',
|
|
|
|
coerce: {
|
|
from: ['number', 'string'],
|
|
method(value, { schema }) {
|
|
|
|
return { value: internals.parse(value, schema._flags.format) || value };
|
|
}
|
|
},
|
|
|
|
validate(value, { schema, error, prefs }) {
|
|
|
|
if (value instanceof Date &&
|
|
!isNaN(value.getTime())) {
|
|
|
|
return;
|
|
}
|
|
|
|
const format = schema._flags.format;
|
|
|
|
if (!prefs.convert ||
|
|
!format ||
|
|
typeof value !== 'string') {
|
|
|
|
return { value, errors: error('date.base') };
|
|
}
|
|
|
|
return { value, errors: error('date.format', { format }) };
|
|
},
|
|
|
|
rules: {
|
|
|
|
compare: {
|
|
method: false,
|
|
validate(value, helpers, { date }, { name, operator, args }) {
|
|
|
|
const to = date === 'now' ? Date.now() : date.getTime();
|
|
if (Common.compare(value.getTime(), to, operator)) {
|
|
return value;
|
|
}
|
|
|
|
return helpers.error('date.' + name, { limit: args.date, value });
|
|
},
|
|
args: [
|
|
{
|
|
name: 'date',
|
|
ref: true,
|
|
normalize: (date) => {
|
|
|
|
return date === 'now' ? date : internals.parse(date);
|
|
},
|
|
assert: (date) => date !== null,
|
|
message: 'must have a valid date format'
|
|
}
|
|
]
|
|
},
|
|
|
|
format: {
|
|
method(format) {
|
|
|
|
assert(['iso', 'javascript', 'unix'].includes(format), 'Unknown date format', format);
|
|
|
|
return this.$_setFlag('format', format);
|
|
}
|
|
},
|
|
|
|
greater: {
|
|
method(date) {
|
|
|
|
return this.$_addRule({ name: 'greater', method: 'compare', args: { date }, operator: '>' });
|
|
}
|
|
},
|
|
|
|
iso: {
|
|
method() {
|
|
|
|
return this.format('iso');
|
|
}
|
|
},
|
|
|
|
less: {
|
|
method(date) {
|
|
|
|
return this.$_addRule({ name: 'less', method: 'compare', args: { date }, operator: '<' });
|
|
}
|
|
},
|
|
|
|
max: {
|
|
method(date) {
|
|
|
|
return this.$_addRule({ name: 'max', method: 'compare', args: { date }, operator: '<=' });
|
|
}
|
|
},
|
|
|
|
min: {
|
|
method(date) {
|
|
|
|
return this.$_addRule({ name: 'min', method: 'compare', args: { date }, operator: '>=' });
|
|
}
|
|
},
|
|
|
|
timestamp: {
|
|
method(type = 'javascript') {
|
|
|
|
assert(['javascript', 'unix'].includes(type), '"type" must be one of "javascript, unix"');
|
|
|
|
return this.format(type);
|
|
}
|
|
}
|
|
},
|
|
|
|
cast: {
|
|
number: {
|
|
from: internals.isDate,
|
|
to(value, helpers) {
|
|
|
|
return value.getTime();
|
|
}
|
|
},
|
|
string: {
|
|
from: internals.isDate,
|
|
to(value, { prefs }) {
|
|
|
|
return Template.date(value, prefs);
|
|
}
|
|
}
|
|
},
|
|
|
|
messages: {
|
|
'date.base': '{{#label}} must be a valid date',
|
|
'date.format': '{{#label}} must be in {msg("date.format." + #format) || #format} format',
|
|
'date.greater': '{{#label}} must be greater than {{:#limit}}',
|
|
'date.less': '{{#label}} must be less than {{:#limit}}',
|
|
'date.max': '{{#label}} must be less than or equal to {{:#limit}}',
|
|
'date.min': '{{#label}} must be greater than or equal to {{:#limit}}',
|
|
|
|
// Messages used in date.format
|
|
|
|
'date.format.iso': 'ISO 8601 date',
|
|
'date.format.javascript': 'timestamp or number of milliseconds',
|
|
'date.format.unix': 'timestamp or number of seconds'
|
|
}
|
|
});
|
|
|
|
|
|
// Helpers
|
|
|
|
internals.parse = function (value, format) {
|
|
|
|
if (value instanceof Date) {
|
|
return value;
|
|
}
|
|
|
|
if (typeof value !== 'string' &&
|
|
(isNaN(value) || !isFinite(value))) {
|
|
|
|
return null;
|
|
}
|
|
|
|
if (/^\s*$/.test(value)) {
|
|
return null;
|
|
}
|
|
|
|
// ISO
|
|
|
|
if (format === 'iso') {
|
|
if (!Common.isIsoDate(value)) {
|
|
return null;
|
|
}
|
|
|
|
return internals.date(value.toString());
|
|
}
|
|
|
|
// Normalize number string
|
|
|
|
const original = value;
|
|
if (typeof value === 'string' &&
|
|
/^[+-]?\d+(\.\d+)?$/.test(value)) {
|
|
|
|
value = parseFloat(value);
|
|
}
|
|
|
|
// Timestamp
|
|
|
|
if (format) {
|
|
if (format === 'javascript') {
|
|
return internals.date(1 * value); // Casting to number
|
|
}
|
|
|
|
if (format === 'unix') {
|
|
return internals.date(1000 * value);
|
|
}
|
|
|
|
if (typeof original === 'string') {
|
|
return null;
|
|
}
|
|
}
|
|
|
|
// Plain
|
|
|
|
return internals.date(value);
|
|
};
|
|
|
|
|
|
internals.date = function (value) {
|
|
|
|
const date = new Date(value);
|
|
if (!isNaN(date.getTime())) {
|
|
return date;
|
|
}
|
|
|
|
return null;
|
|
};
|