+ {/* Fields */}
+
+ {FORM_METADATA_ENTRIES(
+ sortTagsObjectWithoutFavs(uniqueTags ?? [])
+ .map(({ tag, count }) => ({
+ value: tag,
+ annotation: formatCount(count),
+ annotationAria: formatCountDescriptive(count, 'tagged'),
+ })),
+ aiContent !== undefined,
+ )
+ .map(([key, {
+ label,
+ note,
+ required,
+ selectOptions,
+ selectOptionsDefaultLabel,
+ tagOptions,
+ readOnly,
+ validate,
+ validateStringMaxLength,
+ capitalize,
+ hideIfEmpty,
+ shouldHide,
+ loadingMessage,
+ type,
+ }]) =>
+ (
+ (!hideIfEmpty || formData[key]) &&
+ !shouldHide?.(formData)
+ ) &&
+ {
+ const formUpdated = { ...formData, [key]: value };
+ setFormData(formUpdated);
+ if (validate) {
+ setFormErrors({ ...formErrors, [key]: validate(value) });
+ } else if (validateStringMaxLength !== undefined) {
+ setFormErrors({
+ ...formErrors,
+ [key]: value.length > validateStringMaxLength
+ ? `${validateStringMaxLength} characters or less`
+ : undefined,
+ });
+ }
+ if (key === 'title') {
+ onTitleChange?.(value.trim());
+ }
+ }}
+ selectOptions={selectOptions}
+ selectOptionsDefaultLabel={selectOptionsDefaultLabel}
+ tagOptions={tagOptions}
+ required={required}
+ readOnly={readOnly}
+ capitalize={capitalize}
+ placeholder={loadingMessage && !formData[key]
+ ? loadingMessage
+ : undefined}
+ loading={
+ (loadingMessage && !formData[key] ? true : false) ||
+ isFieldGeneratingAi(key)}
+ type={type}
+ accessory={aiButtonForField(key)}
+ />)}
+
+ {/* Actions */}
+
{type === 'create' ? 'Create' : 'Update'}
+
diff --git a/src/photo/form/index.ts b/src/photo/form/index.ts
index 63ab8698..5c772333 100644
--- a/src/photo/form/index.ts
+++ b/src/photo/form/index.ts
@@ -3,6 +3,8 @@ import { Photo, PhotoDbInsert, PhotoExif } from '..';
import {
convertTimestampToNaivePostgresString,
convertTimestampWithOffsetToPostgresString,
+ generateLocalNaivePostgresString,
+ generateLocalPostgresString,
} from '@/utility/date';
import { getAspectRatioFromExif, getOffsetFromExif } from '@/utility/exif';
import { toFixedNumber } from '@/utility/number';
@@ -284,5 +286,25 @@ export const convertFormDataToPhotoDbInsert = (
? parseFloat(photoForm.priorityOrder)
: undefined,
hidden: photoForm.hidden === 'true',
+ ...generateTakenAtFields(photoForm),
};
};
+
+export const getChangedFormFields = (
+ original: Partial