add date picker

This commit is contained in:
Casey 2025-11-07 10:29:37 -06:00
parent 09a514ae86
commit 82f9b1aac2
8 changed files with 1044 additions and 55 deletions

View file

@ -135,6 +135,9 @@
:severity="getBadgeColor(slotProps.data[col.fieldName])"
/>
</template>
<template v-if="col.type === 'date'" #body="slotProps">
<span>{{ formatDate(slotProps.data[col.fieldName]) }}</span>
</template>
<template v-if="col.type === 'button'" #body="slotProps">
<Button
:label="slotProps.data[col.fieldName]"
@ -626,16 +629,47 @@ const handleFilterInput = (fieldName, value, filterCallback) => {
};
const getBadgeColor = (status) => {
console.log("DEBUG: - getBadgeColor status", status);
switch (status?.toLowerCase()) {
case "completed":
return "success"; // green
case "open":
case "active":
return "success";
case "in progress":
return "warn";
case "pending":
return "warning";
case "not started":
return "danger"; // red
case "closed":
case "cancelled":
return "danger";
default:
return "info"; // blue fallback
return "info";
}
};
const formatDate = (dateValue) => {
if (!dateValue) return "";
try {
// Handle different date formats
let date;
if (typeof dateValue === "string") {
date = new Date(dateValue);
} else if (dateValue instanceof Date) {
date = dateValue;
} else {
return "";
}
// Check if date is valid
if (isNaN(date.getTime())) {
return dateValue; // Return original value if can't parse
}
// Format as MM/DD/YYYY
return date.toLocaleDateString("en-US");
} catch (error) {
console.error("Error formatting date:", error);
return dateValue;
}
};
console.log("DEBUG: - DataTable props.columns", props.columns);

View file

