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

[WORKING] Implement Edit recipe functionality (fix cache/ui) #147

Merged
merged 1 commit into from
Feb 15, 2021
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
31 changes: 24 additions & 7 deletions src/components/Recipe/Edit/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ import { useMutation } from 'urql'
import { useReducer } from 'react'
import { useHistory, Link } from 'react-router-dom'
import { useAlert, alertType } from 'context/AlertContext'
import { UPDATE_RECIPES } from 'queries'
import { UPDATE_RECIPE_WITH_STAGES } from 'queries'
import InputRow from 'components/InputRow'
import Dropdown from 'components/DropDown'
import TextArea from 'components/TextArea'
Expand All @@ -24,9 +24,17 @@ const recipeType = {
instructions: { isNullable: false },
}

/**
* 'stages'
* - omit 'id' so that update mutation can create new id's
* - omit '__typename' due to metadata
*/
const setInitState = ({ stages, ...recipe }) => ({
form: recipe,
stages,
stages:
stages.length > 1
? stages.map(({ id, __typename, ...stage }) => stage)
: null,
isVisible: false,
hasIssues: false,
})
Expand Down Expand Up @@ -65,7 +73,12 @@ function Edit({ recipe, id }) {
setInitState(recipe)
)

const [, updateRecipe] = useMutation(UPDATE_RECIPES)
/**
* $id: Int!
* $recipe: recipe_set_input
* $stages: [stage_insert_input!]!
*/
const [, updateRecipe] = useMutation(UPDATE_RECIPE_WITH_STAGES)

