model-report-form
Build a report screen (turnover sheet, account card, register listing) by extending the shared ReportBase class, which already provides the sticky toolbar…
Skill by IgorAIvanov
Chalk Labs
Need help deploying model-report-form?
Chalk Labs sets up agents like this inside real teams: connected to your tools, tested on your data, with a human in the loop where it matters.
Install
Works with Claude Code, Claude apps, Codex, Cursor, Gemini CLI, GitHub Copilot, VS Code, OpenCode, Goose, Windsurf. Read the repository before you give any tool access to your data.
npx agentmarket install skill/igoraivanov-model-report-form
Asks which of your tools to add it to (Claude Code, Codex, Cursor, VS Code, Windsurf, Gemini CLI, Claude Desktop) and shows the change first. See https://agentmart.dev/cli.
Get the weekly diff
The weekly diff: one email a week with new agent tools, notable updates and grade changes, plus security alerts for tools you follow. Double opt-in, one-click unsubscribe.
About
Use cases: documents, spreadsheets
Reviews
Write a reviewNo reviews yet. Installed it? Reviews come from verified installers only. How reviews work
Permissions
What this skill can do and what it asks for, from its SKILL.md. Checked Oct 25, 2026.
- Tools
- No tools found
- Secrets
- None asked for
- Install scripts
- None found
Network hosts it talks to 1
codeload.github.com· found in install command
Files it touches
~/.claude/skills/model-report-form
What's inside
- Skill name
model-report-form- Description
- Build a report screen (turnover sheet, account card, register listing) by extending the shared ReportBase class, which already provides the sticky toolbar with refresh, print and Excel export.
- License
- MIT
- Metadata
- audience: app
- Instructions
- 250 lines in SKILL.md · view on GitHub
Files (1)
- SKILL.md
SKILL.md (first lines)
# Model Report Form Skill
Use this skill when:
- creating a `<Report>Report.ts` screen for a model of type `report`
- adding print or Excel export to a screen that shows a report table
- changing filters or columns of an existing report
**Do not hand-write the toolbar, the print logic or any Excel/CSV generation.**
All of it lives in the shared base class `ReportBase`. Print and export take the
already rendered table, so a report gets both actions with zero configuration —
no column metadata, no manifest block, no server command.
## Base class
`@client/ui-kit/base/report-base.ts` → `ReportBase<Root> extends BaseUI<Root>`
(`@client/` is the alias for the `@altera/client` package).
It owns: the sticky toolbar (**Оновити / Друк / Excel**), the error banner, the
print-only header (report title + subtitle), `window.print()` wiring and the
`.xlsx` build + download.
The subclass declares:
| Member | Required | What it is |
| --- | --- | --- |
| `model` | yes | model name from `manifest.json` |
| `reportTitle` | yes | locale key of the report name (paper + file + sheet name) |
| `buildReport()` | yes | run the query, usually `loadInto("index", …)` |
| `renderBody()` | yes | the `<table>` itself |
| `canRun` | no | guard for required filters (default: not busy) |
| `renderFilters()` | no | pickers and dates under the toolbar |
| `renderToolbarExtra()` | no | extra action buttons in the toolbar |
| `printSubtitle()` | no | organization and period line under the title |
| `hasData` | no | what counts as data (default: `$root.rows` is non-empty) |
| `emptyHint()` | no | what narrowed the result (default: `printSubtitle()`) |
## The empty report is drawn by the base
`renderBody()` is called **only when there is data**. Otherwise the base draws
the empty state, and it tells two cases apart: the report has not been built yet
(«press Refresh») versus it was built and came back empty («no data» plus the
selection that produced it — organization, period).
That difference matters more in a report than in a list: a report's filter is
mandatory, so "we are looking in the wrong place" is the *usual* reason for an
empty screen — a second organization in the database, a period with no documents
— and it is the easiest thing to mistake for a broken report. People go looking
in the SQL.
So **do not write your own «no data» row inside `<tbody>`** — it is unreachable
now. And if an empty row list is still a meaningful answer for your report,
override `hasData` rather than fighting the base: an account card with an opening
balance and no movements in the period has an answer to show, and the same flag
also enables Print and Excel.
A report keeps its own root schema (`<model>.schema.ts` with `$filters`, `rows`,
`totals`) and passes it to `super(...)` — unlike a list, where the base supplies
a generic schema.
## Filters — the same machinery as a list
Report filters are not a separate invention: `$root.$filters`, written through
`setFilter` / `setFilters`, read through `filterValue`, and sent to SQL as a nested
`filters` object. A reference filter is **one key holding `{id, name}`** — never an
`…Id` plus a separate label. The whole contract, with the reasoning and the traps, is in
**[model-list-filters](../model-list-filters/SKILL.md)**; read it before adding a filter
to a report.
**A report whose question is a whole month filters by one date, not a range.** Closing
statements, returns and yearly balances are asked in units, and a range would offer to ask
for a month and a half — a question the report cannot answer. The annotation is the plain
`"x-filter": true`, and the control is `<ui-period units="month">`, which picks the unit
itself instead of taking a date and rounding it behind the user's back. See
[A question asked in whole units](../model-list-filters/SKILL.md).
Two things are specific to reports, and both follow from what a report costs:
- **a filter change does not rebuild the report.** `onFiltersChanged()` stays empty (a
list overrides it to reload) — a turnover sheet for a year is not something to re-run on
every click in a picker. `Refresh` builds it, and `canRun` guards the required filters;
- **only the query is hand-written.** `sql:gen` generates the `index` wrapper from the
filters schema — filter parsing, the required-filter refusal, the `$filters` echo and
the envelope. What you write is the part a report exists for: the query itself.
**A report that no longer matches its filters says so.** Change a filter after the report
was built and the base blurs the body, disables clicking through it, and floats a
`Parameters changed` notice with a `Rebuild` button over it. Nothing to declare: the base
sets the mark in `onFiltersChanged()` and clears it when the `index` command returns —
one place that every path to building the report goes through. Without it the screen would
show numbers for the old filters under a new period in the panel: everything looks fine,
the number is wrong, and it prints that way too. On paper the blur is gone — printing is a
deliberate act, and a blurred sheet would just be spoilt.
```ts
protected override get canRun(): boolean {
return !this.busy && !!this.filterValue<Ref>("organization")?.id;
}
protected override async buildReport() {
await this.loadInto("index", this.filtersPayload()); // → { filters: {…} }
}
```
## SQL: a generated wrapper and a hand-written body
Two functions, and only the second one is yours:
| file | function | who writes it |
|---|---|---|
| `db/_generated/<report>.index.gen.sql` | `app.<report>_index(user_id, payload)` | `deno task sql:gen` |
| `db/<report>.sql` | `app.<report>_data(user_id, filters)` | you |
The wrapper reads the **filters schema** — `<Pascal>FiltersSchema`, the same one the
screen binds to — and does everything around the query:
- parses `payload.filters`, collapsing a reference to its id: `organization` (an object
`{id, name}`) arrives at your function as `organizationId`, an empty string becomes
"not set";
- refuses when a required filter is missing, with the message bound to that field —From IgorAIvanov/altera03, MIT.
More from IgorAIvanov/altera03
- 0.584score
typebox-model-schema
IgorAIvanov
Define the complete model schema using TypeBox as the single source of truth for backend validation, SQL contract documentation, and frontend form/list/picker…
SkillAFreshness: A★ 1Claude Code +9 - 0.584score
model-form-root
IgorAIvanov
Build a model edit form (and understand the shared $root data contract behind every list, picker and form) by extending BaseUI, seeding state from the TypeBox…
SkillAFreshness: A★ 1Claude Code +9 - 0.584score
model-feature-architecture
IgorAIvanov
Design or refactor a model into a feature-centered structure where frontend UI files and database SQL source live together under app/<family>/<model>.
SkillAFreshness: A★ 1Claude Code +9 - 0.575score
db-function-contract
IgorAIvanov
Define PostgreSQL function contracts for models that use List, Get, Save, Delete, and Lookup with JSONB payloads and JSONB results.
SkillAFreshness: A★ 1Claude Code +9 - 0.547score
lit
IgorAIvanov
Rules and patterns for writing Lit Web Components correctly in this project.
SkillBFreshness: B★ 1Claude Code +9 - 0.547score
model-command-access
IgorAIvanov
Declare the permission a model command requires in manifest.json (commands.access) so the runtime can enforce it.
SkillBFreshness: B★ 1Claude Code +9 - 0.547score
model-command-response
IgorAIvanov
Derive the exact shape of a model command's response before consuming it in a screen — never hand-write the interface from memory.
SkillBFreshness: B★ 1Claude Code +9 - 0.547score
daisyui
IgorAIvanov
Official daisyUI component library skill.
SkillBFreshness: B★ 1Claude Code +9 - 0.547score
screen-design-rules
IgorAIvanov
Visual and usability rules for drawing any application screen — density, colour tokens, disabled and read-only states, field widths, status marks, keyboard…
SkillBFreshness: B★ 1Claude Code +9 - 0.547score
altera-new-app
IgorAIvanov
Deploy a new application on the Altera framework into an empty directory — from scaffold to a working login screen — and verify that it actually runs.
SkillBFreshness: B★ 1Claude Code +9 - 0.547score
framework-ui-internals
IgorAIvanov
Внутрішні правила client/ui-kit — чому тема перебиває daisyUI, як не зламати граф чанків, пастки Lit і TypeScript у базових класах, де проходить межа між…
SkillBFreshness: B★ 1Claude Code +9 - 0.547score
app-run-and-deploy
IgorAIvanov
Run the application in development or production, fill in .env, publish the schema to a local or remote database, put several instances on one machine, and…
SkillBFreshness: B★ 1Claude Code +9
Signals
- Stars
- 1
- Forks
- 0
- Open issues
- 0
- Last commit (this folder)
- Aug 22, 2026 (2 months ago)
From GitHub (fetched Oct 24, 2026).
Details
- License
- MIT
- Repository
- github.com/IgorAIvanov/altera03
- First seen
- Oct 24, 2026
- Last checked
- Oct 24, 2026
- Source
- GitHubgithub-graphql
More from IgorAIvanov
- 0.584score
model-feature-architecture
IgorAIvanov
Design or refactor a model into a feature-centered structure where frontend UI files and database SQL source live together under app/<family>/<model>.
SkillAFreshness: A★ 1Claude Code +9 - 0.584score
typebox-model-schema
IgorAIvanov
Define the complete model schema using TypeBox as the single source of truth for backend validation, SQL contract documentation, and frontend form/list/picker…
SkillAFreshness: A★ 1Claude Code +9 - 0.584score
model-form-root
IgorAIvanov
Build a model edit form (and understand the shared $root data contract behind every list, picker and form) by extending BaseUI, seeding state from the TypeBox…
SkillAFreshness: A★ 1Claude Code +9 - 0.575score
db-function-contract
IgorAIvanov
Define PostgreSQL function contracts for models that use List, Get, Save, Delete, and Lookup with JSONB payloads and JSONB results.
SkillAFreshness: A★ 1Claude Code +9 - 0.547score
altera-new-app
IgorAIvanov
Deploy a new application on the Altera framework into an empty directory — from scaffold to a working login screen — and verify that it actually runs.
SkillBFreshness: B★ 1Claude Code +9 - 0.547score
framework-ui-internals
IgorAIvanov
Внутрішні правила client/ui-kit — чому тема перебиває daisyUI, як не зламати граф чанків, пастки Lit і TypeScript у базових класах, де проходить межа між…
SkillBFreshness: B★ 1Claude Code +9
Done for you
Rather not set it up yourself?
Chalk Labs scopes, builds and runs agents for your team: the task audit, the eval harness, the integrations and the weekly ops report. Fixed scope, priced before we start.
See how we build AI agents →Read the case studies
Chalk Labs is a two-founder AI and growth studio. We build AI agents, CRMs and MVPs for Web3 and AI companies, and have shipped 50+ product and token launches.
- launches shipped
- 50+
- launches shipped
- media placements
- 500+
- media placements
- founders on every account
- 2
- founders on every account
Press we've placed: Forbes · CoinDesk · Decrypt · Cointelegraph · The Block
Rahil Jain, Co-founder, Chalk Labs. Ex-Goldman Sachs engineer. You talk to the person who builds it.
Get model-report-form running in your business
Tell us what you want to automate. A founder replies within one working day with a plan, not a pitch deck.
Cite this page
model-report-form skill. Agent Mart. https://agentmart.dev/skills/igoraivanov-model-report-form (last updated 2026-10-24)