You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 
 
 
 

189 lines
4.8 KiB

<script lang="ts">
import type Item from "../models/item";
import type { LogResult } from "../models/log";
import type { ProjectResult } from "../models/project";
import Icon from "./Icon.svelte";
interface ListEntry {
item: Item
amount: number
target: number
extras: number
subs: number
noTarget?: boolean
}
export let project: ProjectResult = null;
export let logs: LogResult[] = [];
export let centered = false;
export let vertical = false;
let list: ListEntry[] = [];
$: {
list = [];
if (project != null) {
for (const task of project.tasks) {
let entry = list.find(e => e.item.id === task.item.id);
if (entry == null) {
entry = {item: task.item, amount: 0, target: 0, extras: 0, subs: 0};
list.push(entry);
}
if (task.completedAmount > task.itemAmount) {
entry.extras += task.completedAmount - task.itemAmount;
entry.amount += task.itemAmount;
} else {
entry.amount += task.completedAmount;
}
if (["completed", "failed", "declined"].includes(task.statusTag)) {
entry.target += task.itemAmount - Math.max(0, task.itemAmount - task.completedAmount);
} else {
entry.target += task.itemAmount;
}
for (const log of task.logs) {
if (log.secondaryItem != null) {
let entry = list.find(e => e.item.id === log.secondaryItemId);
if (entry == null) {
entry = {item: log.secondaryItem, amount: 0, target: 0, extras: 0, subs: 0};
list.push(entry);
}
entry.extras += log.secondaryItemAmount;
}
}
}
for (const {item, amount} of project.subtractions) {
const entry = list.find(e => e.item.id === item.id);
if (entry == null) {
continue
}
entry.subs += amount;
entry.target -= amount;
entry.amount -= amount;
}
}
for (const log of logs) {
const items: [Item, number][] = [
[log.item, log.itemAmount],
[log.secondaryItem, log.secondaryItemAmount]
];
for (const [item, count] of items) {
if (item != null) {
let entry = list.find(e => e.item.id === item.id);
if (entry == null) {
entry = {item, amount: 0, target: 0, extras: 0, noTarget: true, subs: 0};
list.push(entry);
}
entry.amount += count;
}
}
}
list = list.sort((a,b) => {
if (a.noTarget && b.noTarget) {
return a.amount - b.amount;
}
if (a.target === b.target) {
return a.item.name.localeCompare(b.item.name);
} else {
return b.target - a.target;
}
}).filter(e => e.extras > 0 || (e.target > 0 || e.subs > 0 || e.noTarget));
}
</script>
<div class="ItemProgress">
<div class="wrapper" class:centered class:vertical={vertical || list.length > 3}>
{#each list as entry (entry.item.id)}
<div class="entry">
{#if entry.noTarget}
<div class="amount">
<span>{entry.amount}&times; </span>
</div>
{:else}
<div class="icon">
<Icon name={entry.item.icon} />
</div>
{/if}
<div class="name">
{entry.item.name}
</div>
{#if entry.target !== 0}
<div class="amount">
<span>{entry.amount}</span>
<span> / </span>
<span>{entry.target}</span>
{#if entry.extras > 0}
<span class="extras">+ {entry.extras}</span>
{/if}
</div>
{:else if !entry.noTarget}
<div class="extras">
&times;{entry.extras}
</div>
{/if}
{#if entry.subs > 0}
<div class="subs" title="These items are from before the project's start time.">
–{entry.subs}
</div>
{/if}
</div>
{/each}
</div>
</div>
<style>
div.ItemProgress {
display: flex;
flex-direction: row;
}
div.ItemProgress div.wrapper {
display: flex;
flex-direction: row;
flex-wrap: wrap;
padding: 0.25em;
font-size: 0.8em;
}
div.ItemProgress div.wrapper.centered {
margin: 0 auto;
}
div.ItemProgress div.wrapper.vertical {
flex-direction: column;
}
div.ItemProgress div.entry {
display: flex;
flex-direction: row;
color: #777;
padding-right: 1ch;
}
div.ItemProgress div.wrapper.vertical div.entry {
padding-right: 0;
}
div.ItemProgress div.entry div {
padding: 0.1em 0.25em;
}
div.ItemProgress div.entry .name {
color: #555;
}
div.ItemProgress div.entry .icon {
padding: 0.20em 0.5ch;
}
div.ItemProgress div.entry .extras {
color: #484;
}
div.ItemProgress div.entry .subs {
color: #852a24;
}
</style>