Skip to end of metadata
Go to start of metadata

You are viewing an old version of this page. View the current version.

Compare with Current View Page History

« Previous Version 2 Next »

Annotated Code

In this section, we’ll describe how each part of the code works and provide notes on tweaking it.

["jira" "cache" "status-history" "atlaskit"] USE-MODULES

We begin by importing all of the words we need from other modules. See Forthix Modules for details.

"config" LOAD-SCREEN

This loads the Forthic code from the app’s config file

# ----- A little more config ------------------------------------------------------------------
["parent_key"] VARIABLES   # A convenience variable to help with JQL construction
: EXTRA-FIELDS           ["Assignee"];
: PARENT-KEY>CHILD-JQL   (parent_key !) ["parent = " parent_key @] CONCAT;
: EXTRA-CHILD-FIELDS     [];

This section defines some additional configuration that you can use to further tweak the report. For instance, you may want have particular JQL for selecting child tickets for your report.

The EXTRA-FIELDS and EXTRA-CHILD-FIELDS are used to pull more data for the parent and child tickets. This can be used to render additional columns in the table or present more information in other ways.

["parent" "child" "ticket" "date" "key" "summary" "comment" "status" "field_key"] VARIABLES

These are variables used throughout the app. They’re often used to set the context for other words.

# ----- Utils -------------------------------------------------------------------------------
: >STATUS       "Status" REC@ UPPERCASE;
: >KEY          "key" REC@;
: TICKET-LINK   (summary ! key !) A  ["href"  key @ jira.ISSUE-URL] <<PROP [ [ key @ ": " summary @ ] CONCAT ] . ;
: |NON-NULL     ">BOOL" SELECT;
: PMAP          20 !INTERPS MAP;

: STATUS-KEY>APPEARANCE   [
    ["new"            "new"]
    ["indeterminate"  "inprogress"]
    ["done"           "success"]
] REC SWAP REC@ "unknown" DEFAULT;

: date-DAYS-AGO   TODAY date @ SUBTRACT-DATES;
: STATUS-KEY>COLOR   [
    ["new"            "color.background.discovery.bold"]
    ["indeterminate"  "color.background.information.bold"]
    ["done"           "color.background.success.bold"]
] REC SWAP REC@ "color.background.neutral" DEFAULT;

: DATE>SHORT       DATE>STR "-" SPLIT 1 DROP "/" JOIN;
: POPUP-XCSS       [ ["maxWidth"  "800px"]] REC atlaskit.XCSS;
: SORT-BY-STATUS   ">STATUS STATUS>ORDER" !COMPARATOR SORT;
: JIRA-SEARCH      MAX-TICKETS jira.!MAX-RESULTS jira.SEARCH MAX-TICKETS TAKE;

Utility words

These words are used throughout the app to provide reusable functionality.

Word

Description

>STATUS

Extracts an uppercase Status from a ticket

>KEY

Extracts a key from a ticket

TICKET-LINK

Given a ticket key and a summary, returns a ticket link React node.

|NON-NULL

Selects all non-null elements

PMAP

A wrapper that allows MAP to run concurrently using 20 Forthic interpreters at a time

STATUS-KEY>APPEARANCE

Used to set the color of the status badges. See Atlassian Lozenge Examples

date-DAYS-AGO

Returns the number of days since today.

STATUS-KEY>COLOR

Used to set the color of the status history chips. See Atlassian Design Colors for more info.

DATE>SHORT

This converts a date into a short string like “01/15”

POPUP-XCSS

This sets the style of the popup that shows when hovering over a status chip.

SORT-BY-STATUS

This is used to sort tickets by status

JIRA-SEARCH

Performs Jira search returning MAX-TICKETS

