Appearance
Capstone: build "Employee Leave Management"
You will build a real application from zero and touch every kind of feature Studio ships in a plugin. The finished plugin is in the repository at help-docs/studio/capstone/leave-management/spk-assembly (27 files). It was installed on the production platform and checked by frontend/e2e/check-capstone-prod.mjs (25 of 25 checks pass), then uninstalled.
What the finished application contains
| Feature | File in the plugin | Proven on production |
|---|---|---|
| Application, module, menu | application/, module/, menu/leave-management-menu.json | installed, menu registered |
| Entities (3) with types, unique, indexed, enum, references | entities/lm_leave_type, lm_leave_request, lm_leave_balance | tables created, records created and listed |
| Roles with permissions (3) | plugin.json roles | installed |
| Seed data | seed-data/leave-types.json (Annual, Sick, Unpaid) | rows present after install |
| Providers (3) | provider/ | installed |
| Data services (count KPIs) | data_service/ | returns a live count |
| Data view (picker options) | data_view/lm-leave-type-options-view.json | installed |
| Page (list with search, add, edit) | page/leave-types.json | installed |
| Form: sections, conditional required, cross-field date check, submit action | form/leave-request-form.json | installed (not clicked through) |
| Dashboard | dashboard/leave-dashboard.json | installed |
| Rules that reject (numeric and date-order) | rules/lm_leave_request_days.json, lm_leave_request_dates.json | 400 with the message |
| Rule that starts a workflow | rules/lm_leave_request_submit_workflow.json | new workflow instance |
| Workflow: two approvals, roles, SLA with breach stage | workflow/leave-management.leave-approval.json | started by the rule |
| Report | report/leave-type-list.json | installed |
| Print template with QR code | print_template/leave-approval-slip.json | installed |
| Mobile navigation | mobile_nav/leave-mobile-nav.json | installed |
| Workspace | workspace/leave-workspace.json | installed |
| Theme | theme/leave-calm.json | installed |
| Translations | i18n/en.json | installed |
| Declared extension point | plugin.json extensionPoints | listed by the platform |
"Installed" means the platform accepted it and it appears in its Studio list. Where a step needs a person to click, it says so.
The 25 steps
01 Requirements. Write the one-page requirements. 02 Create the plugin. Wizard, type Full application, id leave-management. 03 Entities. Add lm_leave_type, lm_leave_request, lm_leave_balance (Entities). 04 Relationships. employee_id and leave_type_id reference fields. 05 Security. Roles LEAVE_HR_ADMIN, LEAVE_MANAGER, LEAVE_EMPLOYEE; add record scope and field permissions in the Permission designer (Security). 06 Navigation. Application, module, menu, workspace, mobile nav (Navigation). 07 Employee page. The list page over a provider (Pages). 08 Request form. Sections, reason required only for unpaid leave, date order check (Forms). 09 APIs. Use the generated entity API; call it with a token (API Designer). 10 Business logic. The reject rule and the submit rule (Rules). 11 Approval workflow. Manager, then HR, two-day SLA. 12 Events. Rule after update on the request. 13 Notifications. A template per event, sent from the workflow. 14 Scheduled job. Nightly accrual (Jobs). 15 Dashboard. KPI cards over the count services (Dashboards). 16 Reports. The leave type report and the approval slip (Reports). 17 Integration. Payroll connector and integration flow (Integrations). 18 Tests. Test cases and simulate (Testing). 19 Security scan. Manual review today (Security scanning). 20 Build. Validate. 21 Package. .spk. 22 Deploy. Publish. 23 Install in a tenant. 24 Configure. Assign roles, adjust entitlements. 25 Upgrade. Raise the version, publish again.
Do it with the files
bash
cd help-docs/studio/capstone/leave-management
node ../../../../tools/spark-cli/spark.js validate spk-assembly
node ../../../../tools/spark-cli/spark.js package spk-assembly -o leave-management.spkThen in Plugin Manager choose Upload and pick the file, or in Plugin Studio File, Import plugin (.zip / .spk). After install:
bash
# a leave type (dates are ISO timestamps)
POST /api/v1/entities/lm_leave_type/records {"type_code":"STUDY","type_name":"Study leave","annual_days":5}
# a request, then submit it (this starts the approval workflow)
POST /api/v1/entities/lm_leave_request/records {"request_number":"LR-1","employee_id":1,"leave_type_id":1,"from_date":"2026-10-01T00:00:00Z","to_date":"2026-10-03T00:00:00Z","days":3}
PUT /api/v1/entities/lm_leave_request/records/1 {"status":"PENDING_APPROVAL"}
# zero days is rejected with "A leave request must be for at least one day."Lessons the build taught (read these before you start)
- A published version is immutable. Installing changed content under the same version fails ("Refusing to overwrite immutable artifact"). Raise the version every time.
- Uninstall keeps your data. Tables and rows stay so a reinstall finds them; a unique key you tested with is still there.
- Rules can compare fields and dates.
lt,lte,gt,gteorder numbers and dates (ISO strings or timestamps), and${record.other_field}compares with another field of the same record. Two values that cannot be ordered give a clear 400 message. - Send dates as ISO timestamps, and update with only the changed fields.
- Lists are
GET ...?page=0&size=20. - Rules from an earlier version stay installed. If you rename or delete a rule in a new version, delete the old one in Entity rules.
- Name every role. An unnamed role fails the install.
Try it yourself in the designers
Open the installed plugin's page in the Page Designer and use Customize to change its title without touching the plugin, then bind the dashboard's KPI cards to lm-requests-pending-count. Both are covered in Customize and Dashboards.
