diff --git a/ui/.air.toml b/ui/.air.toml index 09d1607..f3f8ffe 100644 --- a/ui/.air.toml +++ b/ui/.air.toml @@ -5,7 +5,7 @@ tmp_dir = "tmp" [build] args_bin = [] bin = "./tmp/main" - cmd = "rm **/*_templ.go; templ generate --path ./templates && go build -o ./tmp/main ." + cmd = "rm **/*_templ.go; templ generate --path ./templates && GOEXPERIMENT=loopvar go build -o ./tmp/main ." delay = 1000 exclude_dir = ["assets", "tmp", "vendor", "testdata", "dist", "docker"] exclude_file = [] diff --git a/ui/controllers/components.go b/ui/controllers/components.go new file mode 100644 index 0000000..2a6c8de --- /dev/null +++ b/ui/controllers/components.go @@ -0,0 +1,24 @@ +package controllers + +import ( + "git.preston-baxter.com/Preston_PLB/capstone/frontend-service/templates" + "github.com/gin-gonic/gin" +) + +func GetAddActionForm(c *gin.Context) { + user := getUserFromContext(c) + if user == nil { + log.Warnf("Could not find user in context. Trying to redner Action form") + badRequest(c, "No user available in context") + return + } + + accounts, err := mongo.FindVendorAccountByUser(user.Id) + if err != nil { + log.WithError(err).Errorf("Failed to find vendor accounts for: %s", user.Email) + serverError(c, "No user available in context") + return + } + + renderTempl(c, templates.DashboardActionModal(accounts)) +} diff --git a/ui/controllers/controllers.go b/ui/controllers/controllers.go index 6658c37..4868964 100644 --- a/ui/controllers/controllers.go +++ b/ui/controllers/controllers.go @@ -26,6 +26,8 @@ func BuildRouter(r *gin.Engine) { }) r.Use(cors.Default()) + + r.Static("/static", "./dist") //mainpage r.GET("/", AuthMiddleware(false), LandingPage) @@ -39,6 +41,9 @@ func BuildRouter(r *gin.Engine) { dashboard := r.Group("/dashboard") dashboard.Use(AuthMiddleware(true)) dashboard.GET("", DashboardPage) + //Dashboard Forms + dashboardForms := dashboard.Group("/forms") + dashboardForms.GET("/addAction", GetAddActionForm) //Vendor stuff vendor := r.Group("/vendor") diff --git a/ui/controllers/pages.go b/ui/controllers/pages.go index 8c129cd..b7570aa 100644 --- a/ui/controllers/pages.go +++ b/ui/controllers/pages.go @@ -1,6 +1,9 @@ package controllers import ( + "errors" + "sync" + "git.preston-baxter.com/Preston_PLB/capstone/frontend-service/db/models" "git.preston-baxter.com/Preston_PLB/capstone/frontend-service/templates" "github.com/gin-gonic/gin" @@ -29,16 +32,42 @@ func DashboardPage(c *gin.Context) { if user == nil { log.Error("No user found in context") - c.AbortWithStatus(502) + serverError(c, "No user found in context") return } - vendors, err := mongo.FindVendorAccountByUser(user.MongoId()) - if err != nil { - log.WithError(err).Error("Failed to lookup vendor accounts for user") - c.AbortWithStatus(502) - return + //Split database fetching into go routines + var vendors []models.VendorAccount + var actions []models.ActionMapping + //TODO: find a generic way to do this. + errs := make([]error, 2) + + //Use waitgroup to syncronize + waitGroup := new(sync.WaitGroup) + waitGroup.Add(2) + + go func(wg *sync.WaitGroup){ + vendors, errs[0] = mongo.FindVendorAccountByUser(user.MongoId()) + wg.Done() + }(waitGroup) + + go func(wg *sync.WaitGroup){ + actions, errs[1] = mongo.FindActionMappingsByUser(user.MongoId()) + wg.Done() + }(waitGroup) + + //after this line we are in sync + waitGroup.Wait() + + //handle errors + for _, err := range errs { + if err != nil { + log.WithError(errors.Join(errs...)).Error("Failed to do database lookup when retrieving dashbDashboardPage") + serverError(c, "Failed to do database lookup when retrieving dashbDashboardPage") + return + } } - renderTempl(c, templates.DashboardPage(user, vendors)) + + renderTempl(c, templates.DashboardPage(user, vendors, actions)) } diff --git a/ui/db/actions.go b/ui/db/actions.go new file mode 100644 index 0000000..07f20d3 --- /dev/null +++ b/ui/db/actions.go @@ -0,0 +1,33 @@ +package db + +import ( + "context" + + "git.preston-baxter.com/Preston_PLB/capstone/frontend-service/config" + "git.preston-baxter.com/Preston_PLB/capstone/frontend-service/db/models" + "go.mongodb.org/mongo-driver/bson" + "go.mongodb.org/mongo-driver/bson/primitive" + "go.mongodb.org/mongo-driver/mongo" + "go.mongodb.org/mongo-driver/mongo/options" +) + +func (db *DB) FindActionMappingsByUser(userId primitive.ObjectID) ([]models.ActionMapping, error) { + conf := config.Config() + + opts := options.Find() + res, err := db.client.Database(conf.Mongo.EntDb).Collection(conf.Mongo.EntCol).Find(context.Background(), bson.M{"user_id": userId, "obj_info.ent": models.ACTION_MAPPING_TYPE}, opts) + if err != nil { + if err == mongo.ErrNoDocuments { + return nil, nil + } + return nil, err + } + + actions := []models.ActionMapping{} + err = res.All(context.Background(), &actions) + if err != nil { + return nil, err + } + + return actions, nil +} diff --git a/ui/db/models/actions.go b/ui/db/models/actions.go new file mode 100644 index 0000000..8451bd6 --- /dev/null +++ b/ui/db/models/actions.go @@ -0,0 +1,24 @@ +package models + +import "go.mongodb.org/mongo-driver/bson/primitive" + +const ACTION_MAPPING_TYPE = "action" + +type ActionMapping struct { + *CommonFields `bson:"obj_info"` + Id primitive.ObjectID `bson:"_id,omitempty"` + UserId primitive.ObjectID `bson:"user_id,omitempty"` + SourceEvent *Event `bson:"source_event,omitempty"` + Action *Action `bson:"action,omitempty"` +} + +type Action struct { + VendorName string `bson:"vendor_name,omitempty"` + Type string `bson:"type,omitempty"` + Fields map[string]string `bson:"fields,omitempty"` +} + +type Event struct { + VendorName string `bson:"vendor_name,omitempty"` + Key string `bson:"key,omitempty"` +} diff --git a/ui/db/models/vendor.go b/ui/db/models/vendor.go index ce5a295..cf43e63 100644 --- a/ui/db/models/vendor.go +++ b/ui/db/models/vendor.go @@ -9,8 +9,8 @@ import ( const VENDOR_ACCOUNT_TYPE = "vendor_account" const ( - YOUTUBE_VENDOR_NAME = "YouTube" - PCO_VENDOR_NAME = "PCO" + YOUTUBE_VENDOR_NAME = "youtube" + PCO_VENDOR_NAME = "pco" ) diff --git a/ui/static/index.css b/ui/static/index.css index e69de29..b5c61c9 100644 --- a/ui/static/index.css +++ b/ui/static/index.css @@ -0,0 +1,3 @@ +@tailwind base; +@tailwind components; +@tailwind utilities; diff --git a/ui/tailwing.config.js b/ui/tailwind.config.js similarity index 64% rename from ui/tailwing.config.js rename to ui/tailwind.config.js index c53c079..07fa1cc 100644 --- a/ui/tailwing.config.js +++ b/ui/tailwind.config.js @@ -1,5 +1,5 @@ module.exports = { - content: ["**/*.templ"], + content: ["templates/*.templ"], theme: { extend: {}, }, plugins: [], } diff --git a/ui/templates/dashboard_page.templ b/ui/templates/dashboard_page.templ index de496a5..85f42c5 100644 --- a/ui/templates/dashboard_page.templ +++ b/ui/templates/dashboard_page.templ @@ -2,18 +2,34 @@ package templates import ( "fmt" + "strconv" "git.preston-baxter.com/Preston_PLB/capstone/frontend-service/db/models" ) -templ DashboardPage(user *models.User, vendorAccounts []models.VendorAccount) { +func hasPco(vendors []models.VendorAccount) bool { + for _, vendor := range vendors { + if vendor.Name == models.PCO_VENDOR_NAME { + return true + } + } + return false +} + +templ DashboardPage(user *models.User, vendorAccounts []models.VendorAccount, actionMappings []models.ActionMapping) { @Head("Dashboard")
++ Id + | ++ Event Source + | ++ Event Action + | ++ Action + | +
---|---|---|---|
+ No actions are available. Click + to add one + | +|||
+ { strconv.Itoa(index) } + | ++ { action.SourceEvent.Key } + | ++ { action.Action.VendorName }: { action.Action.Type } + | ++ @DashboardActionEditButton(&action) + | +