Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion API-CONTRACT.md
Original file line number Diff line number Diff line change
Expand Up @@ -393,7 +393,7 @@ Lists all tags in the campaign.
#### POST /tags
Create a new tag.

**Used by:** `import-wizard.mjs` → Step 8 tag creation during import
**Used by:** `import-wizard.mjs` → Step 7 (Review) tag creation during import

**Request:**
```json
Expand Down
6 changes: 0 additions & 6 deletions lang/en.json
Original file line number Diff line number Diff line change
Expand Up @@ -359,7 +359,6 @@
"Tags": "Tags",
"Characters": "Characters",
"Calendar": "Calendar",
"Maps": "Maps",
"Review": "Review"
},
"ConnectDesc": "Verifying connection to Chronicle and detecting your campaign configuration.",
Expand Down Expand Up @@ -403,11 +402,6 @@
"CalendarPull": "Pull Chronicle calendar into Foundry",
"CalendarPush": "Push Foundry calendar to Chronicle",
"CalendarSkip": "Skip calendar sync for now",
"MapsDesc": "Link Foundry scenes to Chronicle maps. Scenes with matching names are auto-suggested.",
"FoundryScene": "Foundry Scene",
"ChronicleMap": "Chronicle Map",
"NoLink": "No link",
"NoScenes": "No unlinked scenes found.",
"ReviewSummary": "The following operations will be performed:",
"Errors": "errors",
"NoPlan": "No import plan generated — go back and configure at least one step.",
Expand Down
67 changes: 3 additions & 64 deletions scripts/import-wizard.mjs
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,6 @@ const STEPS = Object.freeze([
{ key: 'tags', labelKey: 'CHRONICLE.Wizard.Steps.Tags', icon: 'fa-solid fa-tags' },
{ key: 'characters', labelKey: 'CHRONICLE.Wizard.Steps.Characters', icon: 'fa-solid fa-users' },
{ key: 'calendar', labelKey: 'CHRONICLE.Wizard.Steps.Calendar', icon: 'fa-solid fa-calendar' },
{ key: 'maps', labelKey: 'CHRONICLE.Wizard.Steps.Maps', icon: 'fa-solid fa-map' },
{ key: 'review', labelKey: 'CHRONICLE.Wizard.Steps.Review', icon: 'fa-solid fa-clipboard-check' },
]);

Expand Down Expand Up @@ -153,13 +152,11 @@ export class ImportWizard extends HandlebarsApplicationMixin(ApplicationV2) {
/** Step 6: calendar sync direction choice. */
this._calendarDirection = 'skip';

/** Step 7: scene ↔ Chronicle map link plan. */
this._mapLinkPlan = [];

/** Step 8: aggregated import plan built from all previous steps. */
/** Step 7: aggregated import plan built from all previous steps. */
this._importPlan = null;

/** Step 8: live import progress tracking. */
/** Step 7: live import progress tracking. */
this._importProgress = { total: 0, done: 0, errors: 0, log: [] };

/** Whether an import is currently running. */
Expand Down Expand Up @@ -216,11 +213,6 @@ export class ImportWizard extends HandlebarsApplicationMixin(ApplicationV2) {
const calendarAddon = addonSlugs.has('calendar') || addonSlugs.has('calendars');
enabled = calendarAddon && hasCalendarModule;
}
// Maps step: only if maps addon is enabled.
if (step.key === 'maps') {
const mapsAddon = addonSlugs.has('maps') || addonSlugs.has('map');
enabled = mapsAddon;
}
return { ...step, index, enabled };
});
}
Expand Down Expand Up @@ -465,7 +457,7 @@ export class ImportWizard extends HandlebarsApplicationMixin(ApplicationV2) {
}

// ---------------------------------------------------------------------------
// Step 8: Review & Import
// Step 7: Review & Import
// ---------------------------------------------------------------------------

/**
Expand Down Expand Up @@ -508,16 +500,6 @@ export class ImportWizard extends HandlebarsApplicationMixin(ApplicationV2) {
});
}

// Map links (Step 7).
for (const link of this._mapLinkPlan.filter((l) => l.chronicleMapId)) {
plan.push({
type: 'link-map',
label: `Link scene "${link.sceneName}" to Chronicle map`,
data: { sceneId: link.sceneId, mapId: link.chronicleMapId },
status: 'pending',
});
}

this._importPlan = plan;
this._importProgress = { total: plan.length, done: 0, errors: 0, log: [] };
return plan;
Expand Down Expand Up @@ -605,7 +587,6 @@ export class ImportWizard extends HandlebarsApplicationMixin(ApplicationV2) {
tagPlan: this._tagPlan,
characterPlan: this._characterPlan,
calendarDirection: this._calendarDirection,
mapLinkPlan: this._mapLinkPlan,
importPlan: this._importPlan,
importProgress: this._importProgress,
importRunning: this._importRunning,
Expand Down Expand Up @@ -682,16 +663,6 @@ export class ImportWizard extends HandlebarsApplicationMixin(ApplicationV2) {
this._calendarDirection = e.target.value;
});
}

// Step 7: map link selects.
for (const select of el.querySelectorAll('.wizard-map-select')) {
select.addEventListener('change', (e) => {
const idx = Number(e.target.dataset.mapIndex);
if (this._mapLinkPlan[idx]) {
this._mapLinkPlan[idx].chronicleMapId = e.target.value || null;
}
});
}
}

// ---------------------------------------------------------------------------
Expand Down Expand Up @@ -759,9 +730,6 @@ export class ImportWizard extends HandlebarsApplicationMixin(ApplicationV2) {
case 'characters':
if (this._characterPlan.length === 0) this._buildCharacterPlan();
break;
case 'maps':
if (this._mapLinkPlan.length === 0) await this._buildMapLinkPlan();
break;
case 'review':
this._buildImportPlan();
break;
Expand All @@ -783,35 +751,6 @@ export class ImportWizard extends HandlebarsApplicationMixin(ApplicationV2) {
}));
}

/**
* Build the map linking plan by matching Foundry scenes with Chronicle maps.
*/
async _buildMapLinkPlan() {
if (!this._worldScan || !this.api) return;

let chronicleMaps = [];
try {
const result = await this.api.get('/maps');
chronicleMaps = Array.isArray(result) ? result : (result?.data ?? result?.maps ?? []);
} catch {
// Maps endpoint not available.
}

this._mapLinkPlan = this._worldScan.scenes.map((scene) => {
// Auto-match by name.
const nameLower = scene.name.toLowerCase();
const match = chronicleMaps.find(
(m) => (m.name || '').toLowerCase() === nameLower
);
return {
sceneId: scene.id,
sceneName: scene.name,
chronicleMapId: match?.id ?? null,
chronicleMaps,
};
});
}

// ---------------------------------------------------------------------------
// Action handlers
// ---------------------------------------------------------------------------
Expand Down
25 changes: 0 additions & 25 deletions scripts/sync-manager.mjs
Original file line number Diff line number Diff line change
Expand Up @@ -703,17 +703,6 @@ export class SyncManager {
this._activityLog = [];
}

/**
* Create or update a sync mapping on the server. Delegates to
* `ensureMapping` so legacy callers (wizard `link-map`, dashboard
* manual-sync) inherit conflict-tolerant idempotency.
* @param {object} mapping
* @returns {Promise<object>}
*/
async createMapping(mapping) {
return this.ensureMapping(mapping);
}

/**
* Idempotent sync-mapping POST.
*
Expand Down Expand Up @@ -865,20 +854,6 @@ export class SyncManager {
await actorSync._handleCreateActor(actor, {}, game.user.id);
break;
}
case 'link-map': {
const scene = game.scenes.get(item.data.sceneId);
if (!scene) throw new Error(`Scene ${item.data.sceneId} not found`);
await scene.setFlag('chronicle-sync', 'mapId', item.data.mapId);
await this.createMapping({
chronicle_type: 'map',
chronicle_id: item.data.mapId,
external_system: 'foundry',
external_id: item.data.sceneId,
sync_direction: 'both',
sync_metadata: { foundry_type: 'Scene' },
});
break;
}
case 'assign-tags': {
await this.api.bulkAssignTags(item.data);
break;
Expand Down
6 changes: 2 additions & 4 deletions styles/import-wizard.css
Original file line number Diff line number Diff line change
Expand Up @@ -311,8 +311,7 @@
text-align: center;
}

.wizard-type-select,
.wizard-map-select {
.wizard-type-select {
width: 100%;
padding: 4px 6px;
font-size: 12px;
Expand All @@ -322,8 +321,7 @@
color: inherit;
}

.wizard-type-select:focus,
.wizard-map-select:focus {
.wizard-type-select:focus {
border-color: #60a5fa;
outline: none;
}
Expand Down
38 changes: 1 addition & 37 deletions templates/import-wizard.hbs
Original file line number Diff line number Diff line change
Expand Up @@ -270,45 +270,9 @@
</div>

{{!-- ================================================================= --}}
{{!-- STEP 7: Map Linking --}}
{{!-- STEP 7: Review & Import --}}
{{!-- ================================================================= --}}
<div class="wizard-step" data-step="6">
<h2 class="wizard-step-title"><i class="fa-solid fa-map"></i> {{localize "CHRONICLE.Wizard.Steps.Maps"}}</h2>
<p class="wizard-step-desc">{{localize "CHRONICLE.Wizard.MapsDesc"}}</p>

{{#if mapLinkPlan.length}}
<table class="wizard-mapping-table">
<thead>
<tr>
<th>{{localize "CHRONICLE.Wizard.FoundryScene"}}</th>
<th>{{localize "CHRONICLE.Wizard.ChronicleMap"}}</th>
</tr>
</thead>
<tbody>
{{#each mapLinkPlan}}
<tr>
<td><i class="fa-solid fa-image"></i> {{sceneName}}</td>
<td>
<select class="wizard-map-select" data-map-index="{{@index}}">
<option value="">— {{localize "CHRONICLE.Wizard.NoLink"}} —</option>
{{#each chronicleMaps}}
<option value="{{id}}" {{#if (eq id ../chronicleMapId)}}selected{{/if}}>{{name}}</option>
{{/each}}
</select>
</td>
</tr>
{{/each}}
</tbody>
</table>
{{else}}
<div class="wizard-empty">{{localize "CHRONICLE.Wizard.NoScenes"}}</div>
{{/if}}
</div>

{{!-- ================================================================= --}}
{{!-- STEP 8: Review & Import --}}
{{!-- ================================================================= --}}
<div class="wizard-step" data-step="7">
<h2 class="wizard-step-title"><i class="fa-solid fa-clipboard-check"></i> {{localize "CHRONICLE.Wizard.Steps.Review"}}</h2>

{{#if importRunning}}
Expand Down
31 changes: 31 additions & 0 deletions tools/test-import-wizard-steps.mjs
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
#!/usr/bin/env node
/**
* The import wizard's step list and its template panels must line up: a
* panel shows when its data-step equals the current STEPS index. Also pins
* that the scene-to-map link step stays gone: Chronicle maps materialize as
* journal pages on connect, so linking scenes did nothing.
*/

import test from 'node:test';
import assert from 'node:assert/strict';
import { readFile } from 'node:fs/promises';

const src = await readFile(new URL('../scripts/import-wizard.mjs', import.meta.url), 'utf8');
const hbs = await readFile(new URL('../templates/import-wizard.hbs', import.meta.url), 'utf8');

const stepKeys = [...src.matchAll(/\{\s*key:\s*'([a-z]+)',\s*labelKey:\s*'CHRONICLE\.Wizard\.Steps\./g)].map((m) => m[1]);

test('step list has no maps step and ends with review', () => {
assert.deepEqual(stepKeys, ['connect', 'scan', 'types', 'tags', 'characters', 'calendar', 'review']);
});

test('template has one panel per step, numbered 0..n-1', () => {
const panels = [...hbs.matchAll(/class="wizard-step" data-step="(\d+)"/g)].map((m) => Number(m[1]));
assert.deepEqual(panels, stepKeys.map((_, i) => i));
});

test('no scene-to-map linking is left in the wizard or the import runner', async () => {
const manager = await readFile(new URL('../scripts/sync-manager.mjs', import.meta.url), 'utf8');
assert.ok(!/link-map/.test(src + hbs + manager));
assert.ok(!/wizard-map-select|mapLinkPlan/.test(src + hbs));
});
Loading