import { PureComponent, createRef } from 'react'; import Alert from '@mui/material/Alert'; import Box from '@mui/material/Box'; import Button from '@mui/material/Button'; import CircularProgress from '@mui/material/CircularProgress'; import List from '@mui/material/List'; import Typography from '@mui/material/Typography'; import { enqueueSnackbar } from 'notistack'; import BackendError from '../components/BackendError'; import SingleLineTextEdit from 'dashgui/SingleLineTextEdit'; import { PanelTile, PanelTileGrid } from 'dashgui/panels'; import withNavigate from '../components/withNavigate'; import { backendErrorFrom } from '../utils/backendError'; import { notifyError } from '../utils/notify'; import entityAutomat from '../automats/entityAutomat'; import customergroupAutomat from '../automats/customergroupAutomat'; import { Kunde } from '../components/kunde'; import { BLUE_PANEL } from '../colors'; function getInitialParams(props) { const searchParams = typeof window !== 'undefined' ? new URLSearchParams(window.location.search) : null; const prefill = props.location?.state?.name ?? props.location?.state?.newName ?? searchParams?.get('name') ?? searchParams?.get('newName') ?? ''; const returnTo = props.location?.state?.returnTo ?? searchParams?.get('returnTo') ?? null; return { prefill: prefill.trim(), returnTo }; } class KundeSearchField extends PureComponent { constructor(props) { super(props); const initial = entityAutomat.state.filter || props.prefill || ''; this.state = { input: initial, submitting: false, }; if (props.prefill && !entityAutomat.state.filter) { entityAutomat.actions.filter(props.prefill); } this.handleSubmit = this.handleSubmit.bind(this); } componentDidMount() { this.unsub = entityAutomat.subscribe(this, (state) => ({ input: state.filter, })); } componentWillUnmount() { this.unsub?.(); } onInputChange(input) { this.setState({ input }); entityAutomat.actions.filter(input); } async handleSubmit() { const raw = this.state.input.trim(); if (!raw || this.state.submitting) return; this.setState({ submitting: true }); try { await customergroupAutomat.actions.ensureLoaded(); const groups = customergroupAutomat.state.items ?? []; const defaultGroup = groups.find((g) => g.editable !== false) ?? groups[0]; if (!defaultGroup) { throw new Error('Keine Kundengruppe vorhanden.'); } let name = raw; let firstName = null; if (raw.includes(',')) { const parts = raw.split(',').map((p) => p.trim()); name = parts[0]; firstName = parts.slice(1).join(' ') || null; } else if (raw.includes(' ') && !/gmbh|ag|kg|ug|inc|ltd|co\./i.test(raw)) { const parts = raw.split(' '); firstName = parts[0]; name = parts.slice(1).join(' '); } const body = { customergroupId: defaultGroup.id, name, }; if (firstName) body.firstName = firstName; const row = await entityAutomat.actions.create(body); const displayName = [row.firstName, row.name].filter(Boolean).join(' ') || row.name || raw; enqueueSnackbar(`Kunde „${displayName}“ angelegt.`, { variant: 'success' }); this.setState({ input: '', submitting: false }); entityAutomat.actions.filter(''); if (this.props.returnTo && this.props.navigate) { this.props.navigate(this.props.returnTo); } } catch (err) { this.setState({ submitting: false }); notifyError(err); } } render() { const { inputRef, returnTo, navigate } = this.props; const { input, submitting } = this.state; const canSubmit = Boolean(input.trim()) && !submitting; return ( {returnTo ? ( navigate?.(returnTo)} > Zurück } > {input ? `Kundenname „${input}“ übernommen.` : 'Neuen Kunden anlegen.'} ) : null} this.onInputChange(next)} onKeyDown={(event) => { if (event.key === 'Enter') { event.preventDefault(); void this.handleSubmit(); } }} /> ); } } class KundeListePage extends PureComponent { constructor(props) { super(props); const initialEntities = entityAutomat.state.filteredItems ?? entityAutomat.state.items ?? []; this.state = { status: entityAutomat.state.status === 'ready' ? 'ready' : 'loading', error: entityAutomat.state.error ? backendErrorFrom(entityAutomat.state.error) : null, entities: initialEntities, groups: customergroupAutomat.state.items ?? [], filter: entityAutomat.state.filter ?? '', }; this.searchRef = createRef(); this.handleRetry = this.handleRetry.bind(this); this.handleEdit = this.handleEdit.bind(this); } componentDidMount() { this.unsub = entityAutomat.subscribe(this, (state) => ({ entities: state.filteredItems ?? state.items, filter: state.filter ?? '', status: state.status, error: state.error ? backendErrorFrom(state.error) : null, })); this.unsubGroups = customergroupAutomat.subscribe(this, (state) => ({ groups: state.items, })); if (entityAutomat.state.status !== 'ready') { void entityAutomat.actions.load().catch((err) => notifyError(err)); } if (customergroupAutomat.state.status !== 'ready') { void customergroupAutomat.actions.load().catch(() => {}); } } componentWillUnmount() { this.unsub?.(); this.unsubGroups?.(); } handleRetry() { void entityAutomat.actions.load().catch((err) => notifyError(err)); } handleEdit(entity) { if (!entity?.id) return; this.props.navigate?.(`/kunde/${entity.id}`); } render() { const { status, error, entities, groups, filter } = this.state; const { prefill, returnTo } = getInitialParams(this.props); if (status === 'loading' || status === 'idle') { return ( ); } if (status === 'failed') { return ; } const trimmedFilter = filter?.trim() ?? ''; const groupsById = Object.fromEntries(groups.map((g) => [g.id, g])); return ( <> {entities.length === 0 ? ( {trimmedFilter ? `Keine Treffer für „${trimmedFilter}“.` : 'Noch keine Kunden angelegt.'} ) : ( entities.map((entity) => ( )) )} ); } } export { Kunde, KundeSearchField }; export default withNavigate(KundeListePage);