@powerhousedao/academy 2.5.0-dev.3 → 2.5.0-dev.30
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +187 -0
- package/docs/academy/01-GetStarted/00-ExploreDemoPackage.md +19 -15
- package/docs/academy/01-GetStarted/01-CreateNewPowerhouseProject.md +38 -39
- package/docs/academy/01-GetStarted/02-DefineToDoListDocumentModel.md +22 -7
- package/docs/academy/01-GetStarted/03-ImplementOperationReducers.md +9 -4
- package/docs/academy/01-GetStarted/04-BuildToDoListEditor.md +146 -422
- package/docs/academy/01-GetStarted/_04-BuildToDoListEditor +360 -0
- package/docs/academy/01-GetStarted/home.mdx +16 -24
- package/docs/academy/01-GetStarted/styles.module.css +31 -0
- package/docs/academy/02-MasteryTrack/01-BuilderEnvironment/02-StandardDocumentModelWorkflow.md +7 -3
- package/docs/academy/02-MasteryTrack/01-BuilderEnvironment/03-BuilderTools.md +1 -1
- package/docs/academy/02-MasteryTrack/02-DocumentModelCreation/01-WhatIsADocumentModel.md +33 -16
- package/docs/academy/02-MasteryTrack/02-DocumentModelCreation/02-SpecifyTheStateSchema.md +73 -0
- package/docs/academy/02-MasteryTrack/02-DocumentModelCreation/03-SpecifyDocumentOperations.md +59 -4
- package/docs/academy/02-MasteryTrack/02-DocumentModelCreation/04-UseTheDocumentModelGenerator.md +32 -12
- package/docs/academy/02-MasteryTrack/02-DocumentModelCreation/05-ImplementDocumentReducers.md +103 -38
- package/docs/academy/02-MasteryTrack/02-DocumentModelCreation/06-ImplementDocumentModelTests.md +90 -228
- package/docs/academy/02-MasteryTrack/02-DocumentModelCreation/07-ExampleToDoListRepository.md +41 -1
- package/docs/academy/02-MasteryTrack/03-BuildingUserExperiences/01-BuildingDocumentEditors.md +342 -67
- package/docs/academy/02-MasteryTrack/03-BuildingUserExperiences/02-ConfiguringDrives.md +5 -3
- package/docs/academy/02-MasteryTrack/05-Launch/02-PublishYourProject.md +70 -5
- package/docs/academy/02-MasteryTrack/05-Launch/03-SetupEnvironment.md +162 -73
- package/docs/academy/02-MasteryTrack/05-Launch/{03-RunOnACloudServer.md → _03-RunOnACloudServer} +8 -5
- package/docs/academy/04-APIReferences/00-PowerhouseCLI.md +7 -40
- package/docs/academy/05-Architecture/00-PowerhouseArchitecture.md +3 -0
- package/docs/academy/05-Architecture/images/PowerhouseArchitecture.png +0 -0
- package/docs/academy/06-ComponentLibrary/00-DocumentEngineering.md +86 -29
- package/docs/academy/06-ComponentLibrary/02-CreateCustomScalars.md +404 -0
- package/docs/academy/06-ComponentLibrary/03-IntegrateIntoAReactComponent.md +124 -0
- package/docs/academy/07-Cookbook.md +209 -4
- package/docs/academy/08-Glossary.md +20 -18
- package/docs/academy/09-AIResources +131 -0
- package/package.json +1 -1
- package/sidebars.ts +3 -45
- package/docs/academy/06-ComponentLibrary/02-BuildingWithScalars.md +0 -54
- package/docs/academy/06-ComponentLibrary/03-Scalar-Components/01-phid-field.mdx +0 -72
- package/docs/academy/06-ComponentLibrary/03-Scalar-Components/02-input-field.mdx +0 -0
- package/docs/academy/06-ComponentLibrary/04-Complex-Components/01-sidebar.mdx +0 -36
- package/docs/academy/06-ComponentLibrary/05-Layout-Components/01-test-toupdate.mdx +0 -61
- package/docs/academy/06-ComponentLibrary/06-Fragments/01-test-toupdate.mdx +0 -61
- /package/docs/academy/02-MasteryTrack/05-Launch/{02-IntroductionToPackages.md → 01-IntroductionToPackages.md} +0 -0
- /package/docs/academy/02-MasteryTrack/05-Launch/{00-IntegrateInAFront-End → _00-IntegrateInAFront-End} +0 -0
- /package/docs/academy/02-MasteryTrack/05-Launch/{01-IntroducingFusion → _01-IntroducingFusion} +0 -0
- /package/docs/academy/02-MasteryTrack/05-Launch/{04-GraphQLNamespacing → _04-GraphQLNamespacing} +0 -0
- /package/docs/academy/02-MasteryTrack/05-Launch/{05-LaunchYourBackend.md → _05-LaunchYourBackend} +0 -0
- /package/docs/academy/02-MasteryTrack/05-Launch/{06-LaunchYourFrontend.md → _06-LaunchYourFrontend} +0 -0
- /package/docs/academy/04-APIReferences/{01-ReactHooks.md → 01-ReactHooks} +0 -0
- /package/docs/academy/04-APIReferences/{02-ReactorAPI.md → 02-ReactorAPI} +0 -0
- /package/docs/academy/04-APIReferences/{03-Configuration.md → 03-Configuration} +0 -0
|
@@ -21,6 +21,8 @@ GraphQL has a set of built-in **scalar types**:
|
|
|
21
21
|
* `Boolean`: `true` or `false`.
|
|
22
22
|
* `ID`: A unique identifier, often used as a key for a field. It is serialized in the same way as a String; however, it is not intended to be human-readable.
|
|
23
23
|
|
|
24
|
+
In addition to these standard types, the Powerhouse Document-Engineering system introduces custom scalars that are linked to reusable front-end components. These scalars are tailored for the web3 ecosystem and will be explored in the Component Library section of the documentation.
|
|
25
|
+
|
|
24
26
|
### Lists and Non-Null
|
|
25
27
|
You can modify types using lists and non-null indicators:
|
|
26
28
|
* **Lists**: To indicate that a field will return a list of a certain type, you wrap the type in square brackets, e.g., `[ToDoItem!]!`. This means the field `items` in `ToDoListState` will be a list of `ToDoItem` objects.
|
|
@@ -29,6 +31,7 @@ You can modify types using lists and non-null indicators:
|
|
|
29
31
|
## Example: ToDoList State Schema
|
|
30
32
|
|
|
31
33
|
Let's revisit the `ToDoList` example from the "Define the ToDoList document specification" tutorial.
|
|
34
|
+
Only this time, we'll also add a 'Stats' type. Since we want to keep track of the number of completed To-Do's.
|
|
32
35
|
|
|
33
36
|
```graphql
|
|
34
37
|
# The state of our ToDoList
|
|
@@ -42,6 +45,12 @@ type ToDoItem {
|
|
|
42
45
|
text: String!
|
|
43
46
|
checked: Boolean!
|
|
44
47
|
}
|
|
48
|
+
# The statistics on our to-do's
|
|
49
|
+
type ToDoListStats {
|
|
50
|
+
total: Int!
|
|
51
|
+
checked: Int!
|
|
52
|
+
unchecked: Int!
|
|
53
|
+
}
|
|
45
54
|
```
|
|
46
55
|
|
|
47
56
|
### Breakdown:
|
|
@@ -57,6 +66,11 @@ type ToDoItem {
|
|
|
57
66
|
* `text: String!`: The textual description of the to-do item. It cannot be null, ensuring every to-do has a description.
|
|
58
67
|
* `checked: Boolean!`: Indicates whether the to-do item is completed. It defaults to a boolean value (true or false) and cannot be null.
|
|
59
68
|
|
|
69
|
+
* **`ToDoListStats` type**: This type holds the summary statistics for the to-do list.
|
|
70
|
+
* `total: Int!`: The total count of all to-do items. This field must be an integer and cannot be null.
|
|
71
|
+
* `checked: Int!`: The number of to-do items that are marked as completed. This must be an integer and cannot be null.
|
|
72
|
+
* `unchecked: Int!`: The number of to-do items that are still pending. This also must be an integer and cannot be null.
|
|
73
|
+
|
|
60
74
|
## Best Practices for Designing Your State Schema
|
|
61
75
|
|
|
62
76
|
1. **Start Simple, Iterate**: Begin with the core entities and properties. You can always expand and refine your schema as your understanding of the document's requirements grows.
|
|
@@ -70,3 +84,62 @@ type ToDoItem {
|
|
|
70
84
|
6. **Root State Type**: It's a common pattern to have a single root type for your document state (e.g., `ToDoListState`). This provides a clear entry point for accessing all document data.
|
|
71
85
|
|
|
72
86
|
By carefully defining your state schema, you lay a solid foundation for your Powerhouse document model, making it robust, maintainable, and easy to work with. The schema dictates not only how data is stored but also how it can be queried and mutated through operations, which will be covered in the next section.
|
|
87
|
+
|
|
88
|
+
## Practical Implementation: Defining the State Schema in Connect
|
|
89
|
+
|
|
90
|
+
Now that you understand the concepts behind the state schema, let's put it into practice. This section will guide you through creating a document model specification for the advanced ToDoList example discussed above.
|
|
91
|
+
|
|
92
|
+
<details>
|
|
93
|
+
<summary>Tutorial: The State Schema Specification</summary>
|
|
94
|
+
|
|
95
|
+
### Prerequisites
|
|
96
|
+
|
|
97
|
+
- You have a Powerhouse project set up. If not, please follow the [Create a new Powerhouse Project](../../GetStarted/CreateNewPowerhouseProject) tutorial.
|
|
98
|
+
- Connect Studio is running. If not, navigate to your project directory in the terminal and run `ph connect`.
|
|
99
|
+
|
|
100
|
+
### Steps
|
|
101
|
+
|
|
102
|
+
1. **Create a New Document Model**:
|
|
103
|
+
- With Connect Studio open in your browser, navigate into your local drive.
|
|
104
|
+
- At the bottom of the page in the 'New Document' section, click the `DocumentModel` button to create a new document model specification.
|
|
105
|
+
|
|
106
|
+
2. **Define Document Metadata**:
|
|
107
|
+
- **Name**: Give your document model a descriptive name, for example, `ToDoList`. **Pay close attention to capitalization, as it influences our code.**
|
|
108
|
+
- **Document Type**: In the 'Document Type' field, enter a unique identifier for this document type, for instance, `powerhouse/todolist`.
|
|
109
|
+
|
|
110
|
+
3. **Specify the State Schema**:
|
|
111
|
+
- In the code editor provided, you'll see a template for a GraphQL schema.
|
|
112
|
+
- Replace the entire content of the editor with the advanced `ToDoList` schema we've designed in this chapter:
|
|
113
|
+
|
|
114
|
+
```graphql
|
|
115
|
+
# The state of our ToDoList
|
|
116
|
+
type ToDoListState {
|
|
117
|
+
items: [ToDoItem!]!
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
# A single to-do item
|
|
121
|
+
type ToDoItem {
|
|
122
|
+
id: ID!
|
|
123
|
+
text: String!
|
|
124
|
+
checked: Boolean!
|
|
125
|
+
}
|
|
126
|
+
# The statistics on our to-do's
|
|
127
|
+
type ToDoListStats {
|
|
128
|
+
total: Int!
|
|
129
|
+
checked: Int!
|
|
130
|
+
unchecked: Int!
|
|
131
|
+
}
|
|
132
|
+
```
|
|
133
|
+
|
|
134
|
+
4. **Sync Schema and View Initial State**:
|
|
135
|
+
- After pasting the schema, click the **'Sync with schema'** button.
|
|
136
|
+
- This action processes your schema and generates an initial JSON state for your document model based on the `ToDoListState` type. You can view this initial state, which helps you verify that your schema is structured correctly.
|
|
137
|
+
|
|
138
|
+
For now, you can ignore the "Modules & Operations" section. We will define and implement the operations that modify this state in the upcoming sections of this Mastery Track.
|
|
139
|
+
|
|
140
|
+
By completing these steps, you have successfully specified the data structure for the advanced ToDoList document model. The next step is to define the operations that will allow users to interact with and change this state.
|
|
141
|
+
|
|
142
|
+
</details>
|
|
143
|
+
|
|
144
|
+
For a complete, working example, you can always refer to the [Example ToDoList Repository](/academy/MasteryTrack/DocumentModelCreation/ExampleToDoListRepository) which contains the full implementation of the concepts discussed in this Mastery Track.
|
|
145
|
+
|
package/docs/academy/02-MasteryTrack/02-DocumentModelCreation/03-SpecifyDocumentOperations.md
CHANGED
|
@@ -16,10 +16,9 @@ Each operation acts as a command that, when applied, transitions the document fr
|
|
|
16
16
|
|
|
17
17
|
## Connecting Operations to the Schema
|
|
18
18
|
|
|
19
|
-
In the "Define ToDoList Document Model" guide, we used GraphQL `input` types to define the structure of the data required for each operation. Let's revisit that:
|
|
19
|
+
In the "Define ToDoList Document Model" chapter in the "Get Started" guide, we used GraphQL `input` types to define the structure of the data required for each operation. Let's revisit that:
|
|
20
20
|
|
|
21
21
|
```graphql
|
|
22
|
-
# From 02-DefineToDoListDocumentModel.md
|
|
23
22
|
# Defines a GraphQL input type for adding a new to-do item
|
|
24
23
|
input AddTodoItemInput {
|
|
25
24
|
id: ID!
|
|
@@ -96,7 +95,6 @@ The generated code from `ph generate` (as seen in `03-ImplementOperationReducers
|
|
|
96
95
|
For example, the `ToDoListToDoListOperations` type generated by Powerhouse will expect methods corresponding to `addTodoItemOperation`, `updateTodoItemOperation`, and `deleteTodoItemOperation`.
|
|
97
96
|
|
|
98
97
|
```typescript
|
|
99
|
-
// From 03-ImplementOperationReducers.md
|
|
100
98
|
import { ToDoListToDoListOperations } from '../../gen/to-do-list/operations.js';
|
|
101
99
|
|
|
102
100
|
export const reducer: ToDoListToDoListOperations = {
|
|
@@ -112,8 +110,65 @@ export const reducer: ToDoListToDoListOperations = {
|
|
|
112
110
|
};
|
|
113
111
|
```
|
|
114
112
|
|
|
113
|
+
## Practical Implementation: Defining Operations in Connect
|
|
114
|
+
|
|
115
|
+
Now that you understand the theory, let's walk through the practical steps of defining these operations for our `ToDoList` document model within the Powerhouse Connect application.
|
|
116
|
+
|
|
117
|
+
<details>
|
|
118
|
+
<summary>Tutorial: Specifying ToDoList Operations</summary>
|
|
119
|
+
|
|
120
|
+
Assuming you have already defined the state schema for the `ToDoList` as covered in the previous section, follow these steps to add the operations:
|
|
121
|
+
|
|
122
|
+
1. **Create a Module for Operations:**
|
|
123
|
+
Below the schema editor in Connect, find the input field labeled `Add module`. Modules help organize your operations.
|
|
124
|
+
* Type `to_do_list` into the field and press Enter.
|
|
125
|
+
|
|
126
|
+
2. **Add the `ADD_TODO_ITEM` Operation:**
|
|
127
|
+
A new field, `Add operation`, will appear under your new module.
|
|
128
|
+
* Type `ADD_TODO_ITEM` into this field and press Enter.
|
|
129
|
+
* An editor will appear for the operation's input type. You need to define the data required for this operation. Paste the following GraphQL `input` definition into the editor:
|
|
130
|
+
|
|
131
|
+
```graphql
|
|
132
|
+
# Defines a GraphQL input type for adding a new to-do item
|
|
133
|
+
input AddTodoItemInput {
|
|
134
|
+
id: ID!
|
|
135
|
+
text: String!
|
|
136
|
+
}
|
|
137
|
+
```
|
|
138
|
+
|
|
139
|
+
3. **Add the `UPDATE_TODO_ITEM` Operation:**
|
|
140
|
+
* In the `Add operation` field again, type `UPDATE_TODO_ITEM` and press Enter.
|
|
141
|
+
* Paste the corresponding `input` definition into its editor:
|
|
142
|
+
|
|
143
|
+
```graphql
|
|
144
|
+
# Defines a GraphQL input type for updating a to-do item
|
|
145
|
+
input UpdateTodoItemInput {
|
|
146
|
+
id: ID!
|
|
147
|
+
text: String
|
|
148
|
+
checked: Boolean
|
|
149
|
+
}
|
|
150
|
+
```
|
|
151
|
+
|
|
152
|
+
4. **Add the `DELETE_TODO_ITEM` Operation:**
|
|
153
|
+
* Finally, type `DELETE_TODO_ITEM` in the `Add operation` field and press Enter.
|
|
154
|
+
* Paste its `input` definition:
|
|
155
|
+
|
|
156
|
+
```graphql
|
|
157
|
+
# Defines a GraphQL input type for deleting a to-do item
|
|
158
|
+
input DeleteTodoItemInput {
|
|
159
|
+
id: ID!
|
|
160
|
+
}
|
|
161
|
+
```
|
|
162
|
+
|
|
163
|
+
5. **Review and Export:**
|
|
164
|
+
After adding all three operations, your document model specification in Connect is complete for now. You can see how each operation (`ADD_TODO_ITEM`, etc.) is now explicitly linked to an input type that defines its payload.
|
|
165
|
+
|
|
166
|
+
The next step in a real project would be to click the `Export` button to save this specification file. In the next chapter, we will see how this exported file is used to generate code for our reducers.
|
|
167
|
+
|
|
168
|
+
</details>
|
|
169
|
+
|
|
115
170
|
## Conclusion
|
|
116
171
|
|
|
117
|
-
Specifying document operations is a foundational step in building robust and predictable document models in Powerhouse. By clearly defining the "what" (the operation and its input) before implementing the "how" (the reducer logic)
|
|
172
|
+
Specifying document operations is a foundational step in building robust and predictable document models in Powerhouse. By clearly defining the **"what" (the operation and its input)** before implementing the **"how" (the reducer logic)**, you create a clear contract for state transitions. This approach enhances type safety, testability, and the overall maintainability of your document model.
|
|
118
173
|
|
|
119
174
|
In the next section, we will dive deeper into the implementation of the reducer functions for these specified operations.
|
package/docs/academy/02-MasteryTrack/02-DocumentModelCreation/04-UseTheDocumentModelGenerator.md
CHANGED
|
@@ -90,22 +90,42 @@ Leveraging the `ph generate` command offers numerous advantages:
|
|
|
90
90
|
5. **Alignment with Powerhouse Ecosystem:** The generated code is designed to integrate seamlessly with other parts of the Powerhouse ecosystem, such as the reducer execution engine and UI components.
|
|
91
91
|
6. **Single Source of Truth:** Ensures that your codebase (especially types and action creators) stays synchronized with the document model specification defined in Connect. If the specification changes, regenerating the model will update these components accordingly.
|
|
92
92
|
|
|
93
|
-
##
|
|
93
|
+
## Practical Implementation: Generating the `ToDoList` Model
|
|
94
94
|
|
|
95
|
-
|
|
95
|
+
Now that you understand what the Document Model Generator does, let's walk through the practical steps of using it with our `ToDoList` example.
|
|
96
96
|
|
|
97
|
-
|
|
98
|
-
|
|
97
|
+
<details>
|
|
98
|
+
<summary>Tutorial: Generating the ToDoList Document Model</summary>
|
|
99
|
+
|
|
100
|
+
This tutorial assumes you have completed the previous steps in this Mastery Track, where you defined the state schema and operations for the `ToDoList` model in Connect and exported it.
|
|
101
|
+
|
|
102
|
+
### Prerequisites
|
|
103
|
+
|
|
104
|
+
* **`ToDoList.phdm.zip` file**: You must have the document model specification file exported from Connect. If you do not have this file, please revisit the previous sections on specifying the state schema and operations.
|
|
105
|
+
|
|
106
|
+
### Steps
|
|
107
|
+
|
|
108
|
+
1. **Place the Specification File in Your Project**:
|
|
109
|
+
* Navigate to the root directory of your Powerhouse project.
|
|
110
|
+
* Move or copy your `ToDoList.phdm.zip` file into this directory.
|
|
111
|
+
|
|
112
|
+
2. **Run the Generator Command**:
|
|
113
|
+
* Open your terminal in the root directory of your Powerhouse project.
|
|
114
|
+
* Execute the `ph generate` command, pointing to your specification file:
|
|
99
115
|
```bash
|
|
100
|
-
ph generate
|
|
116
|
+
ph generate ToDoList.phdm.zip
|
|
101
117
|
```
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
* `
|
|
105
|
-
*
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
118
|
+
|
|
119
|
+
3. **Explore the Generated Files**:
|
|
120
|
+
* After the command completes successfully, you will find a new directory: `document-models/to-do-list/`.
|
|
121
|
+
* Take a moment to explore its contents, which will match the structure described earlier in this document:
|
|
122
|
+
* `spec.json` and `schema.graphql`: The definition of your model.
|
|
123
|
+
* `gen/`: Type-safe, generated code including `types.ts`, `operations.ts`, etc.
|
|
124
|
+
* `src/`: The skeleton for your implementation, most importantly `src/reducers/to-do-list.ts`, which will contain empty functions for `addTodoItemOperation`, `updateTodoItemOperation`, and `deleteTodoItemOperation`, ready for you to implement.
|
|
125
|
+
|
|
126
|
+
With these files generated, you have successfully scaffolded your document model. The project is now set up for you to implement the core business logic.
|
|
127
|
+
|
|
128
|
+
</details>
|
|
109
129
|
|
|
110
130
|
## Next Steps
|
|
111
131
|
|
package/docs/academy/02-MasteryTrack/02-DocumentModelCreation/05-ImplementDocumentReducers.md
CHANGED
|
@@ -47,7 +47,6 @@ Let's break down its components and principles:
|
|
|
47
47
|
While you can write one large reducer that uses a `switch` statement or `if/else if` blocks based on `action.type`, Powerhouse's generated code typically encourages a more modular approach. You'll often implement a separate function for each operation, which are then combined into a main reducer object or map. The `ph generate` command usually sets up this structure for you. For example, in your `document-models/to-do-list/src/reducers/to-do-list.ts`, you'll find an object structure like this:
|
|
48
48
|
|
|
49
49
|
```typescript
|
|
50
|
-
// Example structure from 01-GetStarted/03-ImplementOperationReducers.md
|
|
51
50
|
import { ToDoListToDoListOperations } from '../../gen/to-do-list/operations.js'; // Generated type for operations
|
|
52
51
|
import { ToDoListState } from '../../gen/types.js'; // Generated type for state
|
|
53
52
|
|
|
@@ -76,9 +75,9 @@ Let's break down its components and principles:
|
|
|
76
75
|
|
|
77
76
|
## Implementing Reducer Logic: A Practical Guide
|
|
78
77
|
|
|
79
|
-
Let's use our familiar `ToDoList` example
|
|
78
|
+
Let's use our familiar `ToDoList` example to illustrate common patterns. For this example, we'll assume our state schema has been updated to include a `stats` object to track the number of total, checked, and unchecked items.
|
|
80
79
|
|
|
81
|
-
|
|
80
|
+
Our `ToDoListState` now looks like this:
|
|
82
81
|
```typescript
|
|
83
82
|
interface ToDoItem {
|
|
84
83
|
id: string;
|
|
@@ -86,8 +85,15 @@ interface ToDoItem {
|
|
|
86
85
|
checked: boolean;
|
|
87
86
|
}
|
|
88
87
|
|
|
88
|
+
interface ToDoListStats {
|
|
89
|
+
total: number;
|
|
90
|
+
checked: number;
|
|
91
|
+
unchecked: number;
|
|
92
|
+
}
|
|
93
|
+
|
|
89
94
|
interface ToDoListState {
|
|
90
95
|
items: ToDoItem[];
|
|
96
|
+
stats: ToDoListStats;
|
|
91
97
|
}
|
|
92
98
|
```
|
|
93
99
|
|
|
@@ -226,46 +232,105 @@ Using these types provides:
|
|
|
226
232
|
* **Autocompletion and IntelliSense**: Improved developer experience in your IDE.
|
|
227
233
|
* **Clearer code**: Types serve as documentation for the expected data structures.
|
|
228
234
|
|
|
229
|
-
##
|
|
235
|
+
## Practical Implementation: Writing the `ToDoList` Reducers
|
|
236
|
+
|
|
237
|
+
Now that you understand the principles, let's put them into practice by implementing the reducers for our `ToDoList` document model.
|
|
238
|
+
|
|
239
|
+
<details>
|
|
240
|
+
<summary>Tutorial: Implementing the ToDoList Reducers</summary>
|
|
230
241
|
|
|
231
|
-
|
|
232
|
-
1. Set up an initial state.
|
|
233
|
-
2. Create an action object (ideally using the generated action creators from `../../gen/creators.js` or `../../gen/operations.js`).
|
|
234
|
-
3. Call the reducer function with the initial state and the action.
|
|
235
|
-
4. Assert that the returned new state is what you expect it to be.
|
|
236
|
-
5. Crucially, also assert that the *original* state object was not modified.
|
|
242
|
+
This tutorial assumes you have followed the steps in the previous chapters, especially using `ph generate ToDoList.phdm.zip` to scaffold your document model's code.
|
|
237
243
|
|
|
238
|
-
|
|
244
|
+
### Implement the Operation Reducers
|
|
245
|
+
|
|
246
|
+
Navigate to `document-models/to-do-list/src/reducers/to-do-list.ts`. The generator will have created a skeleton file. Replace its contents with the following logic.
|
|
239
247
|
|
|
240
248
|
```typescript
|
|
241
|
-
|
|
242
|
-
import
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
}
|
|
249
|
+
import { ToDoListToDoListOperations } from '../../gen/to-do-list/operations.js';
|
|
250
|
+
import { ToDoListState } from '../../gen/types.js'; // Assuming this now includes the 'stats' object
|
|
251
|
+
|
|
252
|
+
// REMARKS: This is our main reducer object. It implements all operations defined in the schema.
|
|
253
|
+
// The ToDoListToDoListOperations type is auto-generated from our GraphQL specification and ensures type safety.
|
|
254
|
+
export const reducer: ToDoListToDoListOperations = {
|
|
255
|
+
// REMARKS: The addTodoItemOperation adds a new item and updates our tracking statistics.
|
|
256
|
+
// - state: The current document state. Powerhouse uses a library like Immer.js,
|
|
257
|
+
// so you can write code that looks like it's mutating the state directly.
|
|
258
|
+
// Behind the scenes, Powerhouse ensures this results in an immutable update.
|
|
259
|
+
// - action: Contains the operation's 'type' and 'input' data from the client.
|
|
260
|
+
// - dispatch: A function to trigger subsequent operations (advanced, not used here).
|
|
261
|
+
addTodoItemOperation(state, action, dispatch) {
|
|
262
|
+
// REMARKS: We update our statistics for total and unchecked items.
|
|
263
|
+
state.stats.total += 1;
|
|
264
|
+
state.stats.unchecked += 1;
|
|
265
|
+
|
|
266
|
+
// REMARKS: We push the new to-do item into the items array.
|
|
267
|
+
// The data for the new item comes from the operation's input.
|
|
268
|
+
state.items.push({
|
|
269
|
+
id: action.input.id,
|
|
270
|
+
text: action.input.text,
|
|
271
|
+
checked: false, // New items always start as unchecked.
|
|
272
|
+
});
|
|
273
|
+
},
|
|
274
|
+
|
|
275
|
+
// REMARKS: The updateTodoItemOperation modifies an existing to-do item.
|
|
276
|
+
// It handles partial updates for text and checked status.
|
|
277
|
+
updateTodoItemOperation(state, action, dispatch) {
|
|
278
|
+
// REMARKS: First, we find the specific item we want to update using its ID.
|
|
279
|
+
const item = state.items.find(item => item.id === action.input.id);
|
|
280
|
+
|
|
281
|
+
// REMARKS: It's good practice to handle cases where the item might not be found.
|
|
282
|
+
if (!item) {
|
|
283
|
+
throw new Error(`Item with id ${action.input.id} not found`);
|
|
284
|
+
}
|
|
285
|
+
|
|
286
|
+
// REMARKS: We only update the text if it was provided in the input.
|
|
287
|
+
// This allows for partial updates (e.g., just checking an item without changing its text).
|
|
288
|
+
if (action.input.text) {
|
|
289
|
+
item.text = action.input.text;
|
|
290
|
+
}
|
|
291
|
+
|
|
292
|
+
// REMARKS: When the checked status changes, we also update our statistics.
|
|
293
|
+
// We check for `true` and `false` explicitly.
|
|
294
|
+
if (action.input.checked) { // This is true only if action.input.checked is true
|
|
295
|
+
// Note: This assumes the item was previously unchecked. For a more robust implementation,
|
|
296
|
+
// you could check `if (item.checked === false)` before updating stats to prevent inconsistencies.
|
|
297
|
+
state.stats.unchecked -= 1;
|
|
298
|
+
state.stats.checked += 1;
|
|
299
|
+
item.checked = action.input.checked;
|
|
300
|
+
}
|
|
301
|
+
if (action.input.checked === false) {
|
|
302
|
+
// Note: This assumes the item was previously checked.
|
|
303
|
+
state.stats.unchecked += 1;
|
|
304
|
+
state.stats.checked -= 1;
|
|
305
|
+
item.checked = action.input.checked;
|
|
306
|
+
}
|
|
307
|
+
},
|
|
308
|
+
|
|
309
|
+
// REMARKS: The deleteTodoItemOperation removes an item from the list.
|
|
310
|
+
deleteTodoItemOperation(state, action, dispatch) {
|
|
311
|
+
// REMARKS: Before removing the item, we find it to determine its checked status.
|
|
312
|
+
// This is necessary to correctly decrement our statistics.
|
|
313
|
+
const item = state.items.find(item => item.id === action.input.id);
|
|
314
|
+
|
|
315
|
+
// REMARKS: We always decrement the total count.
|
|
316
|
+
state.stats.total -= 1;
|
|
317
|
+
|
|
318
|
+
// REMARKS: We then decrement the 'checked' or 'unchecked' count based on the item's status.
|
|
319
|
+
if (item?.checked) { // This is shorthand for item?.checked === true
|
|
320
|
+
state.stats.checked -= 1;
|
|
321
|
+
}
|
|
322
|
+
if (item?.checked === false) {
|
|
323
|
+
state.stats.unchecked -= 1;
|
|
324
|
+
}
|
|
325
|
+
|
|
326
|
+
// REMARKS: Finally, we create a new 'items' array that excludes the deleted item.
|
|
327
|
+
// Assigning to 'state.items' is handled by Powerhouse to produce a new immutable state.
|
|
328
|
+
state.items = state.items.filter(item => item.id !== action.input.id);
|
|
329
|
+
},
|
|
330
|
+
};
|
|
266
331
|
```
|
|
267
332
|
|
|
268
|
-
|
|
333
|
+
</details>
|
|
269
334
|
|
|
270
335
|
## Reducers and the Event Sourcing Model
|
|
271
336
|
|
|
@@ -279,4 +344,4 @@ This is why purity and immutability are so critical:
|
|
|
279
344
|
|
|
280
345
|
Implementing document reducers is where you breathe life into your document model's specification. By adhering to the principles of purity and immutability, and by leveraging the type safety provided by Powerhouse's code generation, you can build predictable, testable, and maintainable business logic. These reducers form the immutable backbone of your document's state management, perfectly aligning with the event sourcing architecture that underpins Powerhouse.
|
|
281
346
|
|
|
282
|
-
With your reducers implemented
|
|
347
|
+
With your reducers implemented, your document model is now functionally complete from a data manipulation perspective. The next chapter covers how to write tests for this logic to ensure its correctness and reliability.
|