Use user profile fields in account ui (#4488)
This commit is contained in:
parent
6da66d8456
commit
447ef677d6
15 changed files with 239 additions and 52 deletions
|
@ -14,6 +14,7 @@
|
||||||
"i18next-http-backend": "^2.1.1",
|
"i18next-http-backend": "^2.1.1",
|
||||||
"keycloak-js": "999.0.0-dev",
|
"keycloak-js": "999.0.0-dev",
|
||||||
"keycloak-masthead": "999.0.0-dev",
|
"keycloak-masthead": "999.0.0-dev",
|
||||||
|
"lodash-es": "^4.17.21",
|
||||||
"react": "^18.2.0",
|
"react": "^18.2.0",
|
||||||
"react-dom": "^18.2.0",
|
"react-dom": "^18.2.0",
|
||||||
"react-hook-form": "^7.43.1",
|
"react-hook-form": "^7.43.1",
|
||||||
|
@ -22,6 +23,7 @@
|
||||||
"ui-shared": "999.0.0-dev"
|
"ui-shared": "999.0.0-dev"
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
|
"@types/lodash-es": "^4.17.6",
|
||||||
"@types/react": "^18.0.28",
|
"@types/react": "^18.0.28",
|
||||||
"@types/react-dom": "^18.0.11",
|
"@types/react-dom": "^18.0.11",
|
||||||
"@vitejs/plugin-react-swc": "^3.2.0",
|
"@vitejs/plugin-react-swc": "^3.2.0",
|
||||||
|
|
|
@ -2,6 +2,8 @@
|
||||||
"accept": "Accept",
|
"accept": "Accept",
|
||||||
"accessGrantedOn": "Access granted on",
|
"accessGrantedOn": "Access granted on",
|
||||||
"accountSecurity": "Account security",
|
"accountSecurity": "Account security",
|
||||||
|
"accountUpdatedError": "Could not update account due to validation errors",
|
||||||
|
"accountUpdatedMessage": "Your account has been updated.",
|
||||||
"add": "Add",
|
"add": "Add",
|
||||||
"application": "Application",
|
"application": "Application",
|
||||||
"applicationDetails": "Application details",
|
"applicationDetails": "Application details",
|
||||||
|
@ -11,6 +13,7 @@
|
||||||
"avatar": "Avatar",
|
"avatar": "Avatar",
|
||||||
"basic-authentication": "Basic authentication",
|
"basic-authentication": "Basic authentication",
|
||||||
"cancel": "Cancel",
|
"cancel": "Cancel",
|
||||||
|
"choose": "Choose...",
|
||||||
"client": "Client",
|
"client": "Client",
|
||||||
"clients": "Clients",
|
"clients": "Clients",
|
||||||
"close": "Close",
|
"close": "Close",
|
||||||
|
@ -20,11 +23,34 @@
|
||||||
"device-activity": "Device activity",
|
"device-activity": "Device activity",
|
||||||
"deviceActivity": "Device activity",
|
"deviceActivity": "Device activity",
|
||||||
"directMembership": "Direct membership",
|
"directMembership": "Direct membership",
|
||||||
|
"doCancel": "Cancel",
|
||||||
"doDeny": "Deny",
|
"doDeny": "Deny",
|
||||||
"done": "Done",
|
"done": "Done",
|
||||||
|
"doSave": "Save",
|
||||||
"doSignOut": "Sign out",
|
"doSignOut": "Sign out",
|
||||||
"edit": "Edit",
|
"edit": "Edit",
|
||||||
"editTheResource": "Share the resource - {{0}}",
|
"editTheResource": "Share the resource - {{0}}",
|
||||||
|
"email": "Email",
|
||||||
|
"error-empty": "Please specify value of '{{0}}'.",
|
||||||
|
"error-invalid-blank": "Please specify value of '{{0}}'.",
|
||||||
|
"error-invalid-date": "'{{0}}' is invalid date.",
|
||||||
|
"error-invalid-email": "Invalid email address.",
|
||||||
|
"error-invalid-length-too-long": "'{{0}}' must have maximal length of {{2}}.",
|
||||||
|
"error-invalid-length-too-short": "'{{0}}' must have minimal length of {{1}}.",
|
||||||
|
"error-invalid-length": "'{{0}}' must have a length between {{1}} and {{2}}.",
|
||||||
|
"error-invalid-number": "'{{0}}' is invalid number.",
|
||||||
|
"error-invalid-uri-fragment": "'{{0}}' is invalid URL fragment.",
|
||||||
|
"error-invalid-uri-scheme": "'{{0}}' has invalid URL scheme.",
|
||||||
|
"error-invalid-uri": "'{{0}}' is invalid URL.",
|
||||||
|
"error-invalid-value": "'{{0}}' has invalid value.",
|
||||||
|
"error-number-out-of-range-too-big": "'{{0}}' must have maximal value of {{2}}.",
|
||||||
|
"error-number-out-of-range-too-small": "'{{0}}' must have minimal value of {{1}}.",
|
||||||
|
"error-number-out-of-range": "'{{0}}' must be a number between {{1}} and {{2}}.",
|
||||||
|
"error-pattern-no-match": "'{{0}}' doesn't match required format.",
|
||||||
|
"error-person-name-invalid-character": "'{{0}}' contains invalid character.",
|
||||||
|
"error-user-attribute-required": "Please specify '{{0}}'.",
|
||||||
|
"error-username-invalid-character": "'{{0}}' contains invalid character.",
|
||||||
|
"errorRemovedMessage": "Could not remove {{userLabel}} due to: {{error}}",
|
||||||
"errorSignOutMessage": "Could not be signed out: {{error}}",
|
"errorSignOutMessage": "Could not be signed out: {{error}}",
|
||||||
"expires": "Expires",
|
"expires": "Expires",
|
||||||
"filterByName": "Filter By Name ...",
|
"filterByName": "Filter By Name ...",
|
||||||
|
@ -69,6 +95,8 @@
|
||||||
"privacyPolicy": "Privacy policy",
|
"privacyPolicy": "Privacy policy",
|
||||||
"refreshPage": "Refresh the page",
|
"refreshPage": "Refresh the page",
|
||||||
"removeButton": "Remove access",
|
"removeButton": "Remove access",
|
||||||
|
"removeConsentError": "Could not remove consent due to: {{error}}",
|
||||||
|
"removeConsentSuccess": "Successfully removed consent",
|
||||||
"removeCred": "Remove {{0}}",
|
"removeCred": "Remove {{0}}",
|
||||||
"removeCredAriaLabel": "Remove credential",
|
"removeCredAriaLabel": "Remove credential",
|
||||||
"removeModalMessage": "This will remove the currently granted access permission for {{0}}. You will need to grant access again if you want to use this app.",
|
"removeModalMessage": "This will remove the currently granted access permission for {{0}}. You will need to grant access again if you want to use this app.",
|
||||||
|
@ -82,9 +110,12 @@
|
||||||
"resourceSharedWith_one": "Resource is shared with <0>{{username}}</0>",
|
"resourceSharedWith_one": "Resource is shared with <0>{{username}}</0>",
|
||||||
"resourceSharedWith_other": "Resource is shared with <0>{{username}}</0> and <1>{{other}}</1> other users",
|
"resourceSharedWith_other": "Resource is shared with <0>{{username}}</0> and <1>{{other}}</1> other users",
|
||||||
"resourceSharedWith_zero": "This resource is not shared.",
|
"resourceSharedWith_zero": "This resource is not shared.",
|
||||||
|
"selectOne": "Select an option",
|
||||||
"setUpNew": "Set up {{0}}",
|
"setUpNew": "Set up {{0}}",
|
||||||
"share": "Share",
|
"share": "Share",
|
||||||
"sharedWithMe": "Shared with Me",
|
"sharedWithMe": "Shared with Me",
|
||||||
|
"shareError": "Could not share the resource due to: {{error}}",
|
||||||
|
"shareSuccess": "Resource successfully shared.",
|
||||||
"shareTheResource": "Share the resource - {{0}}",
|
"shareTheResource": "Share the resource - {{0}}",
|
||||||
"shareUser": "Add users to share your resource with",
|
"shareUser": "Add users to share your resource with",
|
||||||
"shareWith": "Share with ",
|
"shareWith": "Share with ",
|
||||||
|
@ -103,6 +134,7 @@
|
||||||
"started": "Started",
|
"started": "Started",
|
||||||
"status": "Status",
|
"status": "Status",
|
||||||
"stopUsingCred": "Stop using {{0}}?",
|
"stopUsingCred": "Stop using {{0}}?",
|
||||||
|
"successRemovedMessage": "{{userLabel}} was removed.",
|
||||||
"systemDefined": "System defined",
|
"systemDefined": "System defined",
|
||||||
"termsOfService": "Terms of service",
|
"termsOfService": "Terms of service",
|
||||||
"thirdPartyApp": "Third-party",
|
"thirdPartyApp": "Third-party",
|
||||||
|
@ -116,8 +148,12 @@
|
||||||
"unLinkError": "Could not unlink due to: {{error}}",
|
"unLinkError": "Could not unlink due to: {{error}}",
|
||||||
"unLinkSuccess": "Successfully unlinked account",
|
"unLinkSuccess": "Successfully unlinked account",
|
||||||
"unShare": "Unshare all",
|
"unShare": "Unshare all",
|
||||||
|
"unShareError": "Could not un-share the resource due to: {{error}}",
|
||||||
|
"unShareSuccess": "Resource successfully un-shared.",
|
||||||
"update": "Update",
|
"update": "Update",
|
||||||
"updateCredAriaLabel": "Update credential",
|
"updateCredAriaLabel": "Update credential",
|
||||||
|
"updateError": "Could not update the resource due to: {{error}}",
|
||||||
|
"updateSuccess": "Resource successfully updated.",
|
||||||
"user": "User",
|
"user": "User",
|
||||||
"username": "Username",
|
"username": "Username",
|
||||||
"usernamePlaceholder": "Username or email",
|
"usernamePlaceholder": "Username or email",
|
||||||
|
|
|
@ -28,9 +28,9 @@ export const AccountRow = ({ account, isLinked = false }: AccountRowProps) => {
|
||||||
const unLink = async (account: LinkedAccountRepresentation) => {
|
const unLink = async (account: LinkedAccountRepresentation) => {
|
||||||
try {
|
try {
|
||||||
await unLinkAccount(account);
|
await unLinkAccount(account);
|
||||||
addAlert("unLinkSuccess");
|
addAlert(t("unLinkSuccess"));
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
addError("unLinkError", error);
|
addError(t("unLinkError", { error }).toString());
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -39,7 +39,7 @@ export const AccountRow = ({ account, isLinked = false }: AccountRowProps) => {
|
||||||
const { accountLinkUri } = await linkAccount(account);
|
const { accountLinkUri } = await linkAccount(account);
|
||||||
location.href = accountLinkUri;
|
location.href = accountLinkUri;
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
addError("linkError", error);
|
addError(t("linkError", { error }).toString());
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
|
@ -76,7 +76,7 @@ const DeviceActivity = () => {
|
||||||
addAlert(t("signedOutSession", [session.browser, device.os]));
|
addAlert(t("signedOutSession", [session.browser, device.os]));
|
||||||
refresh();
|
refresh();
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
addError("errorSignOutMessage", error);
|
addError(t("errorSignOutMessage", { error }).toString());
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
|
@ -185,10 +185,19 @@ const SigningIn = () => {
|
||||||
onContinue={async () => {
|
onContinue={async () => {
|
||||||
try {
|
try {
|
||||||
await deleteCredentials(meta.credential);
|
await deleteCredentials(meta.credential);
|
||||||
addAlert("successRemovedMessage");
|
addAlert(
|
||||||
|
t("successRemovedMessage", {
|
||||||
|
userLabel: label(meta.credential),
|
||||||
|
})
|
||||||
|
);
|
||||||
refresh();
|
refresh();
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
addError("errorRemovedMessage", error);
|
addError(
|
||||||
|
t("errorRemovedMessage", {
|
||||||
|
userLabel: label(meta.credential),
|
||||||
|
error,
|
||||||
|
}).toString()
|
||||||
|
);
|
||||||
}
|
}
|
||||||
}}
|
}}
|
||||||
/>
|
/>
|
||||||
|
|
|
@ -25,10 +25,21 @@ export type PaginationParams = {
|
||||||
export async function getPersonalInfo({
|
export async function getPersonalInfo({
|
||||||
signal,
|
signal,
|
||||||
}: CallOptions = {}): Promise<UserRepresentation> {
|
}: CallOptions = {}): Promise<UserRepresentation> {
|
||||||
const response = await request("/", { signal });
|
const response = await request("/?userProfileMetadata=true", { signal });
|
||||||
return parseResponse<UserRepresentation>(response);
|
return parseResponse<UserRepresentation>(response);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export async function savePersonalInfo(
|
||||||
|
info: UserRepresentation
|
||||||
|
): Promise<void> {
|
||||||
|
const response = await request("/", { body: info, method: "POST" });
|
||||||
|
if (!response.ok) {
|
||||||
|
const { errors } = await response.json();
|
||||||
|
throw errors;
|
||||||
|
}
|
||||||
|
return undefined;
|
||||||
|
}
|
||||||
|
|
||||||
export async function getPermissionRequests(
|
export async function getPermissionRequests(
|
||||||
resourceId: string,
|
resourceId: string,
|
||||||
{ signal }: CallOptions = {}
|
{ signal }: CallOptions = {}
|
||||||
|
|
|
@ -59,9 +59,9 @@ const Applications = () => {
|
||||||
try {
|
try {
|
||||||
await deleteConsent(id);
|
await deleteConsent(id);
|
||||||
refresh();
|
refresh();
|
||||||
addAlert("removeConsentSuccess");
|
addAlert(t("removeConsentSuccess"));
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
addError("removeConsentError", error);
|
addError(t("removeConsentError", { error }).toString());
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
88
apps/account-ui/src/personal-info/FormField.tsx
Normal file
88
apps/account-ui/src/personal-info/FormField.tsx
Normal file
|
@ -0,0 +1,88 @@
|
||||||
|
import { FormGroup, Select, SelectOption } from "@patternfly/react-core";
|
||||||
|
import { TFuncKey } from "i18next";
|
||||||
|
import { get } from "lodash-es";
|
||||||
|
import { useState } from "react";
|
||||||
|
import { useFormContext, Controller } from "react-hook-form";
|
||||||
|
import { useTranslation } from "react-i18next";
|
||||||
|
import { KeycloakTextInput } from "ui-shared";
|
||||||
|
import { UserProfileAttributeMetadata } from "../api/representations";
|
||||||
|
import { fieldName, isBundleKey, unWrap } from "./PersonalInfo";
|
||||||
|
|
||||||
|
type FormFieldProps = {
|
||||||
|
attribute: UserProfileAttributeMetadata;
|
||||||
|
};
|
||||||
|
|
||||||
|
export const FormField = ({ attribute }: FormFieldProps) => {
|
||||||
|
const { t } = useTranslation();
|
||||||
|
const {
|
||||||
|
formState: { errors },
|
||||||
|
register,
|
||||||
|
control,
|
||||||
|
} = useFormContext();
|
||||||
|
const [open, setOpen] = useState(false);
|
||||||
|
const toggle = () => setOpen(!open);
|
||||||
|
|
||||||
|
const isSelect = (attribute: UserProfileAttributeMetadata) =>
|
||||||
|
Object.hasOwn(attribute.validators, "options");
|
||||||
|
|
||||||
|
return (
|
||||||
|
<FormGroup
|
||||||
|
key={attribute.name}
|
||||||
|
label={
|
||||||
|
(isBundleKey(attribute.displayName)
|
||||||
|
? t(unWrap(attribute.displayName) as TFuncKey)
|
||||||
|
: attribute.displayName) || attribute.name
|
||||||
|
}
|
||||||
|
fieldId={attribute.name}
|
||||||
|
isRequired={attribute.required}
|
||||||
|
validated={get(errors, fieldName(attribute.name)) ? "error" : "default"}
|
||||||
|
helperTextInvalid={
|
||||||
|
get(errors, fieldName(attribute.name))?.message as string
|
||||||
|
}
|
||||||
|
>
|
||||||
|
{isSelect(attribute) ? (
|
||||||
|
<Controller
|
||||||
|
name={fieldName(attribute.name)}
|
||||||
|
defaultValue=""
|
||||||
|
control={control}
|
||||||
|
render={({ field }) => (
|
||||||
|
<Select
|
||||||
|
toggleId={attribute.name}
|
||||||
|
onToggle={toggle}
|
||||||
|
onSelect={(_, value) => {
|
||||||
|
field.onChange(value.toString());
|
||||||
|
toggle();
|
||||||
|
}}
|
||||||
|
selections={field.value}
|
||||||
|
variant="single"
|
||||||
|
aria-label={t("selectOne")}
|
||||||
|
isOpen={open}
|
||||||
|
>
|
||||||
|
{[
|
||||||
|
<SelectOption key="empty" value="">
|
||||||
|
{t("choose")}
|
||||||
|
</SelectOption>,
|
||||||
|
...(
|
||||||
|
attribute.validators.options as { options: string[] }
|
||||||
|
).options.map((option) => (
|
||||||
|
<SelectOption
|
||||||
|
selected={field.value === option}
|
||||||
|
key={option}
|
||||||
|
value={option}
|
||||||
|
>
|
||||||
|
{option}
|
||||||
|
</SelectOption>
|
||||||
|
)),
|
||||||
|
]}
|
||||||
|
</Select>
|
||||||
|
)}
|
||||||
|
/>
|
||||||
|
) : (
|
||||||
|
<KeycloakTextInput
|
||||||
|
id={attribute.name}
|
||||||
|
{...register(fieldName(attribute.name))}
|
||||||
|
/>
|
||||||
|
)}
|
||||||
|
</FormGroup>
|
||||||
|
);
|
||||||
|
};
|
|
@ -1,36 +1,84 @@
|
||||||
import { Form } from "@patternfly/react-core";
|
import { ActionGroup, Button, Form } from "@patternfly/react-core";
|
||||||
import { useForm } from "react-hook-form";
|
import { useState } from "react";
|
||||||
|
import { FormProvider, useForm } from "react-hook-form";
|
||||||
import { useTranslation } from "react-i18next";
|
import { useTranslation } from "react-i18next";
|
||||||
|
import { useAlerts } from "ui-shared";
|
||||||
|
|
||||||
import { TextControl } from "ui-shared";
|
import { getPersonalInfo, savePersonalInfo } from "../api/methods";
|
||||||
import { getPersonalInfo } from "../api/methods";
|
import {
|
||||||
import { UserRepresentation } from "../api/representations";
|
UserProfileMetadata,
|
||||||
|
UserRepresentation,
|
||||||
|
} from "../api/representations";
|
||||||
import { Page } from "../components/page/Page";
|
import { Page } from "../components/page/Page";
|
||||||
import { usePromise } from "../utils/usePromise";
|
import { usePromise } from "../utils/usePromise";
|
||||||
|
import { FormField } from "./FormField";
|
||||||
|
|
||||||
|
type FieldError = {
|
||||||
|
field: string;
|
||||||
|
errorMessage: string;
|
||||||
|
params: string[];
|
||||||
|
};
|
||||||
|
|
||||||
|
const ROOT_ATTRIBUTES = ["username", "firstName", "lastName", "email"];
|
||||||
|
export const isBundleKey = (key?: string) => key?.includes("${");
|
||||||
|
export const unWrap = (key: string) => key.substring(2, key.length - 1);
|
||||||
|
export const isRootAttribute = (attr?: string) =>
|
||||||
|
attr && ROOT_ATTRIBUTES.includes(attr);
|
||||||
|
export const fieldName = (name: string) =>
|
||||||
|
`${isRootAttribute(name) ? "" : "attributes."}${name}`;
|
||||||
|
|
||||||
const PersonalInfo = () => {
|
const PersonalInfo = () => {
|
||||||
const { t } = useTranslation();
|
const { t } = useTranslation();
|
||||||
const { control, reset } = useForm<UserRepresentation>({
|
const [userProfileMetadata, setUserProfileMetadata] =
|
||||||
mode: "onChange",
|
useState<UserProfileMetadata>();
|
||||||
});
|
const form = useForm<UserRepresentation>({ mode: "onChange" });
|
||||||
|
const { handleSubmit, reset, setError } = form;
|
||||||
|
const { addAlert, addError } = useAlerts();
|
||||||
|
|
||||||
usePromise((signal) => getPersonalInfo({ signal }), reset);
|
usePromise(
|
||||||
|
(signal) => getPersonalInfo({ signal }),
|
||||||
|
(personalInfo) => {
|
||||||
|
setUserProfileMetadata(personalInfo.userProfileMetadata);
|
||||||
|
reset(personalInfo);
|
||||||
|
}
|
||||||
|
);
|
||||||
|
|
||||||
|
const onSubmit = async (user: UserRepresentation) => {
|
||||||
|
try {
|
||||||
|
await savePersonalInfo(user);
|
||||||
|
addAlert(t("accountUpdatedMessage"));
|
||||||
|
} catch (error) {
|
||||||
|
addError(t("accountUpdatedError").toString());
|
||||||
|
|
||||||
|
(error as FieldError[]).forEach((e) => {
|
||||||
|
const params = Object.assign(
|
||||||
|
{},
|
||||||
|
e.params.map((p) => (isBundleKey(p) ? unWrap(p) : p))
|
||||||
|
);
|
||||||
|
setError(fieldName(e.field) as keyof UserRepresentation, {
|
||||||
|
message: t(e.errorMessage, { ...params, defaultValue: e.field }),
|
||||||
|
type: "server",
|
||||||
|
});
|
||||||
|
});
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Page title={t("personalInfo")} description={t("personalInfoDescription")}>
|
<Page title={t("personalInfo")} description={t("personalInfoDescription")}>
|
||||||
<Form isHorizontal>
|
<Form isHorizontal onSubmit={handleSubmit(onSubmit)}>
|
||||||
<TextControl
|
<FormProvider {...form}>
|
||||||
control={control}
|
{(userProfileMetadata?.attributes || []).map((attribute) => (
|
||||||
name="username"
|
<FormField key={attribute.name} attribute={attribute} />
|
||||||
rules={{ maxLength: 254, required: true }}
|
))}
|
||||||
label={t("username")}
|
</FormProvider>
|
||||||
/>
|
<ActionGroup>
|
||||||
<TextControl
|
<Button type="submit" id="save-btn" variant="primary">
|
||||||
control={control}
|
{t("doSave")}
|
||||||
name="firstName"
|
</Button>
|
||||||
label={t("firstName")}
|
<Button id="cancel-btn" variant="link" onClick={() => reset()}>
|
||||||
/>
|
{t("doCancel")}
|
||||||
<TextControl control={control} name="lastName" label={t("lastName")} />
|
</Button>
|
||||||
|
</ActionGroup>
|
||||||
</Form>
|
</Form>
|
||||||
</Page>
|
</Page>
|
||||||
);
|
);
|
||||||
|
|
|
@ -42,10 +42,10 @@ export const EditTheResource = ({
|
||||||
updatePermissions(resource._id, [permission])
|
updatePermissions(resource._id, [permission])
|
||||||
)
|
)
|
||||||
);
|
);
|
||||||
addAlert("updateSuccess");
|
addAlert(t("updateSuccess"));
|
||||||
onClose();
|
onClose();
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
addError("updateError", error);
|
addError(t("updateError", { error }).toString());
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
|
@ -55,11 +55,11 @@ export const PermissionRequest = ({
|
||||||
? [...(scopes as string[]), ...(shareRequest.scopes as string[])]
|
? [...(scopes as string[]), ...(shareRequest.scopes as string[])]
|
||||||
: scopes
|
: scopes
|
||||||
);
|
);
|
||||||
addAlert("shareSuccess");
|
addAlert(t("shareSuccess"));
|
||||||
toggle();
|
toggle();
|
||||||
refresh();
|
refresh();
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
addError("shareError", error);
|
addError(t("shareError", { error }).toString());
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
|
@ -106,9 +106,9 @@ export const ResourcesTab = () => {
|
||||||
)!;
|
)!;
|
||||||
await updatePermissions(resource._id, permissions);
|
await updatePermissions(resource._id, permissions);
|
||||||
setDetails({});
|
setDetails({});
|
||||||
addAlert("unShareSuccess");
|
addAlert(t("unShareSuccess"));
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
addError("updateError", error);
|
addError(t("unShareError", { error }).toString());
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
|
@ -82,10 +82,10 @@ export const ShareTheResource = ({
|
||||||
updateRequest(resource._id, username, permissions)
|
updateRequest(resource._id, username, permissions)
|
||||||
)
|
)
|
||||||
);
|
);
|
||||||
addAlert("shareSuccess");
|
addAlert(t("shareSuccess"));
|
||||||
onClose();
|
onClose();
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
addError("shareError", error);
|
addError(t("shareError", { error }).toString());
|
||||||
}
|
}
|
||||||
reset({});
|
reset({});
|
||||||
};
|
};
|
||||||
|
|
|
@ -5,7 +5,6 @@ import {
|
||||||
AlertVariant,
|
AlertVariant,
|
||||||
} from "@patternfly/react-core";
|
} from "@patternfly/react-core";
|
||||||
import { createContext, PropsWithChildren, useContext, useState } from "react";
|
import { createContext, PropsWithChildren, useContext, useState } from "react";
|
||||||
import { useTranslation } from "react-i18next";
|
|
||||||
|
|
||||||
export type AddAlertFunction = (
|
export type AddAlertFunction = (
|
||||||
message: string,
|
message: string,
|
||||||
|
@ -13,7 +12,7 @@ export type AddAlertFunction = (
|
||||||
description?: string
|
description?: string
|
||||||
) => void;
|
) => void;
|
||||||
|
|
||||||
export type AddErrorFunction = (message: string, error: any) => void;
|
export type AddErrorFunction = (message: string) => void;
|
||||||
|
|
||||||
export type AlertProps = {
|
export type AlertProps = {
|
||||||
addAlert: AddAlertFunction;
|
addAlert: AddAlertFunction;
|
||||||
|
@ -32,7 +31,6 @@ export type AlertType = {
|
||||||
};
|
};
|
||||||
|
|
||||||
export const AlertProvider = ({ children }: PropsWithChildren) => {
|
export const AlertProvider = ({ children }: PropsWithChildren) => {
|
||||||
const { t } = useTranslation();
|
|
||||||
const [alerts, setAlerts] = useState<AlertType[]>([]);
|
const [alerts, setAlerts] = useState<AlertType[]>([]);
|
||||||
|
|
||||||
const hideAlert = (id: string) => {
|
const hideAlert = (id: string) => {
|
||||||
|
@ -47,8 +45,7 @@ export const AlertProvider = ({ children }: PropsWithChildren) => {
|
||||||
setAlerts([
|
setAlerts([
|
||||||
{
|
{
|
||||||
id: crypto.randomUUID(),
|
id: crypto.randomUUID(),
|
||||||
//@ts-ignore
|
message,
|
||||||
message: t(message),
|
|
||||||
variant,
|
variant,
|
||||||
description,
|
description,
|
||||||
},
|
},
|
||||||
|
@ -56,14 +53,8 @@ export const AlertProvider = ({ children }: PropsWithChildren) => {
|
||||||
]);
|
]);
|
||||||
};
|
};
|
||||||
|
|
||||||
const addError = (message: string, error: Error | string) => {
|
const addError = (message: string) => {
|
||||||
addAlert(
|
addAlert(message, AlertVariant.danger);
|
||||||
//@ts-ignore
|
|
||||||
t(message, {
|
|
||||||
error,
|
|
||||||
}),
|
|
||||||
AlertVariant.danger
|
|
||||||
);
|
|
||||||
};
|
};
|
||||||
|
|
||||||
return (
|
return (
|
||||||
|
|
2
package-lock.json
generated
2
package-lock.json
generated
|
@ -43,6 +43,7 @@
|
||||||
"i18next-http-backend": "^2.1.1",
|
"i18next-http-backend": "^2.1.1",
|
||||||
"keycloak-js": "999.0.0-dev",
|
"keycloak-js": "999.0.0-dev",
|
||||||
"keycloak-masthead": "999.0.0-dev",
|
"keycloak-masthead": "999.0.0-dev",
|
||||||
|
"lodash-es": "^4.17.21",
|
||||||
"react": "^18.2.0",
|
"react": "^18.2.0",
|
||||||
"react-dom": "^18.2.0",
|
"react-dom": "^18.2.0",
|
||||||
"react-hook-form": "^7.43.1",
|
"react-hook-form": "^7.43.1",
|
||||||
|
@ -51,6 +52,7 @@
|
||||||
"ui-shared": "999.0.0-dev"
|
"ui-shared": "999.0.0-dev"
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
|
"@types/lodash-es": "^4.17.6",
|
||||||
"@types/react": "^18.0.28",
|
"@types/react": "^18.0.28",
|
||||||
"@types/react-dom": "^18.0.11",
|
"@types/react-dom": "^18.0.11",
|
||||||
"@vitejs/plugin-react-swc": "^3.2.0",
|
"@vitejs/plugin-react-swc": "^3.2.0",
|
||||||
|
|
Loading…
Reference in a new issue