# ----- Data -------------------------------------------------------------------------------
: DATES              [END-DATE  "DATE-INTERVAL -1 * ADD-DAYS" NUM-DATES 1 - <REPEAT] REVERSE;
: TICKET>CHANGELOG   >KEY ["Status"] jira.CHANGELOG;
: PARENT>CHILDREN    >KEY PARENT-KEY>CHILD-JQL ["Summary" "Due date" "Resolved" EXTRA-CHILD-FIELDS] FLATTEN UNIQUE  JIRA-SEARCH;
: TICKET>COMMENTS    >KEY jira.COMMENTS;
: <ADD-CHANGELOGS    DUP "TICKET>CHANGELOG" PMAP "'changelog' <REC!" ZIP-WITH;
: <ADD-CHILDREN      DUP "PARENT>CHILDREN"  PMAP "'children'  <REC!" ZIP-WITH;
: <ADD-COMMENTS      DUP "TICKET>COMMENTS"  PMAP "'comments'  <REC!" ZIP-WITH;
: PARENT-TICKETS     PARENT-JQL ["Summary" "Status" EXTRA-FIELDS] FLATTEN UNIQUE JIRA-SEARCH;
: MAIN-REFRESH       PARENT-TICKETS <ADD-CHANGELOGS <ADD-CHILDREN <ADD-COMMENTS CONSOLE.LOG "parents" cache.CACHE!;
@: PARENTS           'parents' cache.CACHE@ MAX-TICKETS TAKE SORT-BY-STATUS;
@: STATUS-BY-NAME    jira.PROJECT "project_statuses" REC@ "'statuses' REC@" MAP FLATTEN "name" BY-FIELD "'statusCategory' REC@" MAP;

Data Words

Word

Description

DATES

Creates an array of dates using END-DATE and DATE-INTERVAL

TICKET>CHANGELOG

Returns the Status history changelog

PARENT>CHILDREN

Returns a parent’s child tickets

TICKET>COMMENTS

Returns the comments for a ticket

<ADD-CHANGELOGS

Adds changelogs to each ticket in an array of tickets

MAIN-REFRESH

This is a special word that’s called automatically when data is more than a day old or when the refresh control is clicked.

This stores the parent tickets in the cache under the “parents” key.

PARENTS

This returns the parent tickets from the cache.

STATUS-BY-NAME

Returns all statuses for all issue types in the current project by name. This is used to look up metainfo for a Status

# ----- ticket Words -------------------------------------------------------------------------------
: STATUS>KEY         STATUS-BY-NAME SWAP REC@ 'key' REC@ "unknown" DEFAULT;
: status-APPEARANCE   status @ STATUS>KEY STATUS-KEY>APPEARANCE;
: STATUS>BADGE        (status !) atlaskit.Lozenge [["appearance"  status-APPEARANCE] ["isBold"  TRUE]] <<PROPS status @ .;
: ticket-SUMMARY          ticket @ "Summary" REC@;
: ticket-KEY              ticket @ "key" REC@;
: ticket-STATUS           ticket @ "Status" REC@;
: ticket-STATUS-BADGE     ticket-STATUS STATUS>BADGE;
: ticket-BADGED-SUMMARY   atlaskit.Inline ["space" "space.100"] <<PROP [ticket-STATUS-BADGE  ticket-KEY ticket-SUMMARY TICKET-LINK] .;

Ticket Words

These are words we can use to construct various things for a ticket. For instance, the ticket-BADGED-SUMMARY is what’s used to render the first column in the report:

image-20240110-194615.png

# ----- parent Words -------------------------------------------------------------------------------
: parent-KEY                parent @ "key" REC@;
: parent-SUMMARY            parent @ "Summary" REC@;
: parent-STATUS             parent @ "Status" REC@;
: parent-CHILDREN           parent @ "children" REC@;
: parent-BADGED-SUMMARY     (parent @ ticket !) ticket-BADGED-SUMMARY;
: parent-COMMENTS           parent @ "comments" REC@;
: parent-COMMENTS-BY-DATE   parent-COMMENTS DATES "DATE>INT" MAP  "'for_date' REC@ '-' '' REPLACE >INT" RANGE-BUCKETS;
: parent/date-COMMENTS      parent-COMMENTS-BY-DATE date @ DATE>INT REC@ []  DEFAULT;
: parent-CHANGELOG          parent @ 'changelog' REC@;
: parent-STATUS-AS-OF       parent-CHANGELOG "Status" jira.FIELD-AS-OF;  # (date -- status)
: parent/date-COLOR-AS-OF   date @ parent-STATUS-AS-OF STATUS>KEY STATUS-KEY>COLOR;
: parent-DATE>STATUS-CHIP   (date !@) parent/date-COLOR-AS-OF parent @ status-history.STATUS-CHIP [
    ["fcontent"    "(date ! parent !) parent/date-COMMENTS LENGTH"]
    ["fmouseOver"  "(date ! parent !) parent/date-POPUP-CONTENT"]
    ["fclick"      "(date ! parent !) parent/date-MODAL-CONTENT atlaskit.MODAL-CONTENT! atlaskit.SHOW-MODAL"]
] <<PROPS;

