diff --git a/scripts/CalendarApp.js b/scripts/CalendarApp.js index b07a00c..71918d6 100644 --- a/scripts/CalendarApp.js +++ b/scripts/CalendarApp.js @@ -22,10 +22,16 @@ export class CalendarApp extends HandlebarsApplicationMixin(ApplicationV2) { }, }; - constructor({ onConfig, onClose } = {}) { + constructor({ onConfig, onClose, onNote } = {}) { super(); + const saved = game.settings?.get(MODULE_ID, 'calendarPosition'); + if (saved?.left != null && saved?.top != null) { + this.position.left = saved.left; + this.position.top = saved.top; + } this._onConfig = onConfig; this._onClose = onClose; + this._onNote = onNote; this._data = { year: 1116, day: 1, hour: 12, minute: 0, planet: '' }; } @@ -46,6 +52,26 @@ export class CalendarApp extends HandlebarsApplicationMixin(ApplicationV2) { html.find('.mgt2-cal-config-btn').on('click', () => { this._onConfig?.(); }); + html.find('.mgt2-cal-note-btn').on('click', () => { + this._onNote?.(); + }); + } + + setPosition(position) { + const result = super.setPosition(position); + this._debouncedSavePos(); + return result; + } + + _debouncedSavePos() { + if (this._savePosTimeout) clearTimeout(this._savePosTimeout); + this._savePosTimeout = setTimeout(() => { + game.settings?.set(MODULE_ID, 'calendarPosition', { + left: this.position.left, + top: this.position.top, + }); + this._savePosTimeout = null; + }, 300); } updateDisplay(data) { @@ -53,8 +79,15 @@ export class CalendarApp extends HandlebarsApplicationMixin(ApplicationV2) { this.render(); } - close() { + async close() { + if (game.settings) { + await game.settings.set(MODULE_ID, 'calendarPosition', { + left: this.position.left, + top: this.position.top, + }); + } this._onConfig = null; + this._onNote = null; this._onClose?.(); this._onClose = null; return super.close(); diff --git a/scripts/JournalNoteDialog.js b/scripts/JournalNoteDialog.js new file mode 100644 index 0000000..7004220 --- /dev/null +++ b/scripts/JournalNoteDialog.js @@ -0,0 +1,59 @@ +const { ApplicationV2, HandlebarsApplicationMixin } = foundry.applications.api; +const MODULE_ID = 'mgt2-compendium-amiral-denisov'; + +export class JournalNoteDialog extends HandlebarsApplicationMixin(ApplicationV2) { + static DEFAULT_OPTIONS = { + id: 'mgt2-journal-note', + classes: ['mgt2-calendar'], + position: { width: 320, height: 'auto' }, + window: { + icon: 'fas fa-pen', + title: 'Note de bord', + resizable: false, + }, + }; + + static PARTS = { + main: { + template: `modules/${MODULE_ID}/templates/journal-note-dialog.hbs`, + root: true, + }, + }; + + constructor({ imperialDate, realDate, onSave } = {}) { + super(); + this._imperialDate = imperialDate; + this._realDate = realDate; + this._onSave = onSave; + } + + get title() { + return 'Note de bord'; + } + + async _prepareContext() { + return { + imperialDate: this._imperialDate, + realDate: this._realDate, + }; + } + + async _onRender(context, options) { + await super._onRender(context, options); + const html = $(this.element); + + html.find('[data-action="save"]').on('click', async () => { + const content = html.find('[name="content"]').val()?.trim(); + try { + await this._onSave?.(content); + } catch (err) { + console.error(`${MODULE_ID} | Erreur sauvegarde note:`, err); + } + }); + } + + close() { + this._onSave = null; + return super.close(); + } +} \ No newline at end of file diff --git a/scripts/calendarDate.js b/scripts/calendarDate.js index 5173245..db3049e 100644 --- a/scripts/calendarDate.js +++ b/scripts/calendarDate.js @@ -93,3 +93,13 @@ export function formatImperialDate({ year, day, hour, minute, planet } = {}) { export function getDefaultCalendar() { return { year: 1116, day: 1, hour: 12, minute: 0, planet: '' }; } + +export function formatRealDate() { + const d = new Date(); + const y = d.getFullYear(); + const m = String(d.getMonth() + 1).padStart(2, '0'); + const day = String(d.getDate()).padStart(2, '0'); + const h = String(d.getHours()).padStart(2, '0'); + const min = String(d.getMinutes()).padStart(2, '0'); + return `${day}/${m}/${y} ${h}:${min}`; +} diff --git a/scripts/imperialCalendar.js b/scripts/imperialCalendar.js index f41a3cc..5b4ef1f 100644 --- a/scripts/imperialCalendar.js +++ b/scripts/imperialCalendar.js @@ -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 = $('
').text(content).html().replace(/\n/g, '
'); + const noteHtml = `
+

${realLabel} — ${dateLabel}

+

${safeContent}

`; + 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`, ]); } diff --git a/styles/npc.css b/styles/npc.css index 340d781..e159492 100644 --- a/styles/npc.css +++ b/styles/npc.css @@ -1193,6 +1193,14 @@ a.mgt2-world-link:hover { flex: 1; } +.mgt2-cal-actions { + display: flex; + flex-direction: row; + align-items: center; + gap: 2px; + flex: 0 0 auto; +} + .mgt2-cal-date { color: #d8c79a; font-size: 0.95em; @@ -1352,3 +1360,79 @@ a.mgt2-world-link:hover { .mgt2-cal-conv-label { color: #8a7a5a; } + +/* Note button on calendar app */ + +.mgt2-cal-note-btn { + flex: 0 0 auto; + width: 22px; + height: 22px; + padding: 0; + border: none; + background: transparent; + color: #8a7a5a; + cursor: pointer; + display: flex; + align-items: center; + justify-content: center; + font-size: 0.85em; + border-radius: 3px; + transition: color 0.15s, background 0.15s; +} + +.mgt2-cal-note-btn:hover { + color: #d9b24c; + background: rgba(201, 162, 39, 0.15); +} + +/* Note dialog */ + +.mgt2-note-form { + display: flex; + flex-direction: column; + gap: 8px; + padding: 8px; + background: #1a1a2e; + color: #d8c79a; +} + +.mgt2-note-row { + display: flex; + flex-direction: column; + gap: 2px; +} + +.mgt2-note-row label { + font-size: 0.8em; + color: #8a7a5a; + text-transform: uppercase; + letter-spacing: 0.5px; +} + +.mgt2-note-value { + font-size: 0.9em; + color: #d8c79a; +} + +.mgt2-note-row textarea { + width: 100%; + padding: 6px; + border: 1px solid #3a3a4e; + border-radius: 3px; + background: #252540; + color: #d8c79a; + font-size: 0.85em; + font-family: inherit; + resize: vertical; +} + +.mgt2-note-row textarea:focus { + outline: none; + border-color: #c9a227; +} + +.mgt2-note-actions { + display: flex; + justify-content: flex-end; + margin-top: 4px; +} diff --git a/templates/calendar-app.hbs b/templates/calendar-app.hbs index 375a4f8..697d5bf 100644 --- a/templates/calendar-app.hbs +++ b/templates/calendar-app.hbs @@ -3,8 +3,13 @@ {{dateLabel}}
{{#if isGM}} - +
+ + +
{{/if}} diff --git a/templates/journal-note-dialog.hbs b/templates/journal-note-dialog.hbs new file mode 100644 index 0000000..fce11cd --- /dev/null +++ b/templates/journal-note-dialog.hbs @@ -0,0 +1,19 @@ +
+
+ + {{imperialDate}} +
+
+ + {{realDate}} +
+
+ + +
+
+ +
+
\ No newline at end of file