Neues Ticket: Formular entschlackt, Kalender und Autovervollständigung
- Work Order Type nur noch Webpage und neu Migration (Kunde kommt von anderem Anbieter) - Affected System, Response Level und Benachrichtigungsfelder entfernt - Service Type: Fernwartung (remote) oder Vor Ort beim Kunden, Standard Fernwartung; gespeichert werden weiter "Remote" / "On Site" - Start und Ende mit Kalender und Uhrzeitfeld; gespeichert weiter als dd.mm.yyyy / hhmm - Requested by/for schlagen Kunden, Mitarbeiter und Namen aus den letzten Tickets vor, die des gewählten Kunden zuerst - Neue Tickets speichern Kundenname und Ort (die Karte zeigte sonst "Unbekannt") - Ticketfilter kennt Webpage und Migration Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
1 parent
cf1d653eb2
commit
d86f9c07cd
7 files changed
+356
-163
No files matched your search
@@ -0,0 +1,87 @@
|
||||
import { useId, useState } from 'react'
|
||||
|
||||
/* Textfeld mit Vorschlagsliste. Freie Eingaben bleiben erlaubt;
|
||||
getSuggestions(text) liefert [{ value, hint }]. */
|
||||
export default function AutocompleteInput({ value, onChange, getSuggestions, id, placeholder, required }) {
|
||||
const listId = useId()
|
||||
const [open, setOpen] = useState(false)
|
||||
const [active, setActive] = useState(-1)
|
||||
|
||||
const items = open ? getSuggestions(value) : []
|
||||
const showList = items.length > 0
|
||||
|
||||
const choose = (item) => {
|
||||
onChange(item.value)
|
||||
setOpen(false)
|
||||
setActive(-1)
|
||||
}
|
||||
|
||||
const handleKeyDown = (e) => {
|
||||
if (e.key === 'ArrowDown') {
|
||||
e.preventDefault()
|
||||
setOpen(true)
|
||||
setActive((i) => Math.min(i + 1, items.length - 1))
|
||||
} else if (e.key === 'ArrowUp') {
|
||||
e.preventDefault()
|
||||
setActive((i) => Math.max(i - 1, -1))
|
||||
} else if (e.key === 'Enter' && showList && items[active]) {
|
||||
e.preventDefault()
|
||||
choose(items[active])
|
||||
} else if (e.key === 'Escape' && showList) {
|
||||
e.preventDefault()
|
||||
setOpen(false)
|
||||
}
|
||||
}
|
||||
|
||||
return (
|
||||
<div className="autocomplete">
|
||||
<input
|
||||
type="text"
|
||||
id={id}
|
||||
className="form-control"
|
||||
role="combobox"
|
||||
aria-autocomplete="list"
|
||||
aria-expanded={showList}
|
||||
aria-controls={listId}
|
||||
aria-activedescendant={showList && items[active] ? `${listId}-${active}` : undefined}
|
||||
autoComplete="off"
|
||||
placeholder={placeholder}
|
||||
required={required}
|
||||
value={value}
|
||||
onChange={(e) => {
|
||||
onChange(e.target.value)
|
||||
setOpen(true)
|
||||
setActive(-1)
|
||||
}}
|
||||
onFocus={() => setOpen(true)}
|
||||
onBlur={() => {
|
||||
setOpen(false)
|
||||
setActive(-1)
|
||||
}}
|
||||
onKeyDown={handleKeyDown}
|
||||
/>
|
||||
{showList && (
|
||||
<ul className="autocomplete-list" id={listId} role="listbox">
|
||||
{items.map((item, index) => (
|
||||
<li
|
||||
key={item.value}
|
||||
id={`${listId}-${index}`}
|
||||
role="option"
|
||||
aria-selected={index === active}
|
||||
className={`autocomplete-item${index === active ? ' active' : ''}`}
|
||||
// mousedown statt click: sonst verliert das Feld vorher den Fokus und die Liste schliesst
|
||||
onMouseDown={(e) => {
|
||||
e.preventDefault()
|
||||
choose(item)
|
||||
}}
|
||||
onMouseEnter={() => setActive(index)}
|
||||
>
|
||||
<span className="autocomplete-value">{item.value}</span>
|
||||
{item.hint && <span className="autocomplete-hint">{item.hint}</span>}
|
||||
</li>
|
||||
))}
|
||||
</ul>
|
||||
)}
|
||||
</div>
|
||||
)
|
||||
}
|
||||
@@ -7,3 +7,4 @@ export { default as Tabs } from './Tabs'
|
||||
export { default as EmptyState } from './EmptyState'
|
||||
export { default as Field } from './Field'
|
||||
export { default as Kpi } from './Kpi'
|
||||
export { default as AutocompleteInput } from './AutocompleteInput'
|
||||
Reference in new issue
Block a user