Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Create organization flow: 172 #3

Merged
merged 6 commits into from
Jul 7, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 4 additions & 4 deletions src/app/SideBar.astro
Original file line number Diff line number Diff line change
Expand Up @@ -128,28 +128,28 @@ import { url } from '../lib/data.js';
<ul id="dropdown-layouts" class="hidden py-2 space-y-2">
<li>
<a
href={url('orgs')}
href={url('organizations')}
class="flex items-center p-2 text-base text-gray-900 transition duration-75 rounded-lg pl-11 group hover:bg-gray-100 dark:text-gray-200 dark:hover:bg-gray-700"
>Organizations</a
>
</li>
<li>
<a
href={url('orgs/users')}
href={url('organizations/users')}
class="flex items-center p-2 text-base text-gray-900 transition duration-75 rounded-lg pl-11 group hover:bg-gray-100 dark:text-gray-200 dark:hover:bg-gray-700"
>Users</a
>
</li>
<li>
<a
href={url('orgs/schemas')}
href={url('organizations/schemas')}
class="flex items-center p-2 text-base text-gray-900 transition duration-75 rounded-lg pl-11 group hover:bg-gray-100 dark:text-gray-200 dark:hover:bg-gray-700"
>Schemas</a
>
</li>
<li>
<a
href={url('orgs/credentials')}
href={url('organizations/credentials')}
class="flex items-center p-2 text-base text-gray-900 transition duration-75 rounded-lg pl-11 group hover:bg-gray-100 dark:text-gray-200 dark:hover:bg-gray-700"
>Credentials</a
>
Expand Down
13 changes: 8 additions & 5 deletions src/components/Authentication/SignInUser.tsx
Original file line number Diff line number Diff line change
@@ -1,3 +1,6 @@
import * as yup from 'yup';

import { Button, Checkbox, Label, TextInput } from 'flowbite-react';
import {
Field,
Form,
Expand All @@ -6,11 +9,10 @@ import {
FormikProps,
FormikValues,
} from 'formik';
import { Button, Checkbox, Label, TextInput } from 'flowbite-react';
import * as yup from 'yup';
import { Alert } from 'flowbite-react';
import { useEffect, useState } from 'react';
import { UserSignInData, loginUser, passwordEncryption } from '../../api/Auth';
import { useEffect, useState } from 'react';

import { Alert } from 'flowbite-react';
import type { AxiosResponse } from 'axios';
import { apiStatusCodes } from '../../config/CommonConstant';

Expand Down Expand Up @@ -45,7 +47,8 @@ const SignInUser = () => {
setLoading(false)

if(data?.statusCode === apiStatusCodes.API_STATUS_SUCCESS){

localStorage.setItem('access_token', data?.data?.access_token);
window.location.href = '/dashboard'
}else{
setFailur(loginRsp as string)
}
Expand Down
43 changes: 43 additions & 0 deletions src/components/Dashboard.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
'use client';

import { Button } from 'flowbite-react';
import CreateOrgFormModal from "./organization/CreateOrgFormModal.js";
import { useState } from 'react';

export default function Dashboard() {

const [openModal, setOpenModal] = useState<boolean>(false);
const props = { openModal, setOpenModal };

const createOrganizationModel = () => {
props.setOpenModal(true)
}

return (
<div className="px-4 pt-6">
<div>
<div
className="p-4 bg-white border border-gray-200 rounded-lg shadow-sm 2xl:col-span-2 dark:border-gray-700 sm:p-6 dark:bg-gray-800"
>
<div className="flex items-center justify-center mb-4">
<Button
onClick={createOrganizationModel}
className='text-base font-medium text-center text-white bg-primary-700 rounded-lg hover:bg-primary-800 focus:ring-4 focus:ring-primary-300 sm:w-auto dark:bg-primary-600 dark:hover:bg-primary-700 dark:focus:ring-primary-800"'
>
Create Organization
</Button>
</div>

{
props.openModal &&
<CreateOrgFormModal
openModal={props.openModal}
setOpenModal= {props.setOpenModal} />
}
</div>
</div>
</div>
)
}


276 changes: 276 additions & 0 deletions src/components/organization/CreateOrgFormModal.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,276 @@
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 { useRef, useState } from "react";

import type { AxiosResponse } from 'axios';
import { asset } from '../../lib/data.js';
import { createOrganization } from "../../services/organization";

interface Values {
name: string;
description: string;
}

interface ILogoImage {
logoFile: string | File
imagePreviewUrl: string | ArrayBuffer | null | File
}


const CreateOrgFormModal = (props: { openModal: boolean; setOpenModal: (flag: boolean) => void }) => {


const [logoImage, setLogoImage] = useState<ILogoImage>({
logoFile: "",
imagePreviewUrl: ""
})

const [loading, setLoading] = useState<boolean>(false)

const [isImageEmpty, setIsImageEmpty] = useState(true)
const [initialOrgData, setOrgData] = useState({
name: '',
description: '',
})
const [erroMsg, setErrMsg] = useState<string | null>(null)

const [imgError, setImgError] = useState('')


const ProcessImg = (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
})
}
}
}
}
}

