diff --git a/api/project.go b/api/project.go index 75a6fc2..30f12e1 100644 --- a/api/project.go +++ b/api/project.go @@ -63,6 +63,9 @@ func Project(g *gin.RouterGroup, db database.Database) { if project.Active && project.StatusTag != nil { project.StatusTag = nil } + if project.SubtractAmount < 0 { + project.SubtractAmount = 0 + } return &models.ProjectResult{ Project: project, diff --git a/database/postgres/project.go b/database/postgres/project.go index 4af290f..04e25dc 100644 --- a/database/postgres/project.go +++ b/database/postgres/project.go @@ -75,9 +75,9 @@ func (r *projectRepository) List(ctx context.Context, filter models.ProjectFilte func (r *projectRepository) Insert(ctx context.Context, project models.Project) error { _, err := r.db.NamedExecContext(ctx, ` INSERT INTO project( - project_id, user_id, name, description, icon, active, created_time, start_time, end_time, status_tag, favorite + project_id, user_id, name, description, icon, active, created_time, start_time, end_time, subtract_amount, status_tag, favorite ) VALUES ( - :project_id, :user_id, :name, :description, :icon, :active, :created_time, :start_time, :end_time, :status_tag, :favorite + :project_id, :user_id, :name, :description, :icon, :active, :created_time, :start_time, :end_time, :subtract_amount, :status_tag, :favorite ) `, &project) if err != nil { @@ -96,6 +96,7 @@ func (r *projectRepository) Update(ctx context.Context, project models.Project) active = :active, start_time = :start_time, end_time = :end_time, + subtract_amount = :subtract_amount, status_tag = :status_tag, favorite = :favorite WHERE project_id=:project_id diff --git a/migrations/postgres/20210405234346_add_project_column_subtract_amount.sql b/migrations/postgres/20210405234346_add_project_column_subtract_amount.sql new file mode 100644 index 0000000..fcb2d63 --- /dev/null +++ b/migrations/postgres/20210405234346_add_project_column_subtract_amount.sql @@ -0,0 +1,11 @@ +-- +goose Up +-- +goose StatementBegin +ALTER TABLE project + ADD COLUMN subtract_amount INT NOT NULL DEFAULT 0; +-- +goose StatementEnd + +-- +goose Down +-- +goose StatementBegin +ALTER TABLE project + DROP COLUMN IF EXISTS subtract_amount; +-- +goose StatementEnd diff --git a/models/project.go b/models/project.go index bb24eb5..1d5946d 100644 --- a/models/project.go +++ b/models/project.go @@ -6,17 +6,18 @@ import ( ) type Project struct { - ID string `json:"id" db:"project_id"` - UserID string `json:"-" db:"user_id"` - Name string `json:"name" db:"name"` - Description string `json:"description" db:"description"` - Icon string `json:"icon" db:"icon"` - Active bool `json:"active" db:"active"` - CreatedTime time.Time `json:"createdTime" db:"created_time"` - StartTime *time.Time `json:"startTime" db:"start_time"` - EndTime *time.Time `json:"endTime" db:"end_time"` - StatusTag *string `json:"statusTag" db:"status_tag"` - Favorite bool `json:"favorite" db:"favorite"` + ID string `json:"id" db:"project_id"` + UserID string `json:"-" db:"user_id"` + Name string `json:"name" db:"name"` + Description string `json:"description" db:"description"` + Icon string `json:"icon" db:"icon"` + Active bool `json:"active" db:"active"` + CreatedTime time.Time `json:"createdTime" db:"created_time"` + StartTime *time.Time `json:"startTime" db:"start_time"` + EndTime *time.Time `json:"endTime" db:"end_time"` + SubtractAmount int `json:"subtractAmount" db:"subtract_amount"` + StatusTag *string `json:"statusTag" db:"status_tag"` + Favorite bool `json:"favorite" db:"favorite"` } func (project *Project) Update(update ProjectUpdate) { @@ -46,6 +47,12 @@ func (project *Project) Update(update ProjectUpdate) { if update.ClearEndTime { project.EndTime = nil } + if update.SubtractAmount != nil { + project.SubtractAmount = *update.SubtractAmount + if project.SubtractAmount < 0 { + project.SubtractAmount = 0 + } + } if update.StatusTag != nil { project.StatusTag = update.StatusTag } @@ -70,6 +77,7 @@ type ProjectUpdate struct { ClearStartTime bool `json:"clearStartTime"` EndTime *time.Time `json:"endTime"` ClearEndTime bool `json:"clearEndTime"` + SubtractAmount *int `json:"subtractAmount"` StatusTag *string `json:"statusTag"` ClearStatusTag bool `json:"clearStatusTag"` Favorite *bool `json:"favorite"` diff --git a/svelte-ui/src/components/Progress.svelte b/svelte-ui/src/components/Progress.svelte index 0cdff1b..07f6ef6 100644 --- a/svelte-ui/src/components/Progress.svelte +++ b/svelte-ui/src/components/Progress.svelte @@ -28,39 +28,46 @@ let title = ""; $: { - let level = Math.floor(count / target); - if (level >= COLORS.length - 1 || (level > 1 && green)) { - offs = 0; - ons = target; - offClass = "gold"; - onClass = "diamond"; - } else { - if (count > 0 && count == (level * target)) { - level -= 1; + if (count > 0 && target > 0) { + let level = Math.floor(count / target); + if (level >= COLORS.length - 1 || (level > 1 && green)) { + offs = 0; + ons = target; + offClass = "gold"; + onClass = "diamond"; + } else { + if (count > 0 && count == (level * target)) { + level -= 1; + } + + ons = count - (level * target); + offs = target - ons; + offClass = COLORS[level]; + onClass = COLORS[level + 1]; } - ons = count - (level * target); - offs = target - ons; - offClass = COLORS[level]; - onClass = COLORS[level + 1]; - } + if (green) { + onClass = "green" + offClass = "none" + } + if (gray) { + onClass = "gray" + offClass = "none" + } + if (contextColor) { + onClass = "sccpb" + offClass = "none" + } - if (green) { - onClass = "green" - offClass = "none" - } - if (gray) { - onClass = "gray" - offClass = "none" - } - if (contextColor) { - onClass = "sccpb" - offClass = "none" + // Mark it non-segmented if the target is too high. This prevents a clunky progress bar, + // or a browser freeze if you set the target very high. + nonSegmented = (target >= 60); + } else { + offClass = "none"; + nonSegmented = false; + ons = 0; + offs = 1; } - - // Mark it non-segmented if the target is too high. This prevents a clunky progress bar, - // or a browser freeze if you set the target very high. - nonSegmented = (target >= 60); } $: { @@ -94,10 +101,10 @@
{/if} {:else} - {#each {length: ons} as _} + {#each {length: Math.max(ons, 0)} as _}
{/each} - {#each {length: offs} as _} + {#each {length: Math.max(offs, 0)} as _}
{/each} {/if} diff --git a/svelte-ui/src/components/ProgressNumbers.svelte b/svelte-ui/src/components/ProgressNumbers.svelte index e066d99..ab7134f 100644 --- a/svelte-ui/src/components/ProgressNumbers.svelte +++ b/svelte-ui/src/components/ProgressNumbers.svelte @@ -23,6 +23,9 @@ progressTarget = Math.max((project.tasks||[]).map(t => t.itemAmount * (t.item.groupWeight || 1)).reduce((n,m) => n+m, 0), 1); + progressAmount = Math.max(progressAmount - project.subtractAmount, 0); + progressTarget = Math.max(progressTarget - project.subtractAmount, 0); + if (project.endTime) { const start = Date.parse(project.startTime || project.createdTime); const end = Date.parse(project.endTime); diff --git a/svelte-ui/src/components/ProjectProgress.svelte b/svelte-ui/src/components/ProjectProgress.svelte index f280fc3..c2382ed 100644 --- a/svelte-ui/src/components/ProjectProgress.svelte +++ b/svelte-ui/src/components/ProjectProgress.svelte @@ -5,6 +5,7 @@ interface ProjectLike { tasks?: TaskResult[] statusTag?: string + subtractAmount?: number, } export let project: ProjectLike; @@ -12,11 +13,16 @@ let progressAmount: number; let progressTarget: number; - $: progressAmount = (project.tasks||[]).map(t => (t.active || t.statusTag === "to do" || t.statusTag === "on hold") - ? Math.min(t.completedAmount, t.itemAmount) * (t.item.groupWeight || 1) - : t.itemAmount * (t.item.groupWeight || 1) - ).reduce((n,m) => n+m, 0); - $: progressTarget = Math.max((project.tasks||[]).map(t => t.itemAmount * (t.item.groupWeight || 1)).reduce((n,m) => n+m, 0), 1); + $: { + progressAmount = (project.tasks||[]).map(t => (t.active || t.statusTag === "to do" || t.statusTag === "on hold") + ? Math.min(t.completedAmount, t.itemAmount) * (t.item.groupWeight || 1) + : t.itemAmount * (t.item.groupWeight || 1) + ).reduce((n,m) => n+m, 0); + progressTarget = Math.max((project.tasks||[]).map(t => t.itemAmount * (t.item.groupWeight || 1)).reduce((n,m) => n+m, 0), 1); + + progressAmount = Math.max(progressAmount - (project.subtractAmount||0), 0); + progressTarget = Math.max(progressTarget - (project.subtractAmount||0), 0); + } diff --git a/svelte-ui/src/forms/ProjectForm.svelte b/svelte-ui/src/forms/ProjectForm.svelte index 155c395..4ea71bb 100644 --- a/svelte-ui/src/forms/ProjectForm.svelte +++ b/svelte-ui/src/forms/ProjectForm.svelte @@ -26,6 +26,7 @@ createdTime: "", tasks: [], favorite: false, + subtractAmount: 0, } let verb = "Add"; if (md.name === "project.edit" || md.name === "project.delete") { @@ -42,6 +43,7 @@ let statusTag = project.statusTag || ""; let icon = project.icon; let favorite = project.favorite; + let subtractAmount = project.subtractAmount; let error = null; let loading = false; @@ -57,6 +59,7 @@ startTime: ( startTime == "" ) ? null : new Date(startTime), endTime: ( endTime == "" ) ? null : new Date(endTime), statusTag: statusTag !== "" ? statusTag : null, + subtractAmount: Math.min(subtractAmount, 0), name, description, icon, favorite, }).then(() => { @@ -85,6 +88,7 @@ active: statusTag === "", statusTag: statusTag || null, clearStatusTag: statusTag === "", + subtractAmount: subtractAmount, name, description, icon, favorite, }).then(() => { @@ -119,6 +123,8 @@ {#if endTime != ""} + + {/if} diff --git a/svelte-ui/src/models/project.ts b/svelte-ui/src/models/project.ts index b11c3fa..d1dce3f 100644 --- a/svelte-ui/src/models/project.ts +++ b/svelte-ui/src/models/project.ts @@ -9,6 +9,7 @@ export default interface Project { active: boolean createdTime: string favorite: boolean + subtractAmount: number startTime?: string endTime?: string statusTag?: string @@ -30,6 +31,7 @@ export interface ProjectInput { description: string icon: IconName active: boolean + subtractAmount: number startTime?: string | Date endTime?: string | Date statusTag?: string @@ -47,5 +49,6 @@ export interface ProjectUpdate { clearEndTime?: boolean statusTag?: string clearStatusTag?: boolean + subtractAmount?: number favorite?: boolean } \ No newline at end of file