This commit is contained in:
+1
-1
@@ -68,7 +68,7 @@ func Task(g *gin.RouterGroup, db database.Database) {
|
||||
|
||||
return &models.TaskResult{
|
||||
Task: task,
|
||||
Logs: []*models.Log{},
|
||||
Logs: []*models.LogWithSecondaryItem{},
|
||||
Item: &item.Item,
|
||||
CompletedAmount: 0,
|
||||
}, nil
|
||||
|
||||
+11
-6
@@ -67,14 +67,19 @@ type LogResult struct {
|
||||
SecondaryItem *Item `json:"secondaryItem"`
|
||||
}
|
||||
|
||||
type LogWithSecondaryItem struct {
|
||||
Log
|
||||
SecondaryItem *Item `json:"secondaryItem,omitempty"`
|
||||
}
|
||||
|
||||
type LogFilter struct {
|
||||
UserID string
|
||||
UserID string
|
||||
ProjectGroupIDs []string
|
||||
ProjectIDs []string
|
||||
TaskIDs []string
|
||||
ItemIDs []string
|
||||
MinTime *time.Time
|
||||
MaxTime *time.Time
|
||||
ProjectIDs []string
|
||||
TaskIDs []string
|
||||
ItemIDs []string
|
||||
MinTime *time.Time
|
||||
MaxTime *time.Time
|
||||
}
|
||||
|
||||
type LogRepository interface {
|
||||
|
||||
+4
-4
@@ -79,10 +79,10 @@ type TaskWithProject struct {
|
||||
|
||||
type TaskResult struct {
|
||||
Task
|
||||
Item *Item `json:"item"`
|
||||
Logs []*Log `json:"logs"`
|
||||
CompletedAmount int `json:"completedAmount"`
|
||||
Project *Project `json:"project,omitempty"`
|
||||
Item *Item `json:"item"`
|
||||
Logs []*LogWithSecondaryItem `json:"logs"`
|
||||
CompletedAmount int `json:"completedAmount"`
|
||||
Project *Project `json:"project,omitempty"`
|
||||
}
|
||||
|
||||
type TaskFilter struct {
|
||||
|
||||
+104
-10
@@ -251,6 +251,12 @@ func (l *Loader) FindProject(ctx context.Context, id string) (*models.ProjectRes
|
||||
UserID: auth.UserID(ctx),
|
||||
TaskIDs: taskIDs,
|
||||
})
|
||||
for _, log := range logs {
|
||||
if log.SecondaryItemID == nil {
|
||||
itemIDs.Add(*log.SecondaryItemID)
|
||||
}
|
||||
}
|
||||
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
@@ -265,12 +271,26 @@ func (l *Loader) FindProject(ctx context.Context, id string) (*models.ProjectRes
|
||||
result.Tasks = make([]*models.TaskResult, len(tasks))
|
||||
for i, task := range tasks {
|
||||
result.Tasks[i] = &models.TaskResult{
|
||||
Logs: []*models.Log{},
|
||||
Logs: []*models.LogWithSecondaryItem{},
|
||||
}
|
||||
result.Tasks[i].Task = *task
|
||||
for _, log := range logs {
|
||||
if log.TaskID == task.ID {
|
||||
result.Tasks[i].Logs = append(result.Tasks[i].Logs, log)
|
||||
log := models.LogWithSecondaryItem{
|
||||
Log: *log,
|
||||
SecondaryItem: nil,
|
||||
}
|
||||
|
||||
if log.SecondaryItemID != nil {
|
||||
for _, item := range items {
|
||||
if item.ID == *log.SecondaryItemID {
|
||||
log.SecondaryItem = item
|
||||
break
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
result.Tasks[i].Logs = append(result.Tasks[i].Logs, &log)
|
||||
}
|
||||
}
|
||||
for _, item := range items {
|
||||
@@ -280,6 +300,10 @@ func (l *Loader) FindProject(ctx context.Context, id string) (*models.ProjectRes
|
||||
}
|
||||
}
|
||||
for _, log := range result.Tasks[i].Logs {
|
||||
if project.StartTime != nil && log.LoggedTime.Before(*project.StartTime) {
|
||||
continue
|
||||
}
|
||||
|
||||
result.Tasks[i].CompletedAmount += log.Amount(result.Tasks[i].ItemID)
|
||||
}
|
||||
}
|
||||
@@ -319,6 +343,11 @@ func (l *Loader) ListProjects(ctx context.Context, filter models.ProjectFilter)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
for _, log := range logs {
|
||||
if log.SecondaryItemID != nil {
|
||||
itemIDs.Add(*log.SecondaryItemID)
|
||||
}
|
||||
}
|
||||
|
||||
items, err := l.DB.Items().List(ctx, models.ItemFilter{
|
||||
UserID: auth.UserID(ctx),
|
||||
@@ -350,11 +379,25 @@ func (l *Loader) ListProjects(ctx context.Context, filter models.ProjectFilter)
|
||||
|
||||
taskResult := &models.TaskResult{
|
||||
Task: *task,
|
||||
Logs: []*models.Log{},
|
||||
Logs: []*models.LogWithSecondaryItem{},
|
||||
}
|
||||
for _, log := range logs {
|
||||
if log.TaskID == task.ID {
|
||||
taskResult.Logs = append(taskResult.Logs, log)
|
||||
log := models.LogWithSecondaryItem{
|
||||
Log: *log,
|
||||
SecondaryItem: nil,
|
||||
}
|
||||
|
||||
if log.SecondaryItemID != nil {
|
||||
for _, item := range items {
|
||||
if item.ID == *log.SecondaryItemID {
|
||||
log.SecondaryItem = item
|
||||
break
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
taskResult.Logs = append(taskResult.Logs, &log)
|
||||
}
|
||||
}
|
||||
for _, item := range items {
|
||||
@@ -364,6 +407,10 @@ func (l *Loader) ListProjects(ctx context.Context, filter models.ProjectFilter)
|
||||
}
|
||||
}
|
||||
for _, log := range taskResult.Logs {
|
||||
if project.StartTime != nil && log.LoggedTime.Before(*project.StartTime) {
|
||||
continue
|
||||
}
|
||||
|
||||
taskResult.CompletedAmount += log.Amount(taskResult.ItemID)
|
||||
}
|
||||
results[i].Tasks = append(results[i].Tasks, taskResult)
|
||||
@@ -468,7 +515,7 @@ func (l *Loader) FindTask(ctx context.Context, id string) (*models.TaskResult, e
|
||||
|
||||
result.Item, _ = l.DB.Items().Find(ctx, task.ItemID)
|
||||
result.Project, _ = l.DB.Projects().Find(ctx, task.ProjectID)
|
||||
result.Logs, err = l.DB.Logs().List(ctx, models.LogFilter{
|
||||
logs, err := l.DB.Logs().List(ctx, models.LogFilter{
|
||||
UserID: task.UserID,
|
||||
TaskIDs: []string{task.ID},
|
||||
})
|
||||
@@ -476,8 +523,34 @@ func (l *Loader) FindTask(ctx context.Context, id string) (*models.TaskResult, e
|
||||
return nil, err
|
||||
}
|
||||
|
||||
for _, log := range result.Logs {
|
||||
itemIds := stringset.New()
|
||||
for _, log := range logs {
|
||||
if log.SecondaryItemID != nil {
|
||||
itemIds.Add(*log.SecondaryItemID)
|
||||
}
|
||||
}
|
||||
items, err := l.DB.Items().List(ctx, models.ItemFilter{
|
||||
UserID: task.UserID,
|
||||
IDs: itemIds.Strings(),
|
||||
})
|
||||
|
||||
for _, log := range logs {
|
||||
result.CompletedAmount += log.Amount(result.ItemID)
|
||||
log := models.LogWithSecondaryItem{
|
||||
Log: *log,
|
||||
SecondaryItem: nil,
|
||||
}
|
||||
|
||||
if log.SecondaryItemID != nil {
|
||||
for _, item := range items {
|
||||
if item.ID == *log.SecondaryItemID {
|
||||
log.SecondaryItem = item
|
||||
break
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
result.Logs = append(result.Logs, &log)
|
||||
}
|
||||
|
||||
return result, nil
|
||||
@@ -509,7 +582,11 @@ func (l *Loader) ListTasks(ctx context.Context, filter models.TaskFilter) ([]*mo
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
for _, log := range logs {
|
||||
if log.SecondaryItemID != nil {
|
||||
itemIDs.Add(*log.SecondaryItemID)
|
||||
}
|
||||
}
|
||||
items, err := l.DB.Items().List(ctx, models.ItemFilter{
|
||||
UserID: auth.UserID(ctx),
|
||||
IDs: itemIDs.Strings(),
|
||||
@@ -530,13 +607,30 @@ func (l *Loader) ListTasks(ctx context.Context, filter models.TaskFilter) ([]*mo
|
||||
for _, task := range tasks {
|
||||
result := &models.TaskResult{
|
||||
Task: *task,
|
||||
Logs: []*models.Log{},
|
||||
Logs: []*models.LogWithSecondaryItem{},
|
||||
}
|
||||
|
||||
for _, log := range logs {
|
||||
if log.TaskID == task.ID {
|
||||
result.Logs = append(result.Logs, log)
|
||||
if log.TaskID != task.ID {
|
||||
continue
|
||||
}
|
||||
|
||||
result.CompletedAmount += log.Amount(result.ItemID)
|
||||
log := models.LogWithSecondaryItem{
|
||||
Log: *log,
|
||||
SecondaryItem: nil,
|
||||
}
|
||||
|
||||
if log.SecondaryItemID != nil {
|
||||
for _, item := range items {
|
||||
if item.ID == *log.SecondaryItemID {
|
||||
log.SecondaryItem = item
|
||||
break
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
result.Logs = append(result.Logs, &log)
|
||||
}
|
||||
|
||||
for _, item := range items {
|
||||
|
||||
@@ -0,0 +1,108 @@
|
||||
<script lang="ts">
|
||||
import type Item from "../models/item";
|
||||
import type { ProjectResult } from "../models/project";
|
||||
import Icon from "./Icon.svelte";
|
||||
|
||||
interface ListEntry {
|
||||
item: Item
|
||||
amount: number
|
||||
target: number
|
||||
extras: number
|
||||
}
|
||||
|
||||
export let project: ProjectResult;
|
||||
|
||||
let list: ListEntry[] = [];
|
||||
|
||||
$: {
|
||||
list = [];
|
||||
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};
|
||||
list.push(entry);
|
||||
}
|
||||
|
||||
if (task.completedAmount > task.itemAmount) {
|
||||
entry.extras += task.completedAmount - task.itemAmount;
|
||||
entry.amount += task.itemAmount;
|
||||
} else {
|
||||
entry.amount += task.completedAmount;
|
||||
}
|
||||
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};
|
||||
list.push(entry);
|
||||
}
|
||||
|
||||
entry.extras += log.secondaryItemAmount;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
list.sort((a,b) => {
|
||||
if (a.target === b.target) {
|
||||
return a.item.name.localeCompare(b.item.name);
|
||||
} else {
|
||||
return b.target - a.target;
|
||||
}
|
||||
})
|
||||
}
|
||||
</script>
|
||||
|
||||
<div class="ItemProgress">
|
||||
{#each list as entry (entry.item.id)}
|
||||
<div class="entry">
|
||||
<div class="icon">
|
||||
<Icon name={entry.item.icon} />
|
||||
</div>
|
||||
<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}
|
||||
<div class="extras">
|
||||
×{entry.extras}
|
||||
</div>
|
||||
{/if}
|
||||
</div>
|
||||
{/each}
|
||||
</div>
|
||||
|
||||
<style>
|
||||
div.ItemProgress {
|
||||
display: inline-block;
|
||||
padding: 0.25em;
|
||||
font-size: 0.8em;
|
||||
}
|
||||
div.ItemProgress div.entry {
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
color: #777;
|
||||
}
|
||||
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;
|
||||
}
|
||||
div.ItemProgress div.entry .extras {
|
||||
color: #484;
|
||||
}
|
||||
</style>
|
||||
@@ -7,6 +7,7 @@ import stuffLogClient from "../clients/stufflog";
|
||||
import type { ModalData } from "../stores/modal";
|
||||
import IS_MOBILE from "../utils/phone-check";
|
||||
import Icon from "./Icon.svelte";
|
||||
import ItemProgress from "./ItemProgress.svelte";
|
||||
import Option from "./Option.svelte";
|
||||
import OptionRow from "./OptionRow.svelte";
|
||||
import ParentEntry from "./ParentEntry.svelte";
|
||||
@@ -120,6 +121,8 @@ import stuffLogClient from "../clients/stufflog";
|
||||
{/if}
|
||||
</div>
|
||||
{#if showAllOptions}
|
||||
<ItemProgress project={project} />
|
||||
|
||||
<OptionRow>
|
||||
<Option open={mdAddTask}>Add Task</Option>
|
||||
<Option open={mdLinkTask}>Link Task</Option>
|
||||
|
||||
@@ -20,6 +20,10 @@ export interface LogFilter {
|
||||
projectGroupIds?: string[]
|
||||
}
|
||||
|
||||
export interface LogWithSecondaryItem extends Log {
|
||||
secondaryItem?: Item
|
||||
}
|
||||
|
||||
export interface LogResult extends Log {
|
||||
task: TaskWithProject
|
||||
item: Item
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
import type { IconName } from "../external/icons";
|
||||
import type Item from "./item";
|
||||
import type Log from "./log";
|
||||
import type { LogWithSecondaryItem } from "./log";
|
||||
import type Project from "./project";
|
||||
|
||||
export default interface Task {
|
||||
@@ -23,7 +23,7 @@ export interface TaskWithProject extends Task {
|
||||
|
||||
export interface TaskResult extends Task {
|
||||
item: Item
|
||||
logs: Log[]
|
||||
logs: LogWithSecondaryItem[]
|
||||
completedAmount: number
|
||||
project?: Project
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user