B: working on add action form

This commit is contained in:
Preston Baxter 2023-11-04 22:43:01 -05:00
parent 65167c3e58
commit 175b6cae8c
11 changed files with 321 additions and 19 deletions

View File

@ -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 = []

View File

@ -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))
}

View File

@ -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")

View File

@ -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())
//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(err).Error("Failed to lookup vendor accounts for user")
c.AbortWithStatus(502)
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))
}

33
ui/db/actions.go Normal file
View File

@ -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
}

24
ui/db/models/actions.go Normal file
View File

@ -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"`
}

View File

@ -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"
)

View File

@ -0,0 +1,3 @@
@tailwind base;
@tailwind components;
@tailwind utilities;

View File

@ -1,5 +1,5 @@
module.exports = {
content: ["**/*.templ"],
content: ["templates/*.templ"],
theme: { extend: {}, },
plugins: [],
}

View File

@ -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) {
<!DOCTYPE html>
<html>
@Head("Dashboard")
<body class="text-blueGray-700 antialiased">
<div
id="add-action-modal"
aria-hidden="false"
tabindex="-1"
class="transition-all hidden"
></div>
<div id="root">
@DashboardNav(user)
@DashboardContent(user, vendorAccounts)
@DashboardContent(user, vendorAccounts, actionMappings)
@Footer()
</div>
</body>
@ -295,7 +311,169 @@ templ DashboardVendorWidget(vendors []models.VendorAccount) {
</div>
}
templ DashboardContent(user *models.User, vendorAccounts []models.VendorAccount) {
templ DashboardActionModalForm(vendors []models.VendorAccount) {
<div class="relative p-6 flex-auto">
<form class="space-y-4 text-gray-700">
<div class="flex flex-wrap">
<div class="w-full">
<div class="relative inline-block w-full text-gray-700">
<select class="w-full h-10 pl-3 pr-6 text-base placeholder-gray-600 border rounded-lg appearance-none focus:shadow-outline" placeholder="Choose action source">
if hasPco(vendors) {
<option value="plan">Plan</option>
<option value="calendar">Calendar</option>
} else {
<option value="nil">None Available</option>
}
</select>
<div class="absolute inset-y-0 right-0 flex items-center px-2 pointer-events-none">
<svg class="w-4 h-4 fill-current" viewBox="0 0 20 20"><path d="M5.293 7.293a1 1 0 011.414 0L10 10.586l3.293-3.293a1 1 0 111.414 1.414l-4 4a1 1 0 01-1.414 0l-4-4a1 1 0 010-1.414z" clip-rule="evenodd" fill-rule="evenodd"></path></svg>
</div>
</div>
</div>
</div>
<div class="flex flex-wrap -mx-2 space-y-4 md:space-y-0">
<div class="w-full px-2 md:w-1/2">
<label class="block mb-1" for="formGridCode_name">First name</label>
<input class="w-full h-10 px-3 text-base placeholder-gray-600 border rounded-lg focus:shadow-outline" type="text" id="formGridCode_name"/>
</div>
<div class="w-full px-2 md:w-1/2">
<label class="block mb-1" for="formGridCode_last">Last name</label>
<input class="w-full h-10 px-3 text-base placeholder-gray-600 border rounded-lg focus:shadow-outline" type="text" id="formGridCode_last"/>
</div>
</div>
<div class="flex flex-wrap -mx-2 space-y-4 md:space-y-0">
<div class="w-full px-2 md:w-1/3">
<label class="block mb-1" for="formGridCode_month">Month</label>
<input class="w-full h-10 px-3 text-base placeholder-gray-600 border rounded-lg focus:shadow-outline" type="text" id="formGridCode_month"/>
</div>
<div class="w-full px-2 md:w-1/3">
<label class="block mb-1" for="formGridCode_year">Year</label>
<input class="w-full h-10 px-3 text-base placeholder-gray-600 border rounded-lg focus:shadow-outline" type="text" id="formGridCode_year"/>
</div>
<div class="w-full px-2 md:w-1/3">
<label class="block mb-1" for="formGridCode_cvc">CVC</label>
<input class="w-full h-10 px-3 text-base placeholder-gray-600 border rounded-lg focus:shadow-outline" type="text" id="formGridCode_cvc"/>
</div>
</div>
</form>
</div>
}
templ DashboardActionModal(vendors []models.VendorAccount) {
<div class="transition-all ease-in-out overflow-x-hidden overflow-y-auto fixed flex inset-0 z-50 outline-none focus:outline-none justify-center items-center" id="add-action-modal">
<div class="relative w-auto my-6 mx-auto max-w-3xl">
<!--content-->
<div class="border-0 rounded-lg shadow-lg relative flex flex-col w-full bg-white outline-none focus:outline-none">
<!--header-->
<div class="flex items-start justify-between p-5 border-b border-solid border-blueGray-200 rounded-t">
<h3 class="text-3xl font-semibold">
Modal Title
</h3>
<button class="p-1 ml-auto bg-transparent border-0 text-black opacity-5 float-right text-3xl leading-none font-semibold outline-none focus:outline-none" onclick="toggleModal(&#39;add-action-modal&#39;)">
<span class="bg-transparent text-black opacity-5 h-6 w-6 text-2xl block outline-none focus:outline-none">
×
</span>
</button>
</div>
<!--body-->
@DashboardActionModalForm(vendors)
<!--footer-->
<div class="flex items-center justify-end p-6 border-t border-solid border-blueGray-200 rounded-b">
<button class="text-red-500 background-transparent font-bold uppercase px-6 py-2 text-sm outline-none focus:outline-none mr-1 mb-1 ease-linear transition-all duration-150" type="button" onclick="toggleModal(&#39;add-action-modal&#39;)">
Close
</button>
<button class="bg-emerald-500 text-white active:bg-emerald-600 font-bold uppercase text-sm px-6 py-3 rounded shadow hover:shadow-lg outline-none focus:outline-none mr-1 mb-1 ease-linear transition-all duration-150" type="button" onclick="toggleModal(&#39;add-action-modal&#39;)">
Save Changes
</button>
</div>
</div>
</div>
</div>
<div class="opacity-25 fixed flex inset-0 z-40 bg-black" id="add-action-modal-backdrop"></div>
}
templ DashboardActionDropDown() {
<button
hx-get="/dashboard/forms/addAction"
hx-target="#add-action-modal"
hx-swap="outerHTML"
class="bg-pink-500 text-white active:bg-pink-600 font-bold uppercase text-sm px-6 py-3 rounded shadow hover:shadow-lg outline-none focus:outline-none mr-1 mb-1 ease-linear transition-all duration-150"
type="button"
>
Add Action
</button>
}
templ DashboardActionEditButton(action *models.ActionMapping) {
}
templ DashboardActionsWidget(actions []models.ActionMapping) {
<div class="w-full xl:w-8/12 mb-12 xl:mb-0 px-4">
<div class="relative flex flex-col min-w-0 break-words bg-white w-full mb-6 shadow-lg rounded">
<div class="rounded-t mb-0 px-4 py-3 border-0">
<div class="flex flex-wrap items-center">
<div class="relative w-full px-4 max-w-full flex-grow flex-1">
<h3 class="font-semibold text-base text-blueGray-700">
Vendors
</h3>
</div>
<div class="relative w-full px-4 max-w-full flex-grow flex-1 text-right">
@DashboardActionDropDown()
</div>
</div>
</div>
<div class="block w-full overflow-x-auto">
<!-- Projects table -->
<table class="items-center w-full bg-transparent border-collapse">
<thead>
<tr>
<th class="px-6 bg-blueGray-50 text-blueGray-500 align-middle border border-solid border-blueGray-100 py-3 text-xs uppercase border-l-0 border-r-0 whitespace-nowrap font-semibold text-left">
Id
</th>
<th class="px-6 bg-blueGray-50 text-blueGray-500 align-middle border border-solid border-blueGray-100 py-3 text-xs uppercase border-l-0 border-r-0 whitespace-nowrap font-semibold text-left">
Event Source
</th>
<th class="px-6 bg-blueGray-50 text-blueGray-500 align-middle border border-solid border-blueGray-100 py-3 text-xs uppercase border-l-0 border-r-0 whitespace-nowrap font-semibold text-left">
Event Action
</th>
<th class="px-6 bg-blueGray-50 text-blueGray-500 align-middle border border-solid border-blueGray-100 py-3 text-xs uppercase border-l-0 border-r-0 whitespace-nowrap font-semibold text-left">
Action
</th>
</tr>
</thead>
<tbody>
if len(actions) == 0 {
<tr>
<th class="border-t-0 px-6 align-middle border-l-0 border-r-0 text-xs whitespace-nowrap p-4 text-left">
No actions are available. Click + to add one
</th>
</tr>
} else {
for index, action := range actions {
<tr>
<th class="border-t-0 px-6 align-middle border-l-0 border-r-0 text-xs whitespace-nowrap p-4 text-left">
{ strconv.Itoa(index) }
</th>
<th class="border-t-0 px-6 align-middle border-l-0 border-r-0 text-xs whitespace-nowrap p-4 text-left">
{ action.SourceEvent.Key }
</th>
<th class="border-t-0 px-6 align-middle border-l-0 border-r-0 text-xs whitespace-nowrap p-4 text-left">
{ action.Action.VendorName }: { action.Action.Type }
</th>
<th class="border-t-0 px-6 align-middle border-l-0 border-r-0 text-xs whitespace-nowrap p-4 text-left">
@DashboardActionEditButton(&action)
</th>
</tr>
}
}
</tbody>
</table>
</div>
</div>
</div>
}
templ DashboardContent(user *models.User, vendorAccounts []models.VendorAccount, actions []models.ActionMapping) {
<noscript>You need to enable JavaScript to run this app.</noscript>
<div class="relative md:ml-64 bg-blueGray-50">
@DashboardContentNav(user)
@ -316,12 +494,22 @@ templ DashboardContent(user *models.User, vendorAccounts []models.VendorAccount)
<div class="flex flex-wrap">
@DashboardVendorWidget(vendorAccounts)
</div>
<div class="flex flex-wrap">
@DashboardActionsWidget(actions)
</div>
</div>
</div>
}
templ DashboardScript() {
<script type="text/javascript">
function toggleModal(modalID) {
document.getElementById(modalID).classList.toggle("hidden");
document.getElementById(modalID + "-backdrop").classList.toggle("hidden");
document.getElementById(modalID).classList.toggle("flex");
document.getElementById(modalID + "-backdrop").classList.toggle("flex");
}
function toggleNavbar(collapseID) {
document.getElementById(collapseID).classList.toggle("hidden");
document.getElementById(collapseID).classList.toggle("bg-white");

View File

@ -16,17 +16,13 @@ templ Head(msg string) {
sizes="76x76"
href="./assets/img/apple-icon.png"
/>
<link
rel="stylesheet"
href="https://cdnjs.cloudflare.com/ajax/libs/Chart.js/2.9.4/Chart.min.css"
/>
<link
rel="stylesheet"
href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/5.15.3/css/all.min.css"
/>
<link
rel="stylesheet"
href="https://cdn.jsdelivr.net/gh/creativetimofficial/tailwind-starter-kit/compiled-tailwind.min.css"
href="/static/output.css"
/>
<title>{ msg } | Capstone - Pbaxt10</title>
<script src="https://unpkg.com/htmx.org@1.9.6" integrity="sha384-FhXw7b6AlE/jyjlZH5iHa/tTe9EpJ1Y55RjcgPbjeWMskSxZt1v9qkxLJWNJaGni" crossorigin="anonymous"></script>