diff --git a/ui/app/controllers/jobs/job/allocations.js b/ui/app/controllers/jobs/job/allocations.js
new file mode 100644
index 000000000..efed1ef2b
--- /dev/null
+++ b/ui/app/controllers/jobs/job/allocations.js
@@ -0,0 +1,39 @@
+import { alias } from '@ember/object/computed';
+import Controller from '@ember/controller';
+import { computed } from '@ember/object';
+import Sortable from 'nomad-ui/mixins/sortable';
+import Searchable from 'nomad-ui/mixins/searchable';
+import WithNamespaceResetting from 'nomad-ui/mixins/with-namespace-resetting';
+
+export default Controller.extend(Sortable, Searchable, WithNamespaceResetting, {
+ queryParams: {
+ currentPage: 'page',
+ searchTerm: 'search',
+ sortProperty: 'sort',
+ sortDescending: 'desc',
+ },
+
+ currentPage: 1,
+ pageSize: 25,
+
+ sortProperty: 'modifyIndex',
+ sortDescending: true,
+
+ job: alias('model'),
+
+ searchProps: computed(() => ['shortId', 'name']),
+
+ allocations: computed('model.allocations.[]', function() {
+ return this.get('model.allocations') || [];
+ }),
+
+ listToSort: alias('allocations'),
+ listToSearch: alias('listSorted'),
+ sortedAllocations: alias('listSearched'),
+
+ actions: {
+ gotoAllocation(allocation) {
+ this.transitionToRoute('allocations.allocation', allocation);
+ },
+ },
+});
diff --git a/ui/app/router.js b/ui/app/router.js
index ce0a1bc74..39446ffa9 100644
--- a/ui/app/router.js
+++ b/ui/app/router.js
@@ -14,6 +14,7 @@ Router.map(function() {
this.route('versions');
this.route('deployments');
this.route('evaluations');
+ this.route('allocations');
});
});
diff --git a/ui/app/routes/jobs/job/allocations.js b/ui/app/routes/jobs/job/allocations.js
new file mode 100644
index 000000000..da59c95a4
--- /dev/null
+++ b/ui/app/routes/jobs/job/allocations.js
@@ -0,0 +1,19 @@
+import Route from '@ember/routing/route';
+import { collect } from '@ember/object/computed';
+import { watchRelationship } from 'nomad-ui/utils/properties/watch';
+import WithWatchers from 'nomad-ui/mixins/with-watchers';
+
+export default Route.extend(WithWatchers, {
+ model() {
+ const job = this.modelFor('jobs.job');
+ return job.get('allocations').then(() => job);
+ },
+
+ startWatchers(controller, model) {
+ controller.set('watchAllocations', this.get('watchAllocations').perform(model));
+ },
+
+ watchAllocations: watchRelationship('allocations'),
+
+ watchers: collect('watchAllocations'),
+});
diff --git a/ui/app/templates/jobs/job/allocations.hbs b/ui/app/templates/jobs/job/allocations.hbs
new file mode 100644
index 000000000..ad53ba6d2
--- /dev/null
+++ b/ui/app/templates/jobs/job/allocations.hbs
@@ -0,0 +1,65 @@
+{{#gutter-menu class="page-body" onNamespaceChange=(action "gotoJobs")}}
+ {{partial "jobs/job/subnav"}}
+
+ {{#t.sort-by prop="shortId"}}ID{{/t.sort-by}}
+ {{#t.sort-by prop="createIndex" title="Create Index"}}Created{{/t.sort-by}}
+ {{#t.sort-by prop="modifyIndex" title="Modify Index"}}Modified{{/t.sort-by}}
+ {{#t.sort-by prop="statusIndex"}}Status{{/t.sort-by}}
+ {{#t.sort-by prop="jobVersion"}}Version{{/t.sort-by}}
+ {{#t.sort-by prop="node.shortId"}}Client{{/t.sort-by}}
+ CPU
+ Memory
+ {{/t.head}}
+ {{#t.body as |row|}}
+ {{allocation-row data-test-allocation=row.model.id allocation=row.model context="job" onClick=(action "gotoAllocation" row.model)}}
+ {{/t.body}}
+ {{/list-table}}
+