Separate out add category modal

This commit is contained in:
Joey Yakimowich-Payne 2020-02-20 17:54:19 -07:00 • committed by Andreas Rumpf
commit 95b21874db
4 changed files with 121 additions and 83 deletions

View file

@ -0,0 +1,87 @@
when defined(js):
import sugar, httpcore, options, json, strutils
import dom except Event
import jsffi except `&`
include karax/prelude
import karax / [kajax, kdom, vdom]
import error, category
import category, karaxutils
type
AddCategoryModal* = ref object of VComponent
modalShown: bool
loading: bool
error: Option[PostError]
onAddCategory: CategoryEvent
let nullCategory: CategoryEvent = proc (category: Category) = discard
proc newAddCategoryModal*(onAddCategory=nullCategory): AddCategoryModal =
result = AddCategoryModal(
modalShown: false,
loading: false,
onAddCategory: onAddCategory
)
proc onAddCategoryPost(httpStatus: int, response: kstring, state: AddCategoryModal) =
postFinished:
state.modalShown = false
let j = parseJson($response)
let category = j.to(Category)
state.onAddCategory(category)
proc onAddCategoryClick(state: AddCategoryModal) =
state.loading = true
state.error = none[PostError]()
let uri = makeUri("createCategory")
let form = dom.document.getElementById("add-category-form")
let formData = newFormData(form)
ajaxPost(uri, @[], formData.to(cstring),
(s: int, r: kstring) => onAddCategoryPost(s, r, state))
proc setModalShown*(state: AddCategoryModal, visible: bool) =
state.modalShown = visible
state.markDirty()
proc onModalClose(state: AddCategoryModal, ev: Event, n: VNode) =
state.setModalShown(false)
ev.preventDefault()
proc render*(state: AddCategoryModal): VNode =
result = buildHtml():
tdiv(class=class({"active": state.modalShown}, "modal modal-sm")):
a(href="", class="modal-overlay", "aria-label"="close",
onClick=(ev: Event, n: VNode) => onModalClose(state, ev, n))
tdiv(class="modal-container"):
tdiv(class="modal-header"):
tdiv(class="card-title h5"):
text "Add New Category"
tdiv(class="modal-body"):
form(id="add-category-form"):
genFormField(
state.error, "name", "Name", "text", false,
placeholder="Category Name")
genFormField(
state.error, "color", "Color", "color", false,
placeholder="#XXYYZZ"
)
genFormField(
state.error,
"description",
"Description",
"text",
true,
placeholder="Description"
)
tdiv(class="modal-footer"):
button(
id="add-category-btn",
class="btn btn-primary",
onClick=(ev: Event, n: VNode) =>
state.onAddCategoryClick()):
text "Add"

View file

@ -10,6 +10,9 @@ type
CategoryList* = ref object
categories*: seq[Category]
CategoryEvent* = proc (category: Category) {.closure.}
CategoryChangeEvent* = proc (oldCategory: Category, newCategory: Category) {.closure.}
const categoryDescriptionCharLimit = 250
proc cmpNames*(cat1: Category, cat2: Category): int =

View file

