diff --git a/plugins/notion/package.json b/plugins/notion/package.json index 75123dd5a..b668041bd 100644 --- a/plugins/notion/package.json +++ b/plugins/notion/package.json @@ -14,7 +14,7 @@ }, "dependencies": { "@notionhq/client": "^3.1.3", - "framer-plugin": "^3.6.0", + "framer-plugin": "3.10.0-alpha.2", "react": "^18.3.1", "react-dom": "^18.3.1", "valibot": "^1.2.0" diff --git a/plugins/notion/src/FieldMapping.tsx b/plugins/notion/src/FieldMapping.tsx index 4b38baf37..9ee2315d9 100644 --- a/plugins/notion/src/FieldMapping.tsx +++ b/plugins/notion/src/FieldMapping.tsx @@ -13,6 +13,7 @@ import { getDatabaseFieldsInfo, getPossibleSlugFieldIds, isMissingCollection, + type VirtualFieldType, } from "./api" import { type DatabaseIdMap, @@ -25,9 +26,10 @@ import { } from "./data" import { assert, syncMethods } from "./utils" -const labelByFieldTypeOption: Record = { +const labelByFieldTypeOption: Record = { boolean: "Toggle", date: "Date", + dateTime: "Date & Time", number: "Number", formattedText: "Formatted Text", color: "Color", @@ -49,7 +51,7 @@ interface FieldMappingRowProps { missingCollection: boolean onToggleIgnored: (fieldId: string) => void onNameChange: (fieldId: string, name: string) => void - onFieldTypeChange: (fieldId: string, type: ManagedCollectionField["type"]) => void + onFieldTypeChange: (fieldId: string, type: VirtualFieldType) => void } function FieldMappingRow({ @@ -211,7 +213,7 @@ export function FieldMapping({ }) } - const changeFieldType = (fieldId: string, type: ManagedCollectionField["type"]) => { + const changeFieldType = (fieldId: string, type: VirtualFieldType) => { setFieldsInfo(prevFieldsInfo => { const updatedFieldInfo = prevFieldsInfo.map(fieldInfo => { if (fieldInfo.id !== fieldId) return fieldInfo diff --git a/plugins/notion/src/api.ts b/plugins/notion/src/api.ts index f3c8d3062..377c3414f 100644 --- a/plugins/notion/src/api.ts +++ b/plugins/notion/src/api.ts @@ -27,12 +27,14 @@ const LAST_CONTENT_IMPORTING_UPDATE_DATE = new Date("2025-07-01T12:00:00.000Z") export type FieldId = string +export type VirtualFieldType = ManagedCollectionField["type"] | "dateTime" + export interface FieldInfo { id: FieldId name: string originalName: string - type: ManagedCollectionField["type"] | null - allowedTypes: ManagedCollectionField["type"][] + type: VirtualFieldType | null + allowedTypes: VirtualFieldType[] notionProperty: NotionProperty | null } @@ -66,12 +68,12 @@ const slugFieldTypes: NotionProperty["type"][] = ["title", "rich_text", "unique_ export const supportedCMSTypeByNotionPropertyType = { checkbox: ["boolean"], - date: ["date"], + date: ["dateTime", "date"], number: ["number"], title: ["string"], rich_text: ["formattedText", "string", "color"], - created_time: ["date"], - last_edited_time: ["date"], + created_time: ["dateTime", "date"], + last_edited_time: ["dateTime", "date"], select: ["enum"], status: ["enum"], url: ["link"], @@ -80,8 +82,8 @@ export const supportedCMSTypeByNotionPropertyType = { files: ["file", "image", "array"], relation: ["multiCollectionReference", "collectionReference"], unique_id: ["string", "number"], - formula: ["string", "number", "boolean", "date", "link", "color"], -} satisfies Partial> + formula: ["string", "number", "boolean", "date", "dateTime", "link", "color"], +} satisfies Partial> // Naive implementation to be authenticated, a token could be expired. // For simplicity we just close the plugin and clear storage in that case. @@ -216,15 +218,18 @@ export async function getNotionDatabases() { export function assertFieldTypeMatchesPropertyType( propertyType: NotionProperty["type"], - fieldType: ManagedCollectionField["type"] -): asserts fieldType is ManagedCollectionField["type"] { + fieldType: VirtualFieldType +): void { if (!isSupportedPropertyType(propertyType)) { throw new Error(`Property type '${propertyType}' is not supported.`) } const allowedFieldTypes = supportedCMSTypeByNotionPropertyType[propertyType] - if (!allowedFieldTypes.includes(fieldType as never)) { + // For dateTime, treat it as "date" for validation purposes + const typeToCheck = fieldType === "dateTime" ? "date" : fieldType + + if (!allowedFieldTypes.includes(typeToCheck as never)) { throw new Error(`Field type '${fieldType}' is not valid for property type '${propertyType}'.`) } } diff --git a/plugins/notion/src/data.ts b/plugins/notion/src/data.ts index fbb782929..493c0c9cb 100644 --- a/plugins/notion/src/data.ts +++ b/plugins/notion/src/data.ts @@ -75,8 +75,16 @@ export function mergeFieldsInfoWithExistingFields( ): FieldInfo[] { return sourceFieldsInfo.map(sourceFieldInfo => { const existingField = existingFields.find(existingField => existingField.id === sourceFieldInfo.id) - if (existingField && sourceFieldInfo.allowedTypes.includes(existingField.type)) { - return { ...sourceFieldInfo, name: existingField.name, type: existingField.type } + if (existingField) { + // Handle date fields with displayTime: convert to dateTime virtual type + let fieldType: FieldInfo["type"] = existingField.type + if (existingField.type === "date" && "displayTime" in existingField && existingField.displayTime === true) { + fieldType = "dateTime" + } + + if (sourceFieldInfo.allowedTypes.includes(fieldType)) { + return { ...sourceFieldInfo, name: existingField.name, type: fieldType } + } } return sourceFieldInfo }) @@ -433,7 +441,6 @@ export function fieldsInfoToCollectionFields( switch (fieldType) { case "boolean": - case "date": case "number": case "string": case "formattedText": @@ -449,6 +456,28 @@ export function fieldsInfoToCollectionFields( }) break } + case "date": { + assertFieldTypeMatchesPropertyType(property.type, fieldType) + fields.push({ + type: "date", + id: fieldInfo.id, + name: fieldName, + userEditable: false, + displayTime: false, + }) + break + } + case "dateTime": { + assertFieldTypeMatchesPropertyType(property.type, "date") + fields.push({ + type: "date", + id: fieldInfo.id, + name: fieldName, + userEditable: false, + displayTime: true, + }) + break + } case "enum": { assertFieldTypeMatchesPropertyType(property.type, fieldType) diff --git a/yarn.lock b/yarn.lock index 98251554a..eaf2a44eb 100644 --- a/yarn.lock +++ b/yarn.lock @@ -5661,7 +5661,7 @@ __metadata: "@notionhq/client": "npm:^3.1.3" "@types/react": "npm:^18.3.24" "@types/react-dom": "npm:^18.3.7" - framer-plugin: "npm:^3.6.0" + framer-plugin: "npm:3.10.0-alpha.2" framer-plugin-tools: "npm:^1.0.0" react: "npm:^18.3.1" react-dom: "npm:^18.3.1"