diff --git a/database/postgres/project.go b/database/postgres/project.go index 04e25dc..f820690 100644 --- a/database/postgres/project.go +++ b/database/postgres/project.go @@ -7,6 +7,8 @@ import ( "github.com/gissleh/stufflog/internal/slerrors" "github.com/gissleh/stufflog/models" "github.com/jmoiron/sqlx" + "github.com/lib/pq" + "time" ) type projectRepository struct { @@ -14,17 +16,17 @@ type projectRepository struct { } func (r *projectRepository) Find(ctx context.Context, id string) (*models.Project, error) { - res := models.Project{} + res := projectDBO{} err := r.db.GetContext(ctx, &res, "SELECT * FROM project WHERE project_id=$1", id) if err != nil { if err == sql.ErrNoRows { - return nil, slerrors.NotFound("Log") + return nil, slerrors.NotFound("Project") } return nil, err } - return &res, nil + return res.ToProject(), nil } func (r *projectRepository) List(ctx context.Context, filter models.ProjectFilter) ([]*models.Project, error) { @@ -60,26 +62,35 @@ func (r *projectRepository) List(ctx context.Context, filter models.ProjectFilte return nil, err } - res := make([]*models.Project, 0, 8) + res := make([]*projectDBO, 0, 8) err = r.db.SelectContext(ctx, &res, query, args...) if err != nil { if err == sql.ErrNoRows { - return res, nil + return []*models.Project{}, nil } return nil, err } - return res, nil + res2 := make([]*models.Project, len(res)) + for i, v := range res { + res2[i] = v.ToProject() + } + + return res2, nil } func (r *projectRepository) Insert(ctx context.Context, project models.Project) error { + if project.Tags == nil { + project.Tags = []string{} + } + _, err := r.db.NamedExecContext(ctx, ` INSERT INTO project( - project_id, user_id, name, description, icon, active, created_time, start_time, end_time, subtract_amount, status_tag, favorite + project_id, user_id, name, description, icon, active, created_time, start_time, end_time, subtract_amount, status_tag, favorite, tags ) VALUES ( - :project_id, :user_id, :name, :description, :icon, :active, :created_time, :start_time, :end_time, :subtract_amount, :status_tag, :favorite + :project_id, :user_id, :name, :description, :icon, :active, :created_time, :start_time, :end_time, :subtract_amount, :status_tag, :favorite, :tags ) - `, &project) + `, toProjectDBO(project)) if err != nil { return err } @@ -88,6 +99,10 @@ func (r *projectRepository) Insert(ctx context.Context, project models.Project) } func (r *projectRepository) Update(ctx context.Context, project models.Project) error { + if project.Tags == nil { + project.Tags = []string{} + } + _, err := r.db.NamedExecContext(ctx, ` UPDATE project SET name = :name, @@ -98,9 +113,10 @@ func (r *projectRepository) Update(ctx context.Context, project models.Project) end_time = :end_time, subtract_amount = :subtract_amount, status_tag = :status_tag, - favorite = :favorite + favorite = :favorite, + tags = :tags WHERE project_id=:project_id - `, &project) + `, toProjectDBO(project)) if err != nil { return err } @@ -124,3 +140,55 @@ func (r *projectRepository) Delete(ctx context.Context, project models.Project) return nil } + +type projectDBO 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"` + SubtractAmount int `json:"subtractAmount" db:"subtract_amount"` + StatusTag *string `json:"statusTag" db:"status_tag"` + Favorite bool `json:"favorite" db:"favorite"` + Tags pq.StringArray `json:"tags" db:"tags"` +} + +func toProjectDBO(project models.Project) *projectDBO { + return &projectDBO{ + ID: project.ID, + UserID: project.UserID, + Name: project.Name, + Description: project.Description, + Icon: project.Icon, + Active: project.Active, + CreatedTime: project.CreatedTime, + StartTime: project.StartTime, + EndTime: project.EndTime, + SubtractAmount: project.SubtractAmount, + StatusTag: project.StatusTag, + Favorite: project.Favorite, + Tags: project.Tags, + } +} + +func (d *projectDBO) ToProject() *models.Project { + return &models.Project{ + ID: d.ID, + UserID: d.UserID, + Name: d.Name, + Description: d.Description, + Icon: d.Icon, + Active: d.Active, + CreatedTime: d.CreatedTime, + StartTime: d.StartTime, + EndTime: d.EndTime, + SubtractAmount: d.SubtractAmount, + StatusTag: d.StatusTag, + Favorite: d.Favorite, + Tags: d.Tags, + } +} diff --git a/migrations/postgres/20210418164739_add_project_column_tags.sql b/migrations/postgres/20210418164739_add_project_column_tags.sql new file mode 100644 index 0000000..cc0df2d --- /dev/null +++ b/migrations/postgres/20210418164739_add_project_column_tags.sql @@ -0,0 +1,11 @@ +-- +goose Up +-- +goose StatementBegin +ALTER TABLE project + ADD COLUMN tags TEXT[] DEFAULT ARRAY[]::TEXT[]; +-- +goose StatementEnd + +-- +goose Down +-- +goose StatementBegin +ALTER TABLE project + DROP COLUMN tags; +-- +goose StatementEnd diff --git a/models/project.go b/models/project.go index 1d5946d..894d84d 100644 --- a/models/project.go +++ b/models/project.go @@ -18,6 +18,7 @@ type Project struct { SubtractAmount int `json:"subtractAmount" db:"subtract_amount"` StatusTag *string `json:"statusTag" db:"status_tag"` Favorite bool `json:"favorite" db:"favorite"` + Tags []string `json:"tags" db:"tags"` } func (project *Project) Update(update ProjectUpdate) { @@ -62,6 +63,9 @@ func (project *Project) Update(update ProjectUpdate) { if update.Favorite != nil { project.Favorite = *update.Favorite } + if update.SetTags != nil { + project.Tags = update.SetTags + } if project.StatusTag != nil && project.Active { project.StatusTag = nil @@ -80,6 +84,7 @@ type ProjectUpdate struct { SubtractAmount *int `json:"subtractAmount"` StatusTag *string `json:"statusTag"` ClearStatusTag bool `json:"clearStatusTag"` + SetTags []string `json:"setTags"` Favorite *bool `json:"favorite"` } diff --git a/models/projectgroup.go b/models/projectgroup.go new file mode 100644 index 0000000..b0a7fd8 --- /dev/null +++ b/models/projectgroup.go @@ -0,0 +1,30 @@ +package models + +import "context" + +type ProjectGroup struct { + ID string `json:"id" db:"id"` + UserID string `json:"-" db:"user_id"` + Name string `json:"name" db:"name"` + Abbreviation string `json:"abbreviation" db:"abbreviation"` + CategoryNames map[string]string `json:"categoryNames" db:"category_names"` +} + +type ProjectGroupUpdate struct { + Name *string `json:"name"` + Abbreviation *string `json:"abbreviation"` + SetCategoryNames map[string]string `json:"setCategoryNames"` + ResetCategoryName *string `json:"resetCategoryName"` +} + +type ProjectGroupFilter struct { + UserID string `json:"userId"` +} + +type ProjectGroupRepository interface { + Find(ctx context.Context, id string) (*ProjectGroup, error) + List(ctx context.Context, filter ProjectGroupFilter) ([]*ProjectGroup, error) + Insert(ctx context.Context, group ProjectGroup) error + Update(ctx context.Context, group ProjectGroup) error + Delete(ctx context.Context, group ProjectGroup) error +} diff --git a/svelte-ui/src/components/ParentEntry.svelte b/svelte-ui/src/components/ParentEntry.svelte index 637f855..5af3956 100644 --- a/svelte-ui/src/components/ParentEntry.svelte +++ b/svelte-ui/src/components/ParentEntry.svelte @@ -92,6 +92,7 @@ import TimeProgress from "./TimeProgress.svelte"; {/if} {/if} + {#if (full)} {/if} diff --git a/svelte-ui/src/components/ProjectEntry.svelte b/svelte-ui/src/components/ProjectEntry.svelte index 9adaf50..08dd026 100644 --- a/svelte-ui/src/components/ProjectEntry.svelte +++ b/svelte-ui/src/components/ProjectEntry.svelte @@ -12,6 +12,7 @@ import stuffLogClient from "../clients/stufflog"; import ParentEntry from "./ParentEntry.svelte"; import ProgressNumbers from "./ProgressNumbers.svelte"; import StatusColor from "./StatusColor.svelte"; + import TagList from "./TagList.svelte"; import TaskList from "./TaskList.svelte"; export let project: ProjectResult = null; @@ -113,6 +114,9 @@ import stuffLogClient from "../clients/stufflog";
+
+ +
{#if showAllOptions} diff --git a/svelte-ui/src/components/QLListItem.svelte b/svelte-ui/src/components/QLListItem.svelte index a715344..cb6f5ff 100644 --- a/svelte-ui/src/components/QLListItem.svelte +++ b/svelte-ui/src/components/QLListItem.svelte @@ -1,11 +1,13 @@ + +
{value}
+ + \ No newline at end of file diff --git a/svelte-ui/src/components/TagList.svelte b/svelte-ui/src/components/TagList.svelte new file mode 100644 index 0000000..905bfa8 --- /dev/null +++ b/svelte-ui/src/components/TagList.svelte @@ -0,0 +1,18 @@ + + +
+ {#each tags as tag (tag)} + + {/each} +
+ + \ No newline at end of file diff --git a/svelte-ui/src/forms/ProjectForm.svelte b/svelte-ui/src/forms/ProjectForm.svelte index 4ea71bb..f595475 100644 --- a/svelte-ui/src/forms/ProjectForm.svelte +++ b/svelte-ui/src/forms/ProjectForm.svelte @@ -27,6 +27,7 @@ tasks: [], favorite: false, subtractAmount: 0, + tags: [], } let verb = "Add"; if (md.name === "project.edit" || md.name === "project.delete") { @@ -46,6 +47,7 @@ let subtractAmount = project.subtractAmount; let error = null; let loading = false; + let tags = project.tags.join(", "); function onSubmit() { loading = true; @@ -60,6 +62,7 @@ endTime: ( endTime == "" ) ? null : new Date(endTime), statusTag: statusTag !== "" ? statusTag : null, subtractAmount: Math.min(subtractAmount, 0), + tags: tags.length > 0 ? tags.split(",").map(t => t.trim()) : [], name, description, icon, favorite, }).then(() => { @@ -89,6 +92,7 @@ statusTag: statusTag || null, clearStatusTag: statusTag === "", subtractAmount: subtractAmount, + setTags: tags.length > 0 ? tags.split(",").map(t => t.trim()) : [], name, description, icon, favorite, }).then(() => { @@ -128,6 +132,8 @@ {/if} + + diff --git a/svelte-ui/src/models/project.ts b/svelte-ui/src/models/project.ts index d1dce3f..f7b5e52 100644 --- a/svelte-ui/src/models/project.ts +++ b/svelte-ui/src/models/project.ts @@ -10,6 +10,7 @@ export default interface Project { createdTime: string favorite: boolean subtractAmount: number + tags: string[] startTime?: string endTime?: string statusTag?: string @@ -36,6 +37,7 @@ export interface ProjectInput { endTime?: string | Date statusTag?: string favorite?: boolean + tags?: string[] } export interface ProjectUpdate { @@ -51,4 +53,5 @@ export interface ProjectUpdate { clearStatusTag?: boolean subtractAmount?: number favorite?: boolean + setTags?: string[] } \ No newline at end of file