@ -1,25 +1,24 @@
when defined(js):
import sugar, httpcore, options, json, strutils, algorithm
import dom except Event
import jsffi except `&`
include karax/prelude
import karax / [kajax, kdom, vdom]
import error, category, user
import category, karaxutils
import category, karaxutils, addcategorymodal
type
CategoryPicker* = ref object of VComponent
list: Option[CategoryList]
selectedCategoryID*: int
loading: bool
modalShown: bool
addEnabled: bool
status: HttpCode
error: Option[PostError]
onCategoryChange: proc (oldCategory: Category, newCategory: Category)
onAddCategory: proc (category: Category)
addCategoryModal: AddCategoryModal
onCategoryChange: CategoryChangeEvent
onAddCategory: CategoryEvent
proc onCategoryLoad(state: CategoryPicker): proc (httpStatus: int, response: kstring) =
return
@ -51,18 +50,26 @@ when defined(js):
return cat
raise newException(IndexError, "Category at " & $id & " not found!")
proc nullAddCategory(category: Category) = discard
proc nullCategoryChange(oldCategory: Category, newCategory: Category) = discard
let nullAddCategory: CategoryEvent = proc (category: Category) = discard
let nullCategoryChange: CategoryChangeEvent = proc (oldCategory: Category, newCategory: Category) = discard
proc newCategoryPicker*(
onCategoryChange: proc(oldCategory: Category, newCategory: Category) = nullCategoryChange,
onAddCategory: proc(category: Category) = nullAddCategory
): CategoryPicker =
proc select*(state: CategoryPicker, id: int) =
state.selectedCategoryID = id
state.markDirty()
proc onCategory(state: CategoryPicker): CategoryEvent =
result =
proc (category: Category) =
state.list.get().categories.add(category)
state.list.get().categories.sort(cmpNames)
state.select(category.id)
state.onAddCategory(category)
proc newCategoryPicker*(onCategoryChange=nullCategoryChange, onAddCategory=nullAddCategory): CategoryPicker =
result = CategoryPicker(
list: none[CategoryList](),
selectedCategoryID: 0,
loading: false,
modalShown: false,
addEnabled: false,
status: Http200,
error: none[PostError](),
@ -70,13 +77,14 @@ when defined(js):
onAddCategory: onAddCategory
)
let state = result
result.addCategoryModal = newAddCategoryModal(
onAddCategory=onCategory(state)
)
proc setAddEnabled*(state: CategoryPicker, enabled: bool) =
state.addEnabled = enabled
proc select*(state: CategoryPicker, id: int) =
state.selectedCategoryID = id
state.markDirty()
proc onCategoryClick(state: CategoryPicker, category: Category): proc (ev: Event, n: VNode) =
# this is necessary to capture the right value
let cat = category
@ -86,81 +94,18 @@ when defined(js):
state.select(cat.id)
state.onCategoryChange(oldCategory, cat)
proc onAddCategoryPost(httpStatus: int, response: kstring, state: CategoryPicker) =
postFinished:
state.modalShown = false
let j = parseJson($response)
let category = j.to(Category)
state.list.get().categories.add(category)
state.list.get().categories.sort(cmpNames)
state.select(category.id)
state.onAddCategory(category)
proc onAddCategoryClick(state: CategoryPicker) =
state.loading = true
state.error = none[PostError]()
let uri = makeUri("createCategory")
let form = dom.document.getElementById("add-category-form")
let formData = newFormData(form)
ajaxPost(uri, @[], formData.to(cstring),
(s: int, r: kstring) => onAddCategoryPost(s, r, state))
proc onClose(ev: Event, n: VNode, state: CategoryPicker) =
state.modalShown = false
state.markDirty()
ev.preventDefault()
proc genAddCategory(state: CategoryPicker): VNode =
result = buildHtml():
tdiv(id="add-category"):
button(class="plus-btn btn btn-link",
onClick=(ev: Event, n: VNode) => (
state.modalShown = true;
state.markDirty()
state.addCategoryModal.setModalShown(true)
)):
italic(class="fas fa-plus")
tdiv(class=class({"active": state.modalShown}, "modal modal-sm")):
a(href="", class="modal-overlay", "aria-label"="close",
onClick=(ev: Event, n: VNode) => onClose(ev, n, state))
tdiv(class="modal-container"):
tdiv(class="modal-header"):
tdiv(class="card-title h5"):
text "Add New Category"
tdiv(class="modal-body"):
form(id="add-category-form"):
genFormField(
state.error, "name", "Name", "text", false,
placeholder="Category Name")
genFormField(
state.error, "color", "Color", "color", false,
placeholder="#XXYYZZ"
)
genFormField(
state.error,
"description",
"Description",
"text",
true,
placeholder="Description"
)
tdiv(class="modal-footer"):
button(
id="add-category-btn",
class="btn btn-primary",
onClick=(ev: Event, n: VNode) =>
state.onAddCategoryClick()):
text "Add"
render(state.addCategoryModal)
proc render*(state: CategoryPicker, currentUser: Option[User], compact=true): VNode =
let loggedIn = currentUser.isSome()
let currentAdmin =
loggedIn and currentUser.get().rank == Admin
if currentAdmin:
if currentUser.isAdmin():
state.setAddEnabled(true)
if state.status != Http200:

View file

@ -1,4 +1,4 @@
import times
import times, options
type
# If you add more "Banned" states, be sure to modify forum's threadsQuery too.
@ -27,6 +27,9 @@ type
proc isOnline*(user: User): bool =
return getTime().toUnix() - user.lastOnline < (60*5)
proc isAdmin*(user: Option[User]): bool =
return user.isSome and user.get().rank == Admin
proc `==`*(u1, u2: User): bool =
u1.name == u2.name