: parent/date-STATUS-BADGE   date @ parent-STATUS-AS-OF STATUS>BADGE;
: parent/date-POPUP-ANNOTATIONS   atlaskit.Box [["padding" "space.100"]] <<PROPS [
    H6 "Annotations" .
    atlaskit.Stack ["space" "space.150"] <<PROP [parent/date-COMMENTS "COMMENT>ANNOTATION" MAP] .
] .;

: parent/date-ANNOTATIONS-SECTION  [
    [TRUE   parent/date-POPUP-ANNOTATIONS]
] REC parent/date-COMMENTS LENGTH 0 > REC@;

: parent/date-POPUP-CONTENT   atlaskit.Stack ["xcss" POPUP-XCSS] <<PROP [
    atlaskit.Box [["padding" "space.100"] ["backgroundColor" "color.background.neutral"]] <<PROPS [
        H5 [date @ DATE>SHORT  " " MDASH " " parent/date-STATUS-BADGE " "  date-DAYS-AGO DAYS-AGO ].
    ] .
    atlaskit.Box [["padding" "space.100"]] <<PROPS [
        P [(parent @ ticket !) ticket-KEY ticket-SUMMARY TICKET-LINK] .
    ] .
    parent/date-ANNOTATIONS-SECTION
] .;

: parent/date-MODAL-CONTENT   [
    atlaskit.ModalHeader [ atlaskit.ModalTitle ["Annotations for " date @ DATE>SHORT  " " MDASH " "  (parent @ ticket !) ticket-KEY ticket-SUMMARY TICKET-LINK] . ].
    atlaskit.ModalBody [
        atlaskit.AnnotationEditor [["ticketKey" parent-KEY] ["forDate" date @] ["annotations" parent-COMMENTS] ["annotationTitle"  "Annotation for " date @ DATE>SHORT CONCAT]] <<PROPS
        atlaskit.Stack ["space" "space.150"] <<PROP [parent/date-COMMENTS "COMMENT>ANNOTATION" MAP] .
    ] .
    atlaskit.ModalFooter [P " " .] .
];

Parent Words

These are all words related to the parent tickets. The first set of these just return fields from the parent

The parent-COMMENTS-BY-DATE groups comments on a ticket into the DATES computed above. We avoid timezone weirdness by converting dates into integers (e.g., 2023-12-20 to 20231220)

Popup Support

The parent/date-POPUP-CONTENT is used to render the popup that’s displayed on hover. If you want to change anything about the hover popup, this is where you can do it. See Forthix React UI Documentation for explanation about using React in Forthix.

Modal Dialog Support

The parent/date-MODAL-CONTENT is used to render the modal dialog that’s displayed when clicking on a status history chip. See the atlaskit module for documentation on the relevant components.

# ----- child Words -------------------------------------------------------------------------------
: child-DUE            child @ "Due date" REC@;
: child-RESOLVED       child @ "Resolved" REC@;
: child-DUE-INFO        [
    [TRUE  ["Due: " child-DUE >DATE DATE>SHORT] CONCAT]
    [FALSE  "No due date"]
] REC child-DUE >BOOL REC@;

: child-RESOLVED-LOZENGE   [
    [TRUE  atlaskit.Lozenge ["appearance" "success"] <<PROP ["Resolved: " child-RESOLVED >DATE DATE>SHORT] CONCAT .]
] REC child-RESOLVED >BOOL REC@;

: child-DUE-LOZENGE   [
    [TRUE   atlaskit.Lozenge [["appearance"  "removed"] ["isBold"  FALSE]] <<PROPS child-DUE-INFO .]
    [FALSE  atlaskit.Lozenge child-DUE-INFO .]
] REC child-DUE >DATE TODAY < child-RESOLVED NOT AND REC@;

: child-STATUS-INFO      atlaskit.Inline ["space"  "space.100"] <<PROP [child-DUE-LOZENGE child-RESOLVED-LOZENGE] |NON-NULL .;
: child-KEY              child @ "key" REC@;
: child-SUMMARY          child @ "Summary" REC@;
: child-BADGED-SUMMARY   (child @ ticket !) ticket-BADGED-SUMMARY;

Child Words

These words are used to render information about the child tickets for each parent.

