diff --git a/ui/app/components/app-item/build.hbs b/ui/app/components/app-item/build.hbs
index b4bc0a2d703..dd995687106 100644
--- a/ui/app/components/app-item/build.hbs
+++ b/ui/app/components/app-item/build.hbs
@@ -1,34 +1,39 @@
-
+
-
- v{{@build.sequence}}
-
+ v{{@build.sequence}}
-
- {{if (eq @model.status.state 1) 'Building' 'Built'}} with
- {{titleize @build.component.name}}
- {{#if (eq @build.status.state 1)}}
- (Started {{date-format-distance-to-now @build.status.startTime.seconds }})
- {{else}}
- {{date-format-distance-to-now @build.status.completeTime.seconds }}
- {{/if}}
-
+ {{#let (or @build.pushedArtifact @build) as |operation|}}
+
+
+
+ {{t
+ (concat
+ "app_item_build.label.prefix"
+ ".type-" operation.component.type
+ ".state-" operation.status.state
+ )
+ }}
+ {{component-name operation.component.name}}
+
+
+ {{/let}}
- {{#if (eq @build.status.state 1)}}
-
+
+ {{#if (and (eq @build.status.state 2) (eq @build.pushedArtifact.status.state 2))}}
+
- Building...
-
- {{else if (eq @build.status.state 2)}}
-
-
- Built in {{date-format-distance @build.status.startTime.seconds @build.status.completeTime.seconds }}
-
- {{else if (eq @build.status.state 3)}}
-
-
- Build failed
+
+ {{t "app_item_build.built_in"
+ duration=(date-format-distance
+ @build.status.startTime.seconds
+ @build.status.completeTime.seconds
+ )
+ }}
+
{{/if}}
\ No newline at end of file
diff --git a/ui/tests/integration/components/app-item/build-test.ts b/ui/tests/integration/components/app-item/build-test.ts
new file mode 100644
index 00000000000..102b03c1acf
--- /dev/null
+++ b/ui/tests/integration/components/app-item/build-test.ts
@@ -0,0 +1,91 @@
+import { module, test } from 'qunit';
+import { setupRenderingTest } from 'ember-qunit';
+import { render } from '@ember/test-helpers';
+import hbs from 'htmlbars-inline-precompile';
+import { getUnixTime, subMinutes } from 'date-fns';
+import { a11yAudit } from 'ember-a11y-testing/test-support';
+import { Timestamp } from 'google-protobuf/google/protobuf/timestamp_pb';
+
+module('Integration | Component | app-item/build', function (hooks) {
+ setupRenderingTest(hooks);
+
+ test('with a build and a push', async function (assert) {
+ this.set('build', {
+ sequence: 3,
+ status: {
+ state: 2,
+ startTime: minutesAgo(3),
+ completeTime: minutesAgo(2),
+ },
+ component: {
+ type: 1,
+ name: 'docker',
+ },
+ pushedArtifact: {
+ component: {
+ type: 2,
+ name: 'docker',
+ },
+ status: {
+ state: 2,
+ startTime: minutesAgo(2),
+ completeTime: minutesAgo(1),
+ },
+ },
+ });
+
+ await render(hbs`
+
+ `);
+ await a11yAudit();
+
+ assert.dom('[data-test-app-item-build]').includesText('v3');
+ assert.dom('[data-test-icon-type="logo-docker-color"]').exists();
+ assert.dom('[data-test-app-item-build]').includesText('Pushed to Docker');
+ assert.dom('[data-test-operation-status-indicator="success"]').exists();
+ assert.dom('[data-test-app-item-build]').includesText('1 minute ago');
+ assert.dom('[data-test-app-item-build]').includesText('Built in 1 minute');
+ });
+
+ test('with no push', async function (assert) {
+ this.set('build', {
+ sequence: 3,
+ status: {
+ state: 2,
+ startTime: minutesAgo(3),
+ completeTime: minutesAgo(2),
+ },
+ component: {
+ type: 1,
+ name: 'docker',
+ },
+ pushedArtifact: null,
+ });
+
+ await render(hbs`
+
+ `);
+ await a11yAudit();
+
+ assert.dom('[data-test-app-item-build]').includesText('v3');
+ assert.dom('[data-test-icon-type="logo-docker-color"]').exists();
+ assert.dom('[data-test-app-item-build]').includesText('Built with Docker');
+ assert.dom('[data-test-operation-status-indicator="success"]').exists();
+ assert.dom('[data-test-app-item-build]').includesText('2 minutes ago');
+ });
+});
+
+function minutesAgo(n: number): Timestamp.AsObject {
+ let now = new Date();
+ let date = subMinutes(now, n);
+ let result = {
+ seconds: getUnixTime(date),
+ nanos: 0,
+ };
+
+ return result;
+}
diff --git a/ui/translations/en-us.yaml b/ui/translations/en-us.yaml
index ef66107103b..33dad96d8b0 100644
--- a/ui/translations/en-us.yaml
+++ b/ui/translations/en-us.yaml
@@ -162,3 +162,19 @@ app_card_build:
state-1: Pushing to # RUNNING
state-2: Pushed to # SUCCESS
state-3: Failed to push to # ERROR
+
+app_item_build:
+ label:
+ prefix:
+ type-1: # BUILDER
+ state-0: Building with # UNKNOWN
+ state-1: Building with # RUNNING
+ state-2: Built with # SUCCESS
+ state-3: Failed to build with # ERROR
+ type-2: # REGISTRY
+ state-0: Pushing to # UNKNOWN
+ state-1: Pushing to # RUNNING
+ state-2: Pushed to # SUCCESS
+ state-3: Failed to push to # ERROR
+ built_in:
+ Built in {duration}