Separate out add category modal
This commit is contained in:
parent
7ec3ff9cac
commit
95b21874db
4 changed files with 121 additions and 83 deletions
87
src/frontend/addcategorymodal.nim
Normal file
87
src/frontend/addcategorymodal.nim
Normal 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"
|
||||
|
|
@ -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 =
|
||||
|
|
|
|||
|
|
@ -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:
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue