github.com/benoitkugler/goacve@v0.0.0-20201217100549-151ce6e55dc8/client/GUI/onglets/suivi_camps.go (about) 1 package onglets 2 3 import ( 4 "fmt" 5 "strings" 6 7 "github.com/benoitkugler/goACVE/client/GUI/messages" 8 "github.com/benoitkugler/goACVE/server/core/apiserver" 9 10 rd "github.com/benoitkugler/goACVE/server/core/rawdata" 11 12 "github.com/benoitkugler/goACVE/client/GUI/common" 13 14 dm "github.com/benoitkugler/goACVE/server/core/datamodel" 15 "github.com/benoitkugler/goACVE/server/core/utils/joomeo" 16 17 "github.com/benoitkugler/goACVE/client/GUI/basic" 18 "github.com/benoitkugler/goACVE/client/GUI/fields" 19 "github.com/benoitkugler/goACVE/client/GUI/lists" 20 "github.com/benoitkugler/goACVE/client/controllers" 21 "github.com/therecipe/qt/core" 22 "github.com/therecipe/qt/widgets" 23 ) 24 25 const stylePreview = `<style> 26 td, th { 27 padding: 2px; 28 text-align: center; 29 } 30 31 table{ 32 border-width: 1px; 33 border-color: #000000; 34 border-style: solid; 35 border-collapse: collapse; 36 margin: 0px; 37 } 38 </style>` 39 40 func (s *SuiviCamps) Render() { 41 s.camps.Model().SetData(s.controller.Liste) 42 if s.controller.Etat.CampCurrent != nil { 43 s.camps.SelectRow(s.controller.Etat.CampCurrent) 44 } 45 s.ignoreClosed.SetChecked(s.controller.Etat.CritereIgnoreClosed) 46 s.terminated.SetData(s.controller.Etat.CritereTerminated) 47 } 48 49 func (s *SuiviCamps) ConfirmeSetupAlbumJoomeo(alreayLinked []string) bool { 50 msg := "Les camps suivants sont déja liés à cet album : <br/>" 51 msg += strings.Join(alreayLinked, "<br/>") 52 msg += "Etes-vous sur de vouloir lier le camp à cet album ?" 53 return basic.Confirmation(msg, "Lier", basic.ONAction) 54 } 55 56 func (s *SuiviCamps) ConfirmeAjoutDirecteurJoomeo(msg string) rd.OptionnalBool { 57 dial := basic.Dialog2("Ajout d'un contact") 58 var out rd.OptionnalBool 59 mail := basic.Button("Ajouter et envoyer un mail") 60 mail.SetObjectName(basic.ONAction) 61 mail.ConnectClicked(func(_ bool) { 62 out = rd.OBOui 63 dial.Accept() 64 }) 65 noMail := basic.Button("Ajouter") 66 noMail.SetObjectName(basic.ONAdd) 67 noMail.ConnectClicked(func(_ bool) { 68 out = rd.OBNon 69 dial.Accept() 70 }) 71 72 lay := widgets.NewQGridLayout(dial) 73 lay.AddWidget3(basic.Label(msg), 0, 0, 1, 3, 0) 74 lay.AddWidget2(noMail, 1, 0, 0) 75 lay.AddWidget2(mail, 1, 2, 0) 76 dial.Exec() 77 return out 78 } 79 80 func (s *SuiviCamps) ConfirmeSupprimeCamp(camp dm.AccesCamp) bool { 81 dia := basic.Dialog("Suppression") 82 dia.Layout().AddWidget(basic.Label(fmt.Sprintf("Confirmez-vous la suppression du séjour <b>%s</b> ? <br/>"+ 83 "Cette opération est irréversible.", camp.RawData().Label()))) 84 valid := basic.Button("Supprimer définitivement") 85 valid.SetObjectName(basic.ONDelete) 86 valid.ConnectClicked(func(_ bool) { 87 dia.Accept() 88 }) 89 dia.Layout().AddWidget(valid) 90 return dia.Exec() > 0 91 } 92 93 func (s *SuiviCamps) ProposePasswordDirecteur(camp dm.AccesCamp, password string) bool { 94 msg := fmt.Sprintf(`Afin de simplifier la vie des directeurs, vous pouvez attribuer au séjour %s 95 un <b>mot de passe propre au directeur</b>. <br/> 96 Il pourra ainsi n'en retenir qu'<i>un seul</i> pour tous ses séjours. <br/> 97 Souhaitez-vous utiliser le mot de passe %s ? 98 `, camp.RawData().Label(), password) 99 return basic.Confirmation(msg, "Utiliser ce mot de passe", basic.ONAdd) 100 } 101 102 type SuiviCamps struct { 103 *widgets.QFrame 104 controller *controllers.SuiviCamps 105 parent *widgets.QTabWidget 106 toolbar *widgets.QToolBar 107 108 camps lists.Table 109 ignoreClosed fields.Bool 110 terminated fields.OptionnalBool 111 } 112 113 func newSuiviCamps(ct *controllers.SuiviCamps) *SuiviCamps { 114 p := SuiviCamps{QFrame: basic.Frame(), controller: ct} 115 116 p.ignoreClosed = fields.NewBool(true) 117 p.ignoreClosed.ConnectClicked(func(_ bool) { 118 ct.Etat.CritereIgnoreClosed = p.ignoreClosed.IsChecked() 119 ct.Reset() 120 }) 121 p.terminated = fields.NewOptionnalBool(true, "Terminés", "En cours") 122 p.terminated.ConnectCurrentIndexChanged(func(_ int) { 123 ct.Etat.CritereTerminated = p.terminated.GetData() 124 ct.Reset() 125 }) 126 rh := widgets.NewQHBoxLayout() 127 rh.AddWidget(basic.Label("Masquer les camps fermés aux inscriptions"), 0, 0) 128 rh.AddWidget(p.ignoreClosed, 0, core.Qt__AlignLeft) 129 rh.AddWidget(basic.Label("Etat :"), 0, core.Qt__AlignRight) 130 rh.AddWidget(p.terminated, 0, 0) 131 p.camps = lists.Table{ 132 Liste: lists.Liste{ 133 Headers: ct.Header, 134 Title: "Séjours", 135 OnDoubleClick: func(acces rd.Item, _ int) { 136 p.editCamp(acces) 137 }, 138 OnClick: func(acces rd.Item, _ int) { 139 ct.Etat.CampCurrent = acces.Id 140 p.UpdateToolbar() 141 }, 142 OnRightClick: p.showContextMenu, 143 OnSort: func(field rd.Field, reverse bool) { 144 lists.DefaultSort(p.camps, field, reverse) 145 }, 146 RightHeader: rh, 147 }, 148 } 149 p.camps.Init() 150 p.camps.View.SetItemDelegate(lists.NewDelegateSuviCamps()) 151 p.camps.HorizontalHeader().SetMinimumSectionSize(150) 152 153 lay := widgets.NewQVBoxLayout2(p) 154 lay.AddWidget(p.camps, 2, 0) 155 return &p 156 } 157 158 func (s *SuiviCamps) showContextMenu(acces rd.Item, pos *core.QPoint) { 159 camp := s.controller.Base.NewCamp(acces.Id.Int64()) 160 menu := widgets.NewQMenu(s.camps) 161 ac := menu.AddAction("Copier le lien vers le formulaire d'inscription") 162 ac.ConnectTriggered(func(_ bool) { 163 lien := s.controller.LienInscription(acces.Id.Int64()) 164 basic.Copy(lien) 165 }) 166 167 if directeur, has := camp.GetDirecteur(); has { 168 ac = menu.AddAction("Aller au directeur") 169 ac.ConnectTriggered(func(_ bool) { 170 s.controller.Main().Controllers.Personnes.SelectPersonne(directeur) 171 }) 172 } else { 173 ac = menu.AddAction("Ajouter un directeur...") 174 ac.ConnectTriggered(func(_ bool) { 175 dial := lists.NewRecherchePersonne(s.controller.Base, false) 176 dial.Exec() 177 if dial.Out == nil { 178 return 179 } 180 s.controller.AjouteDirecteur(camp, dial.Out.Int64()) 181 }) 182 } 183 menu.AddSeparator() 184 ac = menu.AddAction("Suivi financier") 185 ac.ConnectTriggered(func(_ bool) { 186 path := s.controller.ExportSuiviFinancierParticipants(camp) 187 basic.ShowFile(path) 188 }) 189 ac = menu.AddAction("Nombre de nuitées") 190 ac.ConnectTriggered(func(_ bool) { 191 nbEnfants, nbAdultes := s.controller.CalculeNuitees(camp) 192 dial := basic.Dialog("Nuitées") 193 dial.Layout().AddWidget(basic.Label(fmt.Sprintf("Nuitées pour le séjour <i>%s</i> : <b>%d</b> adulte(s) et <b>%d</b> enfant(s).", 194 camp.RawData().Label(), nbAdultes, nbEnfants))) 195 dial.Exec() 196 }) 197 if s.controller.EditRight { 198 menu.AddSeparator() 199 ac = menu.AddAction("Envoyer un sondage") 200 ac.ConnectTriggered(func(_ bool) { 201 s.inviteSondage(camp) 202 }) 203 ac.SetEnabled(bool(!camp.RawData().InscriptionSimple)) 204 } 205 206 s.camps.ShowContextMenu(menu, pos) 207 } 208 209 func (s *SuiviCamps) creer() { 210 var importFrom rd.OptionnalId // zero pour non import 211 if s.controller.Etat.CampCurrent != nil { 212 dial := basic.Dialog2("Importation") 213 camp := s.controller.Base.NewCamp(s.controller.Etat.CampCurrent.Int64()) 214 lab := basic.Label(fmt.Sprintf("Voulez-vous <b>importer</b> le camp courant (%s) comme nouveau camp ?", camp.RawData().Label())) 215 oui := basic.Button("Importer") 216 oui.ConnectClicked(func(_ bool) { 217 importFrom.Valid = true // a compléter 218 dial.Accept() 219 }) 220 oui.SetObjectName(basic.ONAction) 221 non := basic.Button("Créer un camp vierge") 222 non.ConnectClicked(func(_ bool) { 223 dial.Accept() 224 }) 225 non.SetObjectName(basic.ONAdd) 226 lay := widgets.NewQGridLayout(dial) 227 lay.AddWidget3(lab, 0, 0, 1, 3, 0) 228 lay.AddWidget2(non, 1, 0, core.Qt__AlignLeft) 229 lay.AddWidget2(oui, 1, 2, core.Qt__AlignRight) 230 if dial.Exec() == 0 { 231 return 232 } 233 } 234 var camp rd.Camp 235 if importFrom.Valid { 236 toImport := s.controller.ImportCampFrom() 237 camp = toImport.Camp // l'id de camp est invalide 238 importFrom = toImport.CopyLettreFrom 239 } 240 up := EditCamp(true, false, true, camp) 241 if up == nil { 242 return 243 } 244 s.controller.CreeCamp(apiserver.CreateCampIn{Camp: *up, CopyLettreFrom: importFrom}) 245 } 246 247 func (s SuiviCamps) supprimer() { 248 s.controller.SupprimeCamp() 249 } 250 251 func (s *SuiviCamps) bilanFinancier() { 252 bilans := s.controller.BilansFinanciers(s.camps.Selection()) 253 html := stylePreview + "<table>" + "<tr>" 254 for _, h := range controllers.HeadersBilanFinancier { 255 html += "<th>" + h.Label + "</th>" 256 } 257 html += "</tr>" 258 for _, bilan := range bilans { 259 html += rd.Html(bilan, controllers.HeadersBilanFinancier) 260 } 261 html += "</table>" 262 dial := basic.Dialog("Bilan des camps") 263 sc := widgets.NewQScrollArea(nil) 264 sc.SetWidget(basic.Label(html)) 265 sc.SetMinimumSize2(1000, 250) 266 valid := basic.Button("Exporter") 267 valid.SetObjectName(basic.ONAction) 268 valid.ConnectClicked(func(_ bool) { 269 path := s.controller.ExportBilanCamps(bilans) 270 basic.ShowFile(path) 271 dial.Accept() 272 }) 273 dial.Layout().AddWidget(sc) 274 dial.Layout().AddWidget(valid) 275 dial.Exec() 276 } 277 278 func (s *SuiviCamps) editCamp(acces rd.Item) { 279 up := EditCamp(s.controller.EditRight, false, false, s.controller.Base.Camps[acces.Id.Int64()]) 280 if up == nil { 281 return 282 } 283 s.controller.UpdateCamp(*up) 284 } 285 286 func (s *SuiviCamps) afficherJoomeo() { 287 if s.controller.Etat.CampCurrent == nil { 288 return 289 } 290 dial := NewPanelJooemo(s.controller) 291 dial.showAndLoad() 292 s.controller.CloseJoomeoApi() 293 } 294 295 func (s *SuiviCamps) inviteSondage(camp dm.AccesCamp) { 296 idFactures := s.controller.SelectFacturesByCamp(camp.Id) 297 if len(idFactures) == 0 { 298 basic.ShowError(fmt.Errorf("Aucun dossier n'est inscrit sur le séjour %s", camp.RawData().Label())) 299 return 300 } 301 302 keep := basic.Confirmation(fmt.Sprintf("Confirmez-vous l'envoi de %d message(s) ?", len(idFactures)), 303 "Envoyer", basic.ONAction) 304 if !keep { 305 s.controller.Main().ShowStandard("Envoi annulé", false) 306 return 307 } 308 309 ct := s.controller.Main().Controllers.SuiviDossiers 310 dialog := messages.NewMonitor(len(idFactures)) 311 sender := common.NewSondagesSender(nil) 312 // mise en place des callbacks 313 sender.Monitor.OnError = dialog.OnError 314 sender.Monitor.OnSuccess = func(index int, out apiserver.NotifieSondagesOut) { 315 ct.OnSuccesNotifieSondages(out) 316 dialog.OnSuccess(index) 317 } 318 sender.OnFinished = dialog.ShowBilan 319 320 job := func(monitor controllers.SendSondagesSignals) ([]controllers.DossierError, error) { 321 return ct.EnvoiSondages(camp.Id, idFactures, monitor) 322 } 323 sender.SetupLaunch(job) 324 } 325 326 func (s *SuiviCamps) openSondages() { 327 u := s.controller.UrlSondages() 328 s.controller.Main().ShowStandard(fmt.Sprintf("Ouverture de %s ...", u), false) 329 basic.OpenURL(u) 330 } 331 332 // ---------------------------------- Joomeo ---------------------------------- 333 334 type PanelJoomeo struct { 335 *widgets.QDialog 336 337 controller *controllers.SuiviCamps 338 stBar *widgets.QStatusBar 339 tree lists.Tree 340 labelAlbum *widgets.QLabel 341 albums map[string]joomeo.Album 342 contacts map[string]joomeo.Contact 343 addDirButton *widgets.QPushButton 344 removeButton *widgets.QPushButton 345 } 346 347 func NewPanelJooemo(ct *controllers.SuiviCamps) *PanelJoomeo { 348 p := PanelJoomeo{QDialog: basic.Dialog2("Interface Joomeo"), controller: ct} 349 camp := ct.Base.NewCamp(p.controller.Etat.CampCurrent.Int64()) // nil already checked 350 attribue := basic.Button("Lier au camp") 351 attribue.SetEnabled(false) 352 attribue.SetObjectName(basic.ONAction) 353 attribue.ConnectClicked(p.linkAlbum) 354 355 p.removeButton = basic.Button("Délier du camp") 356 p.removeButton.SetObjectName(basic.ONDelete) 357 p.removeButton.ConnectClicked(p.removeAlbum) 358 359 p.tree = lists.Tree{ 360 Liste: lists.Liste{ 361 Headers: []rd.Header{ 362 {Field: joomeo.FLabel, Label: "Nom"}, 363 {Field: joomeo.FDate, Label: "Dernière modification"}, 364 {Field: joomeo.FNbFiles, Label: "Nombre de photos"}, 365 }, 366 Placeholder: "Aucun album Joomeo.", 367 MinHeight: 300, 368 MinWidth: 500, 369 OnClick: func(acces rd.Item, _ int) { 370 _, ok := acces.Id.(joomeo.AlbumId) 371 attribue.SetEnabled(ok) 372 }, 373 }, 374 } 375 p.tree.Init() 376 p.tree.FoldButton.ConnectClicked(p.tree.Fold) 377 378 p.labelAlbum = basic.Label("") 379 p.addDirButton = basic.Button("Ajouter le directeur aux contacts") 380 p.addDirButton.SetObjectName(basic.ONAdd) 381 p.addDirButton.SetEnabled(false) 382 p.addDirButton.ConnectClicked(p.ajouteDirecteur) 383 384 lay := widgets.NewQGridLayout(p) 385 386 lay.AddWidget3(basic.Label(fmt.Sprintf("<b>Camp %s</b>", camp.RawData().Label())), 0, 1, 1, 2, 0) 387 lay.AddWidget2(basic.Label("Album Jooemo lié :"), 1, 1, 0) 388 lay.AddWidget2(p.labelAlbum, 1, 2, 0) 389 lay.AddWidget3(p.addDirButton, 3, 1, 1, 2, 0) 390 lay.AddWidget3(p.removeButton, 4, 1, 1, 2, 0) 391 lay.AddWidget3(p.tree, 0, 0, 4, 1, 0) 392 lay.AddWidget2(attribue, 4, 0, 0) 393 p.stBar = widgets.NewQStatusBar(nil) 394 common.MainWindow.StatusBar().ConnectMessageChanged(func(message string) { 395 p.stBar.ShowMessage(message, 0) 396 }) 397 lay.AddWidget3(p.stBar, 5, 0, 1, 3, 0) 398 return &p 399 } 400 401 func (p *PanelJoomeo) showAndLoad() { 402 p.SetEnabled(false) 403 p.stBar.ShowMessage("Chargement des informations Joomeo...", 0) 404 basic.Delay(func() { 405 var folder []rd.ItemChilds 406 folder, p.albums, p.contacts = p.controller.GetAlbumsContactsJoomeo() 407 p.tree.Model().SetData(folder) 408 p.update() 409 p.SetEnabled(true) 410 p.stBar.ShowMessage("Informations chargées", 3000) 411 }) 412 p.Exec() 413 } 414 415 func (p *PanelJoomeo) update() { 416 camp := p.controller.Base.NewCamp(p.controller.Etat.CampCurrent.Int64()) 417 alb, has := p.albums[string(camp.RawData().JoomeoAlbumId)] 418 lab := alb.Label 419 if !has { 420 lab = "<i>Aucun</i>" 421 } 422 p.labelAlbum.SetText(lab) 423 p.addDirButton.SetEnabled(has) 424 p.removeButton.SetEnabled(has) 425 } 426 427 func (p *PanelJoomeo) linkAlbum(_ bool) { 428 p.SetEnabled(false) 429 if item, ok := p.tree.CurrentData(); ok { 430 if albumId, ok := item.Id.(joomeo.AlbumId); ok { 431 p.controller.SetupAlbumJoomeo(albumId.JoomeoId) 432 } 433 } 434 p.SetEnabled(true) 435 p.update() 436 } 437 438 func (p *PanelJoomeo) removeAlbum(_ bool) { 439 p.SetEnabled(false) 440 p.controller.RemoveAlbumJoomeo() 441 p.SetEnabled(true) 442 p.update() 443 } 444 445 func (p *PanelJoomeo) ajouteDirecteur(_ bool) { 446 p.SetEnabled(false) 447 p.controller.AjouteDirecteurJoomeo(p.albums) 448 p.SetEnabled(true) 449 }