Created
February 3, 2020 16:06
-
-
Save SvitlanaShepitsena/13df1e1284c5d7cd493575d02235c6d5 to your computer and use it in GitHub Desktop.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
import * as faker from 'faker' | |
import gql from 'graphql-tag' | |
import _ from 'lodash' | |
import { SingletonRouter, useRouter } from 'next/router' | |
import pluralize from 'pluralize' | |
import React, { useEffect, useRef, useState } from 'react' | |
import { useForm } from 'react-hook-form' | |
import Select from 'react-select' | |
import { | |
Form, | |
Grid, | |
Message, | |
Segment, | |
TextArea, | |
TextAreaProps, | |
} from 'semantic-ui-react' | |
import styled from 'styled-components' | |
// @ts-ignore | |
import urlSlug from 'url-slug' | |
import * as yup from 'yup' | |
import { | |
ProductsQuery, | |
ProductUpdateInput, | |
useBrandsQuery, | |
useCategoriesByDepProdTypesQuery, | |
useCreateProductMutation, | |
useDepartmentsQuery, | |
useMaterialsQuery, | |
useProductTypesQuery, | |
useUpdateProductMutation, | |
} from '../../../generated/apollo-components' | |
import convertProduct from '../../../lib/utils/convertProduct' | |
import withUser from '../../../lib/withUser' | |
import CancelSubmitButtons from '../../common/buttons/CancelSubmitButtons' | |
require('dotenv').config() | |
const ProductSchema = yup.object().shape({ | |
name: yup.string().required(), | |
description: yup.string().required(), | |
key: yup.string().required(), | |
}) | |
type FormData = { | |
brand: { id: string; value: string } | |
productType: { id: string; value: string } | |
material: { id: string; value: string } | |
department: { id: string; value: string } | |
category: { id: string; value: string } | |
upc: string | |
style: string | |
key: string | |
tags: string | |
features: string | |
name: string | |
description: string | |
} | |
type Props = { | |
id: String | |
randomProdType?: string | |
randomDepartment?: string | |
randomCategory?: string | |
product?: any | |
onCancel?: () => void | |
router: SingletonRouter | |
} | |
type SelectProps = { | |
label: string | |
error?: any | |
loading?: boolean | |
pluralKey: string | |
preSelectedValue?: string | |
name: string | |
setValue?: any | |
stateUpdater?: any | |
data: any | |
register?: any | |
handleChange?: any | |
} | |
function getDatum(data: any, pluralKey: string) { | |
let dataLoc | |
try { | |
dataLoc = data[`${pluralKey}`] | |
} catch (error) { | |
let singleKey = pluralize.singular(pluralKey) | |
dataLoc = data[`${singleKey}`] | |
} | |
const uniqueData = _.uniqBy(dataLoc, (el: any) => el.value) | |
return uniqueData | |
} | |
const SvSelect = (props: SelectProps) => { | |
const { | |
label, | |
error, | |
loading, | |
data, | |
preSelectedValue, | |
pluralKey, | |
name, | |
register, | |
setValue, | |
handleChange, | |
stateUpdater, | |
} = props | |
const [value, setReactSelect] = useState({ | |
selectedOption: [], | |
}) | |
useEffect(() => { | |
if (!data) { | |
return | |
} | |
let newData = getDatum(data, pluralKey).map((e: any) => ({ | |
label: e.value, | |
value: e.id, | |
})) | |
// @ts-ignore | |
if (typeof register === 'function') { | |
register({ name }) | |
} | |
const selectedOption = defaultSelected(newData, preSelectedValue) | |
if ( | |
typeof stateUpdater === 'function' && | |
selectedOption && | |
selectedOption.label | |
) { | |
stateUpdater(selectedOption.label) | |
} | |
if (typeof setValue === 'function') { | |
setValue(name, selectedOption) | |
} | |
let stType = typeof stateUpdater | |
setReactSelect({ selectedOption }) | |
}, [data]) | |
const handleMultiChange = (selectedOption: any) => { | |
// @ts-ignore | |
if (typeof handleChange === 'function') { | |
handleChange(selectedOption) | |
} | |
if (typeof stateUpdater === 'function') { | |
stateUpdater(selectedOption.label) | |
} | |
setReactSelect({ selectedOption }) | |
setValue(name, selectedOption) | |
} | |
return ( | |
<div> | |
<StyledLabel>{label}</StyledLabel> | |
{error ? ( | |
<div>Error Loading {label}</div> | |
) : loading ? ( | |
<div>Loading {label}</div> | |
) : ( | |
data && | |
getDatum(data, pluralKey) && ( | |
<Select | |
placeholder={`Select a ${label}`} | |
options={getDatum(data, pluralKey).map((e: any) => ({ | |
label: e.value, | |
value: e.id, | |
}))} | |
value={value.selectedOption} | |
onChange={handleMultiChange} | |
name={`${name}`} | |
/> | |
) | |
)} | |
</div> | |
) | |
} | |
function defaultSelected(newData: any, preSelected: string | undefined) { | |
if (!preSelected || preSelected.length == 0) { | |
return newData[0] | |
} | |
for (let i = 0; i < newData.length; i++) { | |
let selectedElement = newData[i] | |
if ( | |
selectedElement.value && | |
(selectedElement.value === preSelected || | |
selectedElement.label === preSelected) | |
) { | |
return selectedElement | |
} | |
} | |
} | |
function SvInput(props: { | |
defaultValue?: string | |
label: string | |
errors: any | |
handleChange: any | |
placeholder: string | |
name: string | |
}) { | |
const { | |
placeholder, | |
name, | |
errors, | |
label, | |
handleChange, | |
defaultValue, | |
} = props | |
let errorMessage = errors[name] | |
return ( | |
<> | |
<StyledLabel>{label}</StyledLabel> | |
<Form.Input | |
defaultValue={defaultValue ? defaultValue : ''} | |
placeholder={placeholder} | |
name={name} | |
onChange={handleChange} | |
/> | |
{errors && errorMessage && ( | |
<Message negative>{errorMessage.message}</Message> | |
)} | |
</> | |
) | |
} | |
function SvTextArea(props: { | |
defaultValue: string | |
label: string | |
errors: any | |
handleChange: any | |
placeholder: string | |
name: string | |
}) { | |
const { | |
placeholder, | |
name, | |
errors, | |
label, | |
handleChange, | |
defaultValue, | |
} = props | |
const [val, setVal] = useState(defaultValue) | |
const onChange = ( | |
e: React.FormEvent<HTMLTextAreaElement>, | |
data: TextAreaProps | |
) => { | |
handleChange(e, data) | |
// @ts-ignore | |
setVal(data.value) | |
} | |
let errorMessage = errors[name] | |
return ( | |
<> | |
<StyledLabel>{label}</StyledLabel> | |
<TextArea | |
rows={5} | |
value={val} | |
placeholder={placeholder} | |
name={name} | |
onChange={( | |
e: React.FormEvent<HTMLTextAreaElement>, | |
data: TextAreaProps | |
) => { | |
onChange(e, data) | |
}} | |
/> | |
{errors && errorMessage && ( | |
<Message negative>{errorMessage.message}</Message> | |
)} | |
</> | |
) | |
} | |
export const PRODUCTS: ProductsQuery = gql` | |
query($orderBy: ProductOrderByInput, $where: String) { | |
products(orderBy: $orderBy, where: $where) { | |
id | |
name | |
slug | |
description | |
key | |
upc | |
style | |
tags | |
features | |
createdAt | |
material { | |
id | |
value | |
} | |
brand { | |
id | |
value | |
} | |
productType { | |
id | |
value | |
department { | |
id | |
value | |
category { | |
id | |
value | |
} | |
} | |
} | |
} | |
} | |
` | |
const ProductForm = (props: Props) => { | |
const [productType, setProductType] = useState(props.randomProdType) | |
const [department, setDepartment] = useState(props.randomDepartment) | |
const [category, setCategory] = useState(props.randomCategory) | |
let refProductType = useRef(null) | |
let refDepartment = useRef(null) | |
let refCategory = useRef(null) | |
const router = useRouter() | |
const [createProduct, { data, error, loading }] = useCreateProductMutation() | |
const [ | |
updateProduct, | |
{ data: dataUpdate, error: errorUpdate, loading: loadingUpdate }, | |
] = useUpdateProductMutation() | |
const [values, setReactSelect] = useState({ | |
selectedOption: [], | |
}) | |
const handleSelectChange = (selectedOption: any) => { | |
setValue('reactSelect', selectedOption) | |
setReactSelect({ selectedOption }) | |
} | |
const mute = React.useMemo(() => { | |
return [department, category, productType] | |
}, [productType, department, category]) | |
let { data: dataCategory } = useCategoriesByDepProdTypesQuery({ | |
variables: { | |
department, | |
productType, | |
}, | |
}) | |
let { data: dataDep } = useDepartmentsQuery({ | |
// @ts-ignore | |
variables: { category }, | |
}) | |
let { data: dataProductType } = useProductTypesQuery() | |
if (dataProductType) { | |
// @ts-ignore | |
refProductType = dataProductType | |
} | |
// @ts-ignore | |
refCategory = dataCategory | |
// @ts-ignore | |
refDepartment = dataDep | |
const { | |
data: dataBrand, | |
error: errBrand, | |
loading: loadBrand, | |
} = useBrandsQuery() | |
const { | |
data: dataMaterial, | |
error: errMaterial, | |
loading: loadMaterial, | |
} = useMaterialsQuery() | |
const { product } = props | |
function generateProduct(product: any) { | |
if (product && product.id) { | |
return { ...product } | |
} | |
const defaultValues = { | |
productType: { | |
department: { | |
category: {}, | |
}, | |
}, | |
department: { value: '', id: '' }, | |
category: { value: '', id: '' }, | |
name: faker.commerce.productName(), | |
description: faker.lorem.sentences(12), | |
upc: faker.random.uuid(), | |
key: faker.random.uuid().substr(0, 4), | |
style: faker.commerce.productAdjective(), | |
features: 'Product Features', | |
tags: `${faker.hacker.noun()}, ${faker.hacker.noun()}, ${faker.hacker.noun()}`, | |
} | |
return defaultValues | |
} | |
let defaultValues | |
defaultValues = generateProduct(product) | |
const { register, setValue, handleSubmit, errors } = useForm<FormData>({ | |
validationSchema: ProductSchema, | |
// @ts-ignore | |
defaultValues: defaultValues, | |
}) | |
const onSubmit = async (dataOld: FormData) => { | |
// @ts-ignore | |
let newProduct: ProductUpdateInput = { ...dataOld } | |
try { | |
newProduct = convertProduct(newProduct) | |
console.log( | |
'newProduct' + | |
JSON.stringify(newProduct, null, 2) + | |
'ProductForm + 431' | |
) | |
if (newProduct) { | |
// @ts-ignore | |
newProduct.brand = { | |
// @ts-ignore | |
connect: { id: newProduct.brand.id }, | |
} | |
// @ts-ignore | |
newProduct.department = { | |
// @ts-ignore | |
connect: { id: newProduct.department.id }, | |
} | |
// @ts-ignore | |
newProduct.category = { | |
// @ts-ignore | |
connect: { id: newProduct.category.id }, | |
} | |
// @ts-ignore | |
newProduct.productType = { | |
// @ts-ignore | |
connect: { id: newProduct.productType.id }, | |
} | |
// @ts-ignore | |
newProduct.material = { | |
// @ts-ignore | |
connect: { id: newProduct.material.id }, | |
} | |
newProduct.slug = urlSlug(newProduct.name) | |
} | |
let res | |
try { | |
if (product && product.id) { | |
// Update | |
res = await updateProduct({ | |
variables: { | |
where: { id: product.id }, | |
data: { ...newProduct }, | |
}, | |
}) | |
} else { | |
// Create | |
// @ts-ignore | |
res = await createProduct({ | |
// @ts-ignore | |
variables: { ...newProduct }, | |
update: (cache, { data }) => { | |
// @ts-ignore | |
const { products } = cache.readQuery({ | |
// @ts-ignore | |
query: PRODUCTS, | |
variables: { | |
orderBy: { createdAt: 'desc' }, | |
where: { | |
OR: [ | |
{ name: {} }, | |
{ key: {} }, | |
{ upc: {} }, | |
], | |
}, | |
}, | |
}) | |
const newArray: any = [ | |
data?.createProduct, | |
...products, | |
] | |
cache.writeQuery({ | |
// @ts-ignore | |
query: PRODUCTS, | |
variables: { | |
orderBy: { createdAt: 'desc' }, | |
where: { | |
OR: [ | |
{ name: {} }, | |
{ key: {} }, | |
{ upc: {} }, | |
], | |
}, | |
}, | |
data: { | |
products: [...newArray], | |
}, | |
}) | |
}, | |
}) | |
} | |
await router.push('/ecom/products') | |
} catch (error) { | |
console.log('error', error) | |
} | |
} catch (error) { | |
console.log( | |
'error' + JSON.stringify(error, null, 2) + 'ProductForm + 433' | |
) | |
} | |
} | |
const handleChange = (e: any, data: any) => { | |
setValue(e.target.name, e.target.value) | |
} | |
useEffect(() => { | |
register({ name: 'name' }) | |
register({ name: 'brand' }) | |
register({ name: 'upc' }) | |
register({ name: 'department' }) | |
register({ name: 'category' }) | |
register({ name: 'style' }) | |
register({ name: 'features' }) | |
register({ name: 'material' }) | |
register({ name: 'productType' }) | |
register({ name: 'key' }) | |
register({ name: 'tags' }) | |
register({ name: 'description' }) | |
}, []) | |
return ( | |
<> | |
<StyledHeader as="h1">Create a Product</StyledHeader> | |
<StyledForm onSubmit={handleSubmit(onSubmit)}> | |
<SegmentStyled raised> | |
<Grid stackable columns={3}> | |
<Grid.Column> | |
<SvInput | |
label="* Name" | |
// @ts-ignore | |
defaultValue={defaultValues['name']} | |
name="name" | |
placeholder="Enter a Product Name" | |
handleChange={handleChange} | |
errors={errors} | |
/> | |
</Grid.Column> | |
<Grid.Column> | |
<SvSelect | |
preSelectedValue={product?.brand.id} | |
register={register} | |
setValue={setValue} | |
error={errBrand} | |
loading={loadBrand} | |
data={dataBrand} | |
label="Brand" | |
name="brand" | |
pluralKey="brands" | |
/> | |
</Grid.Column> | |
<Grid.Column> | |
<SvInput | |
label="Unique Product Number" | |
name="upc" | |
// @ts-ignore | |
defaultValue={defaultValues['upc']} | |
placeholder="Enter a UPC number" | |
handleChange={handleChange} | |
errors={errors} | |
/> | |
</Grid.Column> | |
</Grid> | |
</SegmentStyled> | |
<SegmentStyled raised> | |
<Grid stackable columns={3}> | |
<Grid.Column> | |
<SvSelect | |
preSelectedValue={ | |
product | |
? product.productType.id | |
: productType | |
} | |
register={register} | |
setValue={setValue} | |
stateUpdater={setProductType} | |
data={refProductType} | |
label="Product Type (Shoes, Clothes, Jewelry)" | |
pluralKey="productTypes" | |
name="productType" | |
handleChange={handleSelectChange} | |
/> | |
</Grid.Column> | |
<Grid.Column> | |
<SvSelect | |
preSelectedValue={ | |
product | |
? product.productType.department.id | |
: department | |
} | |
stateUpdater={setDepartment} | |
data={refDepartment} | |
register={register} | |
setValue={setValue} | |
label="Department (Baby, Mans Shoes etc)" | |
pluralKey="departments" | |
name="department" | |
handleChange={handleSelectChange} | |
/> | |
</Grid.Column> | |
<Grid.Column> | |
<SvSelect | |
preSelectedValue={ | |
product?.productType.department.category.id | |
} | |
stateUpdater={setCategory} | |
data={refCategory} | |
register={register} | |
setValue={setValue} | |
label="Category (Dress, Coat, Boots etc)" | |
pluralKey="categories" | |
name="category" | |
handleChange={handleSelectChange} | |
/> | |
</Grid.Column> | |
</Grid> | |
</SegmentStyled> | |
<SegmentStyled raised> | |
<Grid stackable columns={3}> | |
<Grid.Column> | |
<SvInput | |
label="Style (Cocktail dress, classic)" | |
name="style" | |
// @ts-ignore | |
defaultValue={defaultValues['style']} | |
placeholder="Enter a product style" | |
handleChange={handleChange} | |
errors={errors} | |
/> | |
</Grid.Column> | |
<Grid.Column> | |
<SvInput | |
label="Features (Hooded, 3/4 sleeves)" | |
name="features" | |
// @ts-ignore | |
defaultValue={defaultValues['features']} | |
placeholder="Enter main product features" | |
handleChange={handleChange} | |
errors={errors} | |
/> | |
</Grid.Column> | |
<Grid.Column> | |
<SvSelect | |
preSelectedValue={product?.material.id} | |
error={errMaterial} | |
data={dataMaterial} | |
loading={loadMaterial} | |
register={register} | |
setValue={setValue} | |
label="Material" | |
pluralKey="materials" | |
name="material" | |
handleChange={handleSelectChange} | |
/> | |
</Grid.Column> | |
</Grid> | |
<br /> | |
<Grid stackable columns={1}> | |
<Grid.Column> | |
<SvTextArea | |
label="* Description" | |
// @ts-ignore | |
defaultValue={defaultValues['description']} | |
name="description" | |
placeholder="Enter a product description" | |
handleChange={handleChange} | |
errors={errors} | |
/> | |
</Grid.Column> | |
<Grid.Column> | |
<SvInput | |
label="Tags" | |
name="tags" | |
// @ts-ignore | |
defaultValue={defaultValues['tags']} | |
placeholder="Enter a product feature" | |
handleChange={handleChange} | |
errors={errors} | |
/> | |
</Grid.Column> | |
<Grid.Column> | |
<SvInput | |
label="Key" | |
name="key" | |
// @ts-ignore | |
defaultValue={defaultValues['key']} | |
placeholder="Enter a product key" | |
handleChange={handleChange} | |
errors={errors} | |
/> | |
</Grid.Column> | |
</Grid> | |
</SegmentStyled> | |
<br /> | |
<CancelSubmitButtons onClick={props.onCancel} /> | |
</StyledForm> | |
</> | |
) | |
} | |
export default withUser(ProductForm) | |
const StyledForm = styled(Form)` | |
&&& { | |
font-size: 16px; | |
} | |
` | |
const StyledLabel = styled.label` | |
&&& { | |
font-size: 16px; | |
display: block; | |
margin-bottom: 5px !important; | |
} | |
` | |
const StyledHeader = styled.label` | |
&&& { | |
color: #595959; | |
} | |
` | |
const FixedNav = styled.div` | |
&&& { | |
position: fixed !important; | |
@media only screen and (min-width: 815px) { | |
// background: rgba(0, 0, 0, 0.87); | |
border-bottom-right-radius: 4px; | |
padding: 7px; | |
top: 0px; | |
left: 600px; | |
z-index: 102 !important; | |
max-width: 220px; | |
} | |
@media (max-width: 814px) { | |
top: 10px; | |
right: 10px; | |
} | |
} | |
` | |
const SegmentStyled = styled(Segment)` | |
&&& { | |
@media only screen and (min-width: 815px) { | |
margin-bottom: 28px; | |
} | |
@media (max-width: 814px) { | |
padding-left: 0 !important; | |
padding-right: 0 !important; | |
} | |
} | |
` |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment