Yu-Gi-Oh! Deck Building and Card Inventory Management web interface written in Common Lisp, utilizing HTMX.
Você não pode selecionar mais de 25 tópicos Os tópicos devem começar com uma letra ou um número, podem incluir traços ('-') e podem ter até 35 caracteres.

245 linhas
9.7KB

  1. #|
  2. src/web/inventory.lisp
  3. Crystal Commerce-style Inventory Management User Interface and helpers.
  4. TODO pass over the results of (make-search-query) such that
  5. :total-qty is the aggregate of:
  6. (select 'ygo-cc-item (where (:= :item_id xxx)))
  7. (reduce #'+
  8. (mapcar (alexandria:compose #'read-from-string #'cc-sell-price-of)
  9. (retrieve-dao 'ygo-cc-item :item-id 48883)))
  10. (reduce #'+ (mapcar #'cc-qty-of (retrieve-dao 'ygo-cc-item :item-id 48883)))
  11. |#
  12. (in-package #:cl-deck-builder2.web)
  13. (defun render-results (&key (active "/inventory") (class 'cc-item) (params nil) (tpl #P"inventory/search-results.html"))
  14. "Mega helper function. Render function for anything that currently queries the databse. We search both PRODUCT-NAME and NAME, as well as querying a list of VARIANTs, with a LIMIT on the number of results and an OFFSET into those search results. You may also specify a DIRECTION, and SORT-BY options.
  15. TODO Major rewrite candidate right here.
  16. TODO Integrate new SEARCH-SESSION object after that's done."
  17. (let ((direction (or (query-param "direction" params) "desc"))
  18. (variant (or (query-param "variant" params)
  19. (find-dao 'variant-condition :name "Near Mint")))
  20. (variants (select-variant-condition))
  21. (limit (or (query-param "limit" params) "10"))
  22. (name (or
  23. (query-param "name" params)
  24. (query-param "product-name" params)))
  25. (offset (or (query-param "offset" params) "0"))
  26. (sort-by (or (query-param "sort-by" params) "id")))
  27. (handler-case
  28. (ratify:with-parsed-forms
  29. ((:integer limit)
  30. (:integer offset)
  31. (:string direction)
  32. (:string sort-by))
  33. ;; NAME can be blank, so...
  34. (let* ((filtered-cards (make-search-query class params))
  35. (length (make-count-query class params))
  36. (pages (generate-pages length offset limit)))
  37. (render-with-env tpl
  38. `(:active ,active
  39. :cards ,filtered-cards
  40. :direction ,direction
  41. :last-page ,(car (last pages))
  42. :length ,length
  43. :limit ,limit
  44. :name ,name
  45. :offset ,offset
  46. :opposite-direction ,(get-opposite-direction direction)
  47. :pages ,pages
  48. :search-params ,+search-params+
  49. :sort-by ,sort-by
  50. :total ,(count-dao class)
  51. :variant ,variant
  52. :variants ,variants))))
  53. (ratify:combined-error (e)
  54. (flash-error (format nil "~a/search.error => ~a~%" active e))))))
  55. ;; USE REINITIALIZE-INSTANCE to update the existing YGO-SET-ITEM from _PARSED
  56. (defun patch-card (card _parsed)
  57. (let ((set-id (query-param "set-id" card))
  58. (variant-id (query-param "variant-id" card)))
  59. (handler-case
  60. (ratify-parsing:with-parsed-forms
  61. ((:integer set-id)
  62. (:integer variant-id))
  63. (let ((found (find-or-create-instance 'ygo-set-item :item-id set-id :variant-id variant-id))
  64. (clean (filter-alist _parsed)))
  65. (v:info :inventory "PATCH-CARD: ~a" found)
  66. (if found
  67. ;; TODO redraw item based on context? Like patching from the inventory list vs patching from the editor?
  68. ;; (redirect (format nil "/inventory/~d/edit" (mito:object-id found)) 303)
  69. (progn
  70. (apply #'reinitialize-instance found (assoc-utils:alist-plist clean))
  71. (update-dao found)
  72. (_ "Save"))
  73. (progn
  74. (flash-error (format nil "Error udpating id ~d:~d" set-id variant-id))))))
  75. (ratify:combined-error (e)
  76. (flash-error e)))))
  77. ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
  78. ;; Inventory Viewer
  79. (defroute ("/inventory" :method :GET) (&key _parsed)
  80. "Inventory Viewer GET route. Will display index with search results."
  81. (v:info :inventory "GET /inventory ~a" _parsed)
  82. ;; (with-logged-in-user
  83. ;; (render-results :params _parsed :tpl #P"inventory/index.html"))
  84. (redirect "/cards"))
  85. #|
  86. (defroute ("/inventory" :method :POST) (&key _parsed)
  87. "Inventory Viewer search POST route. Will display index with search results."
  88. (v:info :inventory "POST /inventory ~a" _parsed)
  89. (with-logged-in-user
  90. (render-results :params _parsed :tpl #P"inventory/index.html")))
  91. (defroute ("/inventory/search" :method :POST) (&key _parsed)
  92. "Inventory Viewer search POST route. Will display index with search results."
  93. (v:info :inventory "POST /inventory/search ~a" _parsed)
  94. (with-logged-in-user
  95. (render-results :params _parsed)))
  96. ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
  97. ;; Inventory Item Editor
  98. ;;
  99. ;; TODO PUT?? POST?? GET??
  100. ;; New Item
  101. (defroute ("/inventory/new" :method :GET) ()
  102. "Display the page for creation of a new inventory item"
  103. (v:info :inventory "GET /inventory/new")
  104. (with-logged-in-user
  105. (render-with-env #P"inventory/new.html"
  106. `(:active "/inventory"
  107. :card ,(make-instance 'cc-item)))))
  108. (defroute ("/inventory/new" :method :POST) (&key _parsed)
  109. "POST method for the processing the information from page for creation of a new inventory item."
  110. (v:info :inventory "POST /inventory/new => ~a" _parsed)
  111. (with-logged-in-user
  112. (let ((clean (filter-alist _parsed)))
  113. (let ((new (apply #'cc-create (assoc-utils:alist-plist clean))))
  114. (when (mito:object-id new)
  115. (redirect (format nil "/inventory/~d/edit" (mito:object-id new))))))))
  116. ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
  117. ;; Inventory Item Importer
  118. (defroute ("/inventory/import" :method :GET) ()
  119. "Inventory Item Importer. TODO"
  120. (v:info :inventory "GET /inventory/import")
  121. (with-logged-in-user
  122. (render-with-env #P"inventory/import.html"
  123. `(:active "/inventory"))))
  124. ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
  125. ;; Seems like this one needs to be last. It has something to do with
  126. ;; the wildcard :ID parameter. Maybe we ought to add the /edit back?
  127. (defroute ("/inventory/:id/edit" :method :GET) (&key id)
  128. "Edit Route for Inventory Item.
  129. ARGUMENTS
  130. ID The ID of the CC-ITEM inventory item you wish to edit."
  131. (v:info :inventory "GET /inventory/~d/edit" id)
  132. (with-logged-in-user
  133. (handler-case
  134. (ratify-parsing:with-parsed-forms
  135. ((:integer id))
  136. (render-with-env #P"inventory/edit.html"
  137. `(:active "/inventory"
  138. :errors ,(flash-gethash :errors)
  139. :messages ,(flash-gethash :messages)
  140. :card ,(cc-select-by-id id))))
  141. (ratify:combined-error (e)
  142. (flash-error e)))))
  143. (defroute ("/inventory/:id/edit" :method :POST) (&key id _parsed)
  144. "CC-ITEM Update POST route. If the item exists, update it using REINITIALIZE-INSTANCE, after filtering _PARSED through FILTER-ALIST.
  145. TODO Differentiate between CC-ITEM and YGO-CC-ITEM?"
  146. (v:info :inventory "POST /inventory/~d/edit => ~a" id _parsed)
  147. (with-logged-in-user
  148. (handler-case
  149. (ratify-parsing:with-parsed-forms
  150. ((:integer id))
  151. (with-connection (db)
  152. (let ((found (mito:find-dao 'cc-item :id id))
  153. (clean (filter-alist _parsed)))
  154. (when found
  155. (apply #'reinitialize-instance found (assoc-utils:alist-plist clean))
  156. (mito:save-dao found)
  157. (redirect (format nil "/inventory/~d/edit" (mito:object-id found)) 303)))))
  158. (ratify:combined-error (e)
  159. (flash-error e)))))
  160. (defroute ("/inventory/:id/delete" :method :DELETE) (&key id)
  161. "DELETE an Inventory Item specified by ID."
  162. (v:info :inventory "DELETE /inventory/~d/delete" id)
  163. (with-logged-in-user
  164. (handler-case
  165. (ratify-parsing:with-parsed-forms
  166. ((:integer id))
  167. (cc-delete-by-id id))
  168. (ratify:combined-error (e)
  169. (flash-error e)))))
  170. |#
  171. (defroute ("/inventory/patch" :method :PATCH) (&key _parsed)
  172. "YGO-CC-ITEM PATCH route. This appears to be functionally identical to EDIT route for ID, but this is using YGO-CC-ITEM as class. The POST route appears to be used for CC-ITEM object."
  173. (v:info :inventory "PATCH /inventory/patch => ~a" _parsed)
  174. (with-logged-in-user
  175. (alexandria:if-let ((cards (query-param "cards" _parsed)))
  176. (dolist (card cards)
  177. (patch-card card _parsed))
  178. (patch-card _parsed _parsed))))
  179. ;; Huh? It works??
  180. (defroute ("/inventory/variants/:id" :method :GET) (&key id)
  181. "Display the VARIANTs for this inventory item using SELECT-YGO-CC-ITEM-VARIANTS."
  182. (v:info :inventory "GET /inventory/variants/~d" id)
  183. (with-logged-in-user
  184. (handler-case
  185. (ratify-parsing:with-parsed-forms
  186. ((:integer id))
  187. (render-with-env #P"inventory/variant-results.html"
  188. (list :active "/inventory"
  189. :id id
  190. :cards (select-ygo-cc-item-variants id))))
  191. ;; :cards ,filtered-cards
  192. ;; :variant ,variant
  193. ;; :variants ,variants
  194. ;; :direction ,direction
  195. ;; :length ,length
  196. ;; :total ,(count-dao class)
  197. ;; :limit ,limit
  198. ;; :name ,name
  199. ;; :offset ,offset
  200. ;; :opposite-direction ,(get-opposite-direction direction)
  201. ;; :pages ,(generate-pages length offset limit)
  202. ;; :search-params ,+search-params+
  203. ;; :sort-by ,sort-by)
  204. (ratify:combined-error (e)
  205. (flash-error e)))))