const onChangeGenerator = (attr) => ({ target }) => {
const { value, type } = target
Expand Down Expand Up @@ -93,10 +106,13 @@ function Edit({ recipe, id }) {
} else {
// use 'value' instead of 'form' because all empty strings are converted to nulls (SAFER)
// removed 'barista_id' because I grab it from JWT 'x-hasura-barista-id' in Hasura
if (stages) {
value.stages = { data: stages }
}
const { error } = await updateRecipe({ object: value })

const { error } = await updateRecipe({
id,
recipe: value,
stages: stages ? stages.map((s) => ({ ...s, recipe_id: id })) : [], // change 'null' to empty array to add no new stages; old stages get deleted regardless
})

if (error) {
addAlert({
type: alertType.ERROR,
Expand Down Expand Up @@ -274,6 +290,7 @@ function Edit({ recipe, id }) {
<div className='mt-4 sm:mt-0 sm:col-span-2 space-y-2'>
{isVisible ? (
<StageForm
isEditPage
editStages={stages}
save={save}
onCancel={closeForm}
Expand Down
7 changes: 4 additions & 3 deletions src/components/StageForm/Row.js
Original file line number Diff line number Diff line change
Expand Up @@ -12,17 +12,18 @@ const Row = ({
}) => {
const onChange = ({ target }) => {
const { name, type, value } = target
let newValue = type === 'number' && value !== '' ? parseInt(value) : value

setStage(
stage.action === 'serve' && name === 'start'
? {
...stage,
start: parseInt(value),
end: parseInt(value),
start: newValue,
end: newValue,
}
: {
...stage,
[name]: type === 'number' && value !== '' ? parseInt(value) : value,
[name]: newValue,
}
)
}
Expand Down
51 changes: 36 additions & 15 deletions src/components/StageForm/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ import { Plus } from 'components/Icon'
import { useState } from 'react'
import Row from './Row'

function StageForm({ editStages, save, onCancel, onDelete }) {
function StageForm({ isEditPage, editStages, save, onCancel, onDelete }) {
const [isValid, setIsValid] = useState(null)
const [stages, setStages] = useState(
editStages !== null
Expand Down Expand Up @@ -116,20 +116,41 @@ function StageForm({ editStages, save, onCancel, onDelete }) {
</div>
)}
<div className='flex justify-start mt-2'>
<button
onClick={submitStages}
type='button'
className='inline-flex items-center px-4 py-2 border border-transparent text-blue-700 text-sm font-medium rounded-md hover:bg-blue-100 focus:outline-none focus:ring-2 focus:ring-offset-2 focus:ring-blue-500'
>
Save
</button>
<button
type='button'
onClick={stages.length > 1 ? onCancel : onDelete}
className='inline-flex items-center px-4 py-2 text-sm font-medium rounded-md text-gray-700 hover:bg-gray-200 focus:outline-none focus:ring-2 focus:ring-offset-2 focus:ring-blue-500'
>
{stages.length > 1 ? 'Cancel' : 'Delete'}
</button>
{isEditPage ? (
<>
<button
onClick={stages.length > 1 ? submitStages : onDelete}
type='button'
className='inline-flex items-center px-4 py-2 border border-transparent text-blue-700 text-sm font-medium rounded-md hover:bg-blue-100 focus:outline-none focus:ring-2 focus:ring-offset-2 focus:ring-blue-500'
>
{stages.length > 1 ? 'Save' : 'Delete'}
</button>
<button
type='button'
onClick={onCancel}
className='inline-flex items-center px-4 py-2 text-sm font-medium rounded-md text-gray-700 hover:bg-gray-200 focus:outline-none focus:ring-2 focus:ring-offset-2 focus:ring-blue-500'
>
Cancel
</button>
</>
) : (
<>
<button
onClick={submitStages}
type='button'
className='inline-flex items-center px-4 py-2 border border-transparent text-blue-700 text-sm font-medium rounded-md hover:bg-blue-100 focus:outline-none focus:ring-2 focus:ring-offset-2 focus:ring-blue-500'
>
Save
</button>
<button
type='button'
onClick={stages.length > 1 ? onCancel : onDelete}
className='inline-flex items-center px-4 py-2 text-sm font-medium rounded-md text-gray-700 hover:bg-gray-200 focus:outline-none focus:ring-2 focus:ring-offset-2 focus:ring-blue-500'
>
{stages.length > 1 ? 'Cancel' : 'Delete'}
</button>
</>
)}
</div>
</div>
)
Expand Down
23 changes: 18 additions & 5 deletions src/helper/cache/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ import {
GET_ALL_BREW_LOGS,
GET_ALL_RECIPES,
GET_SINGLE_RECIPE_REVIEWS_AVG_REVIEW,
fragment,
} from 'queries'

export const updates = {
Expand Down Expand Up @@ -43,14 +44,26 @@ export const updates = {
},
(data) => {
const updateIndex = data.brew_logs.findIndex((b) => b.id === args.id)
return [
...data.brew_logs.slice(0, updateIndex),
result.update_brew_logs_by_pk,
...data.brew_logs.slice(updateIndex + 1),
]
return {
...data,
brew_logs: [
...data.brew_logs.slice(0, updateIndex),
result.update_brew_logs_by_pk,
...data.brew_logs.slice(updateIndex + 1),
],
}
}
)
},
update_recipes_by_pk: (result, args, cache, info) => {
/**
* `writeFragment` is the right function for adding updates since they can be
* made even if a user hasn't requested a list of entities already
* ex. edit recipe 34 even though you haven't cached 'get all recipes'
* (causes null error or bad cache with `updateQuery`)
*/
cache.writeFragment(fragment.recipeInfo, result.update_recipes_by_pk)
},
insert_recipes_one: (result, args, cache, info) => {
cache.updateQuery(
{
Expand Down
34 changes: 20 additions & 14 deletions src/pages/Recipe/Recipe/RecipeCard.js

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

109 changes: 94 additions & 15 deletions src/queries/Recipe.js
Original file line number Diff line number Diff line change
Expand Up @@ -41,7 +41,7 @@ const INSERT_RECIPES_ONE = gql`
}
`
const GET_ALL_RECIPES = gql`
query {
query GetAllRecipes {
recipes(order_by: { id: desc }) {
id
barista_id
Expand All @@ -58,6 +58,13 @@ const GET_ALL_RECIPES = gql`
name
instructions
bean_name_free
stages {
id
action
end
start
weight
}
barista {
id
display_name
Expand Down Expand Up @@ -264,28 +271,39 @@ const DELETE_RECIPE_REVIEW = gql`
}
`

const deleteStages = gql`
fragment DeleteStages on mutation_root {
const UPDATE_RECIPE_WITH_STAGES = gql`
mutation UpdateRecipeWithStages(
$id: Int!
$recipe: recipes_set_input
$stages: [stage_insert_input!]!
) {
delete_stage(where: { recipe_id: { _eq: $id } }) {
affected_rows
returning {
id
}
}
}
`

// delete by pk stage
// update by pk stage
// insert stage

const updateRecipeByPK = gql`
fragment UpdateRecipeByPK on mutation_root {
update_recipes_by_pk(pk_columns: { id: $id }, _set: $object) {
insert_stage(objects: $stages) {
returning {
id
action
start
end
weight
}
}
update_recipes_by_pk(pk_columns: { id: $id }, _set: $recipe) {
id
barista_id
brew_type
bean_weight
bean_grind
water_amount
bean_id
water_temp
is_private
date_added
device
about
name
instructions
Expand All @@ -297,13 +315,73 @@ const updateRecipeByPK = gql`
start
weight
}
barista {
id
display_name
avatar
}
bean {
id
img
name
}
recipe_reviews_aggregate {
aggregate {
avg {
rating
}
}
}
}
}
`

const recipeInfo = gql`
fragment RecipeInfo on recipes {
id
barista_id
brew_type
bean_weight
bean_grind
water_amount
bean_id
water_temp
is_private
date_added
device
about
name
instructions
bean_name_free
stages {
id
action
end
start
weight
}
barista {
id
display_name
avatar
}
bean {
id
img
name
}
recipe_reviews_aggregate {
aggregate {
avg {
rating
}
}
}
}
`

const fragment = {
deleteStages,
updateRecipeByPK,
recipeInfo,
}

export {
Expand All @@ -316,6 +394,7 @@ export {
GET_RECIPE_BY_ID,
UPDATE_RECIPES,
UPDATE_RECIPE_REVIEW,
UPDATE_RECIPE_WITH_STAGES,
DELETE_RECIPES,
INSERT_RECIPE_REVIEW_ONE,
DELETE_RECIPE_REVIEW,
Expand Down
Loading