This commit is contained in:
@@ -63,6 +63,9 @@ func Project(g *gin.RouterGroup, db database.Database) {
|
|||||||
if project.Active && project.StatusTag != nil {
|
if project.Active && project.StatusTag != nil {
|
||||||
project.StatusTag = nil
|
project.StatusTag = nil
|
||||||
}
|
}
|
||||||
|
if project.SubtractAmount < 0 {
|
||||||
|
project.SubtractAmount = 0
|
||||||
|
}
|
||||||
|
|
||||||
return &models.ProjectResult{
|
return &models.ProjectResult{
|
||||||
Project: project,
|
Project: project,
|
||||||
|
|||||||
@@ -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 {
|
func (r *projectRepository) Insert(ctx context.Context, project models.Project) error {
|
||||||
_, err := r.db.NamedExecContext(ctx, `
|
_, err := r.db.NamedExecContext(ctx, `
|
||||||
INSERT INTO project(
|
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 (
|
) 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)
|
`, &project)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
@@ -96,6 +96,7 @@ func (r *projectRepository) Update(ctx context.Context, project models.Project)
|
|||||||
active = :active,
|
active = :active,
|
||||||
start_time = :start_time,
|
start_time = :start_time,
|
||||||
end_time = :end_time,
|
end_time = :end_time,
|
||||||
|
subtract_amount = :subtract_amount,
|
||||||
status_tag = :status_tag,
|
status_tag = :status_tag,
|
||||||
favorite = :favorite
|
favorite = :favorite
|
||||||
WHERE project_id=:project_id
|
WHERE project_id=:project_id
|
||||||
|
|||||||
@@ -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
|
||||||
+19
-11
@@ -6,17 +6,18 @@ import (
|
|||||||
)
|
)
|
||||||
|
|
||||||
type Project struct {
|
type Project struct {
|
||||||
ID string `json:"id" db:"project_id"`
|
ID string `json:"id" db:"project_id"`
|
||||||
UserID string `json:"-" db:"user_id"`
|
UserID string `json:"-" db:"user_id"`
|
||||||
Name string `json:"name" db:"name"`
|
Name string `json:"name" db:"name"`
|
||||||
Description string `json:"description" db:"description"`
|
Description string `json:"description" db:"description"`
|
||||||
Icon string `json:"icon" db:"icon"`
|
Icon string `json:"icon" db:"icon"`
|
||||||
Active bool `json:"active" db:"active"`
|
Active bool `json:"active" db:"active"`
|
||||||
CreatedTime time.Time `json:"createdTime" db:"created_time"`
|
CreatedTime time.Time `json:"createdTime" db:"created_time"`
|
||||||
StartTime *time.Time `json:"startTime" db:"start_time"`
|
StartTime *time.Time `json:"startTime" db:"start_time"`
|
||||||
EndTime *time.Time `json:"endTime" db:"end_time"`
|
EndTime *time.Time `json:"endTime" db:"end_time"`
|
||||||
StatusTag *string `json:"statusTag" db:"status_tag"`
|
SubtractAmount int `json:"subtractAmount" db:"subtract_amount"`
|
||||||
Favorite bool `json:"favorite" db:"favorite"`
|
StatusTag *string `json:"statusTag" db:"status_tag"`
|
||||||
|
Favorite bool `json:"favorite" db:"favorite"`
|
||||||
}
|
}
|
||||||
|
|
||||||
func (project *Project) Update(update ProjectUpdate) {
|
func (project *Project) Update(update ProjectUpdate) {
|
||||||
@@ -46,6 +47,12 @@ func (project *Project) Update(update ProjectUpdate) {
|
|||||||
if update.ClearEndTime {
|
if update.ClearEndTime {
|
||||||
project.EndTime = nil
|
project.EndTime = nil
|
||||||
}
|
}
|
||||||
|
if update.SubtractAmount != nil {
|
||||||
|
project.SubtractAmount = *update.SubtractAmount
|
||||||
|
if project.SubtractAmount < 0 {
|
||||||
|
project.SubtractAmount = 0
|
||||||
|
}
|
||||||
|
}
|
||||||
if update.StatusTag != nil {
|
if update.StatusTag != nil {
|
||||||
project.StatusTag = update.StatusTag
|
project.StatusTag = update.StatusTag
|
||||||
}
|
}
|
||||||
@@ -70,6 +77,7 @@ type ProjectUpdate struct {
|
|||||||
ClearStartTime bool `json:"clearStartTime"`
|
ClearStartTime bool `json:"clearStartTime"`
|
||||||
EndTime *time.Time `json:"endTime"`
|
EndTime *time.Time `json:"endTime"`
|
||||||
ClearEndTime bool `json:"clearEndTime"`
|
ClearEndTime bool `json:"clearEndTime"`
|
||||||
|
SubtractAmount *int `json:"subtractAmount"`
|
||||||
StatusTag *string `json:"statusTag"`
|
StatusTag *string `json:"statusTag"`
|
||||||
ClearStatusTag bool `json:"clearStatusTag"`
|
ClearStatusTag bool `json:"clearStatusTag"`
|
||||||
Favorite *bool `json:"favorite"`
|
Favorite *bool `json:"favorite"`
|
||||||
|
|||||||
@@ -28,39 +28,46 @@
|
|||||||
let title = "";
|
let title = "";
|
||||||
|
|
||||||
$: {
|
$: {
|
||||||
let level = Math.floor(count / target);
|
if (count > 0 && target > 0) {
|
||||||
if (level >= COLORS.length - 1 || (level > 1 && green)) {
|
let level = Math.floor(count / target);
|
||||||
offs = 0;
|
if (level >= COLORS.length - 1 || (level > 1 && green)) {
|
||||||
ons = target;
|
offs = 0;
|
||||||
offClass = "gold";
|
ons = target;
|
||||||
onClass = "diamond";
|
offClass = "gold";
|
||||||
} else {
|
onClass = "diamond";
|
||||||
if (count > 0 && count == (level * target)) {
|
} else {
|
||||||
level -= 1;
|
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);
|
if (green) {
|
||||||
offs = target - ons;
|
onClass = "green"
|
||||||
offClass = COLORS[level];
|
offClass = "none"
|
||||||
onClass = COLORS[level + 1];
|
}
|
||||||
}
|
if (gray) {
|
||||||
|
onClass = "gray"
|
||||||
|
offClass = "none"
|
||||||
|
}
|
||||||
|
if (contextColor) {
|
||||||
|
onClass = "sccpb"
|
||||||
|
offClass = "none"
|
||||||
|
}
|
||||||
|
|
||||||
if (green) {
|
// Mark it non-segmented if the target is too high. This prevents a clunky progress bar,
|
||||||
onClass = "green"
|
// or a browser freeze if you set the target very high.
|
||||||
offClass = "none"
|
nonSegmented = (target >= 60);
|
||||||
|
} else {
|
||||||
|
offClass = "none";
|
||||||
|
nonSegmented = false;
|
||||||
|
ons = 0;
|
||||||
|
offs = 1;
|
||||||
}
|
}
|
||||||
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);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
$: {
|
$: {
|
||||||
@@ -94,10 +101,10 @@
|
|||||||
<div style="flex: {offs}" class={"non-segmented off " + offClass}></div>
|
<div style="flex: {offs}" class={"non-segmented off " + offClass}></div>
|
||||||
{/if}
|
{/if}
|
||||||
{:else}
|
{:else}
|
||||||
{#each {length: ons} as _}
|
{#each {length: Math.max(ons, 0)} as _}
|
||||||
<div class={"on " + onClass}></div>
|
<div class={"on " + onClass}></div>
|
||||||
{/each}
|
{/each}
|
||||||
{#each {length: offs} as _}
|
{#each {length: Math.max(offs, 0)} as _}
|
||||||
<div class={"off " + offClass}></div>
|
<div class={"off " + offClass}></div>
|
||||||
{/each}
|
{/each}
|
||||||
{/if}
|
{/if}
|
||||||
|
|||||||
@@ -23,6 +23,9 @@
|
|||||||
|
|
||||||
progressTarget = Math.max((project.tasks||[]).map(t => t.itemAmount * (t.item.groupWeight || 1)).reduce((n,m) => n+m, 0), 1);
|
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) {
|
if (project.endTime) {
|
||||||
const start = Date.parse(project.startTime || project.createdTime);
|
const start = Date.parse(project.startTime || project.createdTime);
|
||||||
const end = Date.parse(project.endTime);
|
const end = Date.parse(project.endTime);
|
||||||
|
|||||||
@@ -5,6 +5,7 @@
|
|||||||
interface ProjectLike {
|
interface ProjectLike {
|
||||||
tasks?: TaskResult[]
|
tasks?: TaskResult[]
|
||||||
statusTag?: string
|
statusTag?: string
|
||||||
|
subtractAmount?: number,
|
||||||
}
|
}
|
||||||
|
|
||||||
export let project: ProjectLike;
|
export let project: ProjectLike;
|
||||||
@@ -12,11 +13,16 @@
|
|||||||
let progressAmount: number;
|
let progressAmount: number;
|
||||||
let progressTarget: 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)
|
progressAmount = (project.tasks||[]).map(t => (t.active || t.statusTag === "to do" || t.statusTag === "on hold")
|
||||||
: t.itemAmount * (t.item.groupWeight || 1)
|
? Math.min(t.completedAmount, t.itemAmount) * (t.item.groupWeight || 1)
|
||||||
).reduce((n,m) => n+m, 0);
|
: t.itemAmount * (t.item.groupWeight || 1)
|
||||||
$: progressTarget = Math.max((project.tasks||[]).map(t => t.itemAmount * (t.item.groupWeight || 1)).reduce((n,m) => n+m, 0), 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);
|
||||||
|
}
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<Progress thin contextColor count={progressAmount} target={progressTarget} />
|
<Progress thin contextColor count={progressAmount} target={progressTarget} />
|
||||||
|
|||||||
@@ -26,6 +26,7 @@
|
|||||||
createdTime: "",
|
createdTime: "",
|
||||||
tasks: [],
|
tasks: [],
|
||||||
favorite: false,
|
favorite: false,
|
||||||
|
subtractAmount: 0,
|
||||||
}
|
}
|
||||||
let verb = "Add";
|
let verb = "Add";
|
||||||
if (md.name === "project.edit" || md.name === "project.delete") {
|
if (md.name === "project.edit" || md.name === "project.delete") {
|
||||||
@@ -42,6 +43,7 @@
|
|||||||
let statusTag = project.statusTag || "";
|
let statusTag = project.statusTag || "";
|
||||||
let icon = project.icon;
|
let icon = project.icon;
|
||||||
let favorite = project.favorite;
|
let favorite = project.favorite;
|
||||||
|
let subtractAmount = project.subtractAmount;
|
||||||
let error = null;
|
let error = null;
|
||||||
let loading = false;
|
let loading = false;
|
||||||
|
|
||||||
@@ -57,6 +59,7 @@
|
|||||||
startTime: ( startTime == "" ) ? null : new Date(startTime),
|
startTime: ( startTime == "" ) ? null : new Date(startTime),
|
||||||
endTime: ( endTime == "" ) ? null : new Date(endTime),
|
endTime: ( endTime == "" ) ? null : new Date(endTime),
|
||||||
statusTag: statusTag !== "" ? statusTag : null,
|
statusTag: statusTag !== "" ? statusTag : null,
|
||||||
|
subtractAmount: Math.min(subtractAmount, 0),
|
||||||
|
|
||||||
name, description, icon, favorite,
|
name, description, icon, favorite,
|
||||||
}).then(() => {
|
}).then(() => {
|
||||||
@@ -85,6 +88,7 @@
|
|||||||
active: statusTag === "",
|
active: statusTag === "",
|
||||||
statusTag: statusTag || null,
|
statusTag: statusTag || null,
|
||||||
clearStatusTag: statusTag === "",
|
clearStatusTag: statusTag === "",
|
||||||
|
subtractAmount: subtractAmount,
|
||||||
|
|
||||||
name, description, icon, favorite,
|
name, description, icon, favorite,
|
||||||
}).then(() => {
|
}).then(() => {
|
||||||
@@ -119,6 +123,8 @@
|
|||||||
{#if endTime != ""}
|
{#if endTime != ""}
|
||||||
<label for="endTime">Start time</label>
|
<label for="endTime">Start time</label>
|
||||||
<StartTimeSelect disabled={deletion} bind:value={startTime} />
|
<StartTimeSelect disabled={deletion} bind:value={startTime} />
|
||||||
|
<label for="subtractAmount">Subtract progress</label>
|
||||||
|
<input disabled={deletion} name="subtractAmount" type="number" bind:value={subtractAmount} />
|
||||||
{/if}
|
{/if}
|
||||||
<label for="statusTag">Status</label>
|
<label for="statusTag">Status</label>
|
||||||
<StatusTagSelect disabled={deletion} isProject bind:value={statusTag} />
|
<StatusTagSelect disabled={deletion} isProject bind:value={statusTag} />
|
||||||
|
|||||||
@@ -9,6 +9,7 @@ export default interface Project {
|
|||||||
active: boolean
|
active: boolean
|
||||||
createdTime: string
|
createdTime: string
|
||||||
favorite: boolean
|
favorite: boolean
|
||||||
|
subtractAmount: number
|
||||||
startTime?: string
|
startTime?: string
|
||||||
endTime?: string
|
endTime?: string
|
||||||
statusTag?: string
|
statusTag?: string
|
||||||
@@ -30,6 +31,7 @@ export interface ProjectInput {
|
|||||||
description: string
|
description: string
|
||||||
icon: IconName
|
icon: IconName
|
||||||
active: boolean
|
active: boolean
|
||||||
|
subtractAmount: number
|
||||||
startTime?: string | Date
|
startTime?: string | Date
|
||||||
endTime?: string | Date
|
endTime?: string | Date
|
||||||
statusTag?: string
|
statusTag?: string
|
||||||
@@ -47,5 +49,6 @@ export interface ProjectUpdate {
|
|||||||
clearEndTime?: boolean
|
clearEndTime?: boolean
|
||||||
statusTag?: string
|
statusTag?: string
|
||||||
clearStatusTag?: boolean
|
clearStatusTag?: boolean
|
||||||
|
subtractAmount?: number
|
||||||
favorite?: boolean
|
favorite?: boolean
|
||||||
}
|
}
|
||||||
Reference in New Issue
Block a user