diff --git a/api/item.go b/api/item.go new file mode 100644 index 0000000..8eba5cf --- /dev/null +++ b/api/item.go @@ -0,0 +1,130 @@ +package api + +import ( + "github.com/gin-gonic/gin" + "github.com/gisle/stufflog/database" + "github.com/gisle/stufflog/models" + "github.com/gisle/stufflog/services" + "github.com/gisle/stufflog/slerrors" + "sort" +) + +func Items(g *gin.RouterGroup, db database.Database, auth *services.AuthService) { + type resObj struct { + Item *models.Item `json:"item,omitempty"` + Items []*models.Item `json:"items,omitempty"` + } + + g.Use(auth.GinSessionMiddleware(true)) + + // GET / – List own items + g.GET("/", func(c *gin.Context) { + user := auth.UserFromContext(c) + + items, err := db.Items().ListUser(c.Request.Context(), *user) + if err != nil { + slerrors.GinRespond(c, err) + return + } + if items == nil { + items = []*models.Item{} + } + + sort.Sort(models.ItemsByName(items)) + + c.JSON(200, resObj{Items: items}) + }) + + // GET /:id – Find an item + g.GET("/:id", func(c *gin.Context) { + user := auth.UserFromContext(c) + + item, err := db.Items().FindID(c.Request.Context(), c.Param("id")) + if err != nil { + slerrors.GinRespond(c, err) + return + } + if item.UserID != user.ID { + slerrors.GinRespond(c, slerrors.NotFound("Item")) + return + } + + c.JSON(200, resObj{Item: item}) + }) + + // POST / – Create an item + g.POST("/", func(c *gin.Context) { + user := auth.UserFromContext(c) + item := models.Item{} + + err := c.BindJSON(&item) + if err != nil { + slerrors.GinRespond(c, &slerrors.SLError{Code: 400, Text: err.Error()}) + return + } + + item.GenerateID() + item.UserID = user.ID + + err = db.Items().Insert(c.Request.Context(), item) + if err != nil { + slerrors.GinRespond(c, err) + return + } + + c.JSON(200, resObj{Item: &item}) + }) + + // PATCH /:id – Update an item + g.PATCH("/:id", func(c *gin.Context) { + user := auth.UserFromContext(c) + updates := make([]*models.ItemUpdate, 0, 8) + + err := c.BindJSON(&updates) + if err != nil { + slerrors.GinRespond(c, &slerrors.SLError{Code: 400, Text: err.Error()}) + return + } + + item, err := db.Items().FindID(c.Request.Context(), c.Param("id")) + if err != nil { + slerrors.GinRespond(c, err) + return + } + if item.UserID != user.ID { + slerrors.GinRespond(c, slerrors.NotFound("Item")) + return + } + + item, err = db.Items().Update(c.Request.Context(), *item, updates) + if err != nil { + slerrors.GinRespond(c, err) + return + } + + c.JSON(200, resObj{Item: item}) + }) + + // DELETE /:id – Delete an item + g.DELETE("/:id", func(c *gin.Context) { + user := auth.UserFromContext(c) + + item, err := db.Items().FindID(c.Request.Context(), c.Param("id")) + if err != nil { + slerrors.GinRespond(c, err) + return + } + if item.UserID != user.ID { + slerrors.GinRespond(c, slerrors.NotFound("Period")) + return + } + + err = db.Items().Remove(c.Request.Context(), *item) + if err != nil { + slerrors.GinRespond(c, err) + return + } + + c.JSON(200, resObj{Item: item}) + }) +} diff --git a/api/period.go b/api/period.go index 491a531..4728684 100644 --- a/api/period.go +++ b/api/period.go @@ -78,21 +78,12 @@ func Period(g *gin.RouterGroup, db database.Database, scoring *services.ScoringS } period.GenerateIDs() - period.RemoveDuplicateTags() period.UserID = user.ID - period.Logs = make([]models.PeriodLog, 0) + period.Logs = make([]models.Log, 0) period.ShouldReScore = false - if period.Tags == nil { - period.Tags = make([]string, 0) - } if period.Goals == nil { - period.Goals = make([]models.PeriodGoal, 0) - } - for i := range period.Goals { - if period.Goals[i].SubGoals == nil { - period.Goals[i].SubGoals = make([]models.PeriodSubGoal, 0) - } + period.Goals = make([]models.Goal, 0) } err = db.Periods().Insert(c.Request.Context(), period) diff --git a/database/database.go b/database/database.go index 8f55af7..ba5bd79 100644 --- a/database/database.go +++ b/database/database.go @@ -13,6 +13,7 @@ type Database interface { UserSessions() repositories.UserSessionRepository Activities() repositories.ActivityRepository Periods() repositories.PeriodRepository + Items() repositories.ItemRepository } // Init gets you database based on the configuration provided. diff --git a/database/drivers/bolt/db.go b/database/drivers/bolt/db.go index a64f2fa..6d57b82 100644 --- a/database/drivers/bolt/db.go +++ b/database/drivers/bolt/db.go @@ -15,6 +15,7 @@ type Database struct { userSessions repositories.UserSessionRepository activities repositories.ActivityRepository periods repositories.PeriodRepository + items repositories.ItemRepository } func (database *Database) Users() repositories.UserRepository { @@ -33,6 +34,10 @@ func (database *Database) Periods() repositories.PeriodRepository { return database.periods } +func (database *Database) Items() repositories.ItemRepository { + return database.items +} + func Init(cfg config.Database) (*Database, error) { opts := *bbolt.DefaultOptions opts.Timeout = time.Second * 5 @@ -57,12 +62,17 @@ func Init(cfg config.Database) (*Database, error) { if err != nil { return nil, err } + items, err := newItemRepository(db) + if err != nil { + return nil, err + } database := &Database{ users: users, userSessions: userSessions, periods: periods, activities: activities, + items: items, } return database, nil diff --git a/database/drivers/bolt/item.go b/database/drivers/bolt/item.go new file mode 100644 index 0000000..093b06b --- /dev/null +++ b/database/drivers/bolt/item.go @@ -0,0 +1,197 @@ +package bolt + +import ( + "context" + "github.com/gisle/stufflog/database/repositories" + "github.com/gisle/stufflog/models" + "github.com/gisle/stufflog/slerrors" + "github.com/vmihailenco/msgpack/v4" + "go.etcd.io/bbolt" +) + +var bnItems = []byte("Item") + +type itemRepository struct { + db *bbolt.DB + userIdIdx *index +} + +func (r *itemRepository) FindID(ctx context.Context, id string) (*models.Item, error) { + item := new(models.Item) + err := r.db.View(func(tx *bbolt.Tx) error { + value := tx.Bucket(bnItems).Get(unsafeStringToBytes(id)) + if value == nil { + return slerrors.NotFound("Period") + } + err := msgpack.Unmarshal(value, item) + if err != nil { + return err + } + return nil + }) + if err != nil { + return nil, err + } + + return item, nil +} + +func (r *itemRepository) List(ctx context.Context) ([]*models.Item, error) { + items := make([]*models.Item, 0, 16) + err := r.db.View(func(tx *bbolt.Tx) error { + cursor := tx.Bucket(bnItems).Cursor() + + for key, value := cursor.First(); key != nil; key, value = cursor.Next() { + item := new(models.Item) + err := msgpack.Unmarshal(value, item) + if err != nil { + return err + } + + items = append(items, item) + } + + return nil + }) + if err != nil { + return nil, err + } + + return items, nil +} + +func (r *itemRepository) ListUser(ctx context.Context, user models.User) ([]*models.Item, error) { + items := make([]*models.Item, 0, 16) + err := r.db.View(func(tx *bbolt.Tx) error { + bucket := tx.Bucket(bnItems) + + ids, err := r.userIdIdx.WithTx(tx).Get(user.ID) + if err != nil { + return err + } + + for _, id := range ids { + value := bucket.Get(id) + if value == nil { + continue + } + + item := new(models.Item) + err := msgpack.Unmarshal(value, item) + if err != nil { + return err + } + + items = append(items, item) + } + + return nil + }) + if err != nil { + return nil, err + } + + return items, nil +} + +func (r *itemRepository) Insert(ctx context.Context, item models.Item) error { + value, err := msgpack.Marshal(&item) + if err != nil { + return err + } + + return r.db.Update(func(tx *bbolt.Tx) error { + err := tx.Bucket(bnItems).Put(unsafeStringToBytes(item.ID), value) + if err != nil { + return err + } + + err = r.index(tx, &item) + if err != nil { + return err + } + + return nil + }) +} + +func (r *itemRepository) Update(ctx context.Context, item models.Item, updates []*models.ItemUpdate) (*models.Item, error) { + for _, update := range updates { + err := item.ApplyUpdate(*update) + if err != nil { + return nil, err + } + } + + value, err := msgpack.Marshal(&item) + if err != nil { + return nil, err + } + + err = r.db.Update(func(tx *bbolt.Tx) error { + return tx.Bucket(bnItems).Put(unsafeStringToBytes(item.ID), value) + }) + if err != nil { + return nil, err + } + + return &item, nil +} + +func (r *itemRepository) Remove(ctx context.Context, item models.Item) error { + return r.db.Update(func(tx *bbolt.Tx) error { + err := tx.Bucket(bnItems).Delete(unsafeStringToBytes(item.ID)) + if err != nil { + return err + } + + err = r.unIndex(tx, &item) + if err != nil { + return err + } + + return nil + }) +} + +func (r *itemRepository) index(tx *bbolt.Tx, item *models.Item) error { + idBytes := unsafeStringToBytes(item.ID) + + err := r.userIdIdx.WithTx(tx).Set(idBytes, item.UserID) + if err != nil { + return err + } + + return nil +} + +func (r *itemRepository) unIndex(tx *bbolt.Tx, item *models.Item) error { + idBytes := unsafeStringToBytes(item.ID) + + err := r.userIdIdx.WithTx(tx).Set(idBytes) + if err != nil { + return err + } + + return nil +} + +func newItemRepository(db *bbolt.DB) (repositories.ItemRepository, error) { + err := db.Update(func(tx *bbolt.Tx) error { + _, err := tx.CreateBucketIfNotExists(bnItems) + return err + }) + if err != nil { + return nil, err + } + + userIdIdx, err := newModelIndex(db, "Item", "UserID") + if err != nil { + return nil, err + } + + return &itemRepository{ + db: db, + userIdIdx: userIdIdx, + }, nil +} diff --git a/database/repositories/item.go b/database/repositories/item.go new file mode 100644 index 0000000..110a151 --- /dev/null +++ b/database/repositories/item.go @@ -0,0 +1,15 @@ +package repositories + +import ( + "context" + "github.com/gisle/stufflog/models" +) + +type ItemRepository interface { + FindID(ctx context.Context, id string) (*models.Item, error) + List(ctx context.Context) ([]*models.Item, error) + ListUser(ctx context.Context, user models.User) ([]*models.Item, error) + Insert(ctx context.Context, item models.Item) error + Update(ctx context.Context, item models.Item, updates []*models.ItemUpdate) (*models.Item, error) + Remove(ctx context.Context, item models.Item) error +} diff --git a/main.go b/main.go index a2d1931..ecce7a4 100644 --- a/main.go +++ b/main.go @@ -64,6 +64,7 @@ func main() { api.User(router.Group("/api/user"), auth) api.Activity(router.Group("/api/activity"), db, auth) api.Period(router.Group("/api/period"), db, scoring, auth) + api.Items(router.Group("/api/item"), db, auth) // Setup UI if uiRoot != "" { @@ -71,6 +72,9 @@ func main() { router.GET("/activities/", func(c *gin.Context) { http.ServeFile(c.Writer, c.Request, path.Join(uiRoot, "index.html")) }) + router.GET("/items/", func(c *gin.Context) { + http.ServeFile(c.Writer, c.Request, path.Join(uiRoot, "index.html")) + }) } // Start listening diff --git a/models/goal.go b/models/goal.go new file mode 100644 index 0000000..6d0fed3 --- /dev/null +++ b/models/goal.go @@ -0,0 +1,9 @@ +package models + +// A Goal is a declared investment into the goal. +type Goal struct { + ID string `json:"id"` + ActivityID string `json:"activityId"` + ItemID string `json:"itemId"` + PointCount int `json:"pointCount"` +} diff --git a/models/item.go b/models/item.go new file mode 100644 index 0000000..bbd860a --- /dev/null +++ b/models/item.go @@ -0,0 +1,45 @@ +package models + +import "github.com/gisle/stufflog/internal/generate" + +type Item struct { + ID string `json:"id" msgpack:"id"` + UserID string `json:"userId" msgpack:"userId"` + Active bool `json:"active" msgpack:"active"` + Name string `json:"name" msgpack:"name"` + Multiplier float64 `json:"multiplier" msgpack:"multiplier"` +} + +func (item *Item) GenerateID() { + item.ID = generate.ID("I", 16) +} + +func (item *Item) ApplyUpdate(update ItemUpdate) error { + if update.SetActive != nil { + item.Active = *update.SetActive + } + if update.SetName != nil { + item.Name = *update.SetName + } + + return nil +} + +type ItemUpdate struct { + SetName *string `json:"setName"` + SetActive *bool `json:"setActive"` +} + +type ItemsByName []*Item + +func (items ItemsByName) Len() int { + return len(items) +} + +func (items ItemsByName) Less(i, j int) bool { + return items[i].Name < items[j].Name +} + +func (items ItemsByName) Swap(i, j int) { + items[i], items[j] = items[j], items[i] +} diff --git a/models/log.go b/models/log.go new file mode 100644 index 0000000..e820413 --- /dev/null +++ b/models/log.go @@ -0,0 +1,17 @@ +package models + +import "time" + +// Log is a logged performance of an activity during the period. SubGoalID is optional, but GoalID is not. The +// points is the points calculated on the time of submission and does not need to reflect the latest. +type Log struct { + Date time.Time `json:"date"` + ID string `json:"id"` + SubActivityID string `json:"subActivityId"` + GoalID string `json:"goalId"` + SubGoalID string `json:"subGoalId"` + Description string `json:"description"` + SubmitTime time.Time `json:"submitTime"` + Amount int `json:"amount"` + Score *Score `json:"score"` +} diff --git a/models/period.go b/models/period.go index 9c2e8a4..7c946f0 100644 --- a/models/period.go +++ b/models/period.go @@ -16,9 +16,8 @@ type Period struct { ShouldReScore bool `json:"-" msgpack:"-"` - Tags []string `json:"tags"` - Goals []PeriodGoal `json:"goals"` - Logs []PeriodLog `json:"logs"` + Goals []Goal `json:"goals"` + Logs []Log `json:"logs"` } // GenerateIDs generates IDs. It should only be used initially. @@ -26,10 +25,6 @@ func (period *Period) GenerateIDs() { period.ID = generate.ID("P", 12) for i := range period.Goals { period.Goals[i].ID = generate.ID("PG", 16) - - for j := range period.Goals[i].SubGoals { - period.Goals[i].SubGoals[j].ID = generate.ID("PGS", 20) - } } for i := range period.Logs { period.Logs[i].ID = generate.ID("PL", 16) @@ -46,7 +41,6 @@ func (period *Period) Clear() { period.ShouldReScore = false - period.Tags = period.Tags[:0] period.Goals = period.Goals[:0] period.Logs = period.Logs[:0] } @@ -55,11 +49,9 @@ func (period *Period) Clear() { func (period *Period) Copy() *Period { periodCopy := *period - periodCopy.Tags = make([]string, len(period.Tags)) - copy(periodCopy.Tags, period.Tags) - periodCopy.Goals = make([]PeriodGoal, len(period.Goals)) + periodCopy.Goals = make([]Goal, len(period.Goals)) copy(periodCopy.Goals, period.Goals) - periodCopy.Logs = make([]PeriodLog, len(period.Logs)) + periodCopy.Logs = make([]Log, len(period.Logs)) copy(periodCopy.Logs, period.Logs) return &periodCopy @@ -87,29 +79,6 @@ func (period *Period) ApplyUpdate(update PeriodUpdate) (changed bool, err error) period.Name = *update.SetName } - if update.RemoveTag != nil { - for i, existingTag := range period.Tags { - if *update.AddTag == existingTag { - period.Tags = append(period.Tags[:i], period.Tags[i+1:]...) - changed = true - break - } - } - } - if update.AddTag != nil { - found := false - for _, existingTag := range period.Tags { - if *update.AddTag == existingTag { - found = true - break - } - } - if !found { - changed = true - period.Tags = append(period.Tags, *update.AddTag) - } - } - if update.AddGoal != nil { goal := *update.AddGoal goal.ID = generate.ID("PG", 16) @@ -125,9 +94,6 @@ func (period *Period) ApplyUpdate(update PeriodUpdate) (changed bool, err error) if update.AddLog != nil && update.AddLog.GoalID == "NEW" { update.AddLog.GoalID = goal.ID } - for i := range goal.SubGoals { - goal.SubGoals[i].ID = generate.ID("PGS", 20) - } changed = true } @@ -139,7 +105,6 @@ func (period *Period) ApplyUpdate(update PeriodUpdate) (changed bool, err error) } goal.PointCount = update.ReplaceGoal.PointCount - goal.SubGoals = update.ReplaceGoal.SubGoals changed = true period.ShouldReScore = true @@ -176,6 +141,30 @@ func (period *Period) ApplyUpdate(update PeriodUpdate) (changed bool, err error) period.Logs = append(period.Logs, log) changed = true } + if update.ReplaceLog != nil { + log := *update.ReplaceLog + + found := false + for i, log2 := range period.Logs { + if log.ID == log2.ID { + found = true + + goal := period.Goal(log.GoalID) + if goal == nil { + err = slerrors.NotFound("Goal") + return + } + + period.Logs[i] = log + break + } + } + + if !found { + err = slerrors.NotFound("Log") + return + } + } if update.RemoveLog != nil { found := false for i, log := range period.Logs { @@ -197,7 +186,7 @@ func (period *Period) ApplyUpdate(update PeriodUpdate) (changed bool, err error) } func (period *Period) RemoveBrokenLogs() { - goodLogs := make([]PeriodLog, 0, len(period.Logs)) + goodLogs := make([]Log, 0, len(period.Logs)) for _, log := range period.Logs { goal := period.Goal(log.GoalID) @@ -205,34 +194,13 @@ func (period *Period) RemoveBrokenLogs() { continue } - if log.SubGoalID != "" { - if goal.SubGoal(log.SubGoalID) == nil { - continue - } - } - goodLogs = append(goodLogs, log) } period.Logs = goodLogs } -func (period *Period) RemoveDuplicateTags() { - deleteList := make([]int, 0, 2) - - found := make(map[string]bool) - for i, tag := range period.Tags { - if found[tag] { - deleteList = append(deleteList, i-len(deleteList)) - } - found[tag] = true - } - for _, index := range deleteList { - period.Tags = append(period.Tags[:index], period.Tags[index+1:]...) - } -} - -func (period *Period) Goal(id string) *PeriodGoal { +func (period *Period) Goal(id string) *Goal { for i, goal := range period.Goals { if goal.ID == id { return &period.Goals[i] @@ -263,79 +231,33 @@ func (period *Period) DayHadActivity(date time.Time, activityID string) bool { return false } -// A PeriodGoal is a declared investment into the goal. -type PeriodGoal struct { - ID string `json:"id"` - ActivityID string `json:"activityId"` - PointCount int `json:"pointCount"` - - SubGoals []PeriodSubGoal `json:"subGoals"` -} - -func (goal *PeriodGoal) SubGoal(id string) *PeriodSubGoal { - if goal == nil { - return nil - } - - for i, subGoal := range goal.SubGoals { - if subGoal.ID == id { - return &goal.SubGoals[i] - } - } - - return nil -} - -// A PeriodSubGoal is a specific sub-goal that should either punish or boost performance. For example, it could represent -// a project or technique that should take priority, or a guilty pleasure that should definitely not be. -type PeriodSubGoal struct { - ID string `json:"id"` - Name string `json:"name"` - Multiplier float64 `json:"multiplier"` -} - -// PeriodLog is a logged performance of an activity during the period. SubGoalID is optional, but GoalID is not. The -// points is the points calculated on the time of submission and does not need to reflect the latest. -type PeriodLog struct { - Date time.Time `json:"date"` - ID string `json:"id"` - SubActivityID string `json:"subActivityId"` - GoalID string `json:"goalId"` - SubGoalID string `json:"subGoalId"` - Description string `json:"description"` - SubmitTime time.Time `json:"submitTime"` - Amount int `json:"amount"` - Score *Score `json:"score"` -} - // PeriodUpdate describes a change to a period type PeriodUpdate struct { SetFrom *time.Time `json:"setFrom"` SetTo *time.Time `json:"setTo"` SetName *string `json:"setName"` - AddLog *PeriodLog `json:"addLog"` - RemoveLog *string `json:"removeLog"` - AddGoal *PeriodGoal `json:"addGoal"` - ReplaceGoal *PeriodGoal `json:"replaceGoal"` - RemoveGoal *string `json:"removeGoal"` - AddTag *string `json:"addTag"` - RemoveTag *string `json:"removeTag"` + AddLog *Log `json:"addLog"` + ReplaceLog *Log `json:"replaceGoal"` + RemoveLog *string `json:"removeLog"` + AddGoal *Goal `json:"addGoal"` + ReplaceGoal *Goal `json:"replaceGoal"` + RemoveGoal *string `json:"removeGoal"` } type Score struct { - ActivityScore float64 `json:"activityScore"` - Amount int `json:"amount"` - SubGoalMultiplier *float64 `json:"subGoalMultiplier"` - DailyBonus int `json:"dailyBonus"` + ActivityScore float64 `json:"activityScore"` + Amount int `json:"amount"` + ItemMultiplier *float64 `json:"subGoalMultiplier"` + DailyBonus int `json:"dailyBonus"` Total int64 `json:"total"` } func (s *Score) Calc() { subGoalMultiplier := 1.0 - if s.SubGoalMultiplier != nil { - subGoalMultiplier = *s.SubGoalMultiplier + if s.ItemMultiplier != nil { + subGoalMultiplier = *s.ItemMultiplier } s.Total = int64(s.DailyBonus) + int64(s.ActivityScore*float64(s.Amount)*subGoalMultiplier) diff --git a/services/scoring.go b/services/scoring.go index 0367743..a504268 100644 --- a/services/scoring.go +++ b/services/scoring.go @@ -13,11 +13,11 @@ type ScoringService struct { db database.Database } -func (s *ScoringService) ScoreOne(ctx context.Context, period models.Period, log models.PeriodLog) (*models.Score, error) { +func (s *ScoringService) ScoreOne(ctx context.Context, period models.Period, log models.Log) (*models.Score, error) { return s.scoreOne(ctx, period, log) } -func (s *ScoringService) scoreOne(ctx context.Context, period models.Period, log models.PeriodLog) (*models.Score, error) { +func (s *ScoringService) scoreOne(ctx context.Context, period models.Period, log models.Log) (*models.Score, error) { goal := period.Goal(log.GoalID) if goal == nil { return nil, slerrors.NotFound("Goal") @@ -37,16 +37,6 @@ func (s *ScoringService) scoreOne(ctx context.Context, period models.Period, log Amount: log.Amount, } - if log.SubGoalID != "" { - subGoal := goal.SubGoal(log.SubGoalID) - if subGoal == nil { - return nil, slerrors.NotFound("Sub-Goal") - } - - multiplier := subGoal.Multiplier - score.SubGoalMultiplier = &multiplier - } - if activity.DailyBonus > 0 { isFirst := true for _, log2 := range period.Logs { diff --git a/svelte-ui/src/App.svelte b/svelte-ui/src/App.svelte index 8ae50a5..819ddab 100644 --- a/svelte-ui/src/App.svelte +++ b/svelte-ui/src/App.svelte @@ -3,6 +3,7 @@ import LogPage from "./routes/LogPage"; import ActivitiesPage from "./routes/ActivitiesPage"; + import ItemPage from "./routes/ItemPage"; import LoginModal from "./modals/LoginModal"; import CreateActivityModal from "./modals/CreateActivityModal"; @@ -31,7 +32,9 @@ - StufflogActivities + Stufflog + Activities + Items
{#if ($auth.checked && $auth.user !== null)} @@ -43,6 +46,7 @@ + diff --git a/svelte-ui/src/api/stufflog.js b/svelte-ui/src/api/stufflog.js index bd96b88..5a40fb0 100644 --- a/svelte-ui/src/api/stufflog.js +++ b/svelte-ui/src/api/stufflog.js @@ -1,6 +1,7 @@ import ky from "ky"; import Activity, {ActivityUpdate} from "../models/activity"; import Period, {PeriodUpdate} from "../models/period"; +import Item, {ItemUpdate} from "../models/item"; function json(data) { return { @@ -33,7 +34,7 @@ export class StuffLogAPI { /** * List activities * - * @returns {Activity[]} + * @returns {Promise} */ async listActivities() { const data = await k.get("/api/activity/").json(); @@ -42,7 +43,7 @@ export class StuffLogAPI { /** * List periods * - * @returns {{periods: Period[], activities: Activity[]}} + * @returns {Promise<{>periods: Period[], activities: Activity[]}} */ async listPeriods() { const data = await k.get("/api/period/").json(); @@ -53,7 +54,7 @@ export class StuffLogAPI { * Find activity * * @param {string} id - * @returns {Activity} + * @returns {Promise} */ async findActivity(id) { const data = await k.get(`/api/activity/${id}`).json(); @@ -63,7 +64,7 @@ export class StuffLogAPI { * Find period * * @param {string} id - * @returns {Period} + * @returns {Promise} */ async findPeriod(id) { const data = await k.get(`/api/period/${id}`).json(); @@ -73,7 +74,7 @@ export class StuffLogAPI { * Create a new activity * * @param {Activity} activity - * @returns {Activity} + * @returns {Promise} */ async postActivity(activity) { const data = await k.post(`/api/activity/`, json(activity)).json(); @@ -83,7 +84,7 @@ export class StuffLogAPI { * Create a new period * * @param {Period} period - * @returns {Period} + * @returns {Promise} */ async postPeriod(period) { const data = await k.post(`/api/period/`, json(period)).json(); @@ -94,7 +95,7 @@ export class StuffLogAPI { * * @param {string} id * @param {ActivityUpdate[]} updates - * @returns {Activity} + * @returns {Promise} */ async patchActivity(id, ...updates) { const data = await k.patch(`/api/activity/${id}`, json(updates)).json(); @@ -105,7 +106,7 @@ export class StuffLogAPI { * * @param {string} id * @param {PeriodUpdate[]} updates - * @returns {Period} + * @returns {Promise} */ async patchPeriod(id, ...updates) { const data = await k.patch(`/api/period/${id}`, json(updates)).json(); @@ -115,23 +116,78 @@ export class StuffLogAPI { * Update an activity * * @param {string} id - * @returns {Activity} + * @returns {Promise} */ async deleteActivity(id) { const data = await k.delete(`/api/activity/${id}`).json(); return new Activity(data.activity); } - /** * Update an period * * @param {string} id - * @returns {Period} + * @returns {Promise} */ async deletePeriod(id) { const data = await k.delete(`/api/period/${id}`).json(); return new Period(data.period); } + + /** + * Get items + * + * @param {string} id + * @returns {Promise} + */ + async listItems() { + const data = await k.get(`/api/item/`).json(); + return data.items + } + + /** + * Get an item + * + * @param {string} id + * @returns {Promise} + */ + async getItem(id) { + const data = await k.get(`/api/item/${id}`).json(); + return data.item + } + + /** + * Post an item + * + * @param {Item} item + * @returns {Promise} + */ + async postItem(item) { + const data = await k.post(`/api/item/`, json(item)).json(); + return data.item + } + + /** + * Delete an item + * + * @param {string} id + * @param {ItemUpdate} updates + * @returns {Promise} + */ + async updateItem(id, ...updates) { + const data = await k.patch(`/api/item/${id}`, json(updates)).json(); + return data.item + } + + /** + * Delete an item + * + * @param {string} id + * @returns {Promise} + */ + async deleteItem(id) { + const data = await k.delete(`/api/item/${id}`).json(); + return data.item + } } const slApi = new StuffLogAPI(); diff --git a/svelte-ui/src/components/ActivityAmount.svelte b/svelte-ui/src/components/ActivityAmount.svelte new file mode 100644 index 0000000..f9a195f --- /dev/null +++ b/svelte-ui/src/components/ActivityAmount.svelte @@ -0,0 +1,23 @@ + + +{#if subActivity != null} + {amount} {pluralize(subActivity.unitName, amount)} +{:else} + {amount} (unknown unit) +{/if} + + \ No newline at end of file diff --git a/svelte-ui/src/components/ActivityDisplay.svelte b/svelte-ui/src/components/ActivityDisplay.svelte new file mode 100644 index 0000000..b0b58c4 --- /dev/null +++ b/svelte-ui/src/components/ActivityDisplay.svelte @@ -0,0 +1,26 @@ + + +{#if activity != null && !activity.deleted} +
+ {activity.name}{(subActivity && !subActivity.deleted) ? " " + subActivity.name : ""} +{:else} +
+ (Unknown) +{/if} + + \ No newline at end of file diff --git a/svelte-ui/src/components/ActivityIcon.svelte b/svelte-ui/src/components/ActivityIcon.svelte index 0ea63f9..0b8fab8 100644 --- a/svelte-ui/src/components/ActivityIcon.svelte +++ b/svelte-ui/src/components/ActivityIcon.svelte @@ -14,6 +14,8 @@ import { faCodeBranch } from "@fortawesome/free-solid-svg-icons/faCodeBranch"; import { faGuitar } from "@fortawesome/free-solid-svg-icons/faGuitar"; import { faMusic } from "@fortawesome/free-solid-svg-icons/faMusic"; + import { faArchive } from "@fortawesome/free-solid-svg-icons/faArchive"; + import { faCheck } from "@fortawesome/free-solid-svg-icons/faCheck"; const icons = { "plus": faPlus, @@ -29,6 +31,8 @@ "code": faCode, "code_branch": faCodeBranch, "guitar": faGuitar, + "archive": faArchive, + "check": faCheck, }; export const iconNames = Object.keys(icons); diff --git a/svelte-ui/src/components/AddBoi.svelte b/svelte-ui/src/components/AddBoi.svelte index 09d10cd..00cb7de 100644 --- a/svelte-ui/src/components/AddBoi.svelte +++ b/svelte-ui/src/components/AddBoi.svelte @@ -2,11 +2,10 @@ import Icon from 'fa-svelte' import { faPlus } from '@fortawesome/free-solid-svg-icons/faPlus' - export let header = ""; - export let icon = null; + export let top = false; -
+
@@ -26,6 +25,11 @@ transition: 250ms; } + div.addboi.top { + margin-bottom: 0.5em; + padding: 0.25em 0; + font-size: 1.5em; + } div.addboi:hover { color: #777; diff --git a/svelte-ui/src/components/Col.svelte b/svelte-ui/src/components/Col.svelte deleted file mode 100644 index 7555fa4..0000000 --- a/svelte-ui/src/components/Col.svelte +++ /dev/null @@ -1,51 +0,0 @@ - - -
- - \ No newline at end of file diff --git a/svelte-ui/src/components/ItemDisplay.svelte b/svelte-ui/src/components/ItemDisplay.svelte new file mode 100644 index 0000000..1263d22 --- /dev/null +++ b/svelte-ui/src/components/ItemDisplay.svelte @@ -0,0 +1,20 @@ + + +{#if item != null} + {item.name} +{:else if id != ""} + (Deleted) +{:else} + (None) +{/if} + + \ No newline at end of file diff --git a/svelte-ui/src/components/Menu.svelte b/svelte-ui/src/components/Menu.svelte index 8299cc9..e80e734 100644 --- a/svelte-ui/src/components/Menu.svelte +++ b/svelte-ui/src/components/Menu.svelte @@ -16,5 +16,7 @@ background-color: #222; margin: 0; user-select: none; + display: flex; + flex-direction: row; } \ No newline at end of file diff --git a/svelte-ui/src/components/MenuItem.svelte b/svelte-ui/src/components/MenuItem.svelte index 4379352..557f2f6 100644 --- a/svelte-ui/src/components/MenuItem.svelte +++ b/svelte-ui/src/components/MenuItem.svelte @@ -8,7 +8,7 @@ \ No newline at end of file diff --git a/svelte-ui/src/components/Table.svelte b/svelte-ui/src/components/Table.svelte new file mode 100644 index 0000000..21264a3 --- /dev/null +++ b/svelte-ui/src/components/Table.svelte @@ -0,0 +1,51 @@ + + + + + + {#each zipped as h} + + {/each} + + + + + +
{h.header}
+ + \ No newline at end of file diff --git a/svelte-ui/src/components/tables/GoalTable.svelte b/svelte-ui/src/components/tables/GoalTable.svelte new file mode 100644 index 0000000..7015e72 --- /dev/null +++ b/svelte-ui/src/components/tables/GoalTable.svelte @@ -0,0 +1,38 @@ + + + + {#each period.goals as goal (goal.id)} + + + + + + {/each} +
+ a.id === goal.activityId)} /> + + onOption("periodgoal.remove", goal.id)}>Delete +
\ No newline at end of file diff --git a/svelte-ui/src/components/tables/ItemTable.svelte b/svelte-ui/src/components/tables/ItemTable.svelte new file mode 100644 index 0000000..e4583ac --- /dev/null +++ b/svelte-ui/src/components/tables/ItemTable.svelte @@ -0,0 +1,30 @@ + + + + {#each items as item (item.id)} + + + + + + {/each} +
{item.name}{item.multiplier.toFixed(2)} + onOption("item.stats", item.id)}>Stats, + onOption("item.edit", item.id)}>Edit, + onOption("item.remove", item.id)}>Delete +
\ No newline at end of file diff --git a/svelte-ui/src/components/tables/LogTable.svelte b/svelte-ui/src/components/tables/LogTable.svelte new file mode 100644 index 0000000..c814875 --- /dev/null +++ b/svelte-ui/src/components/tables/LogTable.svelte @@ -0,0 +1,66 @@ + + + + {#each period.logs as log (log.id)} + + + + + + + + + {/each} +
{dateStr(log.date)} + a.id === (period.goals.find(g => g.id === log.goalId) || {}).activityId)} + subActivity={subActivityMap[log.subActivityId]} + /> + + i.id === log.itemId)} /> + + + + onOption("periodlog.info", log.id)}>{log.score.total} + + onOption("periodlog.remove", log.id)}>Delete +
diff --git a/svelte-ui/src/components/tables/SubActivityTable.svelte b/svelte-ui/src/components/tables/SubActivityTable.svelte new file mode 100644 index 0000000..9ff7bcb --- /dev/null +++ b/svelte-ui/src/components/tables/SubActivityTable.svelte @@ -0,0 +1,30 @@ + + + + {#each activity.subActivities as subActivtiy (subActivtiy.id)} + + + + + + {/each} +
{subActivtiy.name}{subActivtiy.value} per {pluralize(subActivtiy.unitName, 1)} + onOption("subactivity.edit", subActivtiy.id)}>Edit, + onOption("subactivity.remove", subActivtiy.id)}>Delete +
\ No newline at end of file diff --git a/svelte-ui/src/modals/AddPeriodGoalModal.svelte b/svelte-ui/src/modals/AddPeriodGoalModal.svelte index 24c3ab7..b1e565b 100644 --- a/svelte-ui/src/modals/AddPeriodGoalModal.svelte +++ b/svelte-ui/src/modals/AddPeriodGoalModal.svelte @@ -11,7 +11,6 @@ let error = null; let activityId = ""; let pointCount = "1000"; - let subGoals = [{name:"",multiplier:"1.0"}]; function addPeriodGoal() { error = null; @@ -26,28 +25,9 @@ return } - const parsedSubGoals = []; - for (const subGoal of subGoals) { - if (subGoal.name === "") { - continue; - } - - const parsedMultiplier = parseFloat(subGoal.multiplier); - if (Number.isNaN(parsedMultiplier) || parsedMultiplier < 0) { - error = `Sub goal ${subGoal.name} needs a numeric multiplier.`; - return - } - - parsedSubGoals.push({ - name: subGoal.name, - multiplier: parsedMultiplier, - }) - } - const addGoal = { activityId: activityId, pointCount: parsedPointCount, - subGoals: parsedSubGoals, } stufflog.updatePeriod(period.id, {addGoal}).then(() => { @@ -58,24 +38,6 @@ }); } - function updateSubGoalForm(subGoals) { - let last = -1; - for (let i = 0; i < subGoals.length; ++i) { - if (subGoals[i].name != "") { - last = i; - } - } - - if (last == subGoals.length - 1) { - return [...subGoals, {name: "", value: "1.0"}]; - } else if (last + 2 < subGoals.length && subGoals.length > 1) { - return subGoals.slice(0, last + 2); - } else { - return subGoals; - } - } - - $: subGoals = updateSubGoalForm(subGoals); $: if (activityId === "" && $stufflog.activities.length > 0) { activityId = $stufflog.activities[0].id; } @@ -91,10 +53,6 @@ - - {#each subGoals as subGoal} - - {/each}

The amount of points must be in an increment of 1000, which should be diff --git a/svelte-ui/src/modals/AddPeriodLogModal.svelte b/svelte-ui/src/modals/AddPeriodLogModal.svelte index 7312960..8a7d1a5 100644 --- a/svelte-ui/src/modals/AddPeriodLogModal.svelte +++ b/svelte-ui/src/modals/AddPeriodLogModal.svelte @@ -16,12 +16,13 @@ let goal = null; let activity = null; let subActivity = null; - let dateInput = dateStr(period.from); + let dateInput = dateStr(new Date); let goalId = ""; let subGoalId = ""; let subActivityId = ""; let amount = "200"; let description = ""; + let dateInPeriod = false; function addPeriodLog() { error = null; @@ -56,9 +57,8 @@ subActivityId = ""; } } - $: if (goal != null && !goal.subGoals.find(s => s.id === subGoalId)) { - subGoalId = ""; - } + + $: dateInPeriod = Date.parse(dateInput) > Date.parse(period.from) && Date.parse(dateInput) < Date.parse(period.to) modal.close()}> @@ -66,6 +66,10 @@ + {#if (!dateInPeriod)} +

Date is outside period!

+ {/if} + {/if} - {#if (goal != null && goal.subGoals.length > 0)} - - - {/if} - {#if (subActivity != null)} @@ -103,9 +96,14 @@
- + \ No newline at end of file diff --git a/svelte-ui/src/modals/InfoPeriodLogModal.svelte b/svelte-ui/src/modals/InfoPeriodLogModal.svelte index 4f34c1e..89dc254 100644 --- a/svelte-ui/src/modals/InfoPeriodLogModal.svelte +++ b/svelte-ui/src/modals/InfoPeriodLogModal.svelte @@ -14,18 +14,20 @@ export let log = {}; export let activity = {}; export let subActivity = {}; - export let subGoal = null; + export let item = null; let activityPoints = 0; - let subGoalBonus = 0; + let itemBonus = 0; let roundingError = 0; + let title = "" $: activityPoints = log.score.amount * log.score.activityScore; - $: subGoalBonus = subGoal.multiplier ? activityPoints * (subGoal.multiplier - 1) : 0; - $: roundingError = (log.score.total) - (Math.floor(activityPoints) + Math.floor(subGoalBonus) + log.score.dailyBonus); + $: itemBonus = item ? activityPoints * (item.multiplier - 1) : 0; + $: roundingError = (log.score.total) - (Math.floor(activityPoints) + Math.floor(itemBonus) + log.score.dailyBonus); + $: title = `${dateStr(log.date)} - ${(activity||{name:"(Unknown)"}).name} ${(subActivity||{name:"(Unknown)"}).name}` - modal.close()}> + modal.close()}>
modal.close()}> {#if log.description !== ""} @@ -35,12 +37,16 @@ {Math.floor(activityPoints + roundingError)} points - ({log.score.amount} {pluralize(subActivity.unitName, log.score.amount)}) + {#if (subActivity != null)} + + ({log.score.amount} {pluralize(subActivity.unitName, log.score.amount)}) + + {/if} - {#if subGoal.multiplier != null} + {#if item != null} - {Math.floor(subGoalBonus)} points - ({subGoal.name}) + {Math.floor(itemBonus)} points + ({item.name}) {/if} {#if log.score.dailyBonus != 0} diff --git a/svelte-ui/src/modals/RemovePeriodLogModal.svelte b/svelte-ui/src/modals/RemovePeriodLogModal.svelte index b507252..d00e51f 100644 --- a/svelte-ui/src/modals/RemovePeriodLogModal.svelte +++ b/svelte-ui/src/modals/RemovePeriodLogModal.svelte @@ -27,7 +27,7 @@ let error = null; removePeriodLog()}>

Are you sure you want to remove log {log.description} - ({dateStr(log.date)}, {activity.name}) from + ({dateStr(log.date)}, {activity ? activity.name : "(Unknown)"}) from period {period.name}?

diff --git a/svelte-ui/src/modals/RemoveSubActivityModal.svelte b/svelte-ui/src/modals/RemoveSubActivityModal.svelte index 63ef350..d38c2bc 100644 --- a/svelte-ui/src/modals/RemoveSubActivityModal.svelte +++ b/svelte-ui/src/modals/RemoveSubActivityModal.svelte @@ -6,12 +6,12 @@ import stufflog from "../stores/stufflog"; export let activity = {}; - export let subActivtiy = {}; + export let subActivity = {}; let error = null; function removeSubActivity() { - stufflog.updateActivity(activity.id, {removeSub: subActivtiy.id}).then(() => { + stufflog.updateActivity(activity.id, {removeSub: subActivity.id}).then(() => { modal.close(); }).catch(err => { error = err.message || err; @@ -23,7 +23,7 @@ modal.close()}> removeSubActivity()}>

- Are you sure you want to remove sub-acitvity {subActivtiy.name || "(unnamed)"} from + Are you sure you want to remove sub-acitvity {subActivity.name || "(Unnamed)"} from activity {activity.name}?

diff --git a/svelte-ui/src/models/item.d.ts b/svelte-ui/src/models/item.d.ts new file mode 100644 index 0000000..b9d6579 --- /dev/null +++ b/svelte-ui/src/models/item.d.ts @@ -0,0 +1,12 @@ +export default interface Item { + id: string + userId: string + active: boolean + name: string + multiplier: number +} + +export interface ItemUpdate { + setName: string + setActive: boolean +} \ No newline at end of file diff --git a/svelte-ui/src/models/item.js b/svelte-ui/src/models/item.js new file mode 100644 index 0000000..7c6d6c7 --- /dev/null +++ b/svelte-ui/src/models/item.js @@ -0,0 +1 @@ +module.exports = {} \ No newline at end of file diff --git a/svelte-ui/src/routes/ActivitiesPage.svelte b/svelte-ui/src/routes/ActivitiesPage.svelte index 53f5f88..0d691ec 100644 --- a/svelte-ui/src/routes/ActivitiesPage.svelte +++ b/svelte-ui/src/routes/ActivitiesPage.svelte @@ -10,55 +10,42 @@ import Boi from "../components/Boi.svelte"; import AddBoi from "../components/AddBoi.svelte"; - import Property from "../components/Property.svelte"; - import Row from "../components/Row.svelte"; - import Col from "../components/Col.svelte"; import Link from "../components/Link.svelte"; + + import Table from "../components/Table.svelte"; + import SubActivityTable from "../components/tables/SubActivityTable"; - function openModal(modalName, id, subId) { + function onActivityOption(modalName, id) { const activity = get(stufflog).activities.find(a => a.id === id); - const subActivity = (activity || {subActivities:[]}).subActivities.find(s => s.id === subId); - modal.open(modalName, {activity, subActivity}) + modal.open(modalName, {activity}) }
+ modal.open("activity.create")}>Activity {#each $stufflog.activities as activity (activity.id)} - - - - - - openModal("activity.edit", activity.id)}>Edit Activity, - openModal("subactivity.add", activity.id)}>Add Sub-Activity, - openModal("activity.delete", activity.id)}>Delete Activity - - - - +
- - - + + + - {#each activity.subActivities as subActivtiy (subActivtiy.id)} - - - - - - {/each} -
Sub-ActivityValueOptions{activity.id}{activity.dailyBonus} + onActivityOption("activity.edit", activity.id)}>Edit Activity, + onActivityOption("subactivity.add", activity.id)}>Add Sub-Activity, + onActivityOption("activity.delete", activity.id)}>Delete Activity +
{subActivtiy.name}{subActivtiy.value} per {pluralize(subActivtiy.unitName, 1)} - openModal("subactivity.edit", activity.id, subActivtiy.id)}>Edit, - openModal("subactivity.remove", activity.id, subActivtiy.id)}>Delete -
+ + + modal.open(e.detail.name, e.detail)} + />
{:else}
No data.
{/each} - modal.open("activity.create")}>Activity
\ No newline at end of file diff --git a/svelte-ui/src/routes/ItemPage.svelte b/svelte-ui/src/routes/ItemPage.svelte new file mode 100644 index 0000000..64ba23a --- /dev/null +++ b/svelte-ui/src/routes/ItemPage.svelte @@ -0,0 +1,49 @@ + + +
+ modal.open("item.create")}>Item + + + + + + +
+ + \ No newline at end of file diff --git a/svelte-ui/src/routes/LogPage.svelte b/svelte-ui/src/routes/LogPage.svelte index d8bfaa7..54e4951 100644 --- a/svelte-ui/src/routes/LogPage.svelte +++ b/svelte-ui/src/routes/LogPage.svelte @@ -1,117 +1,56 @@ - -
+ modal.open("period.create")}>Period {#each $stufflog.periods as period (period.id)} - - - - - - openGoalModal("period.edit", period.id)}>Edit Period, - openGoalModal("periodgoal.add", period.id)}>Add Goal, + + + + +
{dateStr(period.from)}{dateStr(period.to)} + modal.open("period.edit", {period: getPeriod(period.id)})}>Edit Period, + modal.open("periodgoal.add", {period: getPeriod(period.id)})}>Add Goal, {#if period.goals.length > 0} - openGoalModal("periodlog.add", period.id)}>Add Log, + modal.open("periodlog.add", {period: getPeriod(period.id)})}>Add Log, {/if} - openGoalModal("period.delete", period.id)}>Delete Period - - - - - - - - - - {#each period.goals as goal (goal.id)} - - - - - - {/each} -
ActivityPointsOptions
-
-
{(activityMap[goal.activityId] || {name: "(Unknown)"}).name}
-
- openGoalModal("periodgoal.remove", period.id, goal.id)}>Delete -
- - - - - - - - - - {#each period.table as row (row.log.id)} - - - - - - - - - {/each} -
DateGoalSub-ActivityAmountPointsOptions
{dateStr(row.log.date)} -
-
{row.activity.name} {row.subActivity.name}
-
{row.subGoal.name}{row.log.amount} {pluralize(row.subActivity.unitName, row.log.amount)} - openLogModal("periodlog.info", period.id, row.log.id)}>{row.log.score.total} - - openLogModal("periodlog.remove", period.id, row.log.id)}>Delete -
+ modal.open("period.delete", {period: getPeriod(period.id)})}>Delete Period +
+ + modal.open(e.detail.name, e.detail)} + /> + modal.open(e.detail.name, e.detail)} + />
{/each} - modal.open("period.create")}>Period
\ No newline at end of file diff --git a/svelte-ui/src/stores/items.js b/svelte-ui/src/stores/items.js new file mode 100644 index 0000000..45cf6a7 --- /dev/null +++ b/svelte-ui/src/stores/items.js @@ -0,0 +1,58 @@ +import { writable } from "svelte/store"; + +import slApi from "../api/stufflog"; + +import stufflogStore from "./stufflog"; + +function createItemStore() { + const {set, update, subscribe} = writable([]) + + return { + subscribe, + + listItems() { + return slApi.listItems().then(items => { + set(items) + console.log(items) + return items + }) + }, + + findItem(id) { + return slApi.getItem(id).then(item => { + update(s => replaceItem(s, item)) + return item + }) + }, + + createItem(item) { + return slApi.postItem(item).then(item => { + update(s => replaceItem(s, item)) + return item + }) + }, + + updateItem(id, ...updates) { + return slApi.updateItem(id, ...updates).then(item => { + update(s => replaceItem(s, item)) + return item + }) + }, + + deleteItem(id) { + return slApi.deleteItem(id).then(item => { + update(s => s.items.filter(i => i.id !== item.id)) + return item + }) + }, + } +} + +function replaceItem(s, item) { + return ([ + ...s.items.filter(i => i.id !== item.id), + item + ].sort((a, b) => a.name.localeCompare(b.name))) +} + +export default createItemStore(); \ No newline at end of file diff --git a/svelte-ui/src/stores/modal.js b/svelte-ui/src/stores/modal.js index 9795c70..a9a8b8c 100644 --- a/svelte-ui/src/stores/modal.js +++ b/svelte-ui/src/stores/modal.js @@ -7,6 +7,7 @@ function createModalStore() { subscribe, open(name, data = {}) { + console.log("Opening modal", name, data) set({name, data}); }, diff --git a/svelte-ui/src/stores/stufflog.js b/svelte-ui/src/stores/stufflog.js index 93c78dc..faea0fa 100644 --- a/svelte-ui/src/stores/stufflog.js +++ b/svelte-ui/src/stores/stufflog.js @@ -18,25 +18,6 @@ function calculateTotalScore(period) { )}), {}) } -function generateLogTable(activities, period) { - const newTable = []; - - for (const log of period.logs) { - const goal = period.goals.find(g => g.id === log.goalId) || UNKNOWN_GOAL; - const subGoal = log.subGoalId - ? goal.subGoals.find(s => s.id === log.subGoalId) || UNKNOWN_SUB_GOAL - : NO_SUB_GOAL; - const activity = activities.find(a => a.id === goal.activityId) || UNKNOWN_ACTIVITY; - const subActivity = activity.subActivities.find(s => s.id === log.subActivityId) || UNKNOWN_SUB_ACTIVITY; - - newTable.push({log, goal, subGoal, activity, subActivity}) - } - - newTable.sort((a, b) => a.log.date - b.log.date); - - return newTable; -} - function replaceActivity(d, activity) { const data = { ...d, @@ -49,7 +30,6 @@ function replaceActivity(d, activity) { data.periods = data.periods.map(p => ({ ...p, scores: calculateTotalScore(p), - table: generateLogTable(data.activities, p), })); return data; @@ -63,7 +43,6 @@ function replacePeriod(d, period) { { ...period, scores: calculateTotalScore(period), - table: generateLogTable(d.activities, period), }, ].sort((a,b) => b.from - a.from), }; @@ -100,7 +79,6 @@ function createStufflogStore() { periods: d.periods.map(p => ({ ...p, scores: calculateTotalScore(p), - table: generateLogTable(activities, p), })), })); }, @@ -117,7 +95,6 @@ function createStufflogStore() { periods: periods.sort((a,b) => b.from - a.from).map(p => ({ ...p, scores: calculateTotalScore(p), - table: generateLogTable(d.activities, p), })), })); },