add quest comparison page
This commit is contained in:
parent
cf903daa71
commit
e6b1d1b09b
5 changed files with 302 additions and 1 deletions
|
|
@ -106,6 +106,13 @@ export class Armory {
|
||||||
},
|
},
|
||||||
hasInProgressQuests: function(quests: any[]) {
|
hasInProgressQuests: function(quests: any[]) {
|
||||||
return quests.some(quest => quest.status === 'In Progress');
|
return quests.some(quest => quest.status === 'In Progress');
|
||||||
|
},
|
||||||
|
getDiffClass: function(quest: any) {
|
||||||
|
if (!quest.char1Status && !quest.char2Status) return '';
|
||||||
|
if (!quest.char1Status) return 'quest-missing-1';
|
||||||
|
if (!quest.char2Status) return 'quest-missing-2';
|
||||||
|
if (quest.char1Status !== quest.char2Status) return 'quest-diff';
|
||||||
|
return '';
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
}),
|
}),
|
||||||
|
|
@ -162,6 +169,7 @@ export class Armory {
|
||||||
app.get("/character/:realm/:name/pvp", this.wrapRoute(charsController.pvp.bind(charsController)));
|
app.get("/character/:realm/:name/pvp", this.wrapRoute(charsController.pvp.bind(charsController)));
|
||||||
app.get("/character/:realm/:name/reputation", this.wrapRoute(charsController.reputation.bind(charsController)));
|
app.get("/character/:realm/:name/reputation", this.wrapRoute(charsController.reputation.bind(charsController)));
|
||||||
app.get("/character/:realm/:name/quests", this.wrapRoute(charsController.quests.bind(charsController)));
|
app.get("/character/:realm/:name/quests", this.wrapRoute(charsController.quests.bind(charsController)));
|
||||||
|
app.get("/character/:realm/:name/quests/compare/:otherRealm/:otherName", this.wrapRoute(charsController.questsCompare.bind(charsController)));
|
||||||
|
|
||||||
const guildsController = new GuildController(this);
|
const guildsController = new GuildController(this);
|
||||||
app.get("/guild/:realm/:name", this.wrapRoute(guildsController.guild.bind(guildsController)));
|
app.get("/guild/:realm/:name", this.wrapRoute(guildsController.guild.bind(guildsController)));
|
||||||
|
|
|
||||||
|
|
@ -392,11 +392,86 @@ export class CharacterController {
|
||||||
return acc;
|
return acc;
|
||||||
}, {});
|
}, {});
|
||||||
|
|
||||||
|
// Get list of all characters
|
||||||
|
const allCharacters = await this.getAllCharacters(realm.name);
|
||||||
|
|
||||||
res.render("character-quests.hbs", {
|
res.render("character-quests.hbs", {
|
||||||
title: `Armory - ${charData.name} - Quests`,
|
title: `Armory - ${charData.name} - Quests`,
|
||||||
...this.makeSharedDataObject(realm, charData),
|
...this.makeSharedDataObject(realm, charData),
|
||||||
data: {
|
data: {
|
||||||
categories: questsByCategory
|
categories: questsByCategory,
|
||||||
|
otherCharacters: allCharacters.filter(c => c.guid !== charData.guid)
|
||||||
|
},
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
public async questsCompare(req: express.Request, res: express.Response, next: express.NextFunction): Promise<void> {
|
||||||
|
const realmName = req.params.realm;
|
||||||
|
const charName = req.params.name;
|
||||||
|
const otherRealmName = req.params.otherRealm;
|
||||||
|
const otherCharName = req.params.otherName;
|
||||||
|
|
||||||
|
const realm = this.armory.getRealm(realmName);
|
||||||
|
const otherRealm = this.armory.getRealm(otherRealmName);
|
||||||
|
if (realm === undefined || otherRealm === undefined) {
|
||||||
|
return next(404);
|
||||||
|
}
|
||||||
|
|
||||||
|
const charData = await this.getCharacterData(realm, charName);
|
||||||
|
const otherCharData = await this.getCharacterData(otherRealm, otherCharName);
|
||||||
|
if (charData === null || otherCharData === null) {
|
||||||
|
return next(404);
|
||||||
|
}
|
||||||
|
|
||||||
|
const charQuests = await this.getQuests(realm.name, charData.guid);
|
||||||
|
const otherQuests = await this.getQuests(otherRealm.name, otherCharData.guid);
|
||||||
|
|
||||||
|
// Group quests by category
|
||||||
|
const categories: { [key: string]: any[] } = {};
|
||||||
|
const addQuestsToCategories = (quests: any[], source: string) => {
|
||||||
|
quests.forEach(quest => {
|
||||||
|
const category = quest.questSortID > 0 ?
|
||||||
|
this.getZoneName(quest.questSortID) :
|
||||||
|
this.getProfessionName(quest.questSortID);
|
||||||
|
|
||||||
|
if (!categories[category]) {
|
||||||
|
categories[category] = [];
|
||||||
|
}
|
||||||
|
|
||||||
|
const existingQuest = categories[category].find(q => q.id === quest.id);
|
||||||
|
if (existingQuest) {
|
||||||
|
existingQuest[source] = quest.status;
|
||||||
|
} else {
|
||||||
|
categories[category].push({
|
||||||
|
id: quest.id,
|
||||||
|
title: quest.title,
|
||||||
|
questLevel: quest.questLevel,
|
||||||
|
[source]: quest.status,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
addQuestsToCategories(charQuests, 'char1Status');
|
||||||
|
addQuestsToCategories(otherQuests, 'char2Status');
|
||||||
|
|
||||||
|
// Get list of all characters
|
||||||
|
const allCharacters = await this.getAllCharacters(realm.name);
|
||||||
|
|
||||||
|
res.render("character-quests-compare.hbs", {
|
||||||
|
title: `Armory - Quest Compare - ${charData.name} vs ${otherCharData.name}`,
|
||||||
|
...this.makeSharedDataObject(realm, charData),
|
||||||
|
data: {
|
||||||
|
categories: categories,
|
||||||
|
char1: {
|
||||||
|
name: charData.name,
|
||||||
|
realm: realm.name
|
||||||
|
},
|
||||||
|
char2: {
|
||||||
|
name: otherCharData.name,
|
||||||
|
realm: otherRealm.name
|
||||||
|
},
|
||||||
|
otherCharacters: allCharacters.filter(c => c.guid !== charData.guid)
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
@ -1485,4 +1560,17 @@ export class CharacterController {
|
||||||
return row;
|
return row;
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private async getAllCharacters(currentRealm: string): Promise<Array<{name: string, realmName: string, guid: number}>> {
|
||||||
|
const [rows] = await this.armory.getCharactersDb(currentRealm).query({
|
||||||
|
sql: 'SELECT name, guid FROM characters WHERE level > 1 order by name asc',
|
||||||
|
timeout: this.armory.config.dbQueryTimeout,
|
||||||
|
});
|
||||||
|
|
||||||
|
return (rows as RowDataPacket[]).map(row => ({
|
||||||
|
name: row.name,
|
||||||
|
guid: row.guid,
|
||||||
|
realmName: currentRealm
|
||||||
|
}));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
127
static/character-quests-compare.hbs
Normal file
127
static/character-quests-compare.hbs
Normal file
|
|
@ -0,0 +1,127 @@
|
||||||
|
<link rel="stylesheet" type="text/css" href="{{websiteRoot}}/css/character-quests.css">
|
||||||
|
{{> icons }}
|
||||||
|
<script type="application/javascript">
|
||||||
|
const aowow_tooltips = { "renamelinks": true, };
|
||||||
|
</script>
|
||||||
|
|
||||||
|
{{> character-header }}
|
||||||
|
|
||||||
|
<div class="quest-compare-section" style="margin-bottom: 20px;">
|
||||||
|
<div class="field is-horizontal">
|
||||||
|
<div class="field-label is-normal">
|
||||||
|
<label class="label">Compare with:</label>
|
||||||
|
</div>
|
||||||
|
<div class="field-body">
|
||||||
|
<div class="field has-addons">
|
||||||
|
<div class="control is-expanded">
|
||||||
|
<div class="select is-fullwidth">
|
||||||
|
<select id="compareCharacter">
|
||||||
|
<option value="">Select a character...</option>
|
||||||
|
{{#each data.otherCharacters}}
|
||||||
|
<option value="{{this.realmName}}/{{this.name}}"
|
||||||
|
{{#if (eq this.realmName ../data.char2.realm)}}
|
||||||
|
{{#if (eq this.name ../data.char2.name)}}
|
||||||
|
selected
|
||||||
|
{{/if}}
|
||||||
|
{{/if}}>
|
||||||
|
{{this.name}} ({{this.realmName}})
|
||||||
|
</option>
|
||||||
|
{{/each}}
|
||||||
|
</select>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="control">
|
||||||
|
<button class="button is-info" id="compareButton" disabled>
|
||||||
|
Compare Quests
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="quest-comparison-header">
|
||||||
|
<h2>Quest Comparison</h2>
|
||||||
|
<div class="characters">
|
||||||
|
<span class="char1">
|
||||||
|
{{#if (eq data.char2.realm data.char1.realm)}}
|
||||||
|
{{data.char1.name}}
|
||||||
|
{{else}}
|
||||||
|
{{data.char1.realm}}/{{data.char1.name}}
|
||||||
|
{{/if}}
|
||||||
|
</span>
|
||||||
|
vs
|
||||||
|
<span class="char2">
|
||||||
|
<a href="{{websiteRoot}}/character/{{data.char2.realm}}/{{data.char2.name}}/quests/compare/{{data.char1.realm}}/{{data.char1.name}}">
|
||||||
|
{{#if (eq data.char2.realm data.char1.realm)}}
|
||||||
|
{{data.char2.name}}
|
||||||
|
{{else}}
|
||||||
|
{{data.char2.realm}}/{{data.char2.name}}
|
||||||
|
{{/if}}
|
||||||
|
</a>
|
||||||
|
</span>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
{{#each data.categories as |quests category|}}
|
||||||
|
<div class="quests-table" style="margin-bottom: 20px;">
|
||||||
|
<h2 class="is-size-4 category-header collapsible">
|
||||||
|
{{@key}}
|
||||||
|
<span class="collapse-icon">▼</span>
|
||||||
|
</h2>
|
||||||
|
|
||||||
|
<div class="category-content">
|
||||||
|
<table class="table is-striped is-hoverable is-fullwidth">
|
||||||
|
<thead>
|
||||||
|
<tr>
|
||||||
|
<th>Quest</th>
|
||||||
|
<th>Level</th>
|
||||||
|
<th>{{../data.char1.name}}</th>
|
||||||
|
<th>{{../data.char2.name}}</th>
|
||||||
|
</tr>
|
||||||
|
</thead>
|
||||||
|
<tbody>
|
||||||
|
{{#each this}}
|
||||||
|
<tr class="{{getDiffClass this}}">
|
||||||
|
<td class="quest-name">
|
||||||
|
<a href="{{@root.aowow}}/?quest={{this.id}}">{{this.title}}</a>
|
||||||
|
</td>
|
||||||
|
<td class="quest-level">{{this.questLevel}}</td>
|
||||||
|
<td class="quest-status {{this.char1Status}}">{{this.char1Status}}</td>
|
||||||
|
<td class="quest-status {{this.char2Status}}">{{this.char2Status}}</td>
|
||||||
|
</tr>
|
||||||
|
{{/each}}
|
||||||
|
</tbody>
|
||||||
|
</table>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
{{/each}}
|
||||||
|
|
||||||
|
<script>
|
||||||
|
document.addEventListener('DOMContentLoaded', function() {
|
||||||
|
document.querySelectorAll('.collapsible').forEach(header => {
|
||||||
|
header.addEventListener('click', function() {
|
||||||
|
const content = this.nextElementSibling;
|
||||||
|
content.classList.toggle('expanded');
|
||||||
|
this.classList.toggle('expanded');
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
const compareSelect = document.getElementById('compareCharacter');
|
||||||
|
const compareButton = document.getElementById('compareButton');
|
||||||
|
|
||||||
|
compareSelect.addEventListener('change', function() {
|
||||||
|
compareButton.disabled = !compareSelect.value;
|
||||||
|
});
|
||||||
|
|
||||||
|
compareButton.addEventListener('click', function() {
|
||||||
|
const selected = compareSelect.value;
|
||||||
|
if (selected) {
|
||||||
|
const [realm, name] = selected.split('/');
|
||||||
|
// Get the base path by removing everything after /quests/compare/
|
||||||
|
const basePath = window.location.pathname.split('/quests/compare/')[0];
|
||||||
|
window.location.href = `${basePath}/quests/compare/${realm}/${name}`;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
});
|
||||||
|
</script>
|
||||||
|
|
@ -6,6 +6,33 @@
|
||||||
|
|
||||||
{{> character-header }}
|
{{> character-header }}
|
||||||
|
|
||||||
|
<div class="quest-compare-section" style="margin-bottom: 20px;">
|
||||||
|
<div class="field is-horizontal">
|
||||||
|
<div class="field-label is-normal">
|
||||||
|
<label class="label">Compare with:</label>
|
||||||
|
</div>
|
||||||
|
<div class="field-body">
|
||||||
|
<div class="field has-addons">
|
||||||
|
<div class="control is-expanded">
|
||||||
|
<div class="select is-fullwidth">
|
||||||
|
<select id="compareCharacter">
|
||||||
|
<option value="">Select a character...</option>
|
||||||
|
{{#each data.otherCharacters}}
|
||||||
|
<option value="{{this.realmName}}/{{this.name}}">{{this.name}} ({{this.realmName}})</option>
|
||||||
|
{{/each}}
|
||||||
|
</select>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="control">
|
||||||
|
<button class="button is-info" id="compareButton" disabled>
|
||||||
|
Compare Quests
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
{{#each data.categories as |quests category|}}
|
{{#each data.categories as |quests category|}}
|
||||||
<div class="quests-table" style="margin-bottom: 20px;">
|
<div class="quests-table" style="margin-bottom: 20px;">
|
||||||
{{#if (hasInProgressQuests quests)}}
|
{{#if (hasInProgressQuests quests)}}
|
||||||
|
|
@ -93,5 +120,20 @@ document.addEventListener('DOMContentLoaded', function() {
|
||||||
this.classList.toggle('expanded');
|
this.classList.toggle('expanded');
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
const compareSelect = document.getElementById('compareCharacter');
|
||||||
|
const compareButton = document.getElementById('compareButton');
|
||||||
|
|
||||||
|
compareSelect.addEventListener('change', function() {
|
||||||
|
compareButton.disabled = !this.value;
|
||||||
|
});
|
||||||
|
|
||||||
|
compareButton.addEventListener('click', function() {
|
||||||
|
const selected = compareSelect.value;
|
||||||
|
if (selected) {
|
||||||
|
const [realm, name] = selected.split('/');
|
||||||
|
window.location.href = `${window.location.pathname}/compare/${realm}/${name}`;
|
||||||
|
}
|
||||||
|
});
|
||||||
});
|
});
|
||||||
</script>
|
</script>
|
||||||
|
|
@ -106,3 +106,39 @@
|
||||||
.category-header:not(.expanded) .collapse-icon {
|
.category-header:not(.expanded) .collapse-icon {
|
||||||
transform: rotate(-90deg);
|
transform: rotate(-90deg);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.quest-comparison-header {
|
||||||
|
margin-bottom: 20px;
|
||||||
|
text-align: center;
|
||||||
|
max-width: 500px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.quest-comparison-header .characters {
|
||||||
|
font-size: 1.2em;
|
||||||
|
margin-top: 10px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.quest-comparison-header .char1,
|
||||||
|
.quest-comparison-header .char2 {
|
||||||
|
font-weight: bold;
|
||||||
|
}
|
||||||
|
|
||||||
|
tr.quest-diff {
|
||||||
|
background-color: rgba(255, 255, 0, 0.2) !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
tr.quest-missing-1 td:nth-child(3) {
|
||||||
|
background-color: rgba(255, 0, 0, 0.2) !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
tr.quest-missing-2 td:nth-child(4) {
|
||||||
|
background-color: rgba(255, 0, 0, 0.2) !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
.quest-compare-section {
|
||||||
|
max-width: 500px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.quest-compare-section .select select {
|
||||||
|
min-width: 200px;
|
||||||
|
}
|
||||||
Loading…
Add table
Add a link
Reference in a new issue