feat: notes de bord et sauvegarde position calendrier

- bouton crayon sur la fenêtre calendrier (MJ) → dialogue de note
- date impériale + date réelle préremplies
- append au journal configuré (créé auto si inexistant)
- appendJournalNote : labels passés depuis l'ouverture (pas de re-lecture)
- échappement XSS via jQuery .text().html()
- onSave async + await + try/catch
- sauvegarde position calendrier (client scope, debounced 300ms)
- restore position au constructeur
- close() async avec await settings.set
- boutons note et config groupés dans .mgt2-cal-actions (flex row)
This commit is contained in:
2026-07-25 17:08:54 +02:00
parent 8882cf8ee2
commit 47442be3ef
7 changed files with 282 additions and 6 deletions
+67 -1
View File
@@ -1,6 +1,7 @@
import { CalendarApp } from './CalendarApp.js';
import { CalendarConfigDialog } from './CalendarConfigDialog.js';
import { getDefaultCalendar } from './calendarDate.js';
import { JournalNoteDialog } from './JournalNoteDialog.js';
import { getDefaultCalendar, formatImperialDate, formatRealDate } from './calendarDate.js';
const MODULE_ID = 'mgt2-compendium-amiral-denisov';
const ChatLogV2 = foundry.applications.sidebar.tabs.ChatLog;
@@ -47,6 +48,7 @@ function openCalendarApp() {
const data = readCalendar();
_calendarApp = new CalendarApp({
onConfig: () => openCalendarConfig(),
onNote: () => openJournalNote(),
onClose: () => { _calendarApp = null; },
});
_calendarApp.updateDisplay(data);
@@ -67,6 +69,55 @@ function openCalendarConfig() {
dialog.render({ force: true });
}
async function appendJournalNote(content, { dateLabel, realLabel } = {}) {
if (!content) return false;
const journalName = game.settings.get(MODULE_ID, 'calendarJournalName') || 'Journal de bord';
let journal = game.journal.getName(journalName);
if (!journal) {
journal = await JournalEntry.create({ name: journalName });
if (!journal) {
ui.notifications.error('Impossible de créer le journal');
return false;
}
}
if (!dateLabel || !realLabel) {
const c = readCalendar();
dateLabel = formatImperialDate(c);
realLabel = formatRealDate();
}
const safeContent = $('<div>').text(content).html().replace(/\n/g, '<br>');
const noteHtml = `<hr>
<h3>${realLabel}${dateLabel}</h3>
<p>${safeContent}</p>`;
let page = journal.pages.contents[0];
if (!page) {
[page] = await journal.createEmbeddedDocuments('JournalEntryPage', [{ name: 'Notes', text: { content: '' } }]);
}
if (!page) {
ui.notifications.error('Impossible de créer une page dans le journal');
return false;
}
const existing = page.text?.content || '';
await page.update({ 'text.content': existing + '\n' + noteHtml });
ui.notifications.info(`Note ajoutée au journal « ${journalName} »`);
return true;
}
function openJournalNote() {
const calendar = readCalendar();
const dateLabel = formatImperialDate(calendar);
const realLabel = formatRealDate();
const dialog = new JournalNoteDialog({
imperialDate: dateLabel,
realDate: realLabel,
onSave: async (content) => {
const ok = await appendJournalNote(content, { dateLabel, realLabel });
if (ok) dialog.close();
},
});
dialog.render({ force: true });
}
function registerCalCommand() {
if (!ChatLogV2?.CHAT_COMMANDS) return;
ChatLogV2.CHAT_COMMANDS.cal = {
@@ -125,12 +176,27 @@ Hooks.once('init', () => {
type: Boolean,
default: true,
});
game.settings.register(MODULE_ID, 'calendarJournalName', {
name: 'Journal de bord',
hint: 'Nom du journal utilisé pour les notes de session. Créé automatiquement s\'il n\'existe pas.',
scope: 'world',
config: true,
type: String,
default: 'Journal de bord',
});
game.settings.register(MODULE_ID, 'calendarPosition', {
scope: 'client',
config: false,
type: Object,
default: {},
});
const loadTemplatesFn = foundry.applications?.handlebars?.loadTemplates || loadTemplates;
if (loadTemplatesFn) {
loadTemplatesFn([
`modules/${MODULE_ID}/templates/calendar-app.hbs`,
`modules/${MODULE_ID}/templates/calendar-config.hbs`,
`modules/${MODULE_ID}/templates/journal-note-dialog.hbs`,
]);
}