99 lines
3.0 KiB
TypeScript
99 lines
3.0 KiB
TypeScript
import React, { useEffect, useState } from "react";
|
|
|
|
import { Alert, Box, Button, Paper, TextField, Typography } from "@mui/material";
|
|
|
|
import { api, getApiErrorMessage } from "../api";
|
|
import { useToast } from "../toast";
|
|
import { useI18n } from "../i18n/I18nProvider";
|
|
|
|
type RuleSettings = {
|
|
id: number;
|
|
appliedFollowUpDays: number;
|
|
appliedGhostDays: number;
|
|
offerFollowUpDays: number;
|
|
offerGhostDays: number;
|
|
feedbackFollowUpDays: number;
|
|
feedbackGhostDays: number;
|
|
};
|
|
|
|
export default function RulesSettingsCard() {
|
|
const { toast } = useToast();
|
|
const { t } = useI18n();
|
|
const [s, setS] = useState<RuleSettings | null>(null);
|
|
const [saving, setSaving] = useState(false);
|
|
const [loadError, setLoadError] = useState<string | null>(null);
|
|
|
|
useEffect(() => {
|
|
api.get<RuleSettings>("/rules")
|
|
.then((r) => {
|
|
setS(r.data);
|
|
setLoadError(null);
|
|
})
|
|
.catch((e: any) => {
|
|
setS(null);
|
|
setLoadError(getApiErrorMessage(e, t("rulesLoadFailed")));
|
|
});
|
|
}, [t]);
|
|
|
|
const save = async () => {
|
|
if (!s) return;
|
|
setSaving(true);
|
|
try {
|
|
await api.put("/rules", s);
|
|
toast(t("rulesSave"), "success");
|
|
} catch {
|
|
toast(t("rulesSaveFailed"), "error");
|
|
} finally {
|
|
setSaving(false);
|
|
}
|
|
};
|
|
|
|
if (!s) {
|
|
return (
|
|
<Paper sx={{ mt: 2, p: 2 }}>
|
|
<Typography variant="h6" sx={{ mb: 1 }}>
|
|
{t("rulesTitle")}
|
|
</Typography>
|
|
{loadError ? <Alert severity="error">{loadError}</Alert> : <Typography sx={{ color: "text.secondary" }}>{t("rulesLoading")}</Typography>}
|
|
</Paper>
|
|
);
|
|
}
|
|
|
|
const num = (k: keyof RuleSettings) => ({
|
|
value: s[k],
|
|
onChange: (e: React.ChangeEvent<HTMLInputElement>) =>
|
|
setS((p) => (p ? { ...p, [k]: Number(e.target.value) } : p)),
|
|
type: "number" as const,
|
|
inputProps: { min: 1, max: 365 },
|
|
});
|
|
|
|
return (
|
|
<Paper sx={{ mt: 2, p: 2 }}>
|
|
<Typography variant="h6" sx={{ mb: 1 }}>
|
|
{t("rulesTitle")}
|
|
</Typography>
|
|
<Typography variant="body2" sx={{ color: "text.secondary", mb: 2 }}>
|
|
{t("rulesBody")}
|
|
</Typography>
|
|
|
|
<Box sx={{ display: "grid", gridTemplateColumns: { xs: "1fr", md: "1fr 1fr 1fr" }, gap: 2 }}>
|
|
<TextField label={t("rulesAppliedFollowUpDays")} {...num("appliedFollowUpDays")} />
|
|
<TextField label={t("rulesAppliedGhostDays")} {...num("appliedGhostDays")} />
|
|
<Box />
|
|
<TextField label={t("rulesOfferFollowUpDays")} {...num("offerFollowUpDays")} />
|
|
<TextField label={t("rulesOfferGhostDays")} {...num("offerGhostDays")} />
|
|
<Box />
|
|
<TextField label={t("rulesFeedbackFollowUpDays")} {...num("feedbackFollowUpDays")} />
|
|
<TextField label={t("rulesFeedbackGhostDays")} {...num("feedbackGhostDays")} />
|
|
<Box />
|
|
</Box>
|
|
|
|
<Box sx={{ display: "flex", justifyContent: "flex-end", mt: 2 }}>
|
|
<Button variant="contained" onClick={save} disabled={saving}>
|
|
{saving ? t("rulesSaving") : t("rulesSave")}
|
|
</Button>
|
|
</Box>
|
|
</Paper>
|
|
);
|
|
}
|