290 lines
9.7 KiB
JavaScript
290 lines
9.7 KiB
JavaScript
import { Component } 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 Dialog from '@mui/material/Dialog';
|
|
import DialogContent from '@mui/material/DialogContent';
|
|
import DialogTitle from '@mui/material/DialogTitle';
|
|
import Divider from '@mui/material/Divider';
|
|
import FormControl from '@mui/material/FormControl';
|
|
import FormLabel from '@mui/material/FormLabel';
|
|
import InputAdornment from '@mui/material/InputAdornment';
|
|
import List from '@mui/material/List';
|
|
import ListItemButton from '@mui/material/ListItemButton';
|
|
import ListItemText from '@mui/material/ListItemText';
|
|
import Stack from '@mui/material/Stack';
|
|
import TextField from '@mui/material/TextField';
|
|
import ToggleButton from '@mui/material/ToggleButton';
|
|
import ToggleButtonGroup from '@mui/material/ToggleButtonGroup';
|
|
import Typography from '@mui/material/Typography';
|
|
import BusinessIcon from '@mui/icons-material/Business';
|
|
import BackendError from './BackendError';
|
|
import { company } from '../api';
|
|
import { StorageManagerContext } from '../api/storageManagerContext';
|
|
import authAutomat from '../automats/authAutomat';
|
|
import {
|
|
defaultIso639,
|
|
ISO639_OPTIONS,
|
|
parseTaxRate,
|
|
} from '../utils/companyForm';
|
|
import { backendErrorFrom, isBackendFailure } from '../utils/backendError';
|
|
|
|
class CompanySetupDialog extends Component {
|
|
static contextType = StorageManagerContext;
|
|
|
|
constructor(props) {
|
|
super(props);
|
|
this.state = {
|
|
companies: [],
|
|
loading: true,
|
|
busy: false,
|
|
error: null,
|
|
name: '',
|
|
iso639: defaultIso639(),
|
|
taxrate: '19',
|
|
reducedtaxrate: '7',
|
|
};
|
|
this.handleSelect = this.handleSelect.bind(this);
|
|
this.handleCreate = this.handleCreate.bind(this);
|
|
this.handleLogout = this.handleLogout.bind(this);
|
|
this.handleClose = this.handleClose.bind(this);
|
|
}
|
|
|
|
componentDidMount() {
|
|
void this.loadCompanies();
|
|
}
|
|
|
|
async loadCompanies() {
|
|
this.setState({ loading: true });
|
|
try {
|
|
const data = await company.list();
|
|
this.setState({ companies: data.companies ?? [] });
|
|
} catch {
|
|
this.setState({ companies: [] });
|
|
} finally {
|
|
this.setState({ loading: false });
|
|
}
|
|
}
|
|
|
|
async handleSelect(companyId) {
|
|
this.setState({ busy: true, error: null });
|
|
try {
|
|
await authAutomat.actions.selectCompany(companyId);
|
|
await this.context.updateAuthMe();
|
|
} catch (err) {
|
|
this.setState({ error: backendErrorFrom(err) });
|
|
} finally {
|
|
this.setState({ busy: false });
|
|
}
|
|
}
|
|
|
|
async handleCreate(event) {
|
|
event.preventDefault();
|
|
const { name, iso639, taxrate, reducedtaxrate } = this.state;
|
|
const parsedTaxrate = parseTaxRate(taxrate);
|
|
const parsedReducedtaxrate = parseTaxRate(reducedtaxrate);
|
|
const canCreate =
|
|
name.trim() !== '' &&
|
|
ISO639_OPTIONS.includes(iso639) &&
|
|
parsedTaxrate != null &&
|
|
parsedReducedtaxrate != null;
|
|
|
|
if (!canCreate) {
|
|
this.setState({ error: 'Bitte alle Pflichtfelder ausfüllen.' });
|
|
return;
|
|
}
|
|
|
|
this.setState({ busy: true, error: null });
|
|
try {
|
|
await authAutomat.actions.createCompany({
|
|
name: name.trim(),
|
|
iso639,
|
|
taxrate: parsedTaxrate,
|
|
reducedtaxrate: parsedReducedtaxrate,
|
|
});
|
|
await this.context.updateAuthMe();
|
|
await this.context.updateCompany();
|
|
} catch (err) {
|
|
this.setState({ error: backendErrorFrom(err) });
|
|
} finally {
|
|
this.setState({ busy: false });
|
|
}
|
|
}
|
|
|
|
async handleLogout() {
|
|
try {
|
|
await authAutomat.actions.logout();
|
|
await this.context.updateAuthMe();
|
|
} catch (err) {
|
|
this.setState({ error: backendErrorFrom(err) });
|
|
}
|
|
}
|
|
|
|
handleClose(_event, reason) {
|
|
if (reason === 'backdropClick' || reason === 'escapeKeyDown') return;
|
|
}
|
|
|
|
render() {
|
|
const { companies, loading, busy, error, name, iso639, taxrate, reducedtaxrate } =
|
|
this.state;
|
|
const hasCompanies = companies.length > 0;
|
|
const parsedTaxrate = parseTaxRate(taxrate);
|
|
const parsedReducedtaxrate = parseTaxRate(reducedtaxrate);
|
|
const canCreate =
|
|
name.trim() !== '' &&
|
|
ISO639_OPTIONS.includes(iso639) &&
|
|
parsedTaxrate != null &&
|
|
parsedReducedtaxrate != null;
|
|
|
|
return (
|
|
<Dialog open onClose={this.handleClose} maxWidth="sm" fullWidth>
|
|
<DialogTitle>
|
|
{loading
|
|
? 'Unternehmen'
|
|
: hasCompanies
|
|
? 'Unternehmen auswählen'
|
|
: 'Unternehmen anlegen'}
|
|
</DialogTitle>
|
|
<DialogContent>
|
|
{!loading ? (
|
|
<Typography variant="body2" color="text.secondary" sx={{ mb: 2 }}>
|
|
{hasCompanies
|
|
? 'Wählen Sie ein bestehendes Unternehmen oder legen Sie ein neues an.'
|
|
: 'Legen Sie Ihr Unternehmen an, um fortzufahren.'}
|
|
</Typography>
|
|
) : null}
|
|
|
|
{error ? (
|
|
isBackendFailure(error) ? (
|
|
<BackendError error={error} sx={{ mb: 2 }} />
|
|
) : (
|
|
<Alert severity="error" sx={{ mb: 2 }}>
|
|
{typeof error === 'string' ? error : error.message}
|
|
</Alert>
|
|
)
|
|
) : null}
|
|
|
|
{loading ? (
|
|
<Box sx={{ display: 'flex', justifyContent: 'center', py: 4 }}>
|
|
<CircularProgress />
|
|
</Box>
|
|
) : (
|
|
<Stack spacing={3}>
|
|
{hasCompanies ? (
|
|
<Box>
|
|
<List disablePadding sx={{ bgcolor: 'background.paper', borderRadius: 1 }}>
|
|
{companies.map((item) => (
|
|
<ListItemButton
|
|
key={item.id}
|
|
disabled={busy}
|
|
onClick={() => this.handleSelect(item.id)}
|
|
>
|
|
<BusinessIcon sx={{ mr: 1.5, color: 'primary.main' }} />
|
|
<ListItemText primary={item.name} />
|
|
</ListItemButton>
|
|
))}
|
|
</List>
|
|
</Box>
|
|
) : null}
|
|
|
|
{hasCompanies ? <Divider>oder</Divider> : null}
|
|
|
|
<Box component="form" onSubmit={this.handleCreate} noValidate>
|
|
<Stack spacing={2}>
|
|
{hasCompanies ? (
|
|
<Typography variant="subtitle2">Neues Unternehmen anlegen</Typography>
|
|
) : null}
|
|
|
|
<TextField
|
|
fullWidth
|
|
required
|
|
size="small"
|
|
label="Name"
|
|
value={name}
|
|
disabled={busy}
|
|
onChange={(e) => this.setState({ name: e.target.value })}
|
|
autoComplete="organization"
|
|
autoFocus={!hasCompanies}
|
|
/>
|
|
|
|
<FormControl required>
|
|
<FormLabel sx={{ mb: 0.5 }}>Sprache</FormLabel>
|
|
<ToggleButtonGroup
|
|
exclusive
|
|
required
|
|
size="small"
|
|
value={iso639}
|
|
disabled={busy}
|
|
onChange={(_event, value) => {
|
|
if (value) this.setState({ iso639: value });
|
|
}}
|
|
>
|
|
<ToggleButton value="de">Deutsch</ToggleButton>
|
|
<ToggleButton value="en">English</ToggleButton>
|
|
</ToggleButtonGroup>
|
|
</FormControl>
|
|
|
|
<Stack direction="row" spacing={1}>
|
|
<TextField
|
|
fullWidth
|
|
required
|
|
size="small"
|
|
type="number"
|
|
label="Normalsatz"
|
|
value={taxrate}
|
|
disabled={busy}
|
|
onChange={(e) => this.setState({ taxrate: e.target.value })}
|
|
error={taxrate !== '' && parsedTaxrate == null}
|
|
slotProps={{
|
|
input: {
|
|
endAdornment: <InputAdornment position="end">%</InputAdornment>,
|
|
},
|
|
htmlInput: { min: 0, max: 100, step: 0.01, required: true },
|
|
}}
|
|
/>
|
|
<TextField
|
|
fullWidth
|
|
required
|
|
size="small"
|
|
type="number"
|
|
label="Ermäßigter Satz"
|
|
value={reducedtaxrate}
|
|
disabled={busy}
|
|
onChange={(e) => this.setState({ reducedtaxrate: e.target.value })}
|
|
error={reducedtaxrate !== '' && parsedReducedtaxrate == null}
|
|
slotProps={{
|
|
input: {
|
|
endAdornment: <InputAdornment position="end">%</InputAdornment>,
|
|
},
|
|
htmlInput: { min: 0, max: 100, step: 0.01, required: true },
|
|
}}
|
|
/>
|
|
</Stack>
|
|
|
|
<Button type="submit" variant="contained" disabled={busy || !canCreate}>
|
|
Anlegen
|
|
</Button>
|
|
</Stack>
|
|
</Box>
|
|
|
|
<Button
|
|
type="button"
|
|
variant="text"
|
|
color="inherit"
|
|
disabled={busy}
|
|
onClick={this.handleLogout}
|
|
sx={{ alignSelf: 'center' }}
|
|
>
|
|
Abmelden
|
|
</Button>
|
|
</Stack>
|
|
)}
|
|
</DialogContent>
|
|
</Dialog>
|
|
);
|
|
}
|
|
}
|
|
|
|
export default CompanySetupDialog;
|