@ -267,17 +267,35 @@
<DatePicker
:id="field.name"
v-model="fieldValues[field.name]"
:placeholder="field.placeholder"
:placeholder="field.placeholder || getDatePlaceholder(field)"
:disabled="field.disabled || isFormDisabled"
:readonly="field.readonly"
:minDate="field.minDate"
:maxDate="field.maxDate"
:minDate="parseDateValue(field.minDate)"
:maxDate="parseDateValue(field.maxDate)"
:invalid="!!getFieldError(field.name)"
fluid
showIcon
iconDisplay="input"
:dateFormat="field.dateFormat || 'dd/mm/yy'"
@update:model-value="handleFieldChange(field, $event)"
:dateFormat="getDateFormat(field)"
:showTime="field.showTime || false"
:timeOnly="field.timeOnly || false"
:hourFormat="field.hourFormat || '24'"
:stepHour="field.stepHour || 1"
:stepMinute="field.stepMinute || 1"
:showSeconds="field.showSeconds || false"
:stepSecond="field.stepSecond || 1"
:showButtonBar="field.showButtonBar !== false"
:todayButtonLabel="field.todayButtonLabel || 'Today'"
:clearButtonLabel="field.clearButtonLabel || 'Clear'"
:showWeek="field.showWeek || false"
:manualInput="field.manualInput !== false"
:yearNavigator="field.yearNavigator || false"
:monthNavigator="field.monthNavigator || false"
:yearRange="field.yearRange || '1900:2100'"
:inline="field.inline || false"
:view="field.view || 'date'"
:touchUI="field.touchUI || false"
@update:model-value="handleDateChange(field, $event)"
@blur="handleFieldBlur(field, fieldValues[field.name])"
/>
<small v-if="field.helpText" class="field-help">{{
@ -293,7 +311,7 @@
</Message>
</div>
<!-- DateTime Input -->
<!-- DateTime Input (Legacy - use date with showTime: true) -->
<div v-else-if="field.type === 'datetime'" class="field-wrapper">
<label v-if="field.label" :for="field.name" class="field-label">
{{ field.label }}
@ -302,19 +320,35 @@
<DatePicker
:id="field.name"
v-model="fieldValues[field.name]"
:placeholder="field.placeholder"
:placeholder="
field.placeholder ||
getDatePlaceholder({ ...field, showTime: true })
"
:disabled="field.disabled || isFormDisabled"
:readonly="field.readonly"
:minDate="field.minDate"
:maxDate="field.maxDate"
:minDate="parseDateValue(field.minDate)"
:maxDate="parseDateValue(field.maxDate)"
:invalid="!!getFieldError(field.name)"
fluid
showIcon
iconDisplay="input"
showTime
:hourFormat="field.hourFormat || '24'"
:dateFormat="field.dateFormat || 'dd/mm/yy'"
@update:model-value="handleFieldChange(field, $event)"
:dateFormat="getDateFormat({ ...field, showTime: true })"
:stepHour="field.stepHour || 1"
:stepMinute="field.stepMinute || 1"
:showSeconds="field.showSeconds || false"
:stepSecond="field.stepSecond || 1"
:showButtonBar="field.showButtonBar !== false"
:todayButtonLabel="field.todayButtonLabel || 'Today'"
:clearButtonLabel="field.clearButtonLabel || 'Clear'"
:manualInput="field.manualInput !== false"
:yearNavigator="field.yearNavigator || false"
:monthNavigator="field.monthNavigator || false"
:yearRange="field.yearRange || '1900:2100'"
:inline="field.inline || false"
:touchUI="field.touchUI || false"
@update:model-value="handleDateChange(field, $event)"
@blur="handleFieldBlur(field, fieldValues[field.name])"
/>
<small v-if="field.helpText" class="field-help">{{
@ -536,6 +570,105 @@ const initializeFormData = () => {
});
};
// Date utility functions
const getDateDefaultValue = (field) => {
if (field.defaultValue !== undefined) {
// Handle string date values
if (typeof field.defaultValue === "string") {
if (field.defaultValue === "today" || field.defaultValue === "now") {
return new Date();
}
// Try to parse the string as a date
const parsed = new Date(field.defaultValue);
return !isNaN(parsed.getTime()) ? parsed : null;
}
// Handle Date objects or null/undefined
return field.defaultValue;
}
// Set default based on field configuration
if (field.defaultToToday || field.defaultToNow) {
const now = new Date();
if (field.type === "date" && !field.showTime) {
// For date-only fields, set to start of day
return new Date(now.getFullYear(), now.getMonth(), now.getDate());
}
return now;
}
// No default value
return null;
};
const parseDateValue = (value) => {
if (!value) return null;
if (value instanceof Date) return value;
if (typeof value === "string") {
const parsed = new Date(value);
return !isNaN(parsed.getTime()) ? parsed : null;
}
return null;
};
const getDateFormat = (field) => {
// Return custom format if provided
if (field.dateFormat) {
return field.dateFormat;
}
// Handle predefined format strings
if (field.format) {
switch (field.format.toLowerCase()) {
case "yyyy-mm-dd":
return "yy-mm-dd";
case "mm/dd/yyyy":
return "mm/dd/yy";
case "dd/mm/yyyy":
return "dd/mm/yy";
case "dd-mm-yyyy":
return "dd-mm-yy";
case "mm-dd-yyyy":
return "mm-dd-yy";
default:
break;
}
}
// Default formats based on field configuration
if (field.showTime || field.type === "datetime") {
return "dd/mm/yy"; // PrimeVue will append time format automatically
}
return "dd/mm/yy"; // Default date format
};
const getDatePlaceholder = (field) => {
if (field.placeholder) return field.placeholder;
const format = field.format || (field.showTime ? "dd/mm/yyyy hh:mm" : "dd/mm/yyyy");
if (field.timeOnly) {
return "Select time";
} else if (field.showTime || field.type === "datetime") {
return `Enter date and time (${format})`;
} else {
return `Enter date (${format})`;
}
};
const handleDateChange = (field, value) => {
// Convert Date object to appropriate format if needed
let processedValue = value;
// Apply custom formatting if specified
if (field.onDateChange && typeof field.onDateChange === "function") {
processedValue = field.onDateChange(value);
}
// Call the standard field change handler
handleFieldChange(field, processedValue);
};
// Get default value for a field based on its type
const getDefaultValue = (field) => {
switch (field.type) {
@ -548,6 +681,9 @@ const getDefaultValue = (field) => {
return field.defaultValue !== undefined ? field.defaultValue : "";
case "file":
return null;
case "date":
case "datetime":
return getDateDefaultValue(field);
default:
return field.defaultValue !== undefined ? field.defaultValue : "";
}
@ -634,6 +770,29 @@ const validateField = (field, value) => {
}
}
// Date validation
if ((field.type === "date" || field.type === "datetime") && value) {
const dateValue = value instanceof Date ? value : parseDateValue(value);
if (!dateValue || isNaN(dateValue.getTime())) {
errors.push("Please enter a valid date");
} else {
// Min/Max date validation
if (field.minDate) {
const minDate = parseDateValue(field.minDate);
if (minDate && dateValue < minDate) {
errors.push(`Date must be on or after ${minDate.toLocaleDateString()}`);
}
}
if (field.maxDate) {
const maxDate = parseDateValue(field.maxDate);
if (maxDate && dateValue > maxDate) {
errors.push(`Date must be on or before ${maxDate.toLocaleDateString()}`);
}
}
}
}
// Custom validation (always runs last)
if (field.validate && typeof field.validate === "function") {
const customError = field.validate(value);