diff --git a/api/project.go b/api/project.go index f685215..75a6fc2 100644 --- a/api/project.go +++ b/api/project.go @@ -47,6 +47,9 @@ func Project(g *gin.RouterGroup, db database.Database) { if project.EndTime != nil && project.EndTime.Before(time.Now()) { return nil, slerrors.BadRequest("Project end time must be later than current time.") } + if project.StartTime != nil && (project.EndTime == nil || !project.StartTime.Before(*project.EndTime)) { + return nil, slerrors.BadRequest("Project start time must be before end time.") + } project.ID = generate.ProjectID() project.UserID = auth.UserID(c) @@ -80,6 +83,11 @@ func Project(g *gin.RouterGroup, db database.Database) { } project.Update(update) + + if project.StartTime != nil && (project.EndTime == nil || !project.StartTime.Before(*project.EndTime)) { + return nil, slerrors.BadRequest("Project start time must be before end time.") + } + err = db.Projects().Update(c.Request.Context(), project.Project) if err != nil { return nil, err diff --git a/database/postgres/project.go b/database/postgres/project.go index f7d9d63..4af290f 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, end_time, status_tag, favorite + project_id, user_id, name, description, icon, active, created_time, start_time, end_time, status_tag, favorite ) VALUES ( - :project_id, :user_id, :name, :description, :icon, :active, :created_time, :end_time, :status_tag, :favorite + :project_id, :user_id, :name, :description, :icon, :active, :created_time, :start_time, :end_time, :status_tag, :favorite ) `, &project) if err != nil { @@ -94,6 +94,7 @@ func (r *projectRepository) Update(ctx context.Context, project models.Project) description = :description, icon = :icon, active = :active, + start_time = :start_time, end_time = :end_time, status_tag = :status_tag, favorite = :favorite diff --git a/migrations/postgres/20210405154335_add_project_column_start_time.sql b/migrations/postgres/20210405154335_add_project_column_start_time.sql new file mode 100644 index 0000000..490918b --- /dev/null +++ b/migrations/postgres/20210405154335_add_project_column_start_time.sql @@ -0,0 +1,11 @@ +-- +goose Up +-- +goose StatementBegin +ALTER TABLE project + ADD COLUMN start_time TIMESTAMP; +-- +goose StatementEnd + +-- +goose Down +-- +goose StatementBegin +ALTER TABLE project + DROP COLUMN IF EXISTS start_time; +-- +goose StatementEnd diff --git a/models/project.go b/models/project.go index 89e8e56..bb24eb5 100644 --- a/models/project.go +++ b/models/project.go @@ -13,6 +13,7 @@ type Project struct { 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"` @@ -31,6 +32,13 @@ func (project *Project) Update(update ProjectUpdate) { if update.Active != nil { project.Active = *update.Active } + if update.StartTime != nil { + startTimeCopy := update.StartTime.UTC() + project.StartTime = &startTimeCopy + } + if update.ClearStartTime { + project.StartTime = nil + } if update.EndTime != nil { endTimeCopy := update.EndTime.UTC() project.EndTime = &endTimeCopy @@ -58,6 +66,8 @@ type ProjectUpdate struct { Description *string `json:"description"` Icon *string `json:"icon"` Active *bool `json:"active"` + StartTime *time.Time `json:"startTime"` + ClearStartTime bool `json:"clearStartTime"` EndTime *time.Time `json:"endTime"` ClearEndTime bool `json:"clearEndTime"` StatusTag *string `json:"statusTag"` diff --git a/svelte-ui/src/components/DaysLeft.svelte b/svelte-ui/src/components/DaysLeft.svelte index 32b6739..d445454 100644 --- a/svelte-ui/src/components/DaysLeft.svelte +++ b/svelte-ui/src/components/DaysLeft.svelte @@ -72,7 +72,7 @@ {:else if (started)} {amountStr}{compact ? "" : " "}{unit} {!compact ? "left" : ""} {:else} - In {amountStr}{compact ? "" : " "}{unit} + {compact ? "" : "In "}{amountStr}{compact ? "" : " "}{unit} {/if} diff --git a/svelte-ui/src/components/DeadlineSelect.svelte b/svelte-ui/src/components/DeadlineSelect.svelte index 707840e..d2422c0 100644 --- a/svelte-ui/src/components/DeadlineSelect.svelte +++ b/svelte-ui/src/components/DeadlineSelect.svelte @@ -75,17 +75,17 @@ options.push({ id: "this_year", - label: "This Year", + label: "End of this year", value: endOfYear(now), }); options.push({ id: "next_year", - label: "Next Year", + label: "End of next year", value: endOfYear(new Date(Date.now() + (365.25 * 86400000))), }); options.push({ id: "last_year", - label: "Last Year", + label: "End of last year", value: endOfYear(new Date(Date.now() - (365.25 * 86400000))), }); diff --git a/svelte-ui/src/components/ProgressNumbers.svelte b/svelte-ui/src/components/ProgressNumbers.svelte index 11270e3..e066d99 100644 --- a/svelte-ui/src/components/ProgressNumbers.svelte +++ b/svelte-ui/src/components/ProgressNumbers.svelte @@ -24,9 +24,9 @@ progressTarget = Math.max((project.tasks||[]).map(t => t.itemAmount * (t.item.groupWeight || 1)).reduce((n,m) => n+m, 0), 1); if (project.endTime) { - const start = Date.parse(project.createdTime); + const start = Date.parse(project.startTime || project.createdTime); const end = Date.parse(project.endTime); - const now = Math.min(Date.now(), end); + const now = Math.max(Math.min(Date.now(), end), start); timeProgress = (now - start) / (end - start); } else { @@ -37,7 +37,7 @@ if (goal != null) { const start = Date.parse(goal.startTime); const end = Date.parse(goal.endTime); - const now = Math.min(Date.now(), end); + const now = Math.max(Math.min(Date.now(), end), start); progressAmount = goal.completedAmount; progressTarget = goal.amount; diff --git a/svelte-ui/src/components/QLListItem.svelte b/svelte-ui/src/components/QLListItem.svelte index 0874919..0e910e3 100644 --- a/svelte-ui/src/components/QLListItem.svelte +++ b/svelte-ui/src/components/QLListItem.svelte @@ -33,7 +33,7 @@
{project.name}
{#if project.endTime}
- +
{/if} diff --git a/svelte-ui/src/components/StartTimeSelect.svelte b/svelte-ui/src/components/StartTimeSelect.svelte new file mode 100644 index 0000000..5864e55 --- /dev/null +++ b/svelte-ui/src/components/StartTimeSelect.svelte @@ -0,0 +1,118 @@ + + + + +{#if selected === "custom"} + +{/if} \ No newline at end of file diff --git a/svelte-ui/src/forms/ProjectForm.svelte b/svelte-ui/src/forms/ProjectForm.svelte index d801e67..a347cb2 100644 --- a/svelte-ui/src/forms/ProjectForm.svelte +++ b/svelte-ui/src/forms/ProjectForm.svelte @@ -4,6 +4,7 @@ import DeadlineSelect from "../components/DeadlineSelect.svelte"; import IconSelect from "../components/IconSelect.svelte"; import Modal from "../components/Modal.svelte"; + import StartTimeSelect from "../components/StartTimeSelect.svelte"; import StatusTagSelect from "../components/StatusTagSelect.svelte"; import { DEFAULT_ICON } from "../external/icons"; import type { ProjectResult } from "../models/project"; @@ -34,6 +35,7 @@ throw new Error("Wrong form") } + let startTime = formatFormTime(project.startTime); let endTime = formatFormTime(project.endTime); let name = project.name; let description = project.description; @@ -52,6 +54,7 @@ if (creation) { stuffLogClient.createProject({ active: statusTag === "", + startTime: ( startTime == "" ) ? null : new Date(startTime), endTime: ( endTime == "" ) ? null : new Date(endTime), statusTag: statusTag !== "" ? statusTag : null, @@ -77,6 +80,8 @@ stuffLogClient.updateProject(project.id, { endTime: ( endTime == "" ) ? null : new Date(endTime), clearEndTime: ( endTime == "" ), + startTime: ( startTime == "" ) ? null : new Date(startTime), + clearStartTime: ( startTime == "" ), active: statusTag === "", statusTag: statusTag || null, clearStatusTag: statusTag === "", @@ -111,6 +116,10 @@ + {#if endTime != ""} + + + {/if} diff --git a/svelte-ui/src/models/project.ts b/svelte-ui/src/models/project.ts index ba4359c..b11c3fa 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 + startTime?: string endTime?: string statusTag?: string } @@ -29,6 +30,7 @@ export interface ProjectInput { description: string icon: IconName active: boolean + startTime?: string | Date endTime?: string | Date statusTag?: string favorite?: boolean @@ -39,6 +41,8 @@ export interface ProjectUpdate { description?: string icon?: string active?: boolean + startTime?: string | Date + clearStartTime?: boolean endTime?: string | Date clearEndTime?: boolean statusTag?: string