const isEmpty = (object: any): boolean => {
// eslint-disable-next-line @typescript-eslint/no-unused-vars, guard-for-in
for (const property in object) {
setIsImageEmpty(false)
return false
}
setIsImageEmpty(true)
return true
}


const handleImageChange = (event: any): void => {
setImgError('')
const reader = new FileReader()
const file = event?.target?.files
console.log(file);

const fieSize = 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 (fieSize <= imageSizeAccepted) {
reader.onloadend = (): void => {
ProcessImg(event)
isEmpty(reader.result)
}
reader.readAsDataURL(file[0])
event.preventDefault()
} else {
setImgError("Please check image size")
}
} else {
setImgError("Invalid image type")
}
}

return (
<Modal show={props.openModal === true} onClose={() => {
setLogoImage({
logoFile: "",
imagePreviewUrl: ""
})
setOrgData(initialOrgData)
props.setOpenModal(false)
}
}>
<Modal.Header>Create Organization</Modal.Header>
<Modal.Body>
<Formik
initialValues={initialOrgData}
validationSchema={
yup.object().shape({
name: yup
.string()
.min(2, 'Organization name must be at least 2 characters')
.max(50, 'Organization name must be at most 50 characters')
.required('Organization name is required')
.trim(),
description: yup
.string()
.min(2, 'Organization name must be at least 2 characters')
.max(255, 'Organization name must be at most 255 characters')
.required('Description is required')
})}
validateOnBlur
validateOnChange
enableReinitialize
onSubmit={async (
values: Values,
{ resetForm }: FormikHelpers<Values>
) => {

setLoading(true)

const orgData = {
name: values.name,
description: values.description,
logo: logoImage?.imagePreviewUrl as string || "",
website: ""
}

const resCreateOrg = await createOrganization(orgData)

const { data } = resCreateOrg as AxiosResponse
setLoading(false)

if (data?.statusCode === apiStatusCodes.API_STATUS_CREATED) {
alert(data?.message)
props.setOpenModal(false)

} else {
setErrMsg(resCreateOrg as string)
}

}}
>
{(formikHandlers): JSX.Element => (

<Form className="space-y-6" onSubmit={
formikHandlers.handleSubmit
}>
<div
className="mb-4 bg-white border border-gray-200 rounded-lg shadow-sm 2xl:col-span-2 dark:border-gray-700 sm:p-6 dark:bg-gray-800"
>
<div
className="items-center sm:flex xl:block 2xl:flex sm:space-x-4 xl:space-x-0 2xl:space-x-4"
>
{
typeof (logoImage.logoFile) === "string" ?
<Avatar
size="lg"
/> :
<img
className="mb-4 rounded-lg w-28 h-28 sm:mb-0 xl:mb-4 2xl:mb-0"
src={typeof (logoImage.logoFile) === "string" ? asset('images/users/bonnie-green-2x.png') : URL.createObjectURL(logoImage.logoFile)}
alt="Jese picture"
/>
}

<div>
<h3 className="mb-1 text-xl font-bold text-gray-900 dark:text-white">
Organization Logo
</h3>
<div className="mb-4 text-sm text-gray-500 dark:text-gray-400">
JPG, GIF or PNG. Max size of 1M
</div>
<div className="flex items-center space-x-4">


<div className="camera-btn">
<input type="file" accept="image/*" name="file" id="exampleFile1" title=""
onChange={(event): void => handleImageChange(event)} />
</div>

</div>
</div>
</div>
</div>
<div>
<div
className="block mb-2 text-sm font-medium text-gray-900 dark:text-white"
>
<Label
htmlFor="name"
value="Name"
/>
</div>
<Field
id="name"
name="name"
value={formikHandlers.values.name}
required
className="bg-gray-50 border border-gray-300 text-gray-900 sm:text-sm rounded-lg focus:ring-primary-500 focus:border-primary-500 block w-full p-2.5 dark:bg-gray-700 dark:border-gray-600 dark:placeholder-gray-400 dark:text-white dark:focus:ring-primary-500 dark:focus:border-primary-500"
placeholder="OrgTech" />

</div>
<div>
<div
className="block mb-2 text-sm font-medium text-gray-900 dark:text-white"
>
<Label
htmlFor="description"
value="Description"
/>
</div>

<Field
id="description"
name="description"
value={formikHandlers.values.description}
as='textarea'
required
className="bg-gray-50 border border-gray-300 text-gray-900 sm:text-sm rounded-lg focus:ring-primary-500 focus:border-primary-500 block w-full p-2.5 dark:bg-gray-700 dark:border-gray-600 dark:placeholder-gray-400 dark:text-white dark:focus:ring-primary-500 dark:focus:border-primary-500"
placeholder="Description of your organization" />

</div>

<Button type="submit"
isProcessing={loading}

className='float-right text-base font-medium text-center text-white bg-primary-700 rounded-lg hover:bg-primary-800 focus:ring-4 focus:ring-primary-300 sm:w-auto dark:bg-primary-600 dark:hover:bg-primary-700 dark:focus:ring-primary-800'
>
Create
</Button>
</Form>
)}

</Formik>
</Modal.Body>

</Modal>
)
}

export default CreateOrgFormModal;
Loading