Compare commits
3
Commits
d8a269328f
...
7eb5f07299
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
7eb5f07299 | ||
|
|
d114f0d38c | ||
|
|
a3336b183c |
@@ -36,6 +36,7 @@ type Requirement struct {
|
|||||||
ProjectID int `json:"projectId"`
|
ProjectID int `json:"projectId"`
|
||||||
Name string `json:"name"`
|
Name string `json:"name"`
|
||||||
Description string `json:"description"`
|
Description string `json:"description"`
|
||||||
|
IsCoarse bool `json:"isCoarse"`
|
||||||
Status models.Status `json:"status"`
|
Status models.Status `json:"status"`
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -49,6 +50,9 @@ func (requirement *Requirement) Update(update models.RequirementUpdate) {
|
|||||||
if update.Status != nil && update.Status.Valid() {
|
if update.Status != nil && update.Status.Valid() {
|
||||||
requirement.Status = *update.Status
|
requirement.Status = *update.Status
|
||||||
}
|
}
|
||||||
|
if update.IsCoarse != nil {
|
||||||
|
requirement.IsCoarse = *update.IsCoarse
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
type RequirementStat struct {
|
type RequirementStat struct {
|
||||||
|
|||||||
@@ -24,7 +24,9 @@
|
|||||||
</OptionsRow>
|
</OptionsRow>
|
||||||
<LabeledProgressRow>
|
<LabeledProgressRow>
|
||||||
{#each requirement.stats as stat (stat.id)}
|
{#each requirement.stats as stat (stat.id)}
|
||||||
<LabeledProgress count={stat.acquired} target={stat.required} name={stat.name} />
|
{#if !requirement.isCoarse || stat.required > 0}
|
||||||
|
<LabeledProgress count={stat.acquired} target={stat.required} name={stat.name} />
|
||||||
|
{/if}
|
||||||
{/each}
|
{/each}
|
||||||
</LabeledProgressRow>
|
</LabeledProgressRow>
|
||||||
{#each requirement.items as item (item.id)}
|
{#each requirement.items as item (item.id)}
|
||||||
|
|||||||
@@ -8,6 +8,7 @@
|
|||||||
import { getScopeContext } from "$lib/components/contexts/ScopeContext.svelte";
|
import { getScopeContext } from "$lib/components/contexts/ScopeContext.svelte";
|
||||||
import StatInput from "$lib/components/controls/StatInput.svelte";
|
import StatInput from "$lib/components/controls/StatInput.svelte";
|
||||||
import StatusSelect from "$lib/components/controls/StatusSelect.svelte";
|
import StatusSelect from "$lib/components/controls/StatusSelect.svelte";
|
||||||
|
import Checkbox from "$lib/components/layout/Checkbox.svelte";
|
||||||
import type { ProjectEntry, Requirement, RequirementInput } from "$lib/models/project";
|
import type { ProjectEntry, Requirement, RequirementInput } from "$lib/models/project";
|
||||||
import Status from "$lib/models/status";
|
import Status from "$lib/models/status";
|
||||||
import { statDiff } from "$lib/utils/stat";
|
import { statDiff } from "$lib/utils/stat";
|
||||||
@@ -49,23 +50,25 @@ import { statDiff } from "$lib/utils/stat";
|
|||||||
description: "",
|
description: "",
|
||||||
stats: [],
|
stats: [],
|
||||||
status: Status.Available,
|
status: Status.Available,
|
||||||
|
isCoarse: false,
|
||||||
}
|
}
|
||||||
|
|
||||||
projectId = project.id;
|
projectId = project.id;
|
||||||
show = true;
|
show = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
function initEdit(prev: Requirement, project: ProjectEntry) {
|
function initEdit(current: Requirement, project: ProjectEntry) {
|
||||||
requirement = {
|
requirement = {
|
||||||
name: prev.name,
|
name: current.name,
|
||||||
description: prev.description,
|
description: current.description,
|
||||||
status: prev.status,
|
status: current.status,
|
||||||
stats: prev.stats.map(s => ({acquired: s.acquired, required: s.required, statId: s.id})),
|
stats: current.stats.map(s => ({acquired: s.acquired, required: s.required, statId: s.id})),
|
||||||
|
isCoarse: current.isCoarse,
|
||||||
}
|
}
|
||||||
|
|
||||||
oldStats = [...requirement.stats];
|
oldStats = [...requirement.stats];
|
||||||
projectId = project.id;
|
projectId = project.id;
|
||||||
requirementId = prev.id;
|
requirementId = current.id;
|
||||||
show = true;
|
show = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -118,6 +121,7 @@ import { statDiff } from "$lib/utils/stat";
|
|||||||
<StatusSelect bind:status={requirement.status} />
|
<StatusSelect bind:status={requirement.status} />
|
||||||
<label for="stats">Stats</label>
|
<label for="stats">Stats</label>
|
||||||
<StatInput showRequired bind:value={requirement.stats} />
|
<StatInput showRequired bind:value={requirement.stats} />
|
||||||
|
<Checkbox bind:checked={requirement.isCoarse} label="Hide 0-requirement stats." />
|
||||||
</ModalBody>
|
</ModalBody>
|
||||||
</Modal>
|
</Modal>
|
||||||
</form>
|
</form>
|
||||||
@@ -37,6 +37,7 @@ export interface Requirement {
|
|||||||
totalAcquired: number
|
totalAcquired: number
|
||||||
totalRequired: number
|
totalRequired: number
|
||||||
totalPlanned: number
|
totalPlanned: number
|
||||||
|
isCoarse: boolean
|
||||||
stats: StatProgressWithPlanned[]
|
stats: StatProgressWithPlanned[]
|
||||||
items: Item[]
|
items: Item[]
|
||||||
}
|
}
|
||||||
@@ -45,5 +46,6 @@ export interface RequirementInput {
|
|||||||
name: string
|
name: string
|
||||||
description: string
|
description: string
|
||||||
status: Status
|
status: Status
|
||||||
|
isCoarse: boolean
|
||||||
stats: StatValueInput[]
|
stats: StatValueInput[]
|
||||||
}
|
}
|
||||||
@@ -11,4 +11,5 @@ type RequirementUpdate struct {
|
|||||||
Name *string `json:"name"`
|
Name *string `json:"name"`
|
||||||
Description *string `json:"description"`
|
Description *string `json:"description"`
|
||||||
Status *Status `json:"status"`
|
Status *Status `json:"status"`
|
||||||
|
IsCoarse *bool `json:"isCoarse"`
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -47,6 +47,7 @@ type ProjectRequirement struct {
|
|||||||
Name string
|
Name string
|
||||||
Status int
|
Status int
|
||||||
Description string
|
Description string
|
||||||
|
IsCoarse bool
|
||||||
}
|
}
|
||||||
|
|
||||||
type ProjectRequirementStat struct {
|
type ProjectRequirementStat struct {
|
||||||
|
|||||||
@@ -156,8 +156,8 @@ func (q *Queries) InsertProject(ctx context.Context, arg InsertProjectParams) (s
|
|||||||
}
|
}
|
||||||
|
|
||||||
const insertProjectRequirement = `-- name: InsertProjectRequirement :execresult
|
const insertProjectRequirement = `-- name: InsertProjectRequirement :execresult
|
||||||
INSERT INTO project_requirement (scope_id, project_id, name, status, description)
|
INSERT INTO project_requirement (scope_id, project_id, name, status, description, is_coarse)
|
||||||
VALUES (?, ?, ?, ?, ?)
|
VALUES (?, ?, ?, ?, ?, ?)
|
||||||
`
|
`
|
||||||
|
|
||||||
type InsertProjectRequirementParams struct {
|
type InsertProjectRequirementParams struct {
|
||||||
@@ -166,6 +166,7 @@ type InsertProjectRequirementParams struct {
|
|||||||
Name string
|
Name string
|
||||||
Status int
|
Status int
|
||||||
Description string
|
Description string
|
||||||
|
IsCoarse bool
|
||||||
}
|
}
|
||||||
|
|
||||||
func (q *Queries) InsertProjectRequirement(ctx context.Context, arg InsertProjectRequirementParams) (sql.Result, error) {
|
func (q *Queries) InsertProjectRequirement(ctx context.Context, arg InsertProjectRequirementParams) (sql.Result, error) {
|
||||||
@@ -175,11 +176,12 @@ func (q *Queries) InsertProjectRequirement(ctx context.Context, arg InsertProjec
|
|||||||
arg.Name,
|
arg.Name,
|
||||||
arg.Status,
|
arg.Status,
|
||||||
arg.Description,
|
arg.Description,
|
||||||
|
arg.IsCoarse,
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
const listProjectRequirements = `-- name: ListProjectRequirements :many
|
const listProjectRequirements = `-- name: ListProjectRequirements :many
|
||||||
SELECT id, scope_id, project_id, name, status, description FROM project_requirement WHERE project_id = ?
|
SELECT id, scope_id, project_id, name, status, description, is_coarse FROM project_requirement WHERE project_id = ?
|
||||||
`
|
`
|
||||||
|
|
||||||
func (q *Queries) ListProjectRequirements(ctx context.Context, projectID int) ([]ProjectRequirement, error) {
|
func (q *Queries) ListProjectRequirements(ctx context.Context, projectID int) ([]ProjectRequirement, error) {
|
||||||
@@ -198,6 +200,7 @@ func (q *Queries) ListProjectRequirements(ctx context.Context, projectID int) ([
|
|||||||
&i.Name,
|
&i.Name,
|
||||||
&i.Status,
|
&i.Status,
|
||||||
&i.Description,
|
&i.Description,
|
||||||
|
&i.IsCoarse,
|
||||||
); err != nil {
|
); err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
@@ -326,7 +329,8 @@ const updateProjectRequirement = `-- name: UpdateProjectRequirement :exec
|
|||||||
UPDATE project_requirement
|
UPDATE project_requirement
|
||||||
SET name = ?,
|
SET name = ?,
|
||||||
status = ?,
|
status = ?,
|
||||||
description = ?
|
description = ?,
|
||||||
|
is_coarse = ?
|
||||||
WHERE id = ? AND scope_id = ?
|
WHERE id = ? AND scope_id = ?
|
||||||
`
|
`
|
||||||
|
|
||||||
@@ -334,6 +338,7 @@ type UpdateProjectRequirementParams struct {
|
|||||||
Name string
|
Name string
|
||||||
Status int
|
Status int
|
||||||
Description string
|
Description string
|
||||||
|
IsCoarse bool
|
||||||
ID int
|
ID int
|
||||||
ScopeID int
|
ScopeID int
|
||||||
}
|
}
|
||||||
@@ -343,6 +348,7 @@ func (q *Queries) UpdateProjectRequirement(ctx context.Context, arg UpdateProjec
|
|||||||
arg.Name,
|
arg.Name,
|
||||||
arg.Status,
|
arg.Status,
|
||||||
arg.Description,
|
arg.Description,
|
||||||
|
arg.IsCoarse,
|
||||||
arg.ID,
|
arg.ID,
|
||||||
arg.ScopeID,
|
arg.ScopeID,
|
||||||
)
|
)
|
||||||
|
|||||||
@@ -139,7 +139,7 @@ func (r *projectRepository) FetchRequirements(ctx context.Context, scopeID int,
|
|||||||
return []entities.Requirement{}, []entities.RequirementStat{}, nil
|
return []entities.Requirement{}, []entities.RequirementStat{}, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
query, args, err := squirrel.Select("id, scope_id, project_id, name, status, description").
|
query, args, err := squirrel.Select("id, scope_id, project_id, name, status, description, is_coarse").
|
||||||
From("project_requirement").
|
From("project_requirement").
|
||||||
Where(squirrel.Eq{"scope_id": scopeID}).
|
Where(squirrel.Eq{"scope_id": scopeID}).
|
||||||
Where(squirrel.Eq{"id": requirementIDs}).
|
Where(squirrel.Eq{"id": requirementIDs}).
|
||||||
@@ -162,6 +162,7 @@ func (r *projectRepository) FetchRequirements(ctx context.Context, scopeID int,
|
|||||||
&requirement.Name,
|
&requirement.Name,
|
||||||
&requirement.Status,
|
&requirement.Status,
|
||||||
&requirement.Description,
|
&requirement.Description,
|
||||||
|
&requirement.IsCoarse,
|
||||||
); err != nil {
|
); err != nil {
|
||||||
return nil, nil, err
|
return nil, nil, err
|
||||||
}
|
}
|
||||||
@@ -217,6 +218,7 @@ func (r *projectRepository) ListRequirements(ctx context.Context, projectID int)
|
|||||||
ProjectID: row.ProjectID,
|
ProjectID: row.ProjectID,
|
||||||
Name: row.Name,
|
Name: row.Name,
|
||||||
Description: row.Description,
|
Description: row.Description,
|
||||||
|
IsCoarse: row.IsCoarse,
|
||||||
Status: models.Status(row.Status),
|
Status: models.Status(row.Status),
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
@@ -240,6 +242,7 @@ func (r *projectRepository) CreateRequirement(ctx context.Context, requirement e
|
|||||||
Name: requirement.Name,
|
Name: requirement.Name,
|
||||||
Status: int(requirement.Status),
|
Status: int(requirement.Status),
|
||||||
Description: requirement.Description,
|
Description: requirement.Description,
|
||||||
|
IsCoarse: requirement.IsCoarse,
|
||||||
})
|
})
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
@@ -261,6 +264,7 @@ func (r *projectRepository) UpdateRequirement(ctx context.Context, requirement e
|
|||||||
Name: requirement.Name,
|
Name: requirement.Name,
|
||||||
Status: int(requirement.Status),
|
Status: int(requirement.Status),
|
||||||
Description: requirement.Description,
|
Description: requirement.Description,
|
||||||
|
IsCoarse: requirement.IsCoarse,
|
||||||
ID: requirement.ID,
|
ID: requirement.ID,
|
||||||
ScopeID: requirement.ScopeID,
|
ScopeID: requirement.ScopeID,
|
||||||
})
|
})
|
||||||
|
|||||||
@@ -23,14 +23,15 @@ DELETE FROM project WHERE id = ? AND scope_id = ?;
|
|||||||
SELECT * FROM project_requirement WHERE project_id = ?;
|
SELECT * FROM project_requirement WHERE project_id = ?;
|
||||||
|
|
||||||
-- name: InsertProjectRequirement :execresult
|
-- name: InsertProjectRequirement :execresult
|
||||||
INSERT INTO project_requirement (scope_id, project_id, name, status, description)
|
INSERT INTO project_requirement (scope_id, project_id, name, status, description, is_coarse)
|
||||||
VALUES (?, ?, ?, ?, ?);
|
VALUES (?, ?, ?, ?, ?, ?);
|
||||||
|
|
||||||
-- name: UpdateProjectRequirement :exec
|
-- name: UpdateProjectRequirement :exec
|
||||||
UPDATE project_requirement
|
UPDATE project_requirement
|
||||||
SET name = ?,
|
SET name = ?,
|
||||||
status = ?,
|
status = ?,
|
||||||
description = ?
|
description = ?,
|
||||||
|
is_coarse = ?
|
||||||
WHERE id = ? AND scope_id = ?;
|
WHERE id = ? AND scope_id = ?;
|
||||||
|
|
||||||
-- name: DeleteProjectRequirement :exec
|
-- name: DeleteProjectRequirement :exec
|
||||||
|
|||||||
@@ -0,0 +1,9 @@
|
|||||||
|
-- +goose Up
|
||||||
|
-- +goose StatementBegin
|
||||||
|
ALTER TABLE project_requirement ADD COLUMN is_coarse BOOLEAN NOT NULL DEFAULT 0;
|
||||||
|
-- +goose StatementEnd
|
||||||
|
|
||||||
|
-- +goose Down
|
||||||
|
-- +goose StatementBegin
|
||||||
|
ALTER TABLE project_requirement DROP COLUMN IF EXISTS is_coarse;
|
||||||
|
-- +goose StatementEnd
|
||||||
@@ -3,7 +3,6 @@ package items
|
|||||||
import (
|
import (
|
||||||
"git.aiterp.net/stufflog3/stufflog3/entities"
|
"git.aiterp.net/stufflog3/stufflog3/entities"
|
||||||
"git.aiterp.net/stufflog3/stufflog3/usecases/scopes"
|
"git.aiterp.net/stufflog3/stufflog3/usecases/scopes"
|
||||||
"sort"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
type Result struct {
|
type Result struct {
|
||||||
@@ -53,13 +52,22 @@ func (r *Result) AddStat(scope scopes.Result, stat entities.ItemStat) {
|
|||||||
Acquired: stat.Acquired,
|
Acquired: stat.Acquired,
|
||||||
Required: stat.Required,
|
Required: stat.Required,
|
||||||
})
|
})
|
||||||
r.SortStats()
|
r.SortStats(scope)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (r *Result) SortStats() {
|
func (r *Result) SortStats(scope scopes.Result) {
|
||||||
sort.Slice(r.Stats, func(i, j int) bool {
|
sortedStats := make([]ResultStat, 0, len(r.Stats))
|
||||||
return r.Stats[i].Name < r.Stats[j].Name
|
|
||||||
})
|
for _, scopeStat := range scope.Stats {
|
||||||
|
for _, itemStat := range r.Stats {
|
||||||
|
if itemStat.ID == scopeStat.ID {
|
||||||
|
sortedStats = append(sortedStats, itemStat)
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
r.Stats = sortedStats
|
||||||
}
|
}
|
||||||
|
|
||||||
func (r *Result) RemoveStat(id int) {
|
func (r *Result) RemoveStat(id int) {
|
||||||
@@ -112,7 +120,7 @@ func generateResult(item entities.Item, scope scopes.Result, progresses []entiti
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
res.SortStats()
|
res.SortStats(scope)
|
||||||
|
|
||||||
return res
|
return res
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -6,6 +6,7 @@ import (
|
|||||||
"git.aiterp.net/stufflog3/stufflog3/usecases/items"
|
"git.aiterp.net/stufflog3/stufflog3/usecases/items"
|
||||||
"git.aiterp.net/stufflog3/stufflog3/usecases/scopes"
|
"git.aiterp.net/stufflog3/stufflog3/usecases/scopes"
|
||||||
"math"
|
"math"
|
||||||
|
"sort"
|
||||||
"time"
|
"time"
|
||||||
)
|
)
|
||||||
|
|
||||||
@@ -60,6 +61,7 @@ type RequirementResult struct {
|
|||||||
TotalAcquired int `json:"totalAcquired"`
|
TotalAcquired int `json:"totalAcquired"`
|
||||||
TotalRequired int `json:"totalRequired"`
|
TotalRequired int `json:"totalRequired"`
|
||||||
TotalPlanned int `json:"totalPlanned"`
|
TotalPlanned int `json:"totalPlanned"`
|
||||||
|
IsCoarse bool `json:"isCoarse"`
|
||||||
Stats []RequirementResultStat `json:"stats"`
|
Stats []RequirementResultStat `json:"stats"`
|
||||||
Items []items.Result `json:"items"`
|
Items []items.Result `json:"items"`
|
||||||
|
|
||||||
@@ -150,6 +152,7 @@ func generateRequirementResult(req entities.Requirement, scope scopes.Result, re
|
|||||||
Description: req.Description,
|
Description: req.Description,
|
||||||
Status: req.Status,
|
Status: req.Status,
|
||||||
StatusName: scope.StatusName(req.Status),
|
StatusName: scope.StatusName(req.Status),
|
||||||
|
IsCoarse: req.IsCoarse,
|
||||||
Stats: make([]RequirementResultStat, 0, 8),
|
Stats: make([]RequirementResultStat, 0, 8),
|
||||||
Items: make([]items.Result, 0, 8),
|
Items: make([]items.Result, 0, 8),
|
||||||
|
|
||||||
@@ -194,6 +197,21 @@ func generateRequirementResult(req entities.Requirement, scope scopes.Result, re
|
|||||||
resReq.Items = append(resReq.Items, item)
|
resReq.Items = append(resReq.Items, item)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Sort items so that they're in order of created, with acquired items being in order of acquired below.
|
||||||
|
sort.Slice(resReq.Items, func(i, j int) bool {
|
||||||
|
ii := resReq.Items[i]
|
||||||
|
ij := resReq.Items[j]
|
||||||
|
if ii.AcquiredTime != nil && ij.AcquiredTime != nil {
|
||||||
|
return ii.AcquiredTime.Before(*ij.AcquiredTime)
|
||||||
|
} else if ii.AcquiredTime != nil && ij.AcquiredTime == nil {
|
||||||
|
return false
|
||||||
|
} else if ii.AcquiredTime == nil && ij.AcquiredTime != nil {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
|
return ii.CreatedTime.Before(ij.CreatedTime)
|
||||||
|
})
|
||||||
|
|
||||||
for _, stat := range scope.Stats {
|
for _, stat := range scope.Stats {
|
||||||
if rs := resStats[stat.ID]; rs != nil && rs.Required > 0 {
|
if rs := resStats[stat.ID]; rs != nil && rs.Required > 0 {
|
||||||
resReq.Stats = append(resReq.Stats, *rs)
|
resReq.Stats = append(resReq.Stats, *rs)
|
||||||
|
|||||||
Reference in New Issue
Block a user