# ----- comment Words -------------------------------------------------------------------------------
: comment-BODY           comment @ "renderedBody" REC@ RAW-HTML;
: comment-TIME           comment @ "created" REC@ "." SPLIT 0 NTH "T" " @ " REPLACE;
: comment-AUTHOR         comment @ ["author"  "displayName"] REC@;
: comment-AUTHOR-IMAGE   comment @ ["author"  "avatarUrls" "32x32"] REC@;
: comment-AVATAR         atlaskit.Avatar [
      ["appearance"  "circle"]
      ["src"  comment-AUTHOR-IMAGE]
      ["size"  "small"]
      ["name"  comment-AUTHOR]
] <<PROPS;

: comment-AUTHOR-HEADLINE    atlaskit.Inline [["alignBlock"  "center"] ["space" "space.025"]] <<PROPS [comment-AVATAR comment-AUTHOR ", " comment-TIME] . ;
: COMMENT>ANNOTATION         (comment !) atlaskit.Box [comment-AUTHOR-HEADLINE comment-BODY] .;

Comment Words

These are used to render comments in the popup and modal dialog.

# ----- Tickets Table -------------------------------------------------------------------------------
: COLUMN-KEYS   ["Ticket" "Assignee" "history"];

: HISTORY-HEADER   ["Status  from " DATES 0 NTH DATE>SHORT " to " DATES LAST DATE>SHORT " (" DATE-INTERVAL " day intervals)"] CONCAT;
: KEY>HEADER   (key !) [
    ["history"  HISTORY-HEADER]
] REC key @ REC@ key @ DEFAULT;

: KEY>WIDTHS   [
    ["Ticket"   "700px"]
    ["history"  "700px"]
] REC SWAP REC@ "150px" DEFAULT;

: parent-KEY>CONTENT   (field_key !) [
    ["Ticket"   parent-BADGED-SUMMARY RENDER]
    ["history"  atlaskit.Inline ["space" "space.025"] <<PROP [DATES "parent-DATE>STATUS-CHIP" MAP] .]
] REC field_key @ REC@  parent @ field_key @ REC@ DEFAULT;

: child-KEY>CONTENT   (field_key !) [
    ["Ticket"   atlaskit.Stack ["space" "space.100"] <<PROP [child-BADGED-SUMMARY child-STATUS-INFO ] . ]
] REC field_key @ REC@  child @ field_key @ REC@ DEFAULT;

: PARENT>ITEM   (parent !) [
    ["id"           parent-KEY]
    ["hasChildren"  parent-CHILDREN LENGTH 0 >]
    ["content"      COLUMN-KEYS "[SWAP DUP parent-KEY>CONTENT]" MAP REC]
    ["children"     parent-CHILDREN "(child !) child-ITEM" MAP]
] REC;

: child-ITEM   [
    ["id"           child-KEY]
    ["hasChildren"  FALSE]
    ["content"      COLUMN-KEYS "[SWAP DUP child-KEY>CONTENT]" MAP REC]
] REC;

: TABLE-ITEMS      PARENTS "PARENT>ITEM" MAP;

: TICKETS-TABLE   atlaskit.TableTree [
    ["headers" COLUMN-KEYS "KEY>HEADER" MAP]
    ["columns" COLUMN-KEYS "atlaskit.TABLE-TREE-COLUMN" MAP]
    ["columnWidths"  COLUMN-KEYS "KEY>WIDTHS" MAP]
    ["items" TABLE-ITEMS]
] <<PROPS;

Words for Tickets Table

These next set of words are used to render the main ticket table view. We use an Atlaskit TableTree component for this. Many of the words here are used to support the TableTree.

Tweak: Add columns

You can update COLUMN-KEYS and EXTRA-FIELDS to pull additional info and render it in the table. The “Assignee” column is an example of how you can do this.

# ----- Main page -------------------------------------------------------------------------------
: MAIN-PAGE   [
    H2 [jira.PROJECT 'name' REC@ " Status History"] CONCAT .
    TICKETS-TABLE
];

Main Page

The MAIN-PAGE word is special and is used to render the app. As anticlimactic as this looks, this word is where it all happens. It’s basically a title and a table.

Tweak: Change the title

You can change Line 3 pretty easily to update your report’s title.

Tweak: Add a summary table

If you wanted to add another table with a summary of the child tickets, this is where you’d call that word. We may give an example of this in the future.

  • No labels

0 Comments

You are not logged in. Any changes you make will be marked as anonymous. You may want to Log In if you already have an account.