Browse Source

add project start time.

main
Gisle Aune 4 years ago
parent
commit
d25e2da345
  1. 8
      api/project.go
  2. 5
      database/postgres/project.go
  3. 11
      migrations/postgres/20210405154335_add_project_column_start_time.sql
  4. 10
      models/project.go
  5. 2
      svelte-ui/src/components/DaysLeft.svelte
  6. 6
      svelte-ui/src/components/DeadlineSelect.svelte
  7. 6
      svelte-ui/src/components/ProgressNumbers.svelte
  8. 2
      svelte-ui/src/components/QLListItem.svelte
  9. 118
      svelte-ui/src/components/StartTimeSelect.svelte
  10. 9
      svelte-ui/src/forms/ProjectForm.svelte
  11. 4
      svelte-ui/src/models/project.ts

8
api/project.go

@ -47,6 +47,9 @@ func Project(g *gin.RouterGroup, db database.Database) {
if project.EndTime != nil && project.EndTime.Before(time.Now()) { if project.EndTime != nil && project.EndTime.Before(time.Now()) {
return nil, slerrors.BadRequest("Project end time must be later than current time.") 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.ID = generate.ProjectID()
project.UserID = auth.UserID(c) project.UserID = auth.UserID(c)
@ -80,6 +83,11 @@ func Project(g *gin.RouterGroup, db database.Database) {
} }
project.Update(update) 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) err = db.Projects().Update(c.Request.Context(), project.Project)
if err != nil { if err != nil {
return nil, err return nil, err

5
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 { 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, end_time, status_tag, favorite
project_id, user_id, name, description, icon, active, created_time, start_time, end_time, status_tag, favorite
) VALUES ( ) 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) `, &project)
if err != nil { if err != nil {
@ -94,6 +94,7 @@ func (r *projectRepository) Update(ctx context.Context, project models.Project)
description = :description, description = :description,
icon = :icon, icon = :icon,
active = :active, active = :active,
start_time = :start_time,
end_time = :end_time, end_time = :end_time,
status_tag = :status_tag, status_tag = :status_tag,
favorite = :favorite favorite = :favorite

11
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

10
models/project.go

@ -13,6 +13,7 @@ type Project struct {
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"`
EndTime *time.Time `json:"endTime" db:"end_time"` EndTime *time.Time `json:"endTime" db:"end_time"`
StatusTag *string `json:"statusTag" db:"status_tag"` StatusTag *string `json:"statusTag" db:"status_tag"`
Favorite bool `json:"favorite" db:"favorite"` Favorite bool `json:"favorite" db:"favorite"`
@ -31,6 +32,13 @@ func (project *Project) Update(update ProjectUpdate) {
if update.Active != nil { if update.Active != nil {
project.Active = *update.Active 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 { if update.EndTime != nil {
endTimeCopy := update.EndTime.UTC() endTimeCopy := update.EndTime.UTC()
project.EndTime = &endTimeCopy project.EndTime = &endTimeCopy
@ -58,6 +66,8 @@ type ProjectUpdate struct {
Description *string `json:"description"` Description *string `json:"description"`
Icon *string `json:"icon"` Icon *string `json:"icon"`
Active *bool `json:"active"` Active *bool `json:"active"`
StartTime *time.Time `json:"startTime"`
ClearStartTime bool `json:"clearStartTime"`
EndTime *time.Time `json:"endTime"` EndTime *time.Time `json:"endTime"`
ClearEndTime bool `json:"clearEndTime"` ClearEndTime bool `json:"clearEndTime"`
StatusTag *string `json:"statusTag"` StatusTag *string `json:"statusTag"`

2
svelte-ui/src/components/DaysLeft.svelte

@ -72,7 +72,7 @@
{:else if (started)} {:else if (started)}
<span class:danger class="started">{amountStr}{compact ? "" : " "}{unit} {!compact ? "left" : ""}</span> <span class:danger class="started">{amountStr}{compact ? "" : " "}{unit} {!compact ? "left" : ""}</span>
{:else} {:else}
<span class="pending">In {amountStr}{compact ? "" : " "}{unit}</span>
<span class="pending">{compact ? "" : "In "}{amountStr}{compact ? "" : " "}{unit}</span>
{/if} {/if}
</span> </span>

6
svelte-ui/src/components/DeadlineSelect.svelte

@ -75,17 +75,17 @@
options.push({ options.push({
id: "this_year", id: "this_year",
label: "This Year",
label: "End of this year",
value: endOfYear(now), value: endOfYear(now),
}); });
options.push({ options.push({
id: "next_year", id: "next_year",
label: "Next Year",
label: "End of next year",
value: endOfYear(new Date(Date.now() + (365.25 * 86400000))), value: endOfYear(new Date(Date.now() + (365.25 * 86400000))),
}); });
options.push({ options.push({
id: "last_year", id: "last_year",
label: "Last Year",
label: "End of last year",
value: endOfYear(new Date(Date.now() - (365.25 * 86400000))), value: endOfYear(new Date(Date.now() - (365.25 * 86400000))),
}); });

6
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); progressTarget = Math.max((project.tasks||[]).map(t => t.itemAmount * (t.item.groupWeight || 1)).reduce((n,m) => n+m, 0), 1);
if (project.endTime) { if (project.endTime) {
const start = Date.parse(project.createdTime);
const start = Date.parse(project.startTime || project.createdTime);
const end = Date.parse(project.endTime); 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); timeProgress = (now - start) / (end - start);
} else { } else {
@ -37,7 +37,7 @@
if (goal != null) { if (goal != null) {
const start = Date.parse(goal.startTime); const start = Date.parse(goal.startTime);
const end = Date.parse(goal.endTime); 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; progressAmount = goal.completedAmount;
progressTarget = goal.amount; progressTarget = goal.amount;

2
svelte-ui/src/components/QLListItem.svelte

@ -33,7 +33,7 @@
<div class="content">{project.name}</div> <div class="content">{project.name}</div>
{#if project.endTime} {#if project.endTime}
<div class="times"> <div class="times">
<DaysLeft compact startTime={project.createdTime} endTime={project.endTime} />
<DaysLeft compact startTime={project.startTime || project.createdTime} endTime={project.endTime} />
</div> </div>
{/if} {/if}
</div> </div>

118
svelte-ui/src/components/StartTimeSelect.svelte

@ -0,0 +1,118 @@
<script lang="ts">
import { onMount } from "svelte";
import { startOfMonth, startOfWeek, startOfYear, formatFormTime, lastMonth, nextMonth } from "../utils/time";
import EveryMinute from "./EveryMinute.svelte";
interface DateOption {
id: string
label: string
value: Date
}
export let value: string;
export let disabled: boolean;
let selected: string = "custom";
let options: DateOption[] = [];
let now: Date = new Date();
onMount(() => {
if (value === "") {
selected = "none";
return;
}
for (const option of options) {
if (formatFormTime(option.value) === value) {
selected = option.id;
}
}
});
$: {
if (options.length === 0) {
let current = startOfMonth(now);
const nextWeek = new Date(now.getTime() + (86400000 * 7));
const lastWeek = new Date(now.getTime() - (86400000 * 7));
options.push({
id: "none",
label: "Project's creation date",
value: new Date(Number.NaN),
});
options.push({
id: "this_week",
label: "Start of this Week",
value: startOfWeek(now),
});
options.push({
id: "next_week",
label: "Start of next Week",
value: startOfWeek(nextWeek),
});
options.push({
id: "last_week",
label: "Start of last Week",
value: startOfWeek(lastWeek),
});
options.push({
id: "this_month",
label: "Start of this month",
value: startOfMonth(current),
});
options.push({
id: "next_month",
label: "Start of next month",
value: startOfMonth(nextMonth(current)),
});
options.push({
id: "last_month",
label: "Start of last month",
value: startOfMonth(lastMonth(current)),
});
options.push({
id: "this_year",
label: "Beginning of this year",
value: startOfYear(now),
});
options.push({
id: "next_year",
label: "Beginning of next year",
value: startOfYear(new Date(Date.now() + (365.25 * 86400000))),
});
options.push({
id: "last_year",
label: "Beginning of last year",
value: startOfYear(new Date(Date.now() - (365.25 * 86400000))),
});
options.push({
id: "custom",
label: "Specific Date",
value: null,
});
}
}
$: {
if (selected !== "custom") {
const option = options.find(o => o.id === selected);
if (option != null) {
value = formatFormTime(option.value);
}
}
}
</script>
<EveryMinute bind:now={now} />
<select name="endTime" disabled={disabled} bind:value={selected}>
{#each options as option (option.id)}
<option value={option.id}>{option.label}</option>
{/each}
</select>
{#if selected === "custom"}
<input disabled={disabled} name="value" type="datetime-local" bind:value={value} />
{/if}

9
svelte-ui/src/forms/ProjectForm.svelte

@ -4,6 +4,7 @@
import DeadlineSelect from "../components/DeadlineSelect.svelte"; import DeadlineSelect from "../components/DeadlineSelect.svelte";
import IconSelect from "../components/IconSelect.svelte"; import IconSelect from "../components/IconSelect.svelte";
import Modal from "../components/Modal.svelte"; import Modal from "../components/Modal.svelte";
import StartTimeSelect from "../components/StartTimeSelect.svelte";
import StatusTagSelect from "../components/StatusTagSelect.svelte"; import StatusTagSelect from "../components/StatusTagSelect.svelte";
import { DEFAULT_ICON } from "../external/icons"; import { DEFAULT_ICON } from "../external/icons";
import type { ProjectResult } from "../models/project"; import type { ProjectResult } from "../models/project";
@ -34,6 +35,7 @@
throw new Error("Wrong form") throw new Error("Wrong form")
} }
let startTime = formatFormTime(project.startTime);
let endTime = formatFormTime(project.endTime); let endTime = formatFormTime(project.endTime);
let name = project.name; let name = project.name;
let description = project.description; let description = project.description;
@ -52,6 +54,7 @@
if (creation) { if (creation) {
stuffLogClient.createProject({ stuffLogClient.createProject({
active: statusTag === "", active: statusTag === "",
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,
@ -77,6 +80,8 @@
stuffLogClient.updateProject(project.id, { stuffLogClient.updateProject(project.id, {
endTime: ( endTime == "" ) ? null : new Date(endTime), endTime: ( endTime == "" ) ? null : new Date(endTime),
clearEndTime: ( endTime == "" ), clearEndTime: ( endTime == "" ),
startTime: ( startTime == "" ) ? null : new Date(startTime),
clearStartTime: ( startTime == "" ),
active: statusTag === "", active: statusTag === "",
statusTag: statusTag || null, statusTag: statusTag || null,
clearStatusTag: statusTag === "", clearStatusTag: statusTag === "",
@ -111,6 +116,10 @@
<IconSelect disabled={deletion} bind:value={icon} /> <IconSelect disabled={deletion} bind:value={icon} />
<label for="endTime">Deadline (Optional)</label> <label for="endTime">Deadline (Optional)</label>
<DeadlineSelect disabled={deletion} bind:value={endTime} /> <DeadlineSelect disabled={deletion} bind:value={endTime} />
{#if endTime != ""}
<label for="endTime">Start time (Optional)</label>
<StartTimeSelect disabled={deletion} bind:value={startTime} />
{/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} />

4
svelte-ui/src/models/project.ts

@ -9,6 +9,7 @@ export default interface Project {
active: boolean active: boolean
createdTime: string createdTime: string
favorite: boolean favorite: boolean
startTime?: string
endTime?: string endTime?: string
statusTag?: string statusTag?: string
} }
@ -29,6 +30,7 @@ export interface ProjectInput {
description: string description: string
icon: IconName icon: IconName
active: boolean active: boolean
startTime?: string | Date
endTime?: string | Date endTime?: string | Date
statusTag?: string statusTag?: string
favorite?: boolean favorite?: boolean
@ -39,6 +41,8 @@ export interface ProjectUpdate {
description?: string description?: string
icon?: string icon?: string
active?: boolean active?: boolean
startTime?: string | Date
clearStartTime?: boolean
endTime?: string | Date endTime?: string | Date
clearEndTime?: boolean clearEndTime?: boolean
statusTag?: string statusTag?: string

Loading…
Cancel
Save