Skip to content

Form Fields Design

Ed Mozley edited this page Oct 29, 2026 · 1 revision

Simpler ticket and task forms - design (#218)

Status: proposal - nothing built yet. This page sets out what I plan to build for issue #218, and why. It was written before any code so the approach can be checked first. @Dr4inder - please read it and say on the issue whether it matches what you asked for, especially the questions at the end. Nothing is built until you have.

The request, from issue #218 (raised by @Dr4inder):

"A small internal service desk may not need every field offered by the ticket and task forms. Even when the underlying modules remain useful, unnecessary selectors make routine work harder to scan."

In plain words

The issue asks for three things, in the post and two follow-up comments. I have added a fourth, which the maintainer suggested while reading it.

Part What it does Size
A. Built-in fields (the post) Hide the ticket Department field, or fill it in from a default. Ask for the ticket Company only when the answer is not obvious. One Use external contractors switch hides the task's Contractor and Contact there pair Large
B. Calmer task Work view (comment 1) Checklists, Forms and Follow-ups with nothing in them shrink to a heading such as Checklists (0), and open with one click Small
C. Portal questions in the main form (comment 2) On the portal's New ticket page, the custom field questions move out of the side panel and into the main form, between the subject and the description Small
D. Custom fields for chosen departments (addition) A custom field can be limited to some departments, the same way it can already be limited to some ticket types - so HR tickets ask HR questions and Finance tickets ask Finance ones Medium

Each part is useful on its own and ships on its own. I suggest B and C first (small and well defined), then A, then D.

Three rules run through all four parts:

  1. Nothing changes on upgrade. Every new switch starts where today's behaviour is. An administrator opts in.
  2. Hiding is presentation, never deletion. A hidden field keeps its value. Saving an unrelated change never clears it. Switching it back on shows it again, exactly as it was.
  3. Hiding is not access control. Permissions, company boundaries and the REST API stay as they are.

1. Is this a natural extension, or a second system?

The risk with "configurable form fields" is a general form builder bolted beside the existing settings, which then disagrees with them. This design avoids that by only adding switches where FreeITSM already has the matching machinery:

  • Per-company settings already exist. tenantSetting() (in includes/tenant_settings.php) answers "what does this company say, or else what does the install say". The category, category-at-close and resolution-code switches on Tickets -> Settings -> Categories already use it, with a per-company table of Follow default / On / Off. Part A copies that screen and that storage.
  • Hidden fields are already handled at closure. The Mandatory fields rule already skips a field the ticket cannot show (category, resolution code, team). Part A adds Department to that list.
  • Saves are already patch-style. Saving a ticket or a task only writes the fields the request sends (TicketsService::updateTicket(), TasksService::saveTask()), so a hidden control cannot null its value by being absent. Rule 2 holds by construction, and the tests will prove it.
  • Custom fields can already be limited to ticket types. Part D adds departments as a second, identical limit.

So this is four small extensions of existing parts, not a new system.


2. Part A - built-in fields

2.1 Where the settings live

  • Tickets -> Settings -> Fields - the existing Custom fields tab, renamed, with a new Built-in fields panel above the custom field list. One place for "what this form asks".
  • Tasks -> Settings -> General - the Use external contractors switch.

Both use the install-wide default + per-company override pattern from the Categories tab: a default row of switches, then a table with one row per company (Follow default / On / Off). On a single-company install the table is not shown, so it is just a switch.

2.2 Department

Settings, per company:

Setting Choices Default
Show the Department field On / Off On
Department for new tickets None / any active department None

Where "Off" hides it. Every place the field appears, so no gap is left:

  • the New ticket form;
  • the ticket panel, the ticket workspace and the collapsed header's Dept line;
  • the right-click Department menu, the bulk Department button and drag-and-drop onto a department folder;
  • the Department column in the ticket table's column picker;
  • the phone view, which shares the same code.

A ticket that already has a department keeps it. The value stays in the database, in reports, in the email merge field [department_name], in workflow conditions and in history.

What a department still does while hidden. This is the important part, and the reason Department needs more care than the other fields:

Department drives With the field hidden
Which tickets each team sees Unchanged. A ticket with no department is visible to every team, as today - hiding the field only means more tickets have none
SLA notification rules per department Unchanged. A ticket with no department matches only the default rules, as today
Notify department teams A ticket with no department notifies no department teams, as today
Confidential departments Unchanged. A workflow or the default department can still put a ticket in one
Workflows (set_ticket_department, conditions) Unchanged. A workflow may still set or test the department

So the honest summary on the settings screen is: "Hiding Department only removes the field from the screen. Tickets with no department are seen by every team. To keep team visibility working, choose a department for new tickets, or let a workflow set it."

The default department. Applied once, when a ticket is created with no department, on every channel that can create one: the New ticket form, the portal, email, chat, the REST API, and catalogue approvals. It is never applied to existing tickets, and never on edit. A department set by the request itself, or by a workflow that runs after creation, wins. (Today email and portal tickets are always created with no department, so this is the only way to give them one without a workflow.)

Checks when saving the setting:

  • The default must be an active department. Deactivating or deleting a department that is someone's default is warned about on the Departments tab, and the default is cleared, never left pointing at nothing.
  • If Mandatory fields requires Department at closure and the field is hidden with no default, the save is allowed but the panel says so plainly: "Department is required at closure, but hidden for Acme with no default. The closure rule will skip Department for Acme's tickets." That matches how hidden categories already behave, rather than letting a hidden field trap every ticket.

Mandatory fields. MandatoryFieldsService::fieldAvailable() gains a Department case: the field counts as available only when it is shown for the ticket's company. Today Department falls through to "always available", which would trap tickets the moment the field could be hidden.

2.3 Company

FreeITSM already gets most of this right. The Company row on New ticket appears only in the all-companies view, when the analyst can reach more than one company. Inside one company it is never asked. I propose no new setting, and instead fixing three gaps found while reading the code:

  1. The server does not enforce the choice. In the all-companies view, a create request with no company falls back to the session's company, or the first one the analyst can reach. Only the browser stops that. The server will refuse it: "Choose which company this ticket is for." This is the request's "never silently choose the first company".
  2. One reachable company is not ambiguous. An analyst in the all-companies view who can reach just one company is not asked, and the ticket goes to that company. The server will make that explicit, rather than relying on the same fallback.
  3. The mailbox list follows the wrong company. It loads for the active company, not the one chosen on the form. It will follow the chosen one.

An optional preference (see question 3): My company for new tickets, under System -> Preferences. In the all-companies view it pre-selects that company, and the row stays visible so you can see where the ticket is going. I would rather pre-select than hide, because a wrong company is a data-protection mistake that is easy to make and hard to notice.

2.4 Use external contractors (Tasks)

Setting: Use external contractors, Tasks -> Settings -> General, per company, default On.

When off:

  • The Contractor and Contact there pair disappears from the task panel, together and with no gap. The assigned analyst stays.
  • The same applies to the task opened inside a ticket and on a phone, which share the same code.
  • The sidebar Contractor filter is hidden.
  • A task that already has a contractor still shows it, read-only, with a note: "Contractors are switched off. This one stays recorded." Hiding it completely would leave a value nobody can see but which still counts, for example in the board chip or in Projects. Saving anything else on the task never touches it. See question 4.

Not changed:

  • Projects. A contractor can be a project member, and the AI assistant can assign one. That is a Projects feature and stays as it is.
  • The REST API. It still reads and writes contractors. As the request says, a UI tidiness setting is not an API restriction.
  • Module availability (#217). This is not a #217 feature switch, because nothing is refused - only hidden. Switching the Contracts module off already removes contractor choosing entirely, since contractors are Contracts suppliers.

2.5 Acceptance criteria, against Part A

# Criterion How
1 Configurable without disabling a module Department and contractor switches, per company
2 Company omitted only when safely determined Already true in a single company. All-companies is enforced on the server (2.3)
3 Department omitted without hidden-required failures or routing changes The Mandatory fields case, the save warning, and routing unchanged (2.2)
4 One contractor switch, assignee stays 2.4
5 Hidden values survive edits and re-enabling Patch-style saves, proved by tests
6 Consistent everywhere, no gaps The lists in 2.2 and 2.4
7 Permissions kept; invalid defaults explained Inactive or deleted defaults refused and warned about; mandatory conflict stated
8 Verification Section 7

3. Part B - a calmer task Work view

3.1 What changes

When a task's Work view opens, each of Checklists, Forms and Follow-ups is drawn in one of three states:

State Shown as
Loading The heading with a small spinner. Never "(0)"
Could not load The heading, open, with "Could not load checklists" and a Retry button. Never "(0)", never hidden
Loaded Has records: open, as today. None: Checklists (0), collapsed

Today, a section that fails to load simply disappears, which hides the failure. Part B makes that visible - a small fix in its own right.

"Empty" means no records at all. A completed checklist, a submitted form or a closed follow-up counts as a record, so its section stays open.

The heading is a button with a chevron and aria-expanded, reusing the subtask disclosure already on the task cards. It works by click, Enter and Space. Opening it shows exactly the controls shown today (attach a checklist, add a form, create or link a follow-up), with the same permission checks.

3.2 Keeping it steady

  • Your choice sticks while the task is open. Open an empty section, and it stays open through every redraw of that task - adding a checklist step, a form saved in another tab, completing a subtask, changing the project. Each redraw keeps the open/closed state for that task, the same way the inline forms already keep which ones are open.
  • Adding the first item keeps it open, so you can see what you added.
  • Follow up from a failed checklist step (or a form) opens Follow-ups, scrolls to it and puts the cursor in the title box, as it does now - but now also when the section was collapsed.
  • Half-typed text survives. Some redraws today rebuild the follow-up and checklist-note boxes, which loses what you were typing. Those redraws will carry the typed text across.

3.3 Who decides

A user preference: Collapse empty work sections, under System -> Preferences -> Tasks, default off, so the Work view looks as it does today until you choose otherwise. It is a personal taste - some people want to see every button - so it belongs to each person, not to the install. See question 5.

It applies everywhere the Work view is drawn: the Work tab, the wide column, the side panel, and a task opened inside a ticket.


4. Part C - portal questions in the main form

4.1 What changes

A new setting, Where the portal asks custom field questions: Side panel / Main form, default Side panel. It goes on Tickets -> Settings -> Fields, beside the existing Where requesters see their answers setting, because both are about custom fields on the portal. It is install-wide, like every other portal setting - see question 6.

With Main form, the New ticket page reads top to bottom:

Subject -> Request type (when it matters) -> Custom field questions -> Description

  • The type picker moves with the questions, because it decides which ones are asked. It still only appears when choosing a type matters, as today.
  • The questions keep their order, labels, hints and required markers. They use the same renderer, so validation and storage are unchanged.
  • Changing the type keeps any answers that still apply, as it does now.
  • The side panel keeps everything else: mailbox, category, priority, the Confidential toggle, equipment, attachments, and Submit.

4.2 The layout change underneath

Today the page is built so that it never scrolls. The description editor stretches to fill whatever height is left, and only the side panel scrolls. That is why the questions live in the side panel: in the main column they would be squeezed by the editor.

With Main form, the main column scrolls instead. The description editor gets a fixed, comfortable height (about 300px, the same idea as the ticket note box) rather than filling the space. So several questions, long answers and error messages all stay readable. On a phone the page is already one column, and the editor's Full screen button still works.

With Side panel, the page is exactly as it is today.


5. Part D - custom fields for chosen departments

5.1 What changes

The custom field dialog already has Which ticket types: All / Only these. It gets a second, matching choice: Which departments: All / Only these, with a tick list of departments.

A field appears on a ticket when both match: its type is one of the field's types, and its department is one of the field's departments. A field set to All departments behaves exactly as today, which is every existing field.

5.2 The catch - when is the department known?

A department-limited field can only appear once the ticket has that department, and on several channels it has none at the start:

Channel Department at creation
Analyst New ticket form Chosen on the form - the questions update when you pick one
REST API, workflows, catalogue approvals Whatever they set
Portal, email, chat None (unless Part A's default department supplies one)

So:

  • On the portal, a department-limited question is never asked, because the requester does not choose a department. The settings screen says so when you tick Ask the requester on a department-limited field. Ticket type is the right way to ask the requester different questions - for example a New starter type asking the HR questions.
  • For analysts the field appears as soon as the ticket is put into the department - by hand, by a workflow, or by the default department. Moving a ticket to another department hides that department's fields and shows the new ones. Values are hidden, never deleted, exactly like changing the type today, and moving it back brings them back.
  • Required on create applies only where a department is chosen at creation: the analyst form and the REST API.
  • Required at closure applies when the ticket is in one of the field's departments when it closes.

5.3 Underneath

  • A new table, ticket_field_departments (field_id, department_id), beside ticket_field_types, plus an all_departments column on ticket_fields, default 1. Database Verification creates both.
  • TicketFieldsService::fieldsFor() gains the department, so every place that asks which fields a ticket has - New ticket, the panel, the workspace, the REST API, the closure rule and the case report - keeps one answer.
  • The ticket panel reloads its custom fields after a department change, as it already does after a type change.
  • If a department is deleted, a field limited to only that department is left limited to nothing. The custom fields list flags it so it can be fixed.

6. What is not in scope

  • A general form or page designer. The request says so explicitly.
  • Hiding other built-in fields (priority, type, category...). Category, category at close and resolution code already have their own switches. Others can follow the same pattern later if asked for.
  • Making departments per company. They are shared by every company today, and changing that touches teams, SLAs and routing. It deserves its own design.
  • Any change to who can see or do what.

7. How it will be tested

  • Part A: create a ticket on every channel with Department hidden, with and without a default. Edit an existing ticket that has a hidden department, and check the department is still there. Close a ticket with Department required and hidden. Check the all-companies view refuses a create with no company. Check a task with a contractor survives an unrelated save with contractors switched off. Switch everything back on and check every value reappears.
  • Part B: each section loaded empty, loaded with records, failed to load and slow to load. Open a section, then trigger each kind of redraw and check it stays open. Follow up from a failed step with Follow-ups collapsed. The same in a task opened inside a ticket.
  • Part C: both placements on desktop and phone, with several required questions and a validation error.
  • Part D: fields limited by type only, by department only, and by both. A department change in the panel. The portal never asking a department-limited question.

8. Questions for the reviewer

  1. Default department on every channel. Should the default department apply to email, portal and chat tickets too (my proposal), or only to tickets raised on the New ticket form?
  2. Department hidden with no default. Is "tickets with no department are seen by every team" acceptable, with the warning on the settings screen? Or should hiding Department require a default?
  3. Company preference. Is a My company for new tickets preference, which pre-selects but keeps the row visible, enough? Or do you need the row hidden entirely when a default exists?
  4. An existing contractor with contractors switched off. Read-only and visible (my proposal), or hidden completely?
  5. Collapse empty sections. A personal preference, default off (my proposal)? Or an install-wide default that each person can override?
  6. Portal placement per company. Install-wide like the other portal settings (my proposal), or per company?
  7. Part D. Does limiting custom fields by department fit how you use departments, given that portal and email tickets have no department to start with?
  8. Order. B and C first, then A, then D. Does that suit you?

See also: Custom ticket fields Β· Tickets settings - developer guide Β· Multi-tenancy settings Β· Task work view Β· Self-service portal Β· Module availability - design (#217)

FreeITSM

Getting Started

Modules

Multi-tenancy (planned)

Blue sky thinking

Bugs resolved

Links

Clone this wiki locally