Skip to content

Instantly share code, notes, and snippets.

@haris-aqeel
Created August 24, 2023 10:23
Show Gist options
  • Save haris-aqeel/692396c649dd2dcfe6bfab9c2b59e7d0 to your computer and use it in GitHub Desktop.
Save haris-aqeel/692396c649dd2dcfe6bfab9c2b59e7d0 to your computer and use it in GitHub Desktop.
import { Button, Dialog, DialogContent, Divider, Grid, MenuItem, Stack, TextField, Typography } from '@mui/material';
import PropTypes from 'prop-types';
import { useForm } from 'react-hook-form';
import * as Yup from 'yup';
import { yupResolver } from '@hookform/resolvers/yup';
import { createFilterOptions } from "@mui/material/Autocomplete";
import { collection, doc, setDoc } from 'firebase/firestore';
import { useSnackbar } from 'notistack';
import { useEffect, useMemo, useRef, useState } from 'react';
import { LoadingButton } from '@mui/lab';
import { httpsCallable } from 'firebase/functions';
import { DEPENDENCY_TAGS_OPTION, INTEGRATION_TAGS_OPTION } from '../../../assets/data';
import { CLOUD_FUNCTIONS, DB } from '../../../auth/FirebaseApp';
import ConfirmDialog from '../../../components/confirm-dialog/ConfirmDialog';
import FormProvider, {
RHFAutocomplete,
RHFMultiLineText,
RHFSelect
} from '../../../components/hook-form';
import Iconify from '../../../components/iconify/Iconify';
import { setCompanyOperation, setCompanyProducts, setProductOperation } from '../../../redux/slices/user';
import { dispatch, useSelector } from '../../../redux/store';
IntegrationWizard.propTypes = {
fromPopup: PropTypes.bool,
source: PropTypes.string,
target: PropTypes.string,
handleClose: PropTypes.func,
open: PropTypes.bool,
}
export default function IntegrationWizard({ open = false, fromPopup = false, source, target, handleClose = () => { } }) {
const { enqueueSnackbar } = useSnackbar();
const filter = createFilterOptions();
const { companyProducts, selectedCompany, adminProductsList, companies } = useSelector(state => state.user);
const [productList, setProductList] = useState([]);
const [completeProductsList, setCompleteProductsList] = useState([]);
const emailRef = useRef(null);
const companyRef = useRef(null);
const product2Ref = useRef(null);
const [isNew, setIsNew] = useState(false);
const defaultValues = useMemo(
() => ({
typeSelection: 'integration',
integrationA: null,
integrationB: null,
tags: [],
description: '',
}),
[]
);
const NewIntegrationSchema = Yup.object().shape({
typeSelection: Yup.string().required('Required'),
integrationA: Yup.object().required('Required'),
integrationB: Yup.object().required('Required'),
tags: Yup.array().required('Required'),
description: Yup.string().required('Required'),
});
const editStatus = false
const methods = useForm({
resolver: yupResolver(NewIntegrationSchema),
defaultValues,
shouldFocusError: true,
});
const {
reset,
handleSubmit,
watch,
setValue,
formState: { isSubmitting }
} = methods;
const values = watch();
useEffect(() => {
reset();
if (companyProducts) {
const tempProductList = [];
companyProducts.forEach((product) => {
if (product) {
tempProductList.push({ label: product?.name, value: product?.id, company: product?.companyName });
if (product?.id === source)
setValue("integrationA", { label: product?.name, value: product?.id });
}
});
setProductList(tempProductList);
}
}, [companyProducts, reset, selectedCompany, setValue, source]);
useEffect(() => {
if (adminProductsList) {
const tempAllProductList = [];
adminProductsList.forEach((product) => {
if (product && product?.id !== source) {
tempAllProductList.push({ label: product?.title, value: product?.id, company: product?.companyName });
if (product?.id === target)
setValue("integrationB", { label: product?.title, value: product?.id });
}
});
setCompleteProductsList(tempAllProductList);
}
}, [adminProductsList, companies, setValue, source, target]);
useEffect(() => {
if (values?.integrationB?.isNew && !isNew) {
setIsNew(true);
} else {
setIsNew(false);
}
// eslint-disable-next-line react-hooks/exhaustive-deps
}, [values?.integrationB])
const onSubmit = async (data) => {
try {
// reset();
const fieldName = data.typeSelection === "integration" ? "integrations" : "dependencies";
const tagField = data.typeSelection === "integration" ? "integrationTags" : "dependencyTags";
if (data.integrationB.isNew) {
const returnedCompanyId = await dispatch(setCompanyOperation(
"create",
{
companyName: data.companyName,
richTextDescription: "generated",
images: [],
},
null,
null,
false
));
const returnedProductId = await dispatch(setProductOperation(
"dataentry-create",
{
companyId: returnedCompanyId,
companyRef: doc(DB, 'company', returnedCompanyId),
name: data.productName,
images: [],
mitreCategory: [],
targetMarket: [],
tags: [],
category: [],
description: "",
richTextDescription: ""
},
null,
false,
));
data.integrationB = {
label: data.productName,
value: returnedProductId,
}
}
const productRef = doc(collection(DB, 'products'), data.integrationA.value);
const productRefOfConnectedProduct = doc(DB, 'products', data.integrationB.value);
const cloudFunctionData = {
productId: productRef.id,
productIdOfConnectedProduct: productRefOfConnectedProduct.id,
dialogValue: {
cid: data.integrationB.value,
title: data.integrationB.label,
[tagField]: data.tags,
description: data.description,
},
activeProduct: {
name: data.integrationA.label,
},
tagField,
fieldName,
};
const newIntegration = httpsCallable(CLOUD_FUNCTIONS, 'newIntegration');
await newIntegration(JSON.stringify(cloudFunctionData));
reset();
enqueueSnackbar('Create success!');
dispatch(setCompanyProducts());
} catch (e) {
console.error(e);
enqueueSnackbar('Create failed!', { variant: 'error' });
} finally {
handleClose();
};
}
const handleInvite = () => {
// put the product name in to a new product in firebase
const companiesRef = doc(collection(DB, 'company'));
const newProduct = {
name: product2Ref.current.value,
company: companiesRef
}
// push that to firebase using setDoc
const productRef = doc(collection(DB, 'products'));
setDoc(productRef, newProduct);
const newCompany = {
name: companyRef.current.value,
companyProducts: [
{
name: product2Ref.current.value,
productRef
}
]
}
setDoc(companiesRef, newCompany);
// const inviteData = JSON.stringify({
// email,
// user,
// selectedCompany
// })
// const handleUserInvitations = httpsCallable(CLOUD_FUNCTIONS, 'handleUserInvitations');
}
return (
<Dialog
maxWidth="md"
open={open || false}
onClose={handleClose}
aria-labelledby="alert-dialog-title"
aria-describedby="alert-dialog-description"
>
<DialogContent sx={{ p: 3 }}>
{/* <Card sx={{ p: 3 }} > */}
<Stack divider={<Divider orientation="vertical" flexItem />}>
<FormProvider methods={methods} onSubmit={handleSubmit(onSubmit)}>
<Typography variant="h4" gutterBottom>
Create New Relation
</Typography>
<Stack spacing={5}>
<RHFSelect
name="typeSelection"
size="Large"
helperText={
<Typography variant="caption" color="text.secondary">
Select the type of connection
</Typography>
}
>
<MenuItem value="integration">
Integration
</MenuItem>
<MenuItem value="dependency">
Dependency
</MenuItem>
</RHFSelect>
</Stack>
<Stack spacing={2}>
<Typography variant="body1" mt={3} mb={3}>
Select the products you want to show how they integrate or depend on each other. Every connection is directional, as in A depends on B or A integrates with B. You can add multiple connections for each product.
</Typography>
</Stack>
<Grid container spacing={3} >
<Grid item xs={12} md={6}>
<Stack direction='column' spacing={2} size={3} justifyContent="center">
<RHFAutocomplete
disabled
name="integrationA"
label="Product 1"
options={productList.map((option) => option)}
ChipProps={{ size: 'Large' }}
isOptionEqualToValue={(option, value) => option.value === value.value
}
getOptionLabel={(option) => option.label || ""}
/>
{!isNew && <RHFAutocomplete
disabled={Boolean(target)}
name="integrationB"
label="Product 2"
options={completeProductsList.map((option) => option)}
ChipProps={{ size: 'Large' }}
isOptionEqualToValue={(option, value) =>
option.value === value.value
}
filterOptions={(options, params) => {
const filtered = filter(options, params);
if (params.inputValue !== '') {
filtered.push({
inputValue: params.inputValue,
company: "",
label: `Invite a new Partner`,
isNew: true
});
}
return filtered.map((option) => {
if (option.company) {
return {
...option,
displayTitle: `${option.company} - ${option.label}`,
};
}
return { ...option, displayTitle: option.label };
});
}}
renderOption={(props, option) => <li {...props} style={{ backgroundColor: values.integrationA?.company === option.company ? 'lightgreen' : 'default' }}>{option.displayTitle || ""}</li>}
/>}
</Stack>
</Grid>
<Grid item xs={12} md={6}>
<Stack direction='column' spacing={2} size={3}>
<RHFAutocomplete
name="tags"
label="Tags"
multiple freeSolo
options={values.typeSelection === "integration" ? INTEGRATION_TAGS_OPTION : DEPENDENCY_TAGS_OPTION}
ChipProps={{ size: 'small' }}
/>
<RHFMultiLineText
multiline
label="Description of Integration"
id="description"
name="description"
/>
<Stack direction='row-reverse' spacing={2} size={3}>
<Button
variant="contained"
color="error"
disabled={isSubmitting}
onClick={() => {
reset();
handleClose();
}}
>
Cancel
</Button>
<LoadingButton
type="submit"
variant="contained"
size="large"
startIcon={<Iconify icon="carbon:add-filled" />}
loadingPosition="start"
loading={isSubmitting}
>
{editStatus ? "Update" : "Add"}
</LoadingButton>
</Stack>
</Stack>
</Grid>
</Grid >
</FormProvider >
</Stack>
{/* </Card > */}
</DialogContent>
<ConfirmDialog
open={isNew}
onClose={() => setIsNew(false)}
title="Invite New Product"
content={
<Stack direction="column" spacing={2} mt={2}>
<TextField
placeholder='Search by Email'
fullWidth
type="email"
inputValue={emailRef}
/>
<TextField
name="companyName"
label="Company Name"
inputValue={companyRef}
/>
<TextField
name="productName"
label="Product 2"
inputValue={product2Ref}
/>
</Stack>
}
action={
<LoadingButton
loading={false}
variant="contained"
color="primary"
// !validateEmail(emailInvite)
disabled={false}
startIcon={<Iconify icon="mdi:invite" />}
loadingPosition="start"
onClick={() => {
handleInvite();
}}
>
Invite
</LoadingButton>
}
/>
</Dialog>
)
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment