Skip to content

Commit e49fbb5

Browse files
authored
Merge pull request #610 from callumalpass/callumalpass/issue607
fix: add field mapping support for Bases views
2 parents 9870641 + 15c1c3f commit e49fbb5

File tree

4 files changed

+55
-23
lines changed

4 files changed

+55
-23
lines changed

src/bases/base-view-factory.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -118,7 +118,7 @@ export function buildTasknotesBaseViewFactory(plugin: TaskNotesPlugin, config: V
118118
}
119119

120120

121-
const taskNotes = await identifyTaskNotesFromBasesData(dataItems);
121+
const taskNotes = await identifyTaskNotesFromBasesData(dataItems, plugin);
122122

123123

124124
// Render body

src/bases/helpers.ts

Lines changed: 40 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -15,32 +15,21 @@ export interface BasesDataItem {
1515
/**
1616
* Create TaskInfo object from a single Bases data item
1717
*/
18-
export function createTaskInfoFromBasesData(basesItem: BasesDataItem): TaskInfo | null {
19-
if (!basesItem?.path) return null;
20-
21-
const props = basesItem.properties || basesItem.frontmatter || {};
22-
23-
// Separate known TaskNotes properties from custom properties
18+
function createTaskInfoFromProperties(props: Record<string, any>, basesItem: BasesDataItem): TaskInfo {
2419
const knownProperties = new Set([
25-
'title', 'status', 'priority', 'archived', 'due', 'scheduled', 'contexts',
26-
'projects', 'tags', 'timeEstimate', 'completedDate', 'recurrence',
20+
'title', 'status', 'priority', 'archived', 'due', 'scheduled', 'contexts',
21+
'projects', 'tags', 'timeEstimate', 'completedDate', 'recurrence',
2722
'dateCreated', 'dateModified', 'timeEntries', 'reminders', 'icsEventId'
2823
]);
2924

30-
// Extract custom properties from frontmatter
3125
const customProperties: Record<string, any> = {};
3226
Object.keys(props).forEach(key => {
3327
if (!knownProperties.has(key)) {
3428
customProperties[key] = props[key];
3529
}
3630
});
3731

38-
// Note: Bases formula properties are handled dynamically in the TaskCard rendering layer
39-
// since they require access to the Bases property computation system
40-
41-
42-
43-
const taskInfo: TaskInfo = {
32+
return {
4433
title: props.title || basesItem.name || basesItem.path!.split('/').pop()?.replace('.md', '') || 'Untitled',
4534
status: props.status || 'open',
4635
priority: props.priority || 'normal',
@@ -60,24 +49,40 @@ export function createTaskInfoFromBasesData(basesItem: BasesDataItem): TaskInfo
6049
reminders: props.reminders,
6150
icsEventId: props.icsEventId,
6251
customProperties: Object.keys(customProperties).length > 0 ? customProperties : undefined,
63-
basesData: basesItem.basesData // Pass raw Bases data for formula computation
52+
basesData: basesItem.basesData
6453
};
54+
}
6555

66-
return taskInfo;
56+
export function createTaskInfoFromBasesData(basesItem: BasesDataItem, plugin?: TaskNotesPlugin): TaskInfo | null {
57+
if (!basesItem?.path) return null;
58+
59+
const props = basesItem.properties || basesItem.frontmatter || {};
60+
61+
if (plugin?.fieldMapper) {
62+
const mappedTaskInfo = plugin.fieldMapper.mapFromFrontmatter(props, basesItem.path, plugin.settings.storeTitleInFilename);
63+
return {
64+
...createTaskInfoFromProperties(mappedTaskInfo, basesItem),
65+
customProperties: mappedTaskInfo.customProperties
66+
};
67+
} else {
68+
return createTaskInfoFromProperties(props, basesItem);
69+
}
6770
}
6871

6972
/**
7073
* Identify TaskNotes from Bases data by converting all items to TaskInfo
7174
*/
7275
export async function identifyTaskNotesFromBasesData(
7376
dataItems: BasesDataItem[],
74-
toTaskInfo: (item: BasesDataItem) => TaskInfo | null = createTaskInfoFromBasesData
77+
plugin?: TaskNotesPlugin,
78+
toTaskInfo?: (item: BasesDataItem, plugin?: TaskNotesPlugin) => TaskInfo | null
7579
): Promise<TaskInfo[]> {
80+
const taskInfoConverter = toTaskInfo || createTaskInfoFromBasesData;
7681
const taskNotes: TaskInfo[] = [];
7782
for (const item of dataItems) {
7883
if (!item?.path) continue;
7984
try {
80-
const taskInfo = toTaskInfo(item);
85+
const taskInfo = taskInfoConverter(item, plugin);
8186
if (taskInfo) taskNotes.push(taskInfo);
8287
} catch (error) {
8388
console.warn('[TaskNotes][BasesPOC] Error converting Bases item to TaskInfo:', error);
@@ -185,15 +190,29 @@ export async function renderTaskNotesInBasesView(
185190
// Map common property names to TaskNotes property names
186191
visibleProperties = visibleProperties.map(propId => {
187192
let mappedId = propId;
193+
194+
// First, try reverse field mapping for user's custom property names
195+
const internalFieldName = plugin.fieldMapper?.fromUserField(propId);
196+
if (internalFieldName) {
197+
// User has a custom field mapping for this property
198+
// Map it to the internal TaskNotes property name for proper rendering
199+
mappedId = internalFieldName;
200+
}
188201
// Handle dotted properties like task.due -> due
189-
if (propId.startsWith('task.')) {
202+
else if (propId.startsWith('task.')) {
190203
mappedId = propId.substring(5);
191204
}
192205
// Handle note properties like note.projects -> projects
193206
else if (propId.startsWith('note.')) {
194207
const stripped = propId.substring(5);
208+
209+
// Try reverse field mapping on the stripped property name
210+
const strippedInternalFieldName = plugin.fieldMapper?.fromUserField(stripped);
211+
if (strippedInternalFieldName) {
212+
mappedId = strippedInternalFieldName;
213+
}
195214
// Map specific note properties to TaskNotes property names
196-
if (stripped === 'dateCreated') mappedId = 'dateCreated';
215+
else if (stripped === 'dateCreated') mappedId = 'dateCreated';
197216
else if (stripped === 'dateModified') mappedId = 'dateModified';
198217
else if (stripped === 'completedDate') mappedId = 'completedDate';
199218
else mappedId = stripped; // projects, contexts, tags, and any other arbitrary properties

src/bases/kanban-view.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -92,7 +92,7 @@ export function buildTasknotesKanbanViewFactory(plugin: TaskNotesPlugin) {
9292

9393
try {
9494
const dataItems = extractDataItems();
95-
const taskNotes = await identifyTaskNotesFromBasesData(dataItems);
95+
const taskNotes = await identifyTaskNotesFromBasesData(dataItems, plugin);
9696

9797
// Clear board
9898
board.innerHTML = '';

src/services/FieldMapper.ts

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -272,6 +272,19 @@ export class FieldMapper {
272272
return { ...this.mapping };
273273
}
274274

275+
/**
276+
* Convert user's property name back to internal field name
277+
* This is the reverse of toUserField()
278+
*/
279+
fromUserField(userPropertyName: string): keyof FieldMapping | null {
280+
for (const [internalName, userName] of Object.entries(this.mapping)) {
281+
if (userName === userPropertyName) {
282+
return internalName as keyof FieldMapping;
283+
}
284+
}
285+
return null;
286+
}
287+
275288
/**
276289
* Validate that a mapping has no empty field names
277290
*/

0 commit comments

Comments
 (0)