| name | module-writer |
| description | Create or update a journal entry module (database, model, actions, views, controllers, API, docs, tests). Use when adding a new journal module or changing an existing module such as mood, health, or obligations. Use when this capability is needed. |
| metadata | {"author":"djaiss"} |
Module Writer
This Skill helps you create a module used by journal entries. A module is a small, focused set of attributes for one journal entry (example: Mood tracks daily mood).
When to use this Skill
Use this Skill when:
- Creating a new module
- Updating a module
Instructions
Quick start
- Pick a module name and ModuleType category.
- Add migration + model + factory.
- Add actions + tests.
- Add view + web controller + presenter + tests.
- Add API controller + resource + tests.
- Update docs, Bruno, translations.
Step 1: Write the database migration
- Verify the migration doesn't exist. Create it via Artisan with
--no-interaction.
php artisan make:migration create_module_MODULE_NAME_table --no-interaction
- A module always belongs to a journal entry. Use this pattern:
Schema::create('module_health', function (Blueprint $table): void {
$table->id();
$table->unsignedBigInteger('journal_entry_id');
$table->string('category')->default(ModuleType::BODY_HEALTH->value);
$table->text('health')->nullable();
$table->timestamps();
$table->foreign('journal_entry_id')->references('id')->on('journal_entries')->onDelete('cascade');
});
- A module always has a category from
ModuleType. Do not add new types. Choose the closest existing category.
Step 2: Create related model
- Models live in
App\Models. Follow existing module models (e.g., ModuleHealth). Add a brief header PHPDoc for the model.
- Add a
module{ModuleName} method to JournalEntry. One module per day, so always HasOne.
Step 3: Create the factory
Create a factory for the model.
Step 4: Create tests
- Add a model test for the relationship to
JournalEntry.
- Add a test for the new
JournalEntry relationship method.
- Never use
for() on factories. Set user_id explicitly.
Step 5: Add module to ModuleCatalog class
- All modules are documented within ModuleCatalog so we can reference it later dynamically.
Step 6: Decide if the module should be added to the default layout for journal action
- When we create a journal, some modules are added to the layout by default. It's defined in
CreateDefaultLayoutForJournal action.
- Enable a module by default only if it is broadly applicable to most users on most days, immediately understandable without explanation, low-effort to use, non-sensitive in nature, and delivers clear value even with incomplete data; otherwise, keep it disabled by default and require explicit user opt-in.
Step 7: Create the actions to manage the data
- Create
Log{ModuleName} (follow existing actions like LogHealth).
- Action flow: validate -> log data -> log user action -> update last activity -> refresh content presence.
- Add tests for happy path and edge cases.
- Create
Reset{ModuleName}Data (see ResetHealthData).
Step 8: Update the CheckPresenceOfContentInJournalEntry job
- Add the presence of the data of the new module in
app/Jobs/CheckPresenceOfContentInJournalEntry.php.
- Update the test to reflect it.
Step 9: Create a view that will let users interact with the module
- Views are stored in the
resources/views/app/journal/entry/partials folder.
- If the module has Yes/No buttons, use the existing components.
- If the module uses multiple choices, group the choices together like in the health.blade.php view file.
- If the module uses multiple choices with multiple accepted values, display them like in the primary_obligation.blade.php view file.
- Add the view at the right place within the
resources/views/app/journal/entry/edit.blade.php file.
- Add the view at the right place within the
resources/views/app/journal/entry/show.blade.php file.
Step 10: Create the web controller to pilot the view
- Add a controller to call the right view.
- Controller names should follow project convention and include the module name (example:
HealthController.php).
- Sanitize input with
TextSanitizer before actions.
- Validate inline (no Form Requests). Strings must be strings and within max length.
- Create a presenter for module data.
- Update the main journal entry presenter to load this data.
- Update the JournalEntryShowPresenter presenter.
- Test both presenters.
- Add the appropriate web route.
- Create controller tests for happy path and edge cases.
Step 11: Create the api controller
- Create an API controller that uses the same actions for log/reset.
- Update
JournalEntryResource to include the new data.
- Add API controller tests.
Step 12: Add marketing documentation
- Marketing docs live in
resources/views/marketing/docs/api.
- Add a new module in the modules folder.
- Update the resources/views/marketing/docs/api/partials/journal-entry-response.blade.php partial.
- Update the sidebar to include this new entry.
- Add a new controller for the documentation of this module.
- Add a controller test that asserts the content loads.
Step 13: Add appropriate Bruno API documentation
- Add Bruno tests for the new API methods in
docs/bruno.
Step 14: Update translations
- Run
composer journalos:locale.
- Ensure there are no empty entries in
lang/fr.json.
Step 15: Test and lint
- Run relevant PHPUnit tests for the new module.
- When tests pass, run
composer test if needed for coverage.
Step 16: Add module name to README
- Add the new module to the README file in the appropriate section: the name and the emoji.
Step 17: Add module to the /modules marketing view
- Add the module to the list of modules in the marketing site.
Do NOT forget any step. Be extremely thorough in your analysis. DO NOT FORGET ANY STEP.
Converted and distributed by TomeVault — claim your Tome and manage your conversions.