wip: implement admin crud
This commit is contained in:
1 parent
306cd6bec4
commit
e902a5e298
78 files changed
+2555
-101
No files matched your search
@@ -0,0 +1,84 @@
|
||||
import { Controller } from '@hotwired/stimulus'
|
||||
import { useFetch } from '../mixins/use_fetch'
|
||||
import Autocomplete from '@trevoreyre/autocomplete-js'
|
||||
|
||||
export default class extends Controller {
|
||||
|
||||
static targets = [ 'input', 'field', 'resetButton', 'searchIcon' ]
|
||||
|
||||
static values = { url: String, choices: Array, selected: Boolean }
|
||||
|
||||
initialize() {
|
||||
useFetch(this)
|
||||
}
|
||||
|
||||
connect() {
|
||||
const searchHandler = input => {
|
||||
return new Promise(resolve => {
|
||||
// Check input minimum length
|
||||
if (input.length < 2) {
|
||||
return resolve([])
|
||||
}
|
||||
|
||||
// Search choices if provided
|
||||
if (this.choicesValue.length > 0) {
|
||||
return resolve(this.choicesValue.filter(choice => {
|
||||
return -1 !== choice.text.toLowerCase().search(input.toLowerCase())
|
||||
}))
|
||||
}
|
||||
|
||||
// Use API otherwise
|
||||
let data = { search: input }
|
||||
fetch(this.urlValue, { method: 'POST', body: JSON.stringify(data), credentials: 'include' })
|
||||
.then(this.checkStatus)
|
||||
.then(this.parseJSON)
|
||||
.then(data => {
|
||||
resolve(data)
|
||||
})
|
||||
})
|
||||
}
|
||||
|
||||
const submitHandler = result => {
|
||||
this.fieldTarget.value = result.value
|
||||
this.selectedValue = true
|
||||
this.element.dispatchEvent(new CustomEvent('select', {
|
||||
detail: { result }
|
||||
}))
|
||||
}
|
||||
|
||||
this.autocomplete = new Autocomplete(this.element, {
|
||||
search: searchHandler,
|
||||
getResultValue: result => result.text,
|
||||
submitOnEnter: true,
|
||||
onSubmit: submitHandler,
|
||||
debounceTime: 250,
|
||||
})
|
||||
|
||||
if (this.fieldTarget.value) {
|
||||
this.selectedValue = true
|
||||
}
|
||||
|
||||
// prevent form submission when selecting entry in autocomplete list with enter key
|
||||
this.element.addEventListener('keypress', e => {
|
||||
if (e.key === 'Enter') {
|
||||
e.preventDefault()
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
reset() {
|
||||
this.inputTarget.value = null
|
||||
this.fieldTarget.value = null
|
||||
this.selectedValue = false
|
||||
this.element.dispatchEvent(new CustomEvent('reset'))
|
||||
}
|
||||
|
||||
selectedValueChanged() {
|
||||
this.resetButtonTarget.classList.toggle('hidden', false === this.selectedValue)
|
||||
this.searchIconTarget.classList.toggle('hidden', this.selectedValue)
|
||||
}
|
||||
|
||||
disconnect() {
|
||||
this.autocomplete.destroy()
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,56 @@
|
||||
import { Controller } from '@hotwired/stimulus'
|
||||
import flatpickr from 'flatpickr';
|
||||
import { German } from 'flatpickr/dist/l10n/de';
|
||||
|
||||
/* stimulusFetch: 'lazy' */
|
||||
export default class extends Controller {
|
||||
|
||||
static targets = [ 'field' ]
|
||||
static values = {
|
||||
minDate: String,
|
||||
maxDate: String,
|
||||
disableWeekends: Boolean,
|
||||
}
|
||||
|
||||
initialize() {
|
||||
flatpickr.localize(German)
|
||||
}
|
||||
|
||||
connect() {
|
||||
const minDate = this.minDateValue
|
||||
const maxDate = this.maxDateValue
|
||||
const options = {
|
||||
dateFormat: 'Y-m-d',
|
||||
altFormat: 'd.m.Y',
|
||||
minDate,
|
||||
maxDate,
|
||||
altInput: true,
|
||||
allowInput: true,
|
||||
onClose(dates, currentdatestring, instance) {
|
||||
instance.setDate(instance.altInput.value, true, instance.config.altFormat)
|
||||
this.element.dispatchEvent(new CustomEvent('datepicker:closed'))
|
||||
},
|
||||
onChange(dates, currentdatestring, instance) {
|
||||
this.element.dispatchEvent(new CustomEvent('datepicker:picked', {
|
||||
detail: {
|
||||
date: instance.altInput.value
|
||||
}
|
||||
}))
|
||||
},
|
||||
}
|
||||
|
||||
if (true === this.disableWeekendsValue) {
|
||||
options['disable'] = [
|
||||
function(date) {
|
||||
return (date.getDay() === 0 || date.getDay() === 6);
|
||||
}
|
||||
]
|
||||
}
|
||||
|
||||
this.picker = flatpickr(this.fieldTarget, options)
|
||||
}
|
||||
|
||||
disconnect() {
|
||||
this.picker.destroy()
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,36 @@
|
||||
import { Controller } from '@hotwired/stimulus'
|
||||
import { useClickOutside } from 'stimulus-use'
|
||||
|
||||
export default class extends Controller {
|
||||
|
||||
static targets = [ 'label', 'dropdown', 'choice' ]
|
||||
static values = { placeholder: String }
|
||||
|
||||
connect() {
|
||||
this.update()
|
||||
useClickOutside(this)
|
||||
}
|
||||
|
||||
toggle() {
|
||||
this.dropdownTarget.classList.toggle('hidden')
|
||||
}
|
||||
|
||||
update() {
|
||||
let labels = []
|
||||
|
||||
this.choiceTargets.forEach((choice) => {
|
||||
if (choice.checked) {
|
||||
labels.push(choice.dataset.label)
|
||||
}
|
||||
this.labelTarget.innerText = labels.join(', ')
|
||||
})
|
||||
|
||||
if (0 === labels.length) {
|
||||
this.labelTarget.innerText = this.placeholderValue
|
||||
}
|
||||
}
|
||||
|
||||
clickOutside(e) {
|
||||
this.dropdownTarget.classList.toggle('hidden', true)
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,42 @@
|
||||
import { Controller } from '@hotwired/stimulus'
|
||||
import Sortable from 'sortablejs'
|
||||
import { useFetch } from '../mixins/use_fetch'
|
||||
|
||||
/* stimulusFetch: 'lazy' */
|
||||
export default class extends Controller {
|
||||
|
||||
static targets = [ 'item', 'handle', 'positionField', 'positionLabel' ]
|
||||
static values = { url: String }
|
||||
|
||||
connect() {
|
||||
useFetch(this)
|
||||
new Sortable(this.element, {
|
||||
item: '[data-sortable-target="item"]',
|
||||
handle: '[data-sortable-target="handle"]',
|
||||
sort: true,
|
||||
onEnd: () => {
|
||||
this.updateOrdering()
|
||||
if (this.urlValue) {
|
||||
this.saveOrdering()
|
||||
}
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
updateOrdering() {
|
||||
this.positionFieldTargets.forEach((field, index) => {
|
||||
field.value = index + 1
|
||||
})
|
||||
this.positionLabelTargets.forEach((label, index) => {
|
||||
label.innerText = index + 1
|
||||
})
|
||||
}
|
||||
|
||||
saveOrdering() {
|
||||
const body = new FormData()
|
||||
this.positionFieldTargets.forEach(field => {
|
||||
body.append(`sorting[${field.dataset.id}]`, field.value)
|
||||
})
|
||||
fetch(this.urlValue, this.getInitObject('post', body))
|
||||
}
|
||||
}
|
||||
Reference in new issue
Block a user