B: working on add action form
This commit is contained in:
parent
65167c3e58
commit
175b6cae8c
|
@ -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 = []
|
||||
|
|
|
@ -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))
|
||||
}
|
|
@ -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")
|
||||
|
|
|
@ -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))
|
||||
}
|
||||
|
|
|
@ -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
|
||||
}
|
|
@ -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"`
|
||||
}
|
|
@ -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"
|
||||
)
|
||||
|
||||
|
||||
|
|
|
@ -0,0 +1,3 @@
|
|||
@tailwind base;
|
||||
@tailwind components;
|
||||
@tailwind utilities;
|
|
@ -1,5 +1,5 @@
|
|||
module.exports = {
|
||||
content: ["**/*.templ"],
|
||||
content: ["templates/*.templ"],
|
||||
theme: { extend: {}, },
|
||||
plugins: [],
|
||||
}
|
|
@ -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('add-action-modal')">
|
||||
<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('add-action-modal')">
|
||||
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('add-action-modal')">
|
||||
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");
|
||||
|
|
|
@ -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>
|
||||
|
|
Loading…
Reference in New Issue