/ associate.go
associate.go
  1  package main
  2  
  3  import (
  4  	"encoding/json"
  5  	"log"
  6  	"slices"
  7  
  8  	"github.com/maxence-charriere/go-app/v10/pkg/app"
  9  	shell "github.com/stateless-minds/go-ipfs-api"
 10  )
 11  
 12  // supplier is a component that holds cyber-gubi. A component is a
 13  // customizable, independent, and reusable UI element. It is created by
 14  // embedding app.Compo into a struct.
 15  type associate struct {
 16  	app.Compo
 17  	sh               *shell.Shell
 18  	loggedIn         bool
 19  	userID           string
 20  	associateName    string
 21  	newAssociateName string
 22  	associates       []string
 23  	currentUser      User
 24  }
 25  
 26  func (a *associate) OnMount(ctx app.Context) {
 27  	sh := shell.NewShell("localhost:5001")
 28  	a.sh = sh
 29  
 30  	ctx.GetState("loggedIn", &a.loggedIn)
 31  	if !a.loggedIn {
 32  		ctx.Navigate("/auth")
 33  	}
 34  
 35  	ctx.GetState("userID", &a.userID)
 36  
 37  	ctx.GetState("associateName", &a.associateName)
 38  
 39  	ctx.GetState("currentUser", &a.currentUser)
 40  
 41  	a.getAssociates()
 42  }
 43  
 44  func (a *associate) getAssociates() {
 45  
 46  	associateNames := []string{}
 47  
 48  	var descriptor map[string]map[string][]string
 49  
 50  	err := json.Unmarshal(a.currentUser.Descriptor, &descriptor)
 51  	if err != nil {
 52  		log.Fatal(err)
 53  	}
 54  
 55  	for name := range descriptor {
 56  		if name != a.associateName {
 57  			associateNames = append(associateNames, name)
 58  		}
 59  	}
 60  	a.associates = associateNames
 61  }
 62  
 63  func (a *associate) addAssociate(ctx app.Context, e app.Event) {
 64  	e.PreventDefault()
 65  	valid := app.Window().GetElementByID("associate-form").Call("reportValidity").Bool()
 66  	if valid {
 67  		ctx.SetState("newAssociateName", a.newAssociateName).Persist()
 68  
 69  		ctx.Notifications().New(app.Notification{
 70  			Title: "Action required",
 71  			Body:  "Associate " + a.newAssociateName + " needs to sit in front of the web camera. Click this notification when ready.",
 72  			Path:  "auth",
 73  		})
 74  	}
 75  }
 76  
 77  func (a *associate) removeAssociate(ctx app.Context, e app.Event) {
 78  	e.PreventDefault()
 79  	name := ctx.JSSrc().Get("value").String()
 80  
 81  	var descriptor map[string]map[string][]string
 82  
 83  	err := json.Unmarshal(a.currentUser.Descriptor, &descriptor)
 84  	if err != nil {
 85  		log.Fatal(err)
 86  	}
 87  
 88  	for associate := range descriptor {
 89  		if name == associate {
 90  			a.updateUser(ctx, name)
 91  		}
 92  	}
 93  
 94  }
 95  
 96  func (a *associate) updateUser(ctx app.Context, name string) {
 97  	ctx.Async(func() {
 98  		user := a.currentUser
 99  
100  		var descriptor map[string]map[string][]string
101  
102  		err := json.Unmarshal(a.currentUser.Descriptor, &descriptor)
103  		if err != nil {
104  			log.Fatal(err)
105  		}
106  
107  		delete(descriptor, name)
108  		userJSON, err := json.Marshal(user)
109  		if err != nil {
110  			log.Fatal(err)
111  		}
112  
113  		err = a.sh.OrbitDocsPut(dbUser, userJSON)
114  		if err != nil {
115  			log.Fatal(err)
116  		}
117  
118  		ctx.Dispatch(func(ctx app.Context) {
119  			delete(descriptor, name)
120  			for i, associate := range a.associates {
121  				if associate == name {
122  					a.associates = slices.Delete(a.associates, i, i+1)
123  				}
124  			}
125  			ctx.Notifications().New(app.Notification{
126  				Title: "Success",
127  				Body:  "Associate " + a.newAssociateName + " has been deleted.",
128  			})
129  		})
130  	})
131  }
132  
133  // The Render method is where the component appearance is defined. Here, a
134  // payment form is displayed.
135  func (a *associate) Render() app.UI {
136  	return app.Div().Class("container").Body(
137  		app.Div().Class("mobile").Body(
138  			app.Div().Class("header").Body(
139  				newNav(),
140  				app.Div().Class("header-summary").Body(
141  					app.Span().Class("logo").Text("cyber-gubi"),
142  					app.Div().Class("summary-text").Body(
143  						app.Span().Text("Associates"),
144  					),
145  				),
146  			),
147  			app.Div().ID("content").Body(
148  				app.Div().Class("card").Body(
149  					app.Div().Class("upper-row").Body(
150  						app.Div().Class("card-item").Body(
151  							app.Span().Class("span-header").Text("Add Associate"),
152  							app.Form().ID("associate-form").Body(
153  								app.Div().ID("associate").Body(
154  									app.Input().ID("associate-name").Type("text").Name("associate-name").Placeholder("Associate name").Required(true).OnChange(a.ValueTo(&a.newAssociateName)),
155  								),
156  								app.Div().Class("drawer drawer-pay").Body(
157  									app.Div().Class("menu-btn").Body(
158  										app.Button().Class("submit").Type("submit").Text("Submit").OnClick(a.addAssociate),
159  									),
160  								),
161  							),
162  						),
163  					),
164  				),
165  				app.Div().Class("list associates").Body(
166  					app.Span().Class("a-desc").Text("Manage Associates"),
167  					app.If(len(a.associates) == 0, func() app.UI {
168  						return app.Div().Class("list-item").Body(
169  							app.Span().Class("empty").Text("No associates yet"),
170  						).Style("pointer-events", "none")
171  					}),
172  					app.Range(a.associates).Slice(func(i int) app.UI {
173  						return app.Div().Class("list-item").Body(
174  							app.Div().Class("a-details").Body(
175  								app.Div().Class("a-title").Body(
176  									app.Span().Text(a.associates[i]),
177  								),
178  							),
179  							app.Div().Class("a-price").Body(
180  								app.Div().Class("menu-btn menu-assoc").Body(
181  									app.Button().Class("submit submit-sub").Type("submit").Text("Remove").Value(a.associates[i]).OnClick(a.removeAssociate),
182  								),
183  							),
184  						)
185  					}),
186  				),
187  			),
188  		),
189  	)
190  }