-
Notifications
You must be signed in to change notification settings - Fork 0
Feature/ssot refactor #150
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
Conversation
|
| content: string; | ||
| isEditable: boolean; | ||
| onChange: (value: string) => void; | ||
| content?: ViewableRecipe; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The onChange and isEditable props were unused, so I removed them.
The recipe content doesn't come down as string anymore but the selector can return undefined, so made it optional with the truthy check on ln21-22 retained.
| // TODO get data and metadata separately | ||
| const getRecipeDataFromFirebase = async (recipeId: string): Promise<RecipeData> => { | ||
| const defaultRecipeData = await getFirebaseRecipe(recipeId); | ||
| return { | ||
| recipeId, | ||
| defaultRecipeData, | ||
| edits: {} | ||
| } | ||
| } | ||
|
|
||
| const getRecipesFromFirebase = async (): Promise<Dictionary<RecipeManifest>> => { | ||
| const docs = await getAllDocsFromCollection(FIRESTORE_COLLECTIONS.PACKING_INPUTS); | ||
| const inputsDict: Dictionary<RecipeManifest> = {}; | ||
| for (const doc of docs) { | ||
| const displayName = doc[FIRESTORE_FIELDS.NAME]; | ||
| const config = doc[FIRESTORE_FIELDS.CONFIG]; | ||
| const recipe = doc[FIRESTORE_FIELDS.RECIPE]; | ||
| const editableFields = await getEditableFieldsList( | ||
| doc[FIRESTORE_FIELDS.EDITABLE_FIELDS] || [] | ||
| ); | ||
| const result = doc[FIRESTORE_FIELDS.RESULT_PATH] || ""; | ||
| if (config && recipe) { | ||
| inputsDict[recipe] = { | ||
| [FIRESTORE_FIELDS.NAME]: displayName, | ||
| [FIRESTORE_FIELDS.CONFIG]: config, | ||
| [FIRESTORE_FIELDS.RECIPE]: recipe, | ||
| [FIRESTORE_FIELDS.EDITABLE_FIELDS]: editableFields, | ||
| const recipeId = doc[FIRESTORE_FIELDS.RECIPE]; | ||
|
|
||
| if (displayName && config && recipeId) { | ||
| const editableFields = await getEditableFieldsList(doc[FIRESTORE_FIELDS.EDITABLE_FIELDS] || []); | ||
| const recipe = await getFirebaseRecipe(recipeId); | ||
| const result = doc[FIRESTORE_FIELDS.RESULT_PATH] || ""; | ||
| inputsDict[recipeId] = { | ||
| recipeId: recipeId, | ||
| configId: config, | ||
| displayName, | ||
| editableFields: editableFields ?? [], | ||
| defaultRecipeData: recipe, | ||
| edits: {}, |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
See #151
| // TODO further refine difference between data and metadata, decide how | ||
| // to store them, and when to load them. | ||
|
|
||
| export interface RecipeData { | ||
| recipeId: string; | ||
| defaultRecipeData: ViewableRecipe; | ||
| edits: Record<string, string | number>; | ||
| } | ||
|
|
||
| export interface RecipeManifest { | ||
| recipeId: string; | ||
| configId: string; | ||
| displayName: string; | ||
| editableFields: EditableField[]; | ||
| defaultResultPath?: string; | ||
| editable_fields?: EditableField[]; | ||
| }; | ||
| defaultRecipeData: ViewableRecipe; | ||
| edits: Record<string, string | number>; | ||
| } | ||
|
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
See #151
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Pull Request Overview
This PR refactors the recipe data management to use a single source of truth (SSOT) pattern by replacing recipe strings with a RecipeData structure that separates default recipe data from user edits.
Key changes:
- Replaced string-based recipe storage with
RecipeDataobjects containingdefaultRecipeDataandedits - Consolidated
updateRecipeStringandupdateRecipeObjinto a singleeditRecipefunction - Updated type definitions to use
RecipeManifestandRecipeDatainterfaces with clearer property names
Reviewed Changes
Copilot reviewed 9 out of 9 changed files in this pull request and generated 2 comments.
Show a summary per file
| File | Description |
|---|---|
| src/types/index.ts | Defined new RecipeData and updated RecipeManifest interfaces with clearer property names |
| src/utils/firebase.ts | Renamed functions and updated to return new data structures with recipe objects instead of strings |
| src/state/utils.ts | Added utility function to build current recipe from defaults and edits |
| src/state/store.ts | Refactored state management to use RecipeData structure and simplified edit operations |
| src/components/PackingInput/index.tsx | Updated to use recipe objects instead of strings |
| src/components/JSONViewer/index.tsx | Modified to accept objects directly instead of parsing strings |
| src/components/InputSwitch/index.tsx | Updated to use new editRecipe function and recipe structure |
| src/components/GradientInput/index.tsx | Simplified to use new editRecipe function |
| src/components/Dropdown/index.tsx | Updated property references to match new interface names |
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
src/state/store.ts
Outdated
|
|
||
| export const useCurrentRecipeObject = () => { | ||
| const recipe = useCurrentRecipeData(); | ||
| return recipe ? buildCurrentRecipeObject(recipe) : undefined; |
This comment was marked as resolved.
This comment was marked as resolved.
Sorry, something went wrong.
| }, | ||
|
|
||
|
|
||
| getCurrentValue: (path) => { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
could have a clearer name of what the value is
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
getRecipeValueAtPath?
getValueForRecipeField?
| return undefined; | ||
| }, | ||
|
|
||
| getOriginalValue: (path) => { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
also could have a clearer name
…nto feature/ssot-refactor
@ascibisz Megan and I were workshopping some changes over the past 24 hours and these errors should be resolved momentarily! |
src/state/utils.ts
Outdated
| import { set as lodashSet } from "lodash-es"; | ||
| import { RecipeData, ViewableRecipe } from "../types"; | ||
|
|
||
| export const buildRecipeObject = (recipe: RecipeData): ViewableRecipe => { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
could you add a comment describing what this function does?
ascibisz
left a comment
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Left some very minor comments, but it looks good and works well for me!
I refrained from commenting on your edits to types/index.ts and firebase.ts because you cover all of the improvements I would like with #151 !
| if (typeof editedValue === "string" || typeof editedValue === "number") { | ||
| return editedValue; | ||
| } | ||
| return undefined; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I can't think of a time where we'd actually hit this return undefined; case, so perhaps it doesn't really matter, but I feel like we would want to try to continue on to trying to get the default value rather than just returning undefined?
src/state/utils.ts
Outdated
| * @returns A deep-cloned ViewableRecipe with all edits applied. | ||
| * Typically represents the "current" version of the selected recipe in state. | ||
| */ | ||
| export const buildRecipeObject = (recipe: RecipeData): ViewableRecipe => { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
sorta nitty: since this is a utility, I wouldn't be sending in such a typed object.
my suggestion:
mergeChangesIntoNestedObject(originalObject, edits) that returns the same type as the originalObject
or mergeNestedObjects
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think this also makes it clearer what the function is doing without having to read comments
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
or: applyChangesToNestedObject
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Ok changed it.
…nto feature/ssot-refactor


Problem
Closes #97 Closes #101
Abandoned some pieces of the original SSOT branch, and deferred some.
Part two of this is #151
Solution
Recipe strings ===>
RecipeDatawhich is the default recipe and an edits object.updateRecipeStringandupdateRecipOb====> editRecipeThis is a streamlined version of old branch which got out of hand.
Did not try to refactor App.tsx, did not try to consolidate recipe data and input options, did not try to get rid of callback pattern in
startPackingwhich is a reason to avoid testing. I think we should streamline that before putting effort into writing tests for it.RecipeData and RecipeManifest are redundant, the plan is to use something like
RecipeDataandRecipeMetadata. Getting that `00% finished is out of scope. Here the focus is on getting rid of recipe strings.