diff --git a/src/api/ecosystem.ts b/src/api/ecosystem.ts index 2ae70dfed..ecc0baec8 100644 --- a/src/api/ecosystem.ts +++ b/src/api/ecosystem.ts @@ -10,7 +10,7 @@ interface CreateEcosystemPayload { name: string description: string logo: string - tags: string + tags?: string userId: number } @@ -44,10 +44,13 @@ export const createEcosystems = async (dataPayload: CreateEcosystemPayload) => { } } -export const updateEcosystem = async (data: object) => { +export const updateEcosystem = async (dataPayload: CreateEcosystemPayload) => { const orgId = await getFromLocalStorage(storageKeys.ORG_ID); - const url = `${apiRoutes.Ecosystem.root}/${orgId}` - const payload = data + const ecosystemId = await getFromLocalStorage(storageKeys.ECOSYSTEM_ID); + + const url = `${apiRoutes.Ecosystem.root}/${ecosystemId}/${orgId}` + const payload = dataPayload + const axiosPayload = { url, payload, @@ -63,6 +66,7 @@ export const updateEcosystem = async (data: object) => { } } + export const getEcosystem = async (orgId: string) => { const url = `${apiRoutes.Ecosystem.root}/${orgId}` diff --git a/src/components/Ecosystem/Dashboard.tsx b/src/components/Ecosystem/Dashboard.tsx index 5e965d26a..3a307f6e3 100644 --- a/src/components/Ecosystem/Dashboard.tsx +++ b/src/components/Ecosystem/Dashboard.tsx @@ -16,10 +16,13 @@ import { AlertComponent } from '../AlertComponent'; import { ICheckEcosystem, checkEcosystem } from '../../config/ecosystem'; import RoleViewButton from '../RoleViewButton'; import SendInvitationModal from '../organization/invitations/SendInvitationModal'; +import { Dropdown } from 'flowbite-react'; +import EditPopupModal from '../EditEcosystemOrgModal'; import { getFromLocalStorage, setToLocalStorage } from '../../api/Auth'; import { getEcosytemReceivedInvitations } from '../../api/invitations'; import { pathRoutes } from '../../config/pathRoutes'; + const initialPageState = { pageNumber: 1, pageSize: 10, @@ -27,36 +30,46 @@ const initialPageState = { }; const Dashboard = () => { - const [ecosystemDetails, setEcosystemDetails] = useState(); - const [success, setSuccess] = useState(null); - const [failure, setFailure] = useState(null); - const [message, setMessage] = useState(null); - const [loading, setLoading] = useState(true); - const [error, setError] = useState(null); - const [ecosystemId, setEcosystemId] = useState(''); - const [openModal, setOpenModal] = useState(false); - const [viewButton, setViewButton] = useState(false); - const [currentPage, setCurrentPage] = useState(initialPageState); - const [isEcosystemLead, setIsEcosystemLead] = useState(false); + const [ecosystemDetails, setEcosystemDetails] = useState(); + const [success, setSuccess] = useState(null); + const [failure, setFailure] = useState(null); + const [message, setMessage] = useState(null); + const [loading, setLoading] = useState(true); + const [ecosystemId, setEcosystemId] = useState('') + const [editOpenModal, setEditOpenModal] = useState(false); + const [dropdownOpen, setDropdownOpen] = useState(false); + const [error, setError] = useState(null); + const [openModal, setOpenModal] = useState(false); + const [viewButton, setViewButton] = useState(false); + const [currentPage, setCurrentPage] = useState(initialPageState); + const [isEcosystemLead, setIsEcosystemLead] = useState(false); + + const createEcosystemModel = () => { + setOpenModal(true); + }; - const props = { openModal, setOpenModal }; + const createInvitationsModel = () => { + setOpenModal(true); + }; - const createEcosystemModel = () => { - props.setOpenModal(true); - }; + const EditEcosystemOrgModal = () => { + setEditOpenModal(true); + }; - const createInvitationsModel = () => { - props.setOpenModal(true); - }; + const handleEditModalClose = () => { + setEditOpenModal(false); + setDropdownOpen(false); + fetchEcosystemDetails() + }; - const getAllEcosystemInvitations = async () => { - setLoading(true); - const response = await getEcosytemReceivedInvitations( - currentPage.pageNumber, - currentPage.pageSize, - '', - ); - const { data } = response as AxiosResponse; + const getAllEcosystemInvitations = async () => { + setLoading(true); + const response = await getEcosytemReceivedInvitations( + currentPage.pageNumber, + currentPage.pageSize, + '' + ); + const { data } = response as AxiosResponse; if (data?.statusCode === apiStatusCodes.API_STATUS_SUCCESS) { const totalPages = data?.data?.totalPages; @@ -142,7 +155,7 @@ const Dashboard = () => { <>
{ { setMessage(data)} - setOpenModal={props.setOpenModal} + setOpenModal={setOpenModal} /> { } onClickEvent={createInvitationsModel} /> -
- -
+ setDropdownOpen(!dropdownOpen)} + renderTrigger={() => } + > + +
+ Edit Ecosystem +
+
+ +
+ Enable/Disable Ecosystem +
+
+ +
+ Manual Registration +
+
+ + +
)} @@ -274,6 +309,16 @@ const Dashboard = () => {
+ { + setSuccess(value); + }} + isOrganization={false} + onEditSuccess={handleEditModalClose} + entityData={ecosystemDetails} + /> )} @@ -288,7 +333,7 @@ const Dashboard = () => {
{ setSuccess(value); fetchEcosystemDetails(); diff --git a/src/components/Ecosystem/interfaces/index.ts b/src/components/Ecosystem/interfaces/index.ts index 3772fadde..14f9608b0 100644 --- a/src/components/Ecosystem/interfaces/index.ts +++ b/src/components/Ecosystem/interfaces/index.ts @@ -15,4 +15,5 @@ export interface Ecosystem { logoUrl: string website: string roles: string[] + logoFile:string } \ No newline at end of file diff --git a/src/components/EditEcosystemOrgModal/index.tsx b/src/components/EditEcosystemOrgModal/index.tsx new file mode 100644 index 000000000..9b2891547 --- /dev/null +++ b/src/components/EditEcosystemOrgModal/index.tsx @@ -0,0 +1,337 @@ +import * as yup from "yup"; +import { Avatar, Button, Label, Modal } from 'flowbite-react'; +import { Field, Form, Formik, FormikHelpers } from 'formik'; +import { IMG_MAX_HEIGHT, IMG_MAX_WIDTH, apiStatusCodes, imageSizeAccepted } from '../../config/CommonConstant'; +import { calculateSize, dataURItoBlob } from "../../utils/CompressImage"; +import { AlertComponent } from "../AlertComponent"; +import type { AxiosResponse } from 'axios'; +import { updateOrganization } from "../../api/organization"; +import { updateEcosystem } from "../../api/ecosystem"; +import type { Organisation } from "../organization/interfaces"; +import type { Ecosystem } from "../Ecosystem/interfaces"; +import React, { useEffect, useState } from "react"; + +interface EditEntityModalProps { + openModal: boolean; + setMessage: (message: string) => void; + setOpenModal: (flag: boolean) => void; + onEditSuccess?: () => void; + entityData: Organisation | Ecosystem | null; + isOrganization: boolean; +} + +interface EditEntityValues { + name: string; + description: string; +} + +interface ILogoImage { + logoFile: string | File; + imagePreviewUrl: string | ArrayBuffer | null | File; + fileName: string; +} + +const EditPopupModal = (props: EditEntityModalProps) => { + const [logoImage, setLogoImage] = useState({ + logoFile: "", + imagePreviewUrl: props?.entityData?.logoUrl ?? "", + fileName: '', + }); + + const [loading, setLoading] = useState(false); + const [isImageEmpty, setIsImageEmpty] = useState(true); + const [initialEntityData, setInitialEntityData] = useState({ + name: "", + description: "", + }); + + useEffect(() => { + console.log(6565, props.entityData) + if (props.openModal && props.entityData) { + setInitialEntityData({ + name: props.entityData.name ?? "", + description: props.entityData.description ?? "", + }); + setLogoImage({ + logoFile: "", + imagePreviewUrl: props.entityData.logoUrl ?? "", + fileName: props.entityData.logoFile ?? "", + }); + } + }, [props.entityData, props.openModal]); + + const [errMsg, setErrMsg] = useState(null); + const [imgError, setImgError] = useState(''); + + + useEffect(() => { + if (!props.openModal) { + setInitialEntityData({ + name: "", + description: "", + }); + + setLogoImage({ + logoFile: "", + imagePreviewUrl: "", + fileName: "", + }); + setImgError(''); + setErrMsg(null); + setLoading(false); + } + }, [props.openModal]); + + const processImage = (e: any): string | undefined => { + const file = e?.target?.files[0]; + if (!file) return; + + const reader = new FileReader(); + reader.readAsDataURL(file); + + reader.onload = (event): void => { + const imgElement = document.createElement("img"); + if (imgElement) { + imgElement.src = typeof event?.target?.result === 'string' ? event.target.result : ""; + imgElement.onload = (e): void => { + let fileUpdated: File | string = file; + let srcEncoded = ''; + const canvas = document.createElement("canvas"); + + const { width, height, ev } = calculateSize(imgElement, IMG_MAX_WIDTH, IMG_MAX_HEIGHT); + canvas.width = width; + canvas.height = height; + + const ctx = canvas.getContext("2d"); + if (ctx && e?.target) { + ctx.imageSmoothingEnabled = true; + ctx.imageSmoothingQuality = "high"; + ctx.drawImage(ev, 0, 0, canvas.width, canvas.height); + srcEncoded = ctx.canvas.toDataURL(ev, file.type); + const blob = dataURItoBlob(srcEncoded, file.type); + fileUpdated = new File([blob], file.name, { type: file.type, lastModified: new Date().getTime() }); + setLogoImage({ + logoFile: fileUpdated, + imagePreviewUrl: srcEncoded, + fileName: file.name + }); + } + }; + } + }; + }; + + + const handleImageChange = (event: any): void => { + setImgError(''); + const reader = new FileReader(); + const file = event?.target?.files; + + const fileSize = Number((file[0]?.size / 1024 / 1024)?.toFixed(2)); + const extension = file[0]?.name?.substring(file[0]?.name?.lastIndexOf(".") + 1)?.toLowerCase(); + + if (extension === "png" || extension === "jpeg" || extension === "jpg") { + if (fileSize <= imageSizeAccepted) { + reader.onloadend = (): void => { + processImage(event); + setIsImageEmpty(false); + }; + reader.readAsDataURL(file[0]); + event.preventDefault(); + } else { + setImgError("Please check image size"); + } + } else { + setImgError("Invalid image type"); + } + }; + + const isEmpty = (object: any): boolean => { + + return true; + }; + + const submitUpdateEntity = async (values: EditEntityValues) => { + setLoading(true); + + const entityData = { + id: props?.entityData?.id, + name: values.name, + description: values.description, + logo: logoImage?.imagePreviewUrl as string || props?.entityData?.logoUrl, + + }; + + try { + if (props.isOrganization) { + const response = await updateOrganization(entityData, entityData.id?.toString() as string); + const { data } = response as AxiosResponse; + if (data?.statusCode === apiStatusCodes.API_STATUS_SUCCESS) { + if (props?.onEditSuccess) { + props?.onEditSuccess(); + } + props.setOpenModal(false); + } else { + setErrMsg(data?.message as string); + } + } else { + const response = await updateEcosystem(entityData); + const { data } = response as AxiosResponse; + + if (data?.statusCode === apiStatusCodes.API_STATUS_SUCCESS) { + if (props?.onEditSuccess) { + props?.onEditSuccess(); + } + props.setOpenModal(false); + } else { + setErrMsg(data?.message as string); + } + } + } catch (error) { + console.error("An error occurred:", error); + setLoading(false); + } + }; + return ( + { + setLogoImage({ + logoFile: "", + imagePreviewUrl: "", + fileName: '' + }); + setInitialEntityData({ + name: "", + description: "", + }); + props.setOpenModal(false); + }}> + Edit {props.isOrganization ? "Organization" : "Ecosystem"} + + { + setErrMsg(null); + }} + /> + + ) => { + await submitUpdateEntity(values); + }} + > + {(formikHandlers): JSX.Element => ( +
+
+
+ {isImageEmpty ? ( + + ) : ( + {`${props.isOrganization + )} +
+

+ {props.isOrganization ? "Organization Logo" : "Ecosystem Logo"} +

+
+ JPG, JPEG and PNG. Max size of 1MB +
+
+
+ +
+
+
+
+
+
+
+
+ + {formikHandlers?.errors?.name && formikHandlers?.touched?.name && ( + {formikHandlers?.errors?.name} + )} +
+
+
+
+ + {formikHandlers?.errors?.description && formikHandlers?.touched?.description && ( + {formikHandlers?.errors?.description} + )} +
+ +
+ )} +
+
+
+ ); +}; + +export default EditPopupModal; diff --git a/src/components/organization/interfaces/index.ts b/src/components/organization/interfaces/index.ts index 3dd8dd250..e685c5560 100644 --- a/src/components/organization/interfaces/index.ts +++ b/src/components/organization/interfaces/index.ts @@ -7,6 +7,7 @@ export interface UserOrgRole { } export interface Organisation { + logoFile: string id: number createDateTime: string createdBy: number @@ -20,6 +21,7 @@ export interface Organisation { userOrgRoles: UserOrgRole[] org_agents: OrgAgent[] publicProfile: boolean + } export interface OrgRole { diff --git a/src/components/organization/invitations/SendInvitationModal.tsx b/src/components/organization/invitations/SendInvitationModal.tsx index e4a34ea44..1d63f0c82 100644 --- a/src/components/organization/invitations/SendInvitationModal.tsx +++ b/src/components/organization/invitations/SendInvitationModal.tsx @@ -153,7 +153,7 @@ const SendInvitationModal = (props: { { setErrMsg(null); }} @@ -236,7 +236,7 @@ const SendInvitationModal = (props: {
    {invitations.map((invitation) => ( -
  • +