# Snippets: scripted fields

> Oktul Code Studio script snippets for scripted fields: the value, the choices and the default, copied from the console and run by its tests.
>
> Documentation for Oktul Code Studio, for Jira Cloud and Confluence Cloud, by Oktul: https://oktul.com/docs/plugins/code-studio/snippets/scripted-fields/

These are the snippets the console's **Snippets** dialog offers under Scripted fields. Each is TypeScript; in a `.js` script the console shows the same code with its types removed. Every one runs unchanged in Code Studio's own tests. How the API they call behaves is in [writing scripts](../../writing-scripts/) and the [script API reference](../../script-api/).

## Percent of subtasks done

For a Code Studio number field: the share of a work item's subtasks that are done, read from the work item Jira hands the script. No subtasks gives an empty field.

```ts
interface WorkItem {
    fields?: { subtasks?: { fields?: { status?: { statusCategory?: { key?: string } } } }[] };
}

export default async function (workItem: WorkItem) {
    const subtasks = workItem.fields?.subtasks ?? [];
    if (subtasks.length === 0) return null;
    const done = subtasks.filter((subtask) => subtask.fields?.status?.statusCategory?.key === 'done').length;
    return Math.round((done / subtasks.length) * 100);
}
```

## Minutes since the last update

For a Code Studio number field whose value moves with time: set Recompute to about every 15 minutes on the field, in Scripted fields, and the value is never more than a refresh behind. Each refresh is one run over the context's work items.

```ts
export default async function (workItem: { fields?: { updated?: string } }) {
    const updated = workItem.fields?.updated ? Date.parse(workItem.fields.updated) : NaN;
    if (Number.isNaN(updated)) return null;
    return Math.max(0, Math.floor((Date.now() - updated) / 60_000));
}
```

## Count what a query finds for each work item

For a Code Studio number field: how many open work items block this one, counted with JQL. Each work item is one count request, so keep the query narrow.

```ts
export default async function (workItem: { key: string }) {
    const found = await jira.post<{ count: number }>('/rest/api/3/search/approximate-count', {
        jql: `issue in linkedWorkItems("${workItem.key}", "is blocked by") AND statusCategory != Done`,
    });
    return found.count;
}
```

## Pick a choice from other fields

For a Code Studio select: the risk from the priority and the due date. options() lists the choices, which Code Studio stores on the field; a value outside them is not stored.

```ts
interface WorkItem {
    fields?: { priority?: { name?: string }; duedate?: string | null };
}

export default async function (workItem: WorkItem) {
    const priority = workItem.fields?.priority?.name ?? 'Medium';
    const due = workItem.fields?.duedate ? Date.parse(workItem.fields.duedate) : null;
    const soon = due !== null && due - Date.now() < 3 * 24 * 60 * 60 * 1000;
    if (priority === 'Highest' || (priority === 'High' && soon)) return 'High';
    return soon ? 'Medium' : 'Low';
}

export function options() {
    return ['Low', 'Medium', 'High'];
}
```

## Select: choices from Jira, a default

For a Code Studio select, multi select, radio buttons or checkboxes: options() reads the choices, here the space's components, and the default export gives a new work item's default. People pick on the create screen, the work item and the portal. options() runs when the script is published and with Refresh choices in Scripted fields; set Recompute on the field to read the choices again as the components change. A value someone chose stays.

```ts
interface Component {
    name: string;
}

export function options() {
    return jira.get<Component[]>('/rest/api/3/project/DEMO/components').then((components) => components.map((component) => component.name));
}

export default async function () {
    return 'Platform';
}
```

## User picker: offer the members of a group

For a Code Studio user picker: options() lists the account ids a person may pick, here a group's members, with names Code Studio shows; the default export makes the reporter the default. With no options() export, anyone can be picked.

```ts
interface Members {
    values: { accountId: string; displayName: string }[];
    isLast: boolean;
}

export async function options() {
    const people: { value: string; label: string }[] = [];
    for (let startAt = 0; ; startAt += 50) {
        const page = await jira.get<Members>('/rest/api/3/group/member', { query: { groupname: 'approvers', startAt, maxResults: 50 } });
        people.push(...(page.values ?? []).map((member) => ({ value: member.accountId, label: member.displayName })));
        if (page.isLast !== false || people.length >= 200) break;
    }
    return people;
}

export default async function (workItem: { fields?: { reporter?: { accountId?: string } } }) {
    return workItem.fields?.reporter?.accountId ?? null;
}
```

## Group picker: offer the support teams

For a Code Studio group picker: options() lists the group ids a person may pick, here every group whose name starts with team-. A group field offers nothing until options() lists some.

```ts
interface Found {
    groups: { name: string; groupId: string }[];
}

export async function options() {
    const found = await jira.get<Found>('/rest/api/3/groups/picker', { query: { query: 'team-', maxResults: 50 } });
    return (found.groups ?? []).map((group) => ({ value: group.groupId, label: group.name }));
}

export default async function () {
    return null;
}
```

## Labels from the components

For a Code Studio labels field: one word per component, lower case with no spaces, so the field can be searched with JQL.

```ts
export default async function (workItem: { fields?: { components?: { name: string }[] } }) {
    return (workItem.fields?.components ?? []).map((component) => component.name.toLowerCase().replace(/\s+/g, '-'));
}
```

## Related

- [Writing scripts](../../writing-scripts/)
- [Requests to other systems](../../requests-to-other-systems/)
- [Script examples](../../script-examples/)

---

A problem or a question? Write to [support@oktul.com](mailto:support@oktul.com) or open a request in the [Help Center](https://oktul.atlassian.net/servicedesk/customer/portals). Both reach the same service desk, so either way the request gets a reference and an SLA measuring the response.
