This commit is contained in:
seb
2026-10-18 19:32:31 +02:00
parent 199bf869a6
commit 587170ce9b
9 changed files with 184 additions and 29 deletions

View File

@@ -113,8 +113,9 @@ class CategoryNameDialog extends Component {
const { open, mode = 'create' } = this.props;
const { mode: formMode, name, bulkText, busy, error } = this.state;
const isBulk = formMode === BULK_MODE;
const canBulk = mode === 'create';
const canSubmitSingle = name.trim() !== '';
const canSubmitSingle =
name.trim() !== '' &&
(mode !== 'edit' || name.trim() !== (this.props.initialName ?? '').trim());
const bulkNames = parseBulkNames(bulkText);
const canSubmitBulk = bulkNames.length > 0;
@@ -157,7 +158,13 @@ class CategoryNameDialog extends Component {
<Button
type="submit"
variant="contained"
color={canSubmitSingle ? 'success' : 'inherit'}
disabled={busy || !canSubmitSingle}
sx={
canSubmitSingle
? { bgcolor: 'success.main', color: '#fff', '&:hover': { bgcolor: 'success.dark' } }
: undefined
}
>
{SUBMIT_LABELS[mode]}
</Button>

View File

@@ -749,13 +749,27 @@ export default class CategoryTree extends PureComponent {
>
Abbrechen
</Button>
<Button
onClick={this.handleConfirmRename}
variant="contained"
disabled={!renameValue.trim() || isSubmitting}
>
Speichern
</Button>
{(() => {
const canSave =
Boolean(renameValue.trim()) &&
!isSubmitting &&
renameValue.trim() !== (renameTarget?.name ?? '').trim();
return (
<Button
onClick={this.handleConfirmRename}
variant="contained"
color={canSave ? 'success' : 'inherit'}
disabled={!canSave}
sx={
canSave
? { bgcolor: 'success.main', color: '#fff', '&:hover': { bgcolor: 'success.dark' } }
: undefined
}
>
Speichern
</Button>
);
})()}
</DialogActions>
</AppDialog>

View File

@@ -272,6 +272,11 @@ class ManifestHeader extends PureComponent {
} = this.props;
const hasArticles = this.hasArticles();
const isEdit = docId != null;
const hasAttachments = Array.isArray(attachments) && attachments.length > 0;
const canSave = isEdit
? Boolean(isDirty)
: Boolean(hasArticles || hasAttachments);
// Delivery: number is server-allocated/read-only; the caller reference
// lives in the editable externalNumber field.
@@ -447,20 +452,33 @@ class ManifestHeader extends PureComponent {
<Stack spacing={1.5} sx={{ pt: 0.5 }}>
<Button
variant="contained"
color={isDirty ? 'warning' : 'primary'}
disabled={saving || orderCreating}
color={canSave ? 'success' : 'inherit'}
disabled={Boolean(saving || orderCreating || !canSave)}
onClick={onSave}
sx={{
height: 40,
fontWeight: 700,
fontSize: '0.875rem',
whiteSpace: 'nowrap',
...(isDirty && {
boxShadow: (theme) => `0 0 0 2px ${theme.palette.warning.main}`,
...(canSave && {
bgcolor: 'success.main',
color: '#fff',
'&:hover': {
bgcolor: 'success.dark',
},
}),
...(!canSave && {
bgcolor: 'action.disabledBackground',
color: 'text.disabled',
boxShadow: 'none',
'&.Mui-disabled': {
bgcolor: 'action.disabledBackground',
color: 'text.disabled',
},
}),
}}
>
{saving ? 'Speichern…' : isDirty ? 'Speichern *' : 'Speichern'}
{saving ? 'Speichern…' : 'Speichern'}
</Button>
{isDirty && onRestore && (
<Button

View File

@@ -362,9 +362,15 @@ export class TextEditorPanel extends PureComponent {
<Button
size="small"
variant="contained"
color={isDirty ? 'success' : 'inherit'}
startIcon={<SaveOutlinedIcon />}
onClick={this.handleSave}
disabled={loading || !isDirty}
sx={
isDirty
? { bgcolor: 'success.main', color: '#fff', '&:hover': { bgcolor: 'success.dark' } }
: undefined
}
>
Speichern
</Button>

View File

@@ -52,11 +52,17 @@ export default class CompanySettingsPanel extends Component {
syncFormFromCompany(companyRow) {
if (!companyRow) return;
this.setState({
this._initial = {
name: companyRow.name ?? '',
iso639: companyRow.iso639 ?? 'de',
taxrate: companyRow.taxrate != null ? String(companyRow.taxrate) : '',
reducedtaxrate: companyRow.reducedtaxrate != null ? String(companyRow.reducedtaxrate) : '',
};
this.setState({
name: this._initial.name,
iso639: this._initial.iso639,
taxrate: this._initial.taxrate,
reducedtaxrate: this._initial.reducedtaxrate,
status: 'ready',
});
}
@@ -100,6 +106,12 @@ export default class CompanySettingsPanel extends Component {
taxrate: parsedTaxrate,
reducedtaxrate: parsedReducedtaxrate,
});
this._initial = {
name: name.trim(),
iso639,
taxrate: String(parsedTaxrate),
reducedtaxrate: String(parsedReducedtaxrate),
};
enqueueSnackbar('Unternehmen gespeichert.', { variant: 'success' });
} catch (err) {
this.setState({ error: backendErrorFrom(err) });
@@ -125,7 +137,14 @@ export default class CompanySettingsPanel extends Component {
const parsedTaxrate = parseTaxRate(taxrate);
const parsedReducedtaxrate = parseTaxRate(reducedtaxrate);
const isDirty =
Boolean(this._initial) &&
(name.trim() !== this._initial.name.trim() ||
iso639 !== this._initial.iso639 ||
taxrate.trim() !== this._initial.taxrate.trim() ||
reducedtaxrate.trim() !== this._initial.reducedtaxrate.trim());
const canSave =
isDirty &&
name.trim() !== '' &&
ISO639_OPTIONS.includes(iso639) &&
parsedTaxrate != null &&
@@ -218,8 +237,14 @@ export default class CompanySettingsPanel extends Component {
<Button
type="submit"
variant="contained"
color={canSave ? 'success' : 'inherit'}
disabled={busy || !canSave}
startIcon={busy ? <CircularProgress size={16} color="inherit" /> : null}
sx={
canSave
? { bgcolor: 'success.main', color: '#fff', '&:hover': { bgcolor: 'success.dark' } }
: undefined
}
>
Speichern
</Button>

View File

@@ -292,7 +292,7 @@ export default class VfsTextEditor extends PureComponent {
<Button
size="small"
variant="contained"
color={isDirty ? 'primary' : 'inherit'}
color={isDirty ? 'success' : 'inherit'}
startIcon={isSaving ? <CircularProgress size={14} color="inherit" /> : <SaveRoundedIcon />}
onClick={onSave}
disabled={!isDirty || isSaving}
@@ -302,6 +302,11 @@ export default class VfsTextEditor extends PureComponent {
py: 0.4,
fontSize: '0.78rem',
fontWeight: 600,
...(isDirty && {
bgcolor: 'success.main',
color: '#fff',
'&:hover': { bgcolor: 'success.dark' },
}),
}}
>
{isSaving ? 'Speichern…' : 'Speichern (Strg+S)'}