mirror of
https://github.com/psforever/PSFPortal.git
synced 2026-01-19 18:14:45 +00:00
outfits
This commit is contained in:
parent
a64195bbed
commit
c53469fbd6
99
api/db.js
99
api/db.js
|
|
@ -380,9 +380,16 @@ export async function get_weaponstats_by_avatar(id) {
|
|||
}
|
||||
|
||||
export async function get_avatar(id) {
|
||||
try {
|
||||
const avatar = await pool.query('SELECT id, name, faction_id, bep, cep, gender_id, head_id FROM avatar WHERE id=$1', [id])
|
||||
return avatar.rows[0];
|
||||
try {
|
||||
const avatar = await pool.query(
|
||||
'SELECT a.id, a.name, a.faction_id, a.bep, a.cep, a.gender_id, a.head_id,' +
|
||||
' o.id AS outfit_id, o.name AS outfit_name' +
|
||||
' FROM avatar a' +
|
||||
' LEFT JOIN outfitmember om ON om.avatar_id = a.id' +
|
||||
' LEFT JOIN outfit o ON o.id = om.outfit_id' +
|
||||
' WHERE a.id = $1',
|
||||
[id])
|
||||
return avatar.rows[0];
|
||||
} catch (e) {
|
||||
if (e.code)
|
||||
e.code = pg_error_inv[e.code]
|
||||
|
|
@ -438,6 +445,92 @@ export async function get_top_kills_byDate() {
|
|||
}
|
||||
}
|
||||
|
||||
export async function get_top_outfits() {
|
||||
try {
|
||||
const outfits = await pool.query(
|
||||
'WITH OutfitData AS (' +
|
||||
' SELECT o.id AS outfit_id,' +
|
||||
' o.faction,' +
|
||||
' o.name AS outfit_name,' +
|
||||
' a.id AS leader_id, a.name AS leader_name,' +
|
||||
' COUNT(om.avatar_id)::int AS members,' +
|
||||
' (op.points / 100.0)::int AS points' +
|
||||
' FROM outfit o' +
|
||||
' JOIN avatar a ON a.id = o.owner_id' +
|
||||
' LEFT JOIN outfitmember om ON om.outfit_id = o.id' +
|
||||
' LEFT JOIN outfitpoint_mv op ON op.outfit_id = o.id' +
|
||||
' GROUP BY o.id, o.faction, o.name, a.name, a.id, op.points' +
|
||||
') ' +
|
||||
'SELECT outfit_id, faction, outfit_name, leader_name, leader_id, members, points ' +
|
||||
'FROM OutfitData ' +
|
||||
'ORDER BY points DESC')
|
||||
return outfits.rows;
|
||||
} catch (e) {
|
||||
if (e.code)
|
||||
e.code = pg_error_inv[e.code]
|
||||
throw e;
|
||||
}
|
||||
}
|
||||
|
||||
export async function get_outfit(id) {
|
||||
try {
|
||||
const outfit = await pool.query(
|
||||
'WITH OutfitData AS (' +
|
||||
' SELECT o.id AS outfit_id, o.faction, o.name AS outfit_name, o.created,' +
|
||||
' a.id AS leader_id,' +
|
||||
' a.name AS leader_name,' +
|
||||
' COUNT(om.avatar_id)::int AS members,' +
|
||||
' (op.points / 100.0)::int AS points' +
|
||||
' FROM outfit o ' +
|
||||
' JOIN avatar a ON a.id = o.owner_id ' +
|
||||
' LEFT JOIN outfitmember om ON om.outfit_id = o.id ' +
|
||||
' LEFT JOIN outfitpoint_mv op ON op.outfit_id = o.id ' +
|
||||
' WHERE o.id = $1 ' +
|
||||
' GROUP BY o.created, o.id, o.faction, o.name, a.id, a.name, op.points) ' +
|
||||
'SELECT outfit_id, faction, outfit_name, leader_id, leader_name, members, points, created ' +
|
||||
'FROM OutfitData',
|
||||
[id])
|
||||
return outfit.rows[0];
|
||||
} catch (e) {
|
||||
if (e.code)
|
||||
e.code = pg_error_inv[e.code];
|
||||
throw e;
|
||||
}
|
||||
}
|
||||
|
||||
export async function get_outfit_members(id) {
|
||||
try {
|
||||
const members = await pool.query(
|
||||
'SELECT av.id AS avatar_id, av.bep, av.cep,' +
|
||||
' av.name AS avatar_name,' +
|
||||
' om.rank AS rank_num,' +
|
||||
' CASE om.rank ' +
|
||||
' WHEN 0 THEN COALESCE(o.rank0, \'Fodder\') ' +
|
||||
' WHEN 1 THEN COALESCE(o.rank1, \'Soldier\') ' +
|
||||
' WHEN 2 THEN COALESCE(o.rank2, \'Commando\') ' +
|
||||
' WHEN 3 THEN COALESCE(o.rank3, \'Master at Arms\') ' +
|
||||
' WHEN 4 THEN COALESCE(o.rank4, \'Tactical Officer\') ' +
|
||||
' WHEN 5 THEN COALESCE(o.rank5, \'Strategic Officer\') ' +
|
||||
' WHEN 6 THEN COALESCE(o.rank6, \'Chief Officer\') ' +
|
||||
' WHEN 7 THEN COALESCE(o.rank7, \'Outfit Leader\') ' +
|
||||
' END AS rank_title,' +
|
||||
' COALESCE((op.points / 100.0)::int, 0) AS points,' +
|
||||
' om.created AS joined' +
|
||||
' FROM outfitmember om ' +
|
||||
' JOIN avatar av ON av.id = om.avatar_id ' +
|
||||
' JOIN outfit o ON o.id = om.outfit_id ' +
|
||||
' LEFT JOIN outfitpoint op ON op.avatar_id = om.avatar_id ' +
|
||||
' WHERE om.outfit_id = $1 ' +
|
||||
' ORDER BY points DESC, avatar_name ASC',
|
||||
[id])
|
||||
return members.rows;
|
||||
} catch (e) {
|
||||
if (e.code)
|
||||
e.code = pg_error_inv[e.code];
|
||||
throw e;
|
||||
}
|
||||
}
|
||||
|
||||
export async function get_characters_by_account(account_id) {
|
||||
try {
|
||||
const characters = await pool.query('SELECT a.*, b.* FROM avatar a LEFT JOIN avatarmodepermission b ON a.id = b.avatar_id WHERE a.account_id = $1 AND a.deleted = false', [account_id])
|
||||
|
|
|
|||
36
api/stats.js
36
api/stats.js
|
|
@ -78,6 +78,38 @@ api.get('/top_kills_byDate', async (req, res, next) => {
|
|||
}
|
||||
});
|
||||
|
||||
api.get('/top_outfits', async (req, res, next) => {
|
||||
try {
|
||||
const outfits = await db.get_top_outfits();
|
||||
res.status(200).json({ outfits: outfits });
|
||||
} catch (e) {
|
||||
console.log(e);
|
||||
res.status(500).json({ message: 'error' });
|
||||
}
|
||||
});
|
||||
|
||||
api.get('/outfit/:outfit', async (req, res, next) => {
|
||||
const fit = req.params.outfit
|
||||
try {
|
||||
const outfit = await db.get_outfit(fit);
|
||||
res.status(200).json(outfit);
|
||||
} catch (e) {
|
||||
console.log(e);
|
||||
res.status(500).json({ message: 'error' });
|
||||
}
|
||||
});
|
||||
|
||||
api.get('/outfit/:outfit/members', async (req, res, next) => {
|
||||
const fit = req.params.outfit
|
||||
try {
|
||||
const members = await db.get_outfit_members(fit);
|
||||
res.status(200).json({ members: members });
|
||||
} catch (e) {
|
||||
console.log(e);
|
||||
res.status(500).json({ message: 'error' });
|
||||
}
|
||||
});
|
||||
|
||||
api.get('/weaponstats/:avatar', async (req, res, next) => {
|
||||
const avatar = req.params.avatar;
|
||||
|
||||
|
|
@ -102,7 +134,9 @@ api.get('/avatar/:avatar', async (req, res, next) => {
|
|||
cep: avatarData.cep,
|
||||
faction: avatarData.faction_id,
|
||||
gender: avatarData.gender_id,
|
||||
head: avatarData.head_id
|
||||
head: avatarData.head_id,
|
||||
outfit: avatarData.outfit_name,
|
||||
outfit_id: avatarData.outfit_id
|
||||
});
|
||||
} catch (e) {
|
||||
console.log(e);
|
||||
|
|
|
|||
|
|
@ -28,6 +28,7 @@ import AdminPanel from './views/AdminPanel.svelte';
|
|||
import CharacterList from './views/CharacterList.svelte';
|
||||
import Leaderboard from './views/Leaderboard.svelte';
|
||||
import Avatar from './views/Avatar.svelte'
|
||||
import Outfit from './views/Outfit.svelte'
|
||||
|
||||
// Defined by webpack
|
||||
let APP_VERSION = __VERSION__;
|
||||
|
|
@ -106,6 +107,7 @@ page("/avatar/:id", setRoute(Avatar));
|
|||
page("/admin", setRoute(AdminPanel));
|
||||
page("/profile", setRoute(Profile, true));
|
||||
page("/user/:id", setRoute(Profile, true));
|
||||
page("/outfit/:id", setRoute(Outfit));
|
||||
if (process.env.NODE_ENV !== 'production') {
|
||||
console.log("Development mode active");
|
||||
const cc = await import('./views/components.svelte');
|
||||
|
|
|
|||
|
|
@ -138,7 +138,11 @@
|
|||
<tr>
|
||||
<td>
|
||||
<span style="color:lightgrey;">Character Name:</span> {avatar.name}<br>
|
||||
<span style="color:lightgrey;">Empire:</span> {getFactionName(avatar.faction)}
|
||||
<span style="color:lightgrey;">Empire:</span> {getFactionName(avatar.faction)}<br>
|
||||
<span style="color:lightgrey;">Outfit:</span>
|
||||
{#if avatar.outfit_id}
|
||||
<a href="/outfit/{avatar.outfit_id}">{avatar.outfit}</a>
|
||||
{/if}<br>
|
||||
</td>
|
||||
<td><img height="60" src={getFactionIcon(avatar.faction)} alt={avatar.faction}/></td>
|
||||
</tr>
|
||||
|
|
|
|||
|
|
@ -8,12 +8,14 @@
|
|||
get_BEPleaderboard();
|
||||
get_CEPleaderboard();
|
||||
get_kills();
|
||||
get_outfits();
|
||||
get_topDateKills();
|
||||
});
|
||||
|
||||
let bepPlayers = [];
|
||||
let cepPlayers = [];
|
||||
let kills = [];
|
||||
let outfits = [];
|
||||
let dateKills = [];
|
||||
let alert;
|
||||
|
||||
|
|
@ -21,7 +23,7 @@
|
|||
try {
|
||||
const resp = await axios.get("/api/char_stats_bep/0");
|
||||
const stats = resp.data;
|
||||
bepPlayers = stats.players;
|
||||
bepPlayers = stats.players.filter(p => p.bep > 14999);
|
||||
// Reset alert message if needed
|
||||
alert.message("");
|
||||
} catch (e) {
|
||||
|
|
@ -34,7 +36,7 @@
|
|||
try {
|
||||
const resp = await axios.get("/api/char_stats_cep/0");
|
||||
const stats = resp.data;
|
||||
cepPlayers = stats.players;
|
||||
cepPlayers = stats.players.filter(p => p.cep > 0);
|
||||
// Reset alert message if needed
|
||||
alert.message("");
|
||||
} catch (e) {
|
||||
|
|
@ -56,6 +58,19 @@
|
|||
}
|
||||
}
|
||||
|
||||
async function get_outfits() {
|
||||
try {
|
||||
const resp = await axios.get("/api/top_outfits");
|
||||
const stats = resp.data;
|
||||
outfits = stats.outfits;
|
||||
// Reset alert message if needed
|
||||
alert.message("");
|
||||
} catch (e) {
|
||||
console.log(e);
|
||||
alert.message("Failed to fetch stats from server");
|
||||
}
|
||||
}
|
||||
|
||||
async function get_topDateKills() {
|
||||
try {
|
||||
const resp = await axios.get("/api/top_kills_byDate");
|
||||
|
|
@ -86,6 +101,9 @@
|
|||
<li class="nav-item">
|
||||
<a class="nav-link" id="cr-tab" data-toggle="tab" href="#cr" role="tab" aria-controls="cr" aria-selected="false">Command Rank</a>
|
||||
</li>
|
||||
<li class="nav-item">
|
||||
<a class="nav-link" id="outfits-tab" data-toggle="tab" href="#outfits" role="tab" aria-controls="outfits" aria-selected="false">Outfits</a>
|
||||
</li>
|
||||
<li class="nav-item">
|
||||
<a class="nav-link" id="top-tab" data-toggle="tab" href="#top" role="tab" aria-controls="top" aria-selected="false">Top X</a>
|
||||
</li>
|
||||
|
|
@ -166,7 +184,32 @@
|
|||
</table>
|
||||
</div>
|
||||
|
||||
<div class="tab-pane" id="top" role="tabpanel" aria-labelledby="top-tab">
|
||||
<div class="tab-pane" id="outfits" role="tabpanel" aria-labelledby="outfits-tab">
|
||||
<table class="table table-sm table-dark table-responsive-md table-striped table-hover">
|
||||
<thead class="thead-light">
|
||||
<th>#</th>
|
||||
<th>Name</th>
|
||||
<th>Leader</th>
|
||||
<th>Members</th>
|
||||
<th>Points</th>
|
||||
</thead>
|
||||
<tbody>
|
||||
{#each outfits as outfit, $index}
|
||||
<tr>
|
||||
<td>{$index + 1}</td>
|
||||
<td>
|
||||
<img height="24" src={getFactionIcon(outfit.faction)} alt={outfit.faction} />
|
||||
<a href="/outfit/{outfit.outfit_id}">{outfit.outfit_name}</a></td>
|
||||
<td><a href="/avatar/{outfit.leader_id}">{outfit.leader_name}</a></td>
|
||||
<td>{outfit.members}</td>
|
||||
<td>{outfit.points}</td>
|
||||
</tr>
|
||||
{/each}
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
|
||||
<div class="tab-pane" id="top" role="tabpanel" aria-labelledby="top-tab">
|
||||
<span style="color:lightgrey; text-align:center;">Top 50 Characters Most Daily Kills</span>
|
||||
<table class="table table-sm table-dark table-responsive-md table-striped table-hover">
|
||||
<thead class="thead-light">
|
||||
|
|
|
|||
117
app/views/Outfit.svelte
Normal file
117
app/views/Outfit.svelte
Normal file
|
|
@ -0,0 +1,117 @@
|
|||
<script>
|
||||
import { onMount } from 'svelte';
|
||||
import axios from 'axios'
|
||||
import Alert from '../components/Alert'
|
||||
import { bepRanges, cepRanges, calculateBr, calculateCr, getFactionIcon, getFactionName } from '../statFunctions';
|
||||
|
||||
onMount(() => {
|
||||
get_outfit();
|
||||
get_outfit_members();
|
||||
});
|
||||
|
||||
export let params;
|
||||
|
||||
let alert;
|
||||
let outfit = {};
|
||||
let outfitMembers = [];
|
||||
let url = params.id || outfit.id
|
||||
|
||||
const outfitUrl = "/api/outfit/"+url
|
||||
const outfitMembersUrl = "/api/outfit/"+url+"/members"
|
||||
|
||||
async function get_outfit() {
|
||||
try {
|
||||
const resp = await axios.get(outfitUrl);
|
||||
outfit = resp.data
|
||||
// Reset alert message if needed
|
||||
alert.message("");
|
||||
} catch (e) {
|
||||
console.log(e);
|
||||
alert.message("Failed to fetch stats from server");
|
||||
}
|
||||
}
|
||||
|
||||
async function get_outfit_members() {
|
||||
try {
|
||||
const resp = await axios.get(outfitMembersUrl);
|
||||
const stats = resp.data;
|
||||
outfitMembers = stats.members
|
||||
// Reset alert message if needed
|
||||
alert.message("");
|
||||
} catch (e) {
|
||||
console.log(e);
|
||||
alert.message("Failed to fetch stats from server");
|
||||
}
|
||||
}
|
||||
|
||||
function stripColorCode(title) {
|
||||
if (!title) return "";
|
||||
if (title.startsWith("\\#")) {
|
||||
return title.slice(8);
|
||||
}
|
||||
return title;
|
||||
}
|
||||
</script>
|
||||
|
||||
<svelte:head>
|
||||
<title>Outfit Stats</title>
|
||||
</svelte:head>
|
||||
|
||||
<table width="70%">
|
||||
<tbody>
|
||||
<tr>
|
||||
<td width="%50" valign="top">
|
||||
<table>
|
||||
<tbody>
|
||||
<tr>
|
||||
<td>
|
||||
<img height="60" src={getFactionIcon(outfit.faction)} alt={outfit.faction}/><br>
|
||||
<span style="color:lightgrey;">Outfit Name:</span> {outfit.outfit_name}<br>
|
||||
<span style="color:lightgrey;">Created:</span>
|
||||
{new Date(outfit.created).toLocaleDateString('en-US', {
|
||||
year: 'numeric',
|
||||
month: 'long',
|
||||
day: 'numeric'
|
||||
})}<br>
|
||||
<span style="color:lightgrey;">Leader:</span> <a href="/avatar/{outfit.leader_id}">{outfit.leader_name}</a><br>
|
||||
<span style="color:lightgrey;">Points:</span> {outfit.points}<br>
|
||||
<span style="color:lightgrey;">Members:</span> {outfit.members}<br>
|
||||
</td>
|
||||
</tbody>
|
||||
</table>
|
||||
</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
<br>
|
||||
<br>
|
||||
<table class="table table-sm table-dark table-responsive-md table-striped table-hover">
|
||||
<thead class="thead-light">
|
||||
<th></th>
|
||||
<th>Name</th>
|
||||
<th>Title</th>
|
||||
<th>Points</th>
|
||||
<th>BR</th>
|
||||
<th>CR</th>
|
||||
<th>Joined</th>
|
||||
</thead>
|
||||
<tbody>
|
||||
{#each outfitMembers as member, $index}
|
||||
<tr>
|
||||
<td>{$index + 1}</td>
|
||||
<td><a href="/avatar/{member.avatar_id}">{member.avatar_name}</a></td>
|
||||
<td>{stripColorCode(member.rank_title)}</td>
|
||||
<td>{member.points}</td>
|
||||
<td>{calculateBr(member.bep)}</td>
|
||||
<td>{calculateCr(member.cep)}</td>
|
||||
<td>
|
||||
{new Date(member.joined).toLocaleDateString('en-US', {
|
||||
year: 'numeric',
|
||||
month: 'long',
|
||||
day: 'numeric'
|
||||
})}
|
||||
</td>
|
||||
</tr>
|
||||
{/each}
|
||||
</tbody>
|
||||
</table>
|
||||
Loading…
Reference in a new issue