UNPKG

@progress/sitefinity-nextjs-sdk

Version:

Provides OOB widgets developed using the Next.js framework, which includes an abstraction layer for Sitefinity communication. Additionally, it offers an expanded API, typings, and tools for further development and integration.

260 lines (259 loc) 12.9 kB
'use client'; import { jsx as _jsx, jsxs as _jsxs, Fragment as _Fragment } from "react/jsx-runtime"; import { useState } from 'react'; import { VisibilityStyle } from '../styling/visibility-style'; import { classNames } from '../../editor/utils/classNames'; import { invalidateElement, isValidEmail } from '../common/utils'; import { SecurityService } from '../../services/security-service'; import { ProfileViewMode } from './interfaces/profile-view-mode'; import { ActivationMethod } from '../../rest-sdk/dto/registration-settings'; import { ProfileForm } from './profile-form'; import { useRouter } from 'next/navigation'; import { ProfilePostUpdateAction } from './interfaces/profile-post-update-action'; export function ProfileClient(props) { const { viewProps, successMessage, setSuccessMessage, formData, setFormData, initialEmail, setInitialEmail } = props; const context = viewProps.widgetContext.requestContext; const labels = viewProps.labels; const visibilityClassHidden = viewProps.visibilityClasses[VisibilityStyle.Hidden]; const allowedAvatarFormats = viewProps.allowedAvatarFormats?.join(', '); const router = useRouter(); const [fileUpload, setFileUpload] = useState(null); const [errorMessages, setErrorMessages] = useState(null); const [showProfileContainer, setShowProfileContainer] = useState(true); const [showFormContainer, setShowFormContainer] = useState(true); const [invalidInputs, setInvalidInputs] = useState({}); const [showPasswordPrompt, setShowPasswordPrompt] = useState(false); const [isEmailChange, setIsEmailChange] = useState(false); const [showConfirmEmailChangeContainer, setShowConfirmEmailChangeContainer] = useState(false); const [showEdit, setShowEdit] = useState(); const confirmContainerClass = classNames({ [visibilityClassHidden]: !showConfirmEmailChangeContainer }); const confirmContainerStyle = { display: !visibilityClassHidden ? showConfirmEmailChangeContainer ? '' : 'none' : '' }; const profileContainerClass = classNames({ [visibilityClassHidden]: !showProfileContainer }); const profileContainerStyle = { display: !visibilityClassHidden ? showProfileContainer ? '' : 'none' : '' }; const formContainerClass = classNames({ [visibilityClassHidden]: !showFormContainer }); const formContainerStyle = { display: !visibilityClassHidden ? showFormContainer ? '' : 'none' : '' }; const errorMessageClass = classNames('alert alert-danger my-3', { [visibilityClassHidden]: !errorMessages }); const errorMessageStyles = { display: !visibilityClassHidden ? errorMessages ? '' : 'none' : '' }; const hideMessages = () => { setErrorMessages(null); setSuccessMessage(false); }; const showSuccessMessage = () => { setErrorMessages(null); setShowFormContainer(true); setSuccessMessage(true); }; const validateFile = (file) => { const fileSize = file.size; const maxSize = 25 * 1024 * 1024; const fileName = file.name; const fileExtension = fileName.substring(fileName.lastIndexOf('.')).toLowerCase(); if (fileSize > maxSize || !allowedAvatarFormats?.split(',').map(x => x.trim()).includes(fileExtension)) { setErrorMessages([labels.invalidPhotoErrorMessage.replace('{0}', maxSize.toString()).replace('{1}', allowedAvatarFormats)]); return false; } return true; }; const submitForm = (form) => { hideMessages(); if (!validateForm(form)) { return; } if (!showPasswordPrompt) { setFormData((prevData) => ({ ...prevData, Password: '' })); } if (formData.Email !== initialEmail && !showPasswordPrompt) { hideMessages(); setShowPasswordPrompt(true); setIsEmailChange(true); return; } SecurityService.setAntiForgeryTokens().then(() => { if (validateForm(form)) { submitFormHandler(form, '', postSubmitAction, onSubmitError); } }, () => { setErrorMessages(['AntiForgery token retrieval failed']); }); }; const validateForm = (form) => { let isValid = true; setInvalidInputs({}); setErrorMessages(null); const emptyInputs = {}; Array.from(form.elements).forEach((element) => { const input = element; if (!input.value && input.required) { invalidateElement(emptyInputs, input); setInvalidInputs(emptyInputs); isValid = false; } }); if (!isValid) { setErrorMessages([labels.validationRequiredMessage]); return isValid; } const emailInput = form.querySelector('[name=\'Email\']'); if (!isValidEmail(emailInput.value)) { invalidateElement(emptyInputs, emailInput); setInvalidInputs(emptyInputs); setErrorMessages([labels.invalidEmailErrorMessage]); return false; } if (fileUpload != null) { if (!validateFile(fileUpload)) { return false; } } return isValid; }; const serializeForm = (form) => { const result = new FormData(); const formData = new FormData(form); if (viewProps.id) { result.append('Id', viewProps.id); } for (const [key, value] of formData.entries()) { if (key === 'Avatar') { if (fileUpload !== null) { result.append('image', fileUpload); } continue; } result.append(key, value); } return result; }; const submitFormHandler = (form, url, onSuccess, onError) => { url = url || form.attributes['action'].value; const requestBody = serializeForm(form); window.fetch(url, { method: 'POST', body: requestBody }).then((response) => { let status = response.status; if (status === 0 || (status >= 200 && status < 400)) { if (onSuccess) { onSuccess(response); } } else { response.json().then((res) => { const message = res.error.message; const fieldsErrors = res.error.fieldsErrors; if (onError) { onError(message, fieldsErrors, form); } }); } }).finally(() => { if (showPasswordPrompt) { setFormData((prevData) => ({ ...prevData, Password: '' })); setShowPasswordPrompt(false); } }); }; const postSubmitAction = (response) => { if (isEmailChange && viewProps.activationMethod === ActivationMethod.AfterConfirmation) { setShowProfileContainer(false); setShowConfirmEmailChangeContainer(true); } else { let action; let redirectUrl = ''; switch (viewProps.viewMode) { case ProfileViewMode.Edit: { action = viewProps.editModeAction; if (viewProps.editModeRedirectUrl) { redirectUrl = viewProps.editModeRedirectUrl; } break; } case ProfileViewMode.ReadEdit: { action = viewProps.readEditModeAction; if (viewProps.readEditModeRedirectUrl) { redirectUrl = viewProps.readEditModeRedirectUrl; } break; } default: break; } switch (action) { case ProfilePostUpdateAction.ViewMessage: { response.json().then((res) => { setFormData((prevData) => ({ ...prevData, Avatar: res.value.AvatarUrl })); setInitialEmail(res.value.Email); }); showSuccessMessage(); break; } case ProfilePostUpdateAction.SwitchToReadMode: { response.json().then((res) => { setFormData((prevData) => ({ ...prevData, Avatar: res.value.AvatarUrl })); setInitialEmail(res.value.Email); }); setShowEdit(false); break; } case ProfilePostUpdateAction.RedirectToPage: { router.push(redirectUrl); break; } default: break; } } }; const onSubmitError = (errorMessage, responseFieldsErrors, form) => { setShowConfirmEmailChangeContainer(false); setIsEmailChange(false); const invalidInputs = {}; let fieldErrors = []; if (errorMessage) { fieldErrors.push(errorMessage); } if (responseFieldsErrors) { Object.keys(responseFieldsErrors).forEach(key => { const inputElement = form.querySelector(`[name='${key}']`); if (inputElement) { invalidateElement(invalidInputs, inputElement); if (inputElement.id) { const fieldName = form.querySelector(`[for='${inputElement.id}']`)?.innerHTML; fieldErrors.push(responseFieldsErrors[key].replace('{0}', fieldName)); } else { fieldErrors.push(responseFieldsErrors[key]); } } }); } setInvalidInputs(invalidInputs); setErrorMessages(fieldErrors); }; return (_jsxs(_Fragment, { children: [_jsx("div", { "data-sf-role": "profile-container", className: profileContainerClass, style: profileContainerStyle, children: viewProps.viewMode === ProfileViewMode.Read || viewProps.viewMode === ProfileViewMode.ReadEdit && !showEdit ? _jsxs("div", { "data-sf-role": "read-container", className: "d-flex", children: [_jsx("div", { className: "flex-shrink-0", children: /* eslint-disable-next-line @next/next/no-img-element */ _jsx("img", { "data-sf-role": "sf-user-profile-avatar", src: formData.Avatar || '', alt: formData.Email || '', width: 100 }) }), _jsxs("div", { className: "flex-grow-1 ms-2", children: [_jsxs("h2", { className: "mb-0", children: [formData.FirstName, " ", formData.LastName] }), _jsx("p", { className: "text-muted mb-3", children: formData.Email }), viewProps.viewMode === ProfileViewMode.ReadEdit && _jsx("a", { "data-sf-role": "editProfileLink", href: "#", onClick: () => setShowEdit(true), style: { textDecoration: 'none' }, children: labels.editProfileLink })] })] }) : _jsxs("div", { "data-sf-role": "form-container", className: formContainerClass, style: formContainerStyle, children: [_jsx("div", { "data-sf-role": "error-message-container", className: errorMessageClass, style: errorMessageStyles, role: "alert", "aria-live": "assertive", children: errorMessages?.map((error, index) => (_jsxs("div", { children: [error, _jsx("br", {})] }, index))) }), successMessage && _jsx("div", { "data-sf-role": "success-message-container", className: "alert alert-success my-3", role: "alert", "aria-live": "assertive", children: labels.successNotification }), _jsx("div", { children: _jsx("h2", { className: "mb-3", children: labels.header }) }), _jsx(ProfileForm, { formData: formData, setFormData: setFormData, submitForm: submitForm, setFileUpload: setFileUpload, viewProps: viewProps, showPasswordPrompt: showPasswordPrompt, invalidInputs: invalidInputs, allowedAvatarFormats: allowedAvatarFormats, isContextLive: context?.isLive })] }) }), _jsx(_Fragment, { children: viewProps.viewMode !== ProfileViewMode.Read && _jsxs("div", { "data-sf-role": "confirm-email-change-container", className: confirmContainerClass, style: confirmContainerStyle, children: [_jsx("div", { className: "mb-3", "data-sf-role": "confirm-email-change-title", children: _jsx("h2", { children: labels.confirmEmailChangeTitleLabel }) }), _jsx("div", { className: "mb-3", "data-sf-role": "confirm-email-change-message", children: labels.confirmEmailChangeDescriptionLabel })] }) })] })); }