@micha.bigler/ui-core-micha 1.4.12 → 1.4.14
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
|
@@ -12,8 +12,7 @@ const SupportRecoveryRequestsTab = () => {
|
|
|
12
12
|
const [statusFilter, setStatusFilter] = useState('pending');
|
|
13
13
|
const [dialogOpen, setDialogOpen] = useState(false);
|
|
14
14
|
const [dialogNote, setDialogNote] = useState('');
|
|
15
|
-
const [
|
|
16
|
-
const [dialogMode, setDialogMode] = useState('approve');
|
|
15
|
+
const [selectedRequest, setSelectedRequest] = useState(null);
|
|
17
16
|
const loadRequests = async () => {
|
|
18
17
|
setLoading(true);
|
|
19
18
|
setErrorKey(null);
|
|
@@ -32,49 +31,57 @@ const SupportRecoveryRequestsTab = () => {
|
|
|
32
31
|
loadRequests();
|
|
33
32
|
// eslint-disable-next-line react-hooks/exhaustive-deps
|
|
34
33
|
}, [statusFilter]);
|
|
35
|
-
const openDialog = (
|
|
36
|
-
|
|
37
|
-
setDialogMode(mode);
|
|
34
|
+
const openDialog = (req) => {
|
|
35
|
+
setSelectedRequest(req);
|
|
38
36
|
setDialogNote('');
|
|
39
37
|
setDialogOpen(true);
|
|
40
38
|
};
|
|
41
39
|
const closeDialog = () => {
|
|
42
40
|
setDialogOpen(false);
|
|
43
|
-
|
|
41
|
+
setSelectedRequest(null);
|
|
44
42
|
setDialogNote('');
|
|
45
43
|
};
|
|
46
|
-
const
|
|
47
|
-
if (!
|
|
44
|
+
const handleApprove = async () => {
|
|
45
|
+
if (!selectedRequest)
|
|
48
46
|
return;
|
|
49
47
|
setErrorKey(null);
|
|
50
48
|
try {
|
|
51
|
-
|
|
52
|
-
await authApi.approveRecoveryRequest(dialogRequestId, dialogNote);
|
|
53
|
-
}
|
|
54
|
-
else {
|
|
55
|
-
await authApi.rejectRecoveryRequest(dialogRequestId, dialogNote);
|
|
56
|
-
}
|
|
49
|
+
await authApi.approveRecoveryRequest(selectedRequest.id, dialogNote);
|
|
57
50
|
await loadRequests();
|
|
58
51
|
closeDialog();
|
|
59
52
|
}
|
|
60
53
|
catch (err) {
|
|
61
|
-
setErrorKey(err.code ||
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
54
|
+
setErrorKey(err.code || 'Support.RECOVERY_REQUEST_APPROVE_FAILED');
|
|
55
|
+
}
|
|
56
|
+
};
|
|
57
|
+
const handleReject = async () => {
|
|
58
|
+
if (!selectedRequest)
|
|
59
|
+
return;
|
|
60
|
+
setErrorKey(null);
|
|
61
|
+
try {
|
|
62
|
+
await authApi.rejectRecoveryRequest(selectedRequest.id, dialogNote);
|
|
63
|
+
await loadRequests();
|
|
64
|
+
closeDialog();
|
|
65
|
+
}
|
|
66
|
+
catch (err) {
|
|
67
|
+
setErrorKey(err.code || 'Support.RECOVERY_REQUEST_REJECT_FAILED');
|
|
65
68
|
}
|
|
66
69
|
};
|
|
67
70
|
if (loading) {
|
|
68
71
|
return (_jsx(Box, { sx: { display: 'flex', justifyContent: 'center', mt: 4 }, children: _jsx(CircularProgress, {}) }));
|
|
69
72
|
}
|
|
70
|
-
return (_jsxs(Box, { children: [_jsx(Typography, { variant: "h6", gutterBottom: true, children: t('Support.RECOVERY_REQUESTS_TITLE', 'Account recovery requests') }), _jsx(Typography, { variant: "body2", sx: { mb: 2 }, children: t('Support.RECOVERY_REQUESTS_DESCRIPTION', 'Users who cannot complete MFA can request support. You can
|
|
73
|
+
return (_jsxs(Box, { children: [_jsx(Typography, { variant: "h6", gutterBottom: true, children: t('Support.RECOVERY_REQUESTS_TITLE', 'Account recovery requests') }), _jsx(Typography, { variant: "body2", sx: { mb: 2 }, children: t('Support.RECOVERY_REQUESTS_DESCRIPTION', 'Users who cannot complete MFA can request support. You can review their request and approve or reject it.') }), errorKey && (_jsx(Alert, { severity: "error", sx: { mb: 2 }, children: t(errorKey) })), _jsxs(Stack, { direction: "row", spacing: 2, sx: { mb: 2 }, children: [_jsx(Button, { variant: statusFilter === 'pending' ? 'contained' : 'outlined', size: "small", onClick: () => setStatusFilter('pending'), children: t('Support.RECOVERY_FILTER_PENDING', 'Open') }), _jsx(Button, { variant: statusFilter === 'approved' ? 'contained' : 'outlined', size: "small", onClick: () => setStatusFilter('approved'), children: t('Support.RECOVERY_FILTER_APPROVED', 'Approved') }), _jsx(Button, { variant: statusFilter === 'rejected' ? 'contained' : 'outlined', size: "small", onClick: () => setStatusFilter('rejected'), children: t('Support.RECOVERY_FILTER_REJECTED', 'Rejected') })] }), requests.length === 0 ? (_jsx(Typography, { variant: "body2", children: t('Support.RECOVERY_REQUESTS_EMPTY', 'No recovery requests for this filter.') })) : (_jsx(TableContainer, { component: Paper, children: _jsxs(Table, { size: "small", children: [_jsx(TableHead, { children: _jsxs(TableRow, { children: [_jsx(TableCell, { children: t('Support.RECOVERY_COL_CREATED', 'Created') }), _jsx(TableCell, { children: t('Support.RECOVERY_COL_USER', 'User') }), _jsx(TableCell, { children: t('Support.RECOVERY_COL_STATUS', 'Status') }), _jsx(TableCell, { children: t('Support.RECOVERY_COL_ACTIONS', 'Actions') })] }) }), _jsx(TableBody, { children: requests.map((req) => (_jsxs(TableRow, { children: [_jsx(TableCell, { children: req.created_at
|
|
71
74
|
? new Date(req.created_at).toLocaleString()
|
|
72
|
-
: '-' }), _jsx(TableCell, { children: req.user_email || req.user }), _jsx(TableCell, { children: req.status }), _jsx(TableCell, { children:
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
75
|
+
: '-' }), _jsx(TableCell, { children: req.user_email || req.user }), _jsx(TableCell, { children: req.status }), _jsx(TableCell, { children: _jsx(Button, { variant: "contained", size: "small", onClick: () => openDialog(req), disabled: req.status !== 'pending', children: t('Support.RECOVERY_ACTION_REVIEW', 'Review') }) })] }, req.id))) })] }) })), _jsxs(Dialog, { open: dialogOpen, onClose: closeDialog, fullWidth: true, maxWidth: "sm", children: [_jsx(DialogTitle, { children: t('Support.RECOVERY_REVIEW_DIALOG_TITLE', 'Review recovery request') }), _jsxs(DialogContent, { dividers: true, children: [selectedRequest && (_jsxs(Box, { sx: { mb: 2 }, children: [_jsxs(Typography, { variant: "body2", sx: { mb: 1 }, children: [_jsxs("strong", { children: [t('Support.RECOVERY_REVIEW_USER', 'User'), ":"] }), ' ', selectedRequest.user_email || selectedRequest.user] }), _jsxs(Typography, { variant: "body2", sx: { mb: 1 }, children: [_jsxs("strong", { children: [t('Support.RECOVERY_REVIEW_CREATED', 'Requested at'), ":"] }), ' ', selectedRequest.created_at
|
|
76
|
+
? new Date(selectedRequest.created_at).toLocaleString()
|
|
77
|
+
: '-'] }), _jsx(Typography, { variant: "body2", sx: { mb: 1 }, children: _jsxs("strong", { children: [t('Support.RECOVERY_REVIEW_MESSAGE', 'User’s explanation'), ":"] }) }), _jsx(Box, { sx: {
|
|
78
|
+
border: 1,
|
|
79
|
+
borderColor: 'divider',
|
|
80
|
+
borderRadius: 1,
|
|
81
|
+
p: 1,
|
|
82
|
+
mb: 2,
|
|
83
|
+
whiteSpace: 'pre-wrap',
|
|
84
|
+
fontSize: '0.875rem',
|
|
85
|
+
}, children: selectedRequest.message || t('Support.RECOVERY_NO_MESSAGE', 'No message provided.') })] })), _jsx(TextField, { label: t('Support.RECOVERY_NOTE_LABEL', 'Reason for your decision'), helperText: t('Support.RECOVERY_NOTE_HELP', 'Explain briefly why you approve or reject this request.'), multiline: true, minRows: 3, fullWidth: true, value: dialogNote, onChange: (e) => setDialogNote(e.target.value) })] }), _jsxs(DialogActions, { children: [_jsx(Button, { onClick: closeDialog, children: t('Common.CANCEL', 'Cancel') }), _jsx(Button, { onClick: handleReject, color: "error", disabled: !dialogNote.trim(), children: t('Support.RECOVERY_REJECT_SUBMIT', 'Reject request') }), _jsx(Button, { onClick: handleApprove, variant: "contained", disabled: !dialogNote.trim(), children: t('Support.RECOVERY_APPROVE_SUBMIT', 'Approve and send link') })] })] })] }));
|
|
79
86
|
};
|
|
80
87
|
export default SupportRecoveryRequestsTab;
|
|
@@ -905,6 +905,161 @@ export const authTranslations = {
|
|
|
905
905
|
"de": "Anfrage ablehnen",
|
|
906
906
|
"fr": "Refuser la demande",
|
|
907
907
|
"en": "Reject request"
|
|
908
|
+
},
|
|
909
|
+
"Support.RECOVERY_REQUESTS_TITLE": {
|
|
910
|
+
"de": "Anfragen zur Kontowiederherstellung",
|
|
911
|
+
"fr": "Demandes de récupération de compte",
|
|
912
|
+
"en": "Account recovery requests"
|
|
913
|
+
},
|
|
914
|
+
"Support.RECOVERY_REQUESTS_DESCRIPTION": {
|
|
915
|
+
"de": "Benutzer, die die MFA nicht abschliessen können, können hier eine Anfrage stellen. Sie können die Anfrage prüfen und anschliessend akzeptieren oder ablehnen.",
|
|
916
|
+
"fr": "Les utilisateurs qui ne peuvent pas terminer la MFA peuvent envoyer ici une demande. Vous pouvez examiner la demande puis l’accepter ou la refuser.",
|
|
917
|
+
"en": "Users who cannot complete MFA can submit a request here. You can review the request and then approve or reject it."
|
|
918
|
+
},
|
|
919
|
+
"Support.RECOVERY_REQUESTS_LOAD_FAILED": {
|
|
920
|
+
"de": "Die Anfragen zur Kontowiederherstellung konnten nicht geladen werden.",
|
|
921
|
+
"fr": "Impossible de charger les demandes de récupération de compte.",
|
|
922
|
+
"en": "Failed to load account recovery requests."
|
|
923
|
+
},
|
|
924
|
+
"Support.RECOVERY_FILTER_PENDING": {
|
|
925
|
+
"de": "Offen",
|
|
926
|
+
"fr": "Ouvertes",
|
|
927
|
+
"en": "Open"
|
|
928
|
+
},
|
|
929
|
+
"Support.RECOVERY_FILTER_APPROVED": {
|
|
930
|
+
"de": "Akzeptiert",
|
|
931
|
+
"fr": "Acceptées",
|
|
932
|
+
"en": "Approved"
|
|
933
|
+
},
|
|
934
|
+
"Support.RECOVERY_FILTER_REJECTED": {
|
|
935
|
+
"de": "Abgelehnt",
|
|
936
|
+
"fr": "Refusées",
|
|
937
|
+
"en": "Rejected"
|
|
938
|
+
},
|
|
939
|
+
"Support.RECOVERY_REQUESTS_EMPTY": {
|
|
940
|
+
"de": "Für diesen Filter liegen keine Anfragen vor.",
|
|
941
|
+
"fr": "Aucune demande pour ce filtre.",
|
|
942
|
+
"en": "No recovery requests for this filter."
|
|
943
|
+
},
|
|
944
|
+
"Support.RECOVERY_COL_CREATED": {
|
|
945
|
+
"de": "Erstellt",
|
|
946
|
+
"fr": "Créée le",
|
|
947
|
+
"en": "Created"
|
|
948
|
+
},
|
|
949
|
+
"Support.RECOVERY_COL_USER": {
|
|
950
|
+
"de": "Benutzer",
|
|
951
|
+
"fr": "Utilisateur",
|
|
952
|
+
"en": "User"
|
|
953
|
+
},
|
|
954
|
+
"Support.RECOVERY_COL_STATUS": {
|
|
955
|
+
"de": "Status",
|
|
956
|
+
"fr": "Statut",
|
|
957
|
+
"en": "Status"
|
|
958
|
+
},
|
|
959
|
+
"Support.RECOVERY_COL_ACTIONS": {
|
|
960
|
+
"de": "Aktionen",
|
|
961
|
+
"fr": "Actions",
|
|
962
|
+
"en": "Actions"
|
|
963
|
+
},
|
|
964
|
+
"Support.RECOVERY_ACTION_REVIEW": {
|
|
965
|
+
"de": "Prüfen",
|
|
966
|
+
"fr": "Examiner",
|
|
967
|
+
"en": "Review"
|
|
968
|
+
},
|
|
969
|
+
"Support.RECOVERY_REVIEW_DIALOG_TITLE": {
|
|
970
|
+
"de": "Anfrage zur Kontowiederherstellung prüfen",
|
|
971
|
+
"fr": "Examiner la demande de récupération de compte",
|
|
972
|
+
"en": "Review recovery request"
|
|
973
|
+
},
|
|
974
|
+
"Support.RECOVERY_REVIEW_USER": {
|
|
975
|
+
"de": "Benutzer",
|
|
976
|
+
"fr": "Utilisateur",
|
|
977
|
+
"en": "User"
|
|
978
|
+
},
|
|
979
|
+
"Support.RECOVERY_REVIEW_CREATED": {
|
|
980
|
+
"de": "Angefragt am",
|
|
981
|
+
"fr": "Demandée le",
|
|
982
|
+
"en": "Requested at"
|
|
983
|
+
},
|
|
984
|
+
"Support.RECOVERY_REVIEW_MESSAGE": {
|
|
985
|
+
"de": "Begründung des Benutzers",
|
|
986
|
+
"fr": "Explication de l’utilisateur",
|
|
987
|
+
"en": "User’s explanation"
|
|
988
|
+
},
|
|
989
|
+
"Support.RECOVERY_NO_MESSAGE": {
|
|
990
|
+
"de": "Keine Begründung angegeben.",
|
|
991
|
+
"fr": "Aucune explication fournie.",
|
|
992
|
+
"en": "No message provided."
|
|
993
|
+
},
|
|
994
|
+
"Support.RECOVERY_NOTE_LABEL": {
|
|
995
|
+
"de": "Begründung für Ihre Entscheidung",
|
|
996
|
+
"fr": "Raison de votre décision",
|
|
997
|
+
"en": "Reason for your decision"
|
|
998
|
+
},
|
|
999
|
+
"Support.RECOVERY_NOTE_HELP": {
|
|
1000
|
+
"de": "Beschreiben Sie kurz, warum Sie diese Anfrage akzeptieren oder ablehnen.",
|
|
1001
|
+
"fr": "Décrivez brièvement pourquoi vous acceptez ou refusez cette demande.",
|
|
1002
|
+
"en": "Briefly explain why you approve or reject this request."
|
|
1003
|
+
},
|
|
1004
|
+
"Support.RECOVERY_REJECT_SUBMIT": {
|
|
1005
|
+
"de": "Anfrage ablehnen",
|
|
1006
|
+
"fr": "Refuser la demande",
|
|
1007
|
+
"en": "Reject request"
|
|
1008
|
+
},
|
|
1009
|
+
"Support.RECOVERY_APPROVE_SUBMIT": {
|
|
1010
|
+
"de": "Akzeptieren und Link senden",
|
|
1011
|
+
"fr": "Accepter et envoyer le lien",
|
|
1012
|
+
"en": "Approve and send link"
|
|
1013
|
+
},
|
|
1014
|
+
"Support.RECOVERY_REQUEST_REJECT_FAILED": {
|
|
1015
|
+
"de": "Die Anfrage konnte nicht abgelehnt werden.",
|
|
1016
|
+
"fr": "La demande n’a pas pu être refusée.",
|
|
1017
|
+
"en": "Failed to reject the recovery request."
|
|
1018
|
+
},
|
|
1019
|
+
"Support.RECOVERY_REQUEST_APPROVE_FAILED": {
|
|
1020
|
+
"de": "Die Anfrage konnte nicht akzeptiert werden.",
|
|
1021
|
+
"fr": "La demande n’a pas pu être acceptée.",
|
|
1022
|
+
"en": "Failed to approve the recovery request."
|
|
1023
|
+
},
|
|
1024
|
+
"Auth.MFA_HELP_DIALOG_TITLE": {
|
|
1025
|
+
"de": "Hilfe bei der Anmeldung",
|
|
1026
|
+
"fr": "Aide pour la connexion",
|
|
1027
|
+
"en": "Help with sign-in"
|
|
1028
|
+
},
|
|
1029
|
+
"Auth.MFA_HELP_DIALOG_DESCRIPTION": {
|
|
1030
|
+
"de": "Beschreiben Sie kurz, warum Sie die verfügbaren Methoden nicht nutzen können. Eine Support-Person wird Ihre Anfrage prüfen.",
|
|
1031
|
+
"fr": "Décrivez brièvement pourquoi vous ne pouvez pas utiliser les méthodes disponibles. Une personne du support examinera votre demande.",
|
|
1032
|
+
"en": "Briefly describe why you cannot use the available methods. A support person will review your request."
|
|
1033
|
+
},
|
|
1034
|
+
"Auth.MFA_HELP_MESSAGE_LABEL": {
|
|
1035
|
+
"de": "Ihre Nachricht an den Support",
|
|
1036
|
+
"fr": "Votre message au support",
|
|
1037
|
+
"en": "Your message to support"
|
|
1038
|
+
},
|
|
1039
|
+
"Auth.MFA_HELP_MESSAGE_HELP": {
|
|
1040
|
+
"de": "Optional, hilft dem Support aber bei der Überprüfung Ihrer Identität.",
|
|
1041
|
+
"fr": "Optionnel, mais aide le support à vérifier votre identité.",
|
|
1042
|
+
"en": "Optional, but helps support verify your identity."
|
|
1043
|
+
},
|
|
1044
|
+
"Auth.MFA_HELP_SUBMIT": {
|
|
1045
|
+
"de": "Anfrage senden",
|
|
1046
|
+
"fr": "Envoyer la demande",
|
|
1047
|
+
"en": "Send request"
|
|
1048
|
+
},
|
|
1049
|
+
"Auth.MFA_IDENTIFIER_REQUIRED": {
|
|
1050
|
+
"de": "Bitte geben Sie eine E-Mail-Adresse an.",
|
|
1051
|
+
"fr": "Veuillez indiquer une adresse e-mail.",
|
|
1052
|
+
"en": "Please provide an email address."
|
|
1053
|
+
},
|
|
1054
|
+
"Auth.MFA_HELP_REQUESTED": {
|
|
1055
|
+
"de": "Falls ein Konto mit dieser E-Mail existiert, wurde Ihre Anfrage an den Support weitergeleitet.",
|
|
1056
|
+
"fr": "Si un compte existe avec cette adresse e-mail, votre demande a été transmise au support.",
|
|
1057
|
+
"en": "If an account with this email exists, your request has been forwarded to support."
|
|
1058
|
+
},
|
|
1059
|
+
"Auth.MFA_HELP_REQUEST_FAILED": {
|
|
1060
|
+
"de": "Die Anfrage an den Support konnte nicht gesendet werden.",
|
|
1061
|
+
"fr": "Impossible d’envoyer la demande au support.",
|
|
1062
|
+
"en": "Failed to send the request to support."
|
|
908
1063
|
}
|
|
909
1064
|
// ...
|
|
910
1065
|
};
|
package/package.json
CHANGED
|
@@ -33,8 +33,7 @@ const SupportRecoveryRequestsTab = () => {
|
|
|
33
33
|
|
|
34
34
|
const [dialogOpen, setDialogOpen] = useState(false);
|
|
35
35
|
const [dialogNote, setDialogNote] = useState('');
|
|
36
|
-
const [
|
|
37
|
-
const [dialogMode, setDialogMode] = useState('approve');
|
|
36
|
+
const [selectedRequest, setSelectedRequest] = useState(null);
|
|
38
37
|
|
|
39
38
|
const loadRequests = async () => {
|
|
40
39
|
setLoading(true);
|
|
@@ -54,37 +53,39 @@ const SupportRecoveryRequestsTab = () => {
|
|
|
54
53
|
// eslint-disable-next-line react-hooks/exhaustive-deps
|
|
55
54
|
}, [statusFilter]);
|
|
56
55
|
|
|
57
|
-
const openDialog = (
|
|
58
|
-
|
|
59
|
-
setDialogMode(mode);
|
|
56
|
+
const openDialog = (req) => {
|
|
57
|
+
setSelectedRequest(req);
|
|
60
58
|
setDialogNote('');
|
|
61
59
|
setDialogOpen(true);
|
|
62
60
|
};
|
|
63
61
|
|
|
64
62
|
const closeDialog = () => {
|
|
65
63
|
setDialogOpen(false);
|
|
66
|
-
|
|
64
|
+
setSelectedRequest(null);
|
|
67
65
|
setDialogNote('');
|
|
68
66
|
};
|
|
69
67
|
|
|
70
|
-
const
|
|
71
|
-
if (!
|
|
68
|
+
const handleApprove = async () => {
|
|
69
|
+
if (!selectedRequest) return;
|
|
72
70
|
setErrorKey(null);
|
|
73
71
|
try {
|
|
74
|
-
|
|
75
|
-
await authApi.approveRecoveryRequest(dialogRequestId, dialogNote);
|
|
76
|
-
} else {
|
|
77
|
-
await authApi.rejectRecoveryRequest(dialogRequestId, dialogNote);
|
|
78
|
-
}
|
|
72
|
+
await authApi.approveRecoveryRequest(selectedRequest.id, dialogNote);
|
|
79
73
|
await loadRequests();
|
|
80
74
|
closeDialog();
|
|
81
75
|
} catch (err) {
|
|
82
|
-
setErrorKey(
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
76
|
+
setErrorKey(err.code || 'Support.RECOVERY_REQUEST_APPROVE_FAILED');
|
|
77
|
+
}
|
|
78
|
+
};
|
|
79
|
+
|
|
80
|
+
const handleReject = async () => {
|
|
81
|
+
if (!selectedRequest) return;
|
|
82
|
+
setErrorKey(null);
|
|
83
|
+
try {
|
|
84
|
+
await authApi.rejectRecoveryRequest(selectedRequest.id, dialogNote);
|
|
85
|
+
await loadRequests();
|
|
86
|
+
closeDialog();
|
|
87
|
+
} catch (err) {
|
|
88
|
+
setErrorKey(err.code || 'Support.RECOVERY_REQUEST_REJECT_FAILED');
|
|
88
89
|
}
|
|
89
90
|
};
|
|
90
91
|
|
|
@@ -105,7 +106,7 @@ const SupportRecoveryRequestsTab = () => {
|
|
|
105
106
|
<Typography variant="body2" sx={{ mb: 2 }}>
|
|
106
107
|
{t(
|
|
107
108
|
'Support.RECOVERY_REQUESTS_DESCRIPTION',
|
|
108
|
-
'Users who cannot complete MFA can request support. You can
|
|
109
|
+
'Users who cannot complete MFA can request support. You can review their request and approve or reject it.',
|
|
109
110
|
)}
|
|
110
111
|
</Typography>
|
|
111
112
|
|
|
@@ -176,28 +177,14 @@ const SupportRecoveryRequestsTab = () => {
|
|
|
176
177
|
<TableCell>{req.user_email || req.user}</TableCell>
|
|
177
178
|
<TableCell>{req.status}</TableCell>
|
|
178
179
|
<TableCell>
|
|
179
|
-
<
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
'Support.RECOVERY_ACTION_SEND_LINK',
|
|
188
|
-
'Send recovery link',
|
|
189
|
-
)}
|
|
190
|
-
</Button>
|
|
191
|
-
<Button
|
|
192
|
-
variant="outlined"
|
|
193
|
-
size="small"
|
|
194
|
-
color="error"
|
|
195
|
-
onClick={() => openDialog(req.id, 'reject')}
|
|
196
|
-
disabled={req.status !== 'pending'}
|
|
197
|
-
>
|
|
198
|
-
{t('Support.RECOVERY_ACTION_REJECT', 'Reject')}
|
|
199
|
-
</Button>
|
|
200
|
-
</Stack>
|
|
180
|
+
<Button
|
|
181
|
+
variant="contained"
|
|
182
|
+
size="small"
|
|
183
|
+
onClick={() => openDialog(req)}
|
|
184
|
+
disabled={req.status !== 'pending'}
|
|
185
|
+
>
|
|
186
|
+
{t('Support.RECOVERY_ACTION_REVIEW', 'Review')}
|
|
187
|
+
</Button>
|
|
201
188
|
</TableCell>
|
|
202
189
|
</TableRow>
|
|
203
190
|
))}
|
|
@@ -208,37 +195,58 @@ const SupportRecoveryRequestsTab = () => {
|
|
|
208
195
|
|
|
209
196
|
<Dialog open={dialogOpen} onClose={closeDialog} fullWidth maxWidth="sm">
|
|
210
197
|
<DialogTitle>
|
|
211
|
-
{
|
|
212
|
-
? t(
|
|
213
|
-
'Support.RECOVERY_APPROVE_DIALOG_TITLE',
|
|
214
|
-
'Confirm account recovery',
|
|
215
|
-
)
|
|
216
|
-
: t(
|
|
217
|
-
'Support.RECOVERY_REJECT_DIALOG_TITLE',
|
|
218
|
-
'Reject account recovery',
|
|
219
|
-
)}
|
|
198
|
+
{t('Support.RECOVERY_REVIEW_DIALOG_TITLE', 'Review recovery request')}
|
|
220
199
|
</DialogTitle>
|
|
221
200
|
<DialogContent dividers>
|
|
222
|
-
|
|
223
|
-
{
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
'
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
201
|
+
{selectedRequest && (
|
|
202
|
+
<Box sx={{ mb: 2 }}>
|
|
203
|
+
<Typography variant="body2" sx={{ mb: 1 }}>
|
|
204
|
+
<strong>
|
|
205
|
+
{t('Support.RECOVERY_REVIEW_USER', 'User')}:
|
|
206
|
+
</strong>{' '}
|
|
207
|
+
{selectedRequest.user_email || selectedRequest.user}
|
|
208
|
+
</Typography>
|
|
209
|
+
<Typography variant="body2" sx={{ mb: 1 }}>
|
|
210
|
+
<strong>
|
|
211
|
+
{t('Support.RECOVERY_REVIEW_CREATED', 'Requested at')}:
|
|
212
|
+
</strong>{' '}
|
|
213
|
+
{selectedRequest.created_at
|
|
214
|
+
? new Date(selectedRequest.created_at).toLocaleString()
|
|
215
|
+
: '-'}
|
|
216
|
+
</Typography>
|
|
217
|
+
<Typography variant="body2" sx={{ mb: 1 }}>
|
|
218
|
+
<strong>
|
|
219
|
+
{t(
|
|
220
|
+
'Support.RECOVERY_REVIEW_MESSAGE',
|
|
221
|
+
'User’s explanation',
|
|
222
|
+
)}
|
|
223
|
+
:
|
|
224
|
+
</strong>
|
|
225
|
+
</Typography>
|
|
226
|
+
<Box
|
|
227
|
+
sx={{
|
|
228
|
+
border: 1,
|
|
229
|
+
borderColor: 'divider',
|
|
230
|
+
borderRadius: 1,
|
|
231
|
+
p: 1,
|
|
232
|
+
mb: 2,
|
|
233
|
+
whiteSpace: 'pre-wrap',
|
|
234
|
+
fontSize: '0.875rem',
|
|
235
|
+
}}
|
|
236
|
+
>
|
|
237
|
+
{selectedRequest.message || t('Support.RECOVERY_NO_MESSAGE', 'No message provided.')}
|
|
238
|
+
</Box>
|
|
239
|
+
</Box>
|
|
240
|
+
)}
|
|
233
241
|
|
|
234
242
|
<TextField
|
|
235
243
|
label={t(
|
|
236
244
|
'Support.RECOVERY_NOTE_LABEL',
|
|
237
|
-
'Reason for
|
|
245
|
+
'Reason for your decision',
|
|
238
246
|
)}
|
|
239
247
|
helperText={t(
|
|
240
248
|
'Support.RECOVERY_NOTE_HELP',
|
|
241
|
-
'
|
|
249
|
+
'Explain briefly why you approve or reject this request.',
|
|
242
250
|
)}
|
|
243
251
|
multiline
|
|
244
252
|
minRows={3}
|
|
@@ -252,14 +260,18 @@ const SupportRecoveryRequestsTab = () => {
|
|
|
252
260
|
{t('Common.CANCEL', 'Cancel')}
|
|
253
261
|
</Button>
|
|
254
262
|
<Button
|
|
255
|
-
onClick={
|
|
263
|
+
onClick={handleReject}
|
|
264
|
+
color="error"
|
|
265
|
+
disabled={!dialogNote.trim()}
|
|
266
|
+
>
|
|
267
|
+
{t('Support.RECOVERY_REJECT_SUBMIT', 'Reject request')}
|
|
268
|
+
</Button>
|
|
269
|
+
<Button
|
|
270
|
+
onClick={handleApprove}
|
|
256
271
|
variant="contained"
|
|
257
272
|
disabled={!dialogNote.trim()}
|
|
258
|
-
color={dialogMode === 'approve' ? 'primary' : 'error'}
|
|
259
273
|
>
|
|
260
|
-
{
|
|
261
|
-
? t('Support.RECOVERY_APPROVE_SUBMIT', 'Send link')
|
|
262
|
-
: t('Support.RECOVERY_REJECT_SUBMIT', 'Reject request')}
|
|
274
|
+
{t('Support.RECOVERY_APPROVE_SUBMIT', 'Approve and send link')}
|
|
263
275
|
</Button>
|
|
264
276
|
</DialogActions>
|
|
265
277
|
</Dialog>
|
|
@@ -267,4 +279,4 @@ const SupportRecoveryRequestsTab = () => {
|
|
|
267
279
|
);
|
|
268
280
|
};
|
|
269
281
|
|
|
270
|
-
export default SupportRecoveryRequestsTab
|
|
282
|
+
export default SupportRecoveryRequestsTab
|
|
@@ -953,7 +953,167 @@ export const authTranslations = {
|
|
|
953
953
|
"de": "Anfrage ablehnen",
|
|
954
954
|
"fr": "Refuser la demande",
|
|
955
955
|
"en": "Reject request"
|
|
956
|
-
}
|
|
956
|
+
},
|
|
957
|
+
"Support.RECOVERY_REQUESTS_TITLE": {
|
|
958
|
+
"de": "Anfragen zur Kontowiederherstellung",
|
|
959
|
+
"fr": "Demandes de récupération de compte",
|
|
960
|
+
"en": "Account recovery requests"
|
|
961
|
+
},
|
|
962
|
+
"Support.RECOVERY_REQUESTS_DESCRIPTION": {
|
|
963
|
+
"de": "Benutzer, die die MFA nicht abschliessen können, können hier eine Anfrage stellen. Sie können die Anfrage prüfen und anschliessend akzeptieren oder ablehnen.",
|
|
964
|
+
"fr": "Les utilisateurs qui ne peuvent pas terminer la MFA peuvent envoyer ici une demande. Vous pouvez examiner la demande puis l’accepter ou la refuser.",
|
|
965
|
+
"en": "Users who cannot complete MFA can submit a request here. You can review the request and then approve or reject it."
|
|
966
|
+
},
|
|
967
|
+
"Support.RECOVERY_REQUESTS_LOAD_FAILED": {
|
|
968
|
+
"de": "Die Anfragen zur Kontowiederherstellung konnten nicht geladen werden.",
|
|
969
|
+
"fr": "Impossible de charger les demandes de récupération de compte.",
|
|
970
|
+
"en": "Failed to load account recovery requests."
|
|
971
|
+
},
|
|
972
|
+
"Support.RECOVERY_FILTER_PENDING": {
|
|
973
|
+
"de": "Offen",
|
|
974
|
+
"fr": "Ouvertes",
|
|
975
|
+
"en": "Open"
|
|
976
|
+
},
|
|
977
|
+
"Support.RECOVERY_FILTER_APPROVED": {
|
|
978
|
+
"de": "Akzeptiert",
|
|
979
|
+
"fr": "Acceptées",
|
|
980
|
+
"en": "Approved"
|
|
981
|
+
},
|
|
982
|
+
"Support.RECOVERY_FILTER_REJECTED": {
|
|
983
|
+
"de": "Abgelehnt",
|
|
984
|
+
"fr": "Refusées",
|
|
985
|
+
"en": "Rejected"
|
|
986
|
+
},
|
|
987
|
+
"Support.RECOVERY_REQUESTS_EMPTY": {
|
|
988
|
+
"de": "Für diesen Filter liegen keine Anfragen vor.",
|
|
989
|
+
"fr": "Aucune demande pour ce filtre.",
|
|
990
|
+
"en": "No recovery requests for this filter."
|
|
991
|
+
},
|
|
992
|
+
"Support.RECOVERY_COL_CREATED": {
|
|
993
|
+
"de": "Erstellt",
|
|
994
|
+
"fr": "Créée le",
|
|
995
|
+
"en": "Created"
|
|
996
|
+
},
|
|
997
|
+
"Support.RECOVERY_COL_USER": {
|
|
998
|
+
"de": "Benutzer",
|
|
999
|
+
"fr": "Utilisateur",
|
|
1000
|
+
"en": "User"
|
|
1001
|
+
},
|
|
1002
|
+
"Support.RECOVERY_COL_STATUS": {
|
|
1003
|
+
"de": "Status",
|
|
1004
|
+
"fr": "Statut",
|
|
1005
|
+
"en": "Status"
|
|
1006
|
+
},
|
|
1007
|
+
"Support.RECOVERY_COL_ACTIONS": {
|
|
1008
|
+
"de": "Aktionen",
|
|
1009
|
+
"fr": "Actions",
|
|
1010
|
+
"en": "Actions"
|
|
1011
|
+
},
|
|
1012
|
+
"Support.RECOVERY_ACTION_REVIEW": {
|
|
1013
|
+
"de": "Prüfen",
|
|
1014
|
+
"fr": "Examiner",
|
|
1015
|
+
"en": "Review"
|
|
1016
|
+
},
|
|
1017
|
+
|
|
1018
|
+
"Support.RECOVERY_REVIEW_DIALOG_TITLE": {
|
|
1019
|
+
"de": "Anfrage zur Kontowiederherstellung prüfen",
|
|
1020
|
+
"fr": "Examiner la demande de récupération de compte",
|
|
1021
|
+
"en": "Review recovery request"
|
|
1022
|
+
},
|
|
1023
|
+
"Support.RECOVERY_REVIEW_USER": {
|
|
1024
|
+
"de": "Benutzer",
|
|
1025
|
+
"fr": "Utilisateur",
|
|
1026
|
+
"en": "User"
|
|
1027
|
+
},
|
|
1028
|
+
"Support.RECOVERY_REVIEW_CREATED": {
|
|
1029
|
+
"de": "Angefragt am",
|
|
1030
|
+
"fr": "Demandée le",
|
|
1031
|
+
"en": "Requested at"
|
|
1032
|
+
},
|
|
1033
|
+
"Support.RECOVERY_REVIEW_MESSAGE": {
|
|
1034
|
+
"de": "Begründung des Benutzers",
|
|
1035
|
+
"fr": "Explication de l’utilisateur",
|
|
1036
|
+
"en": "User’s explanation"
|
|
1037
|
+
},
|
|
1038
|
+
"Support.RECOVERY_NO_MESSAGE": {
|
|
1039
|
+
"de": "Keine Begründung angegeben.",
|
|
1040
|
+
"fr": "Aucune explication fournie.",
|
|
1041
|
+
"en": "No message provided."
|
|
1042
|
+
},
|
|
1043
|
+
|
|
1044
|
+
"Support.RECOVERY_NOTE_LABEL": {
|
|
1045
|
+
"de": "Begründung für Ihre Entscheidung",
|
|
1046
|
+
"fr": "Raison de votre décision",
|
|
1047
|
+
"en": "Reason for your decision"
|
|
1048
|
+
},
|
|
1049
|
+
"Support.RECOVERY_NOTE_HELP": {
|
|
1050
|
+
"de": "Beschreiben Sie kurz, warum Sie diese Anfrage akzeptieren oder ablehnen.",
|
|
1051
|
+
"fr": "Décrivez brièvement pourquoi vous acceptez ou refusez cette demande.",
|
|
1052
|
+
"en": "Briefly explain why you approve or reject this request."
|
|
1053
|
+
},
|
|
1054
|
+
|
|
1055
|
+
"Support.RECOVERY_REJECT_SUBMIT": {
|
|
1056
|
+
"de": "Anfrage ablehnen",
|
|
1057
|
+
"fr": "Refuser la demande",
|
|
1058
|
+
"en": "Reject request"
|
|
1059
|
+
},
|
|
1060
|
+
"Support.RECOVERY_APPROVE_SUBMIT": {
|
|
1061
|
+
"de": "Akzeptieren und Link senden",
|
|
1062
|
+
"fr": "Accepter et envoyer le lien",
|
|
1063
|
+
"en": "Approve and send link"
|
|
1064
|
+
},
|
|
1065
|
+
"Support.RECOVERY_REQUEST_REJECT_FAILED": {
|
|
1066
|
+
"de": "Die Anfrage konnte nicht abgelehnt werden.",
|
|
1067
|
+
"fr": "La demande n’a pas pu être refusée.",
|
|
1068
|
+
"en": "Failed to reject the recovery request."
|
|
1069
|
+
},
|
|
1070
|
+
"Support.RECOVERY_REQUEST_APPROVE_FAILED": {
|
|
1071
|
+
"de": "Die Anfrage konnte nicht akzeptiert werden.",
|
|
1072
|
+
"fr": "La demande n’a pas pu être acceptée.",
|
|
1073
|
+
"en": "Failed to approve the recovery request."
|
|
1074
|
+
},
|
|
1075
|
+
|
|
1076
|
+
"Auth.MFA_HELP_DIALOG_TITLE": {
|
|
1077
|
+
"de": "Hilfe bei der Anmeldung",
|
|
1078
|
+
"fr": "Aide pour la connexion",
|
|
1079
|
+
"en": "Help with sign-in"
|
|
1080
|
+
},
|
|
1081
|
+
"Auth.MFA_HELP_DIALOG_DESCRIPTION": {
|
|
1082
|
+
"de": "Beschreiben Sie kurz, warum Sie die verfügbaren Methoden nicht nutzen können. Eine Support-Person wird Ihre Anfrage prüfen.",
|
|
1083
|
+
"fr": "Décrivez brièvement pourquoi vous ne pouvez pas utiliser les méthodes disponibles. Une personne du support examinera votre demande.",
|
|
1084
|
+
"en": "Briefly describe why you cannot use the available methods. A support person will review your request."
|
|
1085
|
+
},
|
|
1086
|
+
"Auth.MFA_HELP_MESSAGE_LABEL": {
|
|
1087
|
+
"de": "Ihre Nachricht an den Support",
|
|
1088
|
+
"fr": "Votre message au support",
|
|
1089
|
+
"en": "Your message to support"
|
|
1090
|
+
},
|
|
1091
|
+
"Auth.MFA_HELP_MESSAGE_HELP": {
|
|
1092
|
+
"de": "Optional, hilft dem Support aber bei der Überprüfung Ihrer Identität.",
|
|
1093
|
+
"fr": "Optionnel, mais aide le support à vérifier votre identité.",
|
|
1094
|
+
"en": "Optional, but helps support verify your identity."
|
|
1095
|
+
},
|
|
1096
|
+
"Auth.MFA_HELP_SUBMIT": {
|
|
1097
|
+
"de": "Anfrage senden",
|
|
1098
|
+
"fr": "Envoyer la demande",
|
|
1099
|
+
"en": "Send request"
|
|
1100
|
+
},
|
|
1101
|
+
|
|
1102
|
+
"Auth.MFA_IDENTIFIER_REQUIRED": {
|
|
1103
|
+
"de": "Bitte geben Sie eine E-Mail-Adresse an.",
|
|
1104
|
+
"fr": "Veuillez indiquer une adresse e-mail.",
|
|
1105
|
+
"en": "Please provide an email address."
|
|
1106
|
+
},
|
|
1107
|
+
"Auth.MFA_HELP_REQUESTED": {
|
|
1108
|
+
"de": "Falls ein Konto mit dieser E-Mail existiert, wurde Ihre Anfrage an den Support weitergeleitet.",
|
|
1109
|
+
"fr": "Si un compte existe avec cette adresse e-mail, votre demande a été transmise au support.",
|
|
1110
|
+
"en": "If an account with this email exists, your request has been forwarded to support."
|
|
1111
|
+
},
|
|
1112
|
+
"Auth.MFA_HELP_REQUEST_FAILED": {
|
|
1113
|
+
"de": "Die Anfrage an den Support konnte nicht gesendet werden.",
|
|
1114
|
+
"fr": "Impossible d’envoyer la demande au support.",
|
|
1115
|
+
"en": "Failed to send the request to support."
|
|
1116
|
+
}
|
|
957
1117
|
|
|
958
1118
|
|
|
959
1119
|
|