detailing.go 5.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258
  1. package presets
  2. import (
  3. "net/url"
  4. "github.com/jinzhu/inflection"
  5. "github.com/qor5/admin/presets/actions"
  6. . "github.com/qor5/ui/vuetify"
  7. "github.com/qor5/web"
  8. "github.com/qor5/x/perm"
  9. h "github.com/theplant/htmlgo"
  10. "goji.io/pat"
  11. )
  12. type DetailingBuilder struct {
  13. mb *ModelBuilder
  14. actions []*ActionBuilder
  15. pageFunc web.PageFunc
  16. fetcher FetchFunc
  17. tabPanels []ObjectComponentFunc
  18. drawer bool
  19. FieldsBuilder
  20. }
  21. type pageTitle interface {
  22. PageTitle() string
  23. }
  24. // string / []string / *FieldsSection
  25. func (mb *ModelBuilder) Detailing(vs ...interface{}) (r *DetailingBuilder) {
  26. r = mb.detailing
  27. mb.hasDetailing = true
  28. if len(vs) == 0 {
  29. return
  30. }
  31. r.Only(vs...)
  32. return r
  33. }
  34. // string / []string / *FieldsSection
  35. func (b *DetailingBuilder) Only(vs ...interface{}) (r *DetailingBuilder) {
  36. r = b
  37. r.FieldsBuilder = *r.FieldsBuilder.Only(vs...)
  38. return
  39. }
  40. func (b *DetailingBuilder) PageFunc(pf web.PageFunc) (r *DetailingBuilder) {
  41. b.pageFunc = pf
  42. return b
  43. }
  44. func (b *DetailingBuilder) FetchFunc(v FetchFunc) (r *DetailingBuilder) {
  45. b.fetcher = v
  46. return b
  47. }
  48. func (b *DetailingBuilder) GetFetchFunc() FetchFunc {
  49. return b.fetcher
  50. }
  51. func (b *DetailingBuilder) Drawer(v bool) (r *DetailingBuilder) {
  52. b.drawer = v
  53. return b
  54. }
  55. func (b *DetailingBuilder) GetPageFunc() web.PageFunc {
  56. if b.pageFunc != nil {
  57. return b.pageFunc
  58. }
  59. return b.defaultPageFunc
  60. }
  61. func (b *DetailingBuilder) AppendTabsPanelFunc(v ObjectComponentFunc) (r *DetailingBuilder) {
  62. b.tabPanels = append(b.tabPanels, v)
  63. return b
  64. }
  65. func (b *DetailingBuilder) TabsPanelFunc() (r []ObjectComponentFunc) {
  66. return b.tabPanels
  67. }
  68. func (b *DetailingBuilder) CleanTabsPanels() (r *DetailingBuilder) {
  69. b.tabPanels = nil
  70. return b
  71. }
  72. func (b *DetailingBuilder) defaultPageFunc(ctx *web.EventContext) (r web.PageResponse, err error) {
  73. var id string
  74. if b.drawer {
  75. id = ctx.R.FormValue(ParamID)
  76. } else {
  77. id = pat.Param(ctx.R, "id")
  78. }
  79. r.Body = VContainer(h.Text(id))
  80. var obj = b.mb.NewModel()
  81. if id == "" {
  82. panic("not found")
  83. }
  84. obj, err = b.fetcher(obj, id, ctx)
  85. if err != nil {
  86. if err == ErrRecordNotFound {
  87. return b.mb.p.DefaultNotFoundPageFunc(ctx)
  88. }
  89. return
  90. }
  91. if b.mb.Info().Verifier().Do(PermGet).ObjectOn(obj).WithReq(ctx.R).IsAllowed() != nil {
  92. r.Body = h.Div(h.Text(perm.PermissionDenied.Error()))
  93. return
  94. }
  95. msgr := MustGetMessages(ctx.R)
  96. r.PageTitle = msgr.DetailingObjectTitle(inflection.Singular(b.mb.label), getPageTitle(obj, id))
  97. var notice h.HTMLComponent
  98. if msg, ok := ctx.Flash.(string); ok {
  99. notice = VSnackbar(h.Text(msg)).Value(true).Top(true).Color("success").Value(true)
  100. }
  101. comp := b.ToComponent(b.mb.Info(), obj, ctx)
  102. var tabsContent h.HTMLComponent = comp
  103. if len(b.tabPanels) != 0 {
  104. var tabs []h.HTMLComponent
  105. for _, panelFunc := range b.tabPanels {
  106. value := panelFunc(obj, ctx)
  107. if value != nil {
  108. tabs = append(tabs, value)
  109. }
  110. }
  111. if len(tabs) != 0 {
  112. tabsContent = VTabs(
  113. VTab(h.Text(msgr.FormTitle)),
  114. VTabItem(web.Scope(comp).VSlot("{plaidForm}")),
  115. h.Components(tabs...),
  116. ).Class("v-tabs--fixed-tabs")
  117. }
  118. }
  119. r.Body = VContainer(
  120. notice,
  121. ).AppendChildren(tabsContent).Fluid(true)
  122. return
  123. }
  124. func (b *DetailingBuilder) showInDrawer(ctx *web.EventContext) (r web.EventResponse, err error) {
  125. if b.mb.Info().Verifier().Do(PermGet).WithReq(ctx.R).IsAllowed() != nil {
  126. ShowMessage(&r, perm.PermissionDenied.Error(), "warning")
  127. return
  128. }
  129. pr, err := b.GetPageFunc()(ctx)
  130. if err != nil {
  131. return
  132. }
  133. overlayType := ctx.R.FormValue(ParamOverlay)
  134. closeBtnVarScript := closeRightDrawerVarScript
  135. if overlayType == actions.Dialog {
  136. closeBtnVarScript = closeDialogVarScript
  137. }
  138. comp := web.Scope(
  139. VAppBar(
  140. VToolbarTitle("").Class("pl-2").
  141. Children(h.Text(pr.PageTitle)),
  142. VSpacer(),
  143. VBtn("").Icon(true).Children(
  144. VIcon("close"),
  145. ).Attr("@click.stop", closeBtnVarScript),
  146. ).Color("white").Elevation(0).Dense(true),
  147. VSheet(
  148. VCard(pr.Body).Flat(true).Class("pa-1"),
  149. ).Class("pa-2"),
  150. ).VSlot("{ plaidForm }")
  151. b.mb.p.overlay(overlayType, &r, comp, b.mb.rightDrawerWidth)
  152. return
  153. }
  154. func getPageTitle(obj interface{}, id string) string {
  155. title := id
  156. if pt, ok := obj.(pageTitle); ok {
  157. title = pt.PageTitle()
  158. }
  159. return title
  160. }
  161. func (b *DetailingBuilder) doAction(ctx *web.EventContext) (r web.EventResponse, err error) {
  162. action := getAction(b.actions, ctx.R.FormValue(ParamAction))
  163. if action == nil {
  164. panic("action required")
  165. }
  166. id := ctx.R.FormValue(ParamID)
  167. if err := action.updateFunc(id, ctx); err != nil || ctx.Flash != nil {
  168. if ctx.Flash == nil {
  169. ctx.Flash = err
  170. }
  171. r.UpdatePortals = append(r.UpdatePortals, &web.PortalUpdate{
  172. Name: rightDrawerContentPortalName,
  173. Body: b.actionForm(action, ctx),
  174. })
  175. return r, nil
  176. }
  177. r.PushState = web.Location(url.Values{})
  178. r.VarsScript = closeRightDrawerVarScript
  179. return
  180. }
  181. func (b *DetailingBuilder) formDrawerAction(ctx *web.EventContext) (r web.EventResponse, err error) {
  182. action := getAction(b.actions, ctx.R.FormValue(ParamAction))
  183. if action == nil {
  184. panic("action required")
  185. }
  186. b.mb.p.rightDrawer(&r, b.actionForm(action, ctx), "")
  187. return
  188. }
  189. func (b *DetailingBuilder) actionForm(action *ActionBuilder, ctx *web.EventContext) h.HTMLComponent {
  190. msgr := MustGetMessages(ctx.R)
  191. id := ctx.R.FormValue(ParamID)
  192. if id == "" {
  193. panic("id required")
  194. }
  195. return VContainer(
  196. VCard(
  197. VCardText(
  198. action.compFunc(id, ctx),
  199. ),
  200. VCardActions(
  201. VSpacer(),
  202. VBtn(msgr.Update).
  203. Dark(true).
  204. Color(ColorPrimary).
  205. Attr("@click", web.Plaid().
  206. EventFunc(actions.DoAction).
  207. Query(ParamID, id).
  208. Query(ParamAction, ctx.R.FormValue(ParamAction)).
  209. URL(b.mb.Info().DetailingHref(id)).
  210. Go()),
  211. ),
  212. ).Flat(true),
  213. ).Fluid(true)
  214. }