Skip to content

Instantly share code, notes, and snippets.

@SvitlanaShepitsena
Created February 3, 2020 16:06
Show Gist options
  • Save SvitlanaShepitsena/13df1e1284c5d7cd493575d02235c6d5 to your computer and use it in GitHub Desktop.
Save SvitlanaShepitsena/13df1e1284c5d7cd493575d02235c6d5 to your computer and use it in GitHub Desktop.
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