agendash 1.0.0 → 3.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (72) hide show
  1. package/Dockerfile +3 -2
  2. package/History.md +80 -58
  3. package/LICENSE +1 -1
  4. package/README.md +173 -49
  5. package/all-jobs.png +0 -0
  6. package/app.js +12 -7
  7. package/bin/agendash-standalone-fastify.js +61 -0
  8. package/bin/agendash-standalone-hapi.js +60 -0
  9. package/bin/agendash-standalone-koa.js +56 -0
  10. package/bin/agendash-standalone.js +47 -15
  11. package/create-job.png +0 -0
  12. package/entrypoint.sh +1 -1
  13. package/lib/controllers/agendash.js +340 -0
  14. package/lib/middlewares/README.md +12 -5
  15. package/lib/middlewares/express.js +58 -34
  16. package/lib/middlewares/fastify.js +75 -0
  17. package/lib/middlewares/hapi.js +65 -88
  18. package/lib/middlewares/koa.js +71 -0
  19. package/mobile-ui-sm.png +0 -0
  20. package/mobile-ui-xs.png +0 -0
  21. package/package.json +45 -36
  22. package/public/android-chrome-192x192.png +0 -0
  23. package/public/android-chrome-512x512.png +0 -0
  24. package/public/app/assets/ArgensdashV2Logo.svg +18 -0
  25. package/public/app/css/styles.css +221 -0
  26. package/public/app/js/confirmDelete.js +40 -0
  27. package/public/app/js/confirmDeleteMulti.js +45 -0
  28. package/public/app/js/confirmRequeue.js +40 -0
  29. package/public/app/js/confirmRequeueMulti.js +45 -0
  30. package/public/app/js/confirms.js +8 -0
  31. package/public/app/js/jobdetail.js +48 -0
  32. package/public/app/js/joblist.js +239 -0
  33. package/public/app/js/main.js +275 -0
  34. package/public/app/js/newJob.js +89 -0
  35. package/public/app/js/sidebar.js +121 -0
  36. package/public/app/js/topbar.js +95 -0
  37. package/public/apple-touch-icon.png +0 -0
  38. package/public/favicon-16x16.png +0 -0
  39. package/public/favicon-32x32.png +0 -0
  40. package/public/favicon.ico +0 -0
  41. package/public/index.html +40 -164
  42. package/public/site.webmanifest +1 -0
  43. package/search.png +0 -0
  44. package/.editorconfig +0 -7
  45. package/.travis.yml +0 -24
  46. package/job-details.png +0 -0
  47. package/lib/agendash.js +0 -295
  48. package/public/css/bootstrap-theme.css +0 -587
  49. package/public/css/bootstrap-theme.css.map +0 -1
  50. package/public/css/bootstrap-theme.min.css +0 -6
  51. package/public/css/bootstrap-theme.min.css.map +0 -1
  52. package/public/css/bootstrap.css +0 -6760
  53. package/public/css/bootstrap.css.map +0 -1
  54. package/public/css/bootstrap.min.css +0 -6
  55. package/public/css/bootstrap.min.css.map +0 -1
  56. package/public/css/dashboard.css +0 -94
  57. package/public/fonts/glyphicons-halflings-regular.eot +0 -0
  58. package/public/fonts/glyphicons-halflings-regular.svg +0 -288
  59. package/public/fonts/glyphicons-halflings-regular.ttf +0 -0
  60. package/public/fonts/glyphicons-halflings-regular.woff +0 -0
  61. package/public/fonts/glyphicons-halflings-regular.woff2 +0 -0
  62. package/public/js/backbone-min.js +0 -2
  63. package/public/js/backbone-min.map +0 -1
  64. package/public/js/bootstrap.js +0 -2363
  65. package/public/js/bootstrap.min.js +0 -7
  66. package/public/js/jquery-2.2.0.min.js +0 -4
  67. package/public/js/lodash.min.js +0 -120
  68. package/public/js/main.js +0 -414
  69. package/public/js/moment.min.js +0 -7
  70. package/test/test-express.js +0 -98
  71. package/test/test-hapi.js +0 -104
  72. package/yarn.lock +0 -4412
@@ -0,0 +1,40 @@
1
+ const confirmDelete = Vue.component("confirm-delete", {
2
+ props: ["job"],
3
+ methods: {
4
+ deleteOne(id) {
5
+ const url = `api/jobs/delete`;
6
+ let body = { jobIds: [id] };
7
+ return axios
8
+ .post(url, body)
9
+ .then((result) => result.data)
10
+ .then((data) => {
11
+ this.$emit("popup-message", "delete");
12
+ this.$emit("refresh-data");
13
+ })
14
+ .catch(console.log);
15
+ },
16
+ },
17
+ template: `
18
+ <div class="modal fade" id="modalDeleteSure" tabindex="-1" role="dialog" aria-labelledby="exampleModalLabel" aria-hidden="true">
19
+ <!-- Modal -->
20
+ <div class="modal-dialog" role="document">
21
+ <div class="modal-content">
22
+ <div class="modal-header">
23
+ <h5 class="modal-title" id="exampleModalLabel">Confirm Delete Permanently</h5>
24
+ <button type="button" class="close" data-dismiss="modal" aria-label="Close">
25
+ <span aria-hidden="true">&times;</span>
26
+ </button>
27
+ </div>
28
+ <div class="modal-body">
29
+ <p>ID: {{job.job._id}}</p>
30
+ <p>Name: {{job.job.name}}</p>
31
+ </div>
32
+ <div class="modal-footer">
33
+ <button type="button" class="btn btn-danger" data-dismiss="modal" @click="deleteOne(job.job._id)">Delete</button>
34
+ <button type="button" class="btn btn-secondary" data-dismiss="modal">Cancel</button>
35
+ </div>
36
+ </div>
37
+ </div>
38
+ </div>
39
+ `,
40
+ });
@@ -0,0 +1,45 @@
1
+ const confirmDeleteMulti = Vue.component("confirm-multi-delete", {
2
+ props: ["jobs"],
3
+ methods: {
4
+ deleteMulti(ids) {
5
+ const url = `api/jobs/delete`;
6
+ let body = { jobIds: ids };
7
+ return axios
8
+ .post(url, body)
9
+ .then((result) => result.data)
10
+ .then((data) => {
11
+ this.$emit("popup-message", "delete");
12
+ this.$emit("refresh-data");
13
+ })
14
+ .catch(console.log);
15
+ },
16
+ },
17
+ template: `
18
+ <div class="modal fade" id="modalDeleteSureMulti" tabindex="-1" role="dialog" aria-labelledby="exampleModalLabel" aria-hidden="true">
19
+ <!-- Modal -->
20
+ <h1>MULTI</h1>
21
+ <div class="modal-dialog" role="document">
22
+ <div class="modal-content">
23
+ <div class="modal-header">
24
+ <h5 class="modal-title" id="exampleModalLabel">Confirm Delete Permanently</h5>
25
+ <button type="button" class="close" data-dismiss="modal" aria-label="Close">
26
+ <span aria-hidden="true">&times;</span>
27
+ </button>
28
+ </div>
29
+ <div class="modal-body">
30
+ <div class="row px-3" v-for="job in jobs">
31
+ <div class="col">
32
+ <p>Job Id: {{job}}</p>
33
+ </div>
34
+
35
+ </div>
36
+ </div>
37
+ <div class="modal-footer">
38
+ <button type="button" class="btn btn-danger" data-dismiss="modal" @click="deleteMulti(jobs)">Delete</button>
39
+ <button type="button" class="btn btn-secondary" data-dismiss="modal">Cancel</button>
40
+ </div>
41
+ </div>
42
+ </div>
43
+ </div>
44
+ `,
45
+ });
@@ -0,0 +1,40 @@
1
+ const confirmRequeue = Vue.component("confirm-requeue", {
2
+ props: ["job"],
3
+ methods: {
4
+ RequeueOne(id) {
5
+ const url = `api/jobs/requeue`;
6
+ let body = { jobIds: [id] };
7
+ return axios
8
+ .post(url, body)
9
+ .then((result) => result.data)
10
+ .then((data) => {
11
+ this.$emit("popup-message");
12
+ this.$emit("refresh-data");
13
+ })
14
+ .catch(console.log);
15
+ },
16
+ },
17
+ template: `
18
+ <div class="modal fade" id="modalRequeueSure" tabindex="-1" role="dialog" aria-labelledby="exampleModalLabel" aria-hidden="true">
19
+ <!-- Modal -->
20
+ <div class="modal-dialog" role="document">
21
+ <div class="modal-content">
22
+ <div class="modal-header">
23
+ <h5 class="modal-title" id="exampleModalLabel">Confirm requeue job</h5>
24
+ <button type="button" class="close" data-dismiss="modal" aria-label="Close">
25
+ <span aria-hidden="true">&times;</span>
26
+ </button>
27
+ </div>
28
+ <div class="modal-body">
29
+ <p>ID: {{job.job._id}}</p>
30
+ <p>Name: {{job.job.name}}</p>
31
+ </div>
32
+ <div class="modal-footer">
33
+ <button type="button" class="btn btn-info" data-dismiss="modal" @click="RequeueOne(job.job._id)">Requeue Job</button>
34
+ <button type="button" class="btn btn-secondary" data-dismiss="modal">Cancel</button>
35
+ </div>
36
+ </div>
37
+ </div>
38
+ </div>
39
+ `,
40
+ });
@@ -0,0 +1,45 @@
1
+ const confirmRequeueMulti = Vue.component("confirm-multi-requeue", {
2
+ props: ["jobs"],
3
+ methods: {
4
+ RequeueMulti(ids) {
5
+ const url = `api/jobs/requeue`;
6
+ let body = { jobIds: ids };
7
+ return axios
8
+ .post(url, body)
9
+ .then((result) => result.data)
10
+ .then((data) => {
11
+ this.$emit("popup-message");
12
+ this.$emit("refresh-data");
13
+ this.$emit("ready-clean");
14
+ })
15
+ .catch(console.log);
16
+ },
17
+ },
18
+ template: `
19
+ <div class="modal fade" id="modalRequeueSureMulti" tabindex="-1" role="dialog" aria-labelledby="exampleModalLabel" aria-hidden="true">
20
+ <!-- Modal -->
21
+ <h1>MULTI</h1>
22
+ <div class="modal-dialog" role="document">
23
+ <div class="modal-content">
24
+ <div class="modal-header">
25
+ <h5 class="modal-title" id="exampleModalLabel">Confirm requeue job</h5>
26
+ <button type="button" class="close" data-dismiss="modal" aria-label="Close">
27
+ <span aria-hidden="true">&times;</span>
28
+ </button>
29
+ </div>
30
+ <div class="modal-body">
31
+ <div class="row px-3" v-for="job in jobs">
32
+ <div class="col">
33
+ <p>Job Id: {{job}}</p>
34
+ </div>
35
+ </div>
36
+ </div>
37
+ <div class="modal-footer">
38
+ <button type="button" class="btn btn-info" data-dismiss="modal" @click="RequeueMulti(jobs)">Requeue Job</button>
39
+ <button type="button" class="btn btn-secondary" data-dismiss="modal">Cancel</button>
40
+ </div>
41
+ </div>
42
+ </div>
43
+ </div>
44
+ `,
45
+ });
@@ -0,0 +1,8 @@
1
+ const popupmessage = Vue.component("popup-message", {
2
+ props: ["job", "deletec", "requeuec", "createc"],
3
+ template: `
4
+ <div v-if="deletec" class="alert alert-success popupmessage">Job Deleted successfull</div>
5
+ <div v-else-if="requeuec" class="alert alert-success popupmessage">Job Requeue successfull</div>
6
+ <div v-else-if="createc" class="alert alert-success popupmessage">Job Created successfull</div>
7
+ `,
8
+ });
@@ -0,0 +1,48 @@
1
+ const jobDetail = Vue.component("job-detail", {
2
+ props: ["job"],
3
+ filters: {
4
+ formatJSON(jsonstr) {
5
+ return JSON.stringify(jsonstr, null, 2);
6
+ },
7
+ },
8
+ methods: {
9
+ formatDate(date) {
10
+ return moment(date).format("DD-MM-YYYY HH:mm:ss");
11
+ },
12
+ },
13
+ template: `
14
+ <div class="modal fade" id="modalData" tabindex="-1" role="dialog" aria-labelledby="exampleModalLabel" aria-hidden="true">
15
+ <!-- Modal -->
16
+ <div class="modal-dialog" role="document">
17
+ <div class="modal-content">
18
+ <div class="modal-header">
19
+ <h5 class="modal-title" id="exampleModalLabel">Job Data - {{job.job.name}}</h5>
20
+ <button type="button" class="close" data-dismiss="modal" aria-label="Close">
21
+ <span aria-hidden="true">&times;</span>
22
+ </button>
23
+ </div>
24
+ <div class="modal-body">
25
+ <div class="row my-3">
26
+ <div class="col">
27
+ <p><strong>Next run starts: </strong>{{ formatDate(job.job.nextRunAt) }}</p>
28
+ <p><strong>Last run started: </strong>{{ formatDate(job.job.lastRunAt) }}</p>
29
+ </div>
30
+ </div>
31
+ <p><strong>Metadata: </strong></p>
32
+ <prism-editor class="json-editor" :lineNumbers="true" :readonly="true" :code="job.job.data | formatJSON" language="json"></prism-editor>
33
+ <div v-if='job.failed' class="row mt-3">
34
+ <div class="col pt-3 bg-danger text-light">
35
+ <p><strong>Fail Count:</strong> {{job.job.failCount}}</p>
36
+ <p><strong>Failed At:</strong> {{formatDate(job.job.failedAt)}}</p>
37
+ <p><strong>Reason:</strong> {{job.job.failReason}}</p>
38
+ </div>
39
+ </div>
40
+ </div>
41
+ <div class="modal-footer">
42
+ <button type="button" class="btn btn-secondary" data-dismiss="modal">Close</button>
43
+ </div>
44
+ </div>
45
+ </div>
46
+ </div>
47
+ `,
48
+ });
@@ -0,0 +1,239 @@
1
+ const jobList = Vue.component("job-list", {
2
+ data: () => ({
3
+ multijobs: [],
4
+ currentSort: "name",
5
+ currentSortDir: "asc",
6
+ }),
7
+ props: ["jobs", "pagesize", "pagenumber", "sendClean", "loading"],
8
+ computed: {
9
+ sortedJobs: function () {
10
+ return this.jobs.sort((a, b) => {
11
+ let displayA, displayB;
12
+ if (this.currentSort === "name") {
13
+ displayA = a.job[this.currentSort]
14
+ ? a.job[this.currentSort].toLowerCase()
15
+ : "";
16
+ displayB = a.job[this.currentSort]
17
+ ? b.job[this.currentSort].toLowerCase()
18
+ : "";
19
+ } else {
20
+ displayA = moment(a.job[this.currentSort]);
21
+ displayB = moment(b.job[this.currentSort]);
22
+ }
23
+ let modifier = 1;
24
+ if (this.currentSortDir === "desc") modifier = -1;
25
+ if (displayA < displayB) return -1 * modifier;
26
+ if (displayA > displayB) return 1 * modifier;
27
+ return 0;
28
+ });
29
+ },
30
+ },
31
+ watch: {
32
+ jobs() {
33
+ // reset multijobs when jobs have changed
34
+ this.multijobs = [];
35
+ },
36
+ },
37
+ methods: {
38
+ sort(s) {
39
+ //if s == current sort, reverse
40
+ if (s === this.currentSort) {
41
+ this.currentSortDir = this.currentSortDir === "asc" ? "desc" : "asc";
42
+ }
43
+ this.currentSort = s;
44
+ },
45
+ sendQueued() {
46
+ this.$emit("confirm-multi-requeue", this.multijobs);
47
+ // this.multijobs = []
48
+ },
49
+ sendDelete() {
50
+ this.$emit("confirm-multi-delete", this.multijobs);
51
+ // this.multijobs = []
52
+ },
53
+ cleanMulti() {
54
+ return console.log("received Clean Multi");
55
+ },
56
+ formatTitle(date) {
57
+ if (!date) return;
58
+ return moment(date).format();
59
+ },
60
+ formatDate(date) {
61
+ if (!date) return;
62
+ return moment(date).fromNow();
63
+ },
64
+ checkAllCheckboxes() {
65
+ const checkboxes = document.querySelectorAll(".checkbox-triggerable");
66
+ for (const checkbox of checkboxes) {
67
+ checkbox.click();
68
+ }
69
+ },
70
+ toggleList(job) {
71
+ if (this.multijobs.includes(job.job._id)) {
72
+ this.multijobs.splice(this.multijobs.indexOf(job.job._id), 1);
73
+ } else {
74
+ this.multijobs.push(job.job._id);
75
+ }
76
+ },
77
+ },
78
+ template: `
79
+ <div v-on:sendClean="cleanMulti">
80
+ <div >
81
+ <div class="d-flex justify-content-end mb-2">
82
+ <span class="mr-2">{{ multijobs.length }} jobs selected</span>
83
+ <button :disabled="!multijobs.length" data-toggle="modal" data-target="#modalRequeueSureMulti" @click="sendQueued" class="btn btn-primary mr-2" data-placement="top" title="Requeue list of selecteds Jobs"> Multiple Requeue </button>
84
+ <button :disabled="!multijobs.length" data-toggle="modal" data-target="#modalDeleteSureMulti" @click="sendDelete" class="btn btn-danger" data-placement="top" title="Delete list of selecteds Jobs"> Multiple Delete </button>
85
+ </div>
86
+ </div>
87
+
88
+ <table class="table table-striped d-none d-xl-table">
89
+ <thead class="thead-dark">
90
+ <tr>
91
+ <th @click="checkAllCheckboxes()" scope="col"> Multi </th>
92
+ <th @click="sort('status')" scope="col"> Status </th>
93
+ <th @click="sort('name')" scope="col"> Name <i v-if="currentSort === 'name' && currentSortDir === 'asc'" class="material-icons sortable" title="Sort Z to A">arrow_drop_down</i>
94
+ <i v-else-if="currentSort === 'name' && currentSortDir === 'desc'" class="material-icons sortable" title="Sort A to Z">arrow_drop_up</i>
95
+ <i v-else class="material-icons sortableinactive" title="Sort A to Z">arrow_drop_down</i>
96
+ </th>
97
+ <th @click="sort('lastRunAt')" scope="col"> Last run started <i v-if="currentSort === 'lastRunAt' && currentSortDir === 'asc'" class="material-icons sortable" title="Sort Z to A">arrow_drop_up</i>
98
+ <i v-else-if="currentSort === 'lastRunAt' && currentSortDir === 'desc'" class="material-icons sortable" title="Sort A to Z">arrow_drop_down</i>
99
+ <i v-else class="material-icons sortableinactive" title="Sort A to Z">arrow_drop_down</i>
100
+ </th>
101
+ <th @click="sort('nextRunAt')" scope="col"> Next run starts
102
+ <i v-if="currentSort === 'nextRunAt' && currentSortDir === 'asc'" class="material-icons sortable" title="Sort Z to A">arrow_drop_up</i>
103
+ <i v-else-if="currentSort === 'nextRunAt' && currentSortDir === 'desc'" class="material-icons sortable" title="Sort A to Z">arrow_drop_down</i>
104
+ <i v-else class="material-icons sortableinactive" title="Sort A to Z">arrow_drop_down</i>
105
+
106
+ </th>
107
+ <th @click="sort('lastFinishedAt')" scope="col"> Last finished
108
+ <i v-if="currentSort === 'lastFinishedAt' && currentSortDir === 'asc'" class="material-icons sortable" title="Sort Z to A">arrow_drop_up</i>
109
+ <i v-else-if="currentSort === 'lastFinishedAt' && currentSortDir === 'desc'" class="material-icons sortable" title="Sort A to Z">arrow_drop_down</i>
110
+ <i v-else class="material-icons sortableinactive" title="Sort A to Z">arrow_drop_down</i>
111
+ </th>
112
+ <th scope="col"> Locked </th>
113
+ <th scope="col"> Actions </th>
114
+ </tr>
115
+ </thead>
116
+ <tbody v-if="loading">
117
+ <tr>
118
+ <td colspan='10' scope="row">
119
+ <div class="col-12 my-5 ml-auto text-center">
120
+ <div class="text-center my-5 py-5">
121
+ <div class="spinner-border" role="status">
122
+ </div>
123
+ <div>
124
+ <span class="">Loading Jobs...</span>
125
+ </div>
126
+ </div>
127
+ </div>
128
+ </td>
129
+ </tr>
130
+ </tbody>
131
+
132
+ <tbody v-else>
133
+ <tr v-for="job in sortedJobs">
134
+ <td width="10" class="mult-select">
135
+ <input v-model="multijobs" :id='job.job._id' class="checkbox-triggerable" type="checkbox" :value="job.job._id"></input>
136
+ </td>
137
+ <td th scope="row" class="job-name">
138
+ <i v-if="job.repeating" class="oi oi-timer pill-own bg-info"><span>{{job.job.repeatInterval}}</span></i>
139
+ <i v-if="job.scheduled" class="pill-own bg-info pill-withoutIcon"><span>Scheduled</span></i>
140
+ <i v-if="job.completed" class="pill-own bg-success pill-withoutIcon"><span>Completed</span></i>
141
+ <i v-if="job.queued" class="pill-own bg-primary pill-withoutIcon"><span>Queued</span></i>
142
+ <i v-if="job.failed" class="pill-own bg-danger pill-withoutIcon"><span>Failed</span></i>
143
+ <i v-if="job.running" class="pill-own bg-warning pill-withoutIcon"><span>Running</span></i>
144
+ </td>
145
+ <td class="job-name" @click="toggleList(job)"> {{job.job.name}} </td>
146
+ <td class="job-lastRunAt" :title="formatTitle(job.job.lastRunAt)" @click="toggleList(job)"> {{ formatDate(job.job.lastRunAt) }} </td>
147
+ <td class="job-nextRunAt" :title="formatTitle(job.job.nextRunAt)" @click="toggleList(job)"> {{ formatDate(job.job.nextRunAt) }} </td>
148
+ <td class="job-finishedAt" :title="formatTitle(job.job.lastFinishedAt)" @click="toggleList(job)"> {{ formatDate(job.job.lastFinishedAt) }} </td>
149
+ <td class="job-lockedAt" :title="formatTitle(job.job.lockedAt)" @click="toggleList(job)"> {{ formatDate(job.job.lockedAt) }} </td>
150
+ <td class="job-actions">
151
+ <i class="material-icons md-dark md-custom action-btn viewData text-primary" data-toggle="modal" data-target="#modalRequeueSure" @click="$emit('confirm-requeue', job)" data-placement="left" title="Requeue">update</i>
152
+ <i class="material-icons md-dark md-custom action-btn viewData text-success" data-toggle="modal" data-target="#modalData" @click="$emit('show-job-detail', job)" data-placement="top" title="Job Data">visibility</i>
153
+ <i class="material-icons md-dark md-custom action-btn viewData text-danger" data-toggle="modal" data-target="#modalDeleteSure" @click="$emit('confirm-delete', job)" data-placement="top" title="Delete permanently">delete_forever</i>
154
+ </td>
155
+ </tr>
156
+ </tbody>
157
+ </table>
158
+
159
+
160
+ <div class="d-xl-none">
161
+ <div class="row">
162
+ <div v-for="job in sortedJobs" class="col col-xs-6 order-1 p-1">
163
+ <div class="card bg-light" >
164
+ <div class="card-header card-responsive-title-container">
165
+ <div class="card-responsive-name" @click="toggleList(job)">
166
+ {{job.job.name}}
167
+ </div>
168
+ <div class="d-flex align-items-center">
169
+ <div class="card-responsive-status-title mr-2" style="font-size: 18px; display: flex; align-items: center">
170
+ <input v-model="multijobs" :id='job.job._id' type="checkbox" :value="job.job._id" class="card-responsive-checkbox"></input>
171
+ </div>
172
+ <i class="material-icons md-dark md-custom action-btn viewData text-primary material-icons-size mr-1" data-toggle="modal" data-target="#modalRequeueSure" @click="$emit('confirm-requeue', job)" data-placement="left" title="Requeue">update</i>
173
+ <i class="material-icons md-dark md-custom action-btn viewData text-success material-icons-size mr-1" data-toggle="modal" data-target="#modalData" @click="$emit('show-job-detail', job)" data-placement="top" title="Job Data">visibility</i>
174
+ <i class="material-icons md-dark md-custom action-btn viewData text-danger material-icons-size" data-toggle="modal" data-target="#modalDeleteSure" @click="$emit('confirm-delete', job)" data-placement="top" title="Delete permanently">delete_forever</i>
175
+ </div>
176
+ </div>
177
+ <div class="card-body">
178
+ <div class="d-flex justify-content-center mb-2">
179
+ <i v-if="job.repeating" class="oi oi-timer pill-own mr-2 bg-info pill-own-card"><span class="pill-own-card-info">{{job.job.repeatInterval}}</span></i>
180
+ <i v-if="job.scheduled" class="pill-own mr-2 bg-info pill-withoutIcon pill-own-card"><span class="pill-own-card-info">Scheduled</span></i>
181
+ <i v-if="job.completed" class="pill-own mr-2 bg-success pill-withoutIcon pill-own-card"><span class="pill-own-card-info">Completed</span></i>
182
+ <i v-if="job.queued" class="pill-own mr-2 bg-primary pill-withoutIcon pill-own-card"><span class="pill-own-card-info">Queued</span></i>
183
+ <i v-if="job.failed" class="pill-own mr-2 bg-danger pill-withoutIcon pill-own-card"><span class="pill-own-card-info">Failed</span></i>
184
+ <i v-if="job.running" class="pill-own mr-2 bg-warning pill-withoutIcon pill-own-card"><span class="pill-own-card-info">Running</span></i>
185
+ </div>
186
+ <div class="row">
187
+ <div class="col col-md-6 text-center">
188
+ <div class="card-responsive-status-title">
189
+ Last run started
190
+ </div>
191
+ <div class="mb-3" :title="formatTitle(job.job.lastRunAt)">
192
+ {{ formatDate(job.job.lastRunAt) }}
193
+ </div>
194
+ <div class="card-responsive-status-title">
195
+ Last finished
196
+ </div>
197
+ <div :title="formatTitle(job.job.lastFinishedAt)">
198
+ {{ formatDate(job.job.lastFinishedAt) }}
199
+ </div>
200
+ </div>
201
+ <div class="col col-md-6 text-center">
202
+ <div class="card-responsive-status-title">
203
+ Next run starts
204
+ </div>
205
+ <div class="mb-3" :title="formatTitle(job.job.nextRunAt)">
206
+ {{ formatDate(job.job.nextRunAt) }}
207
+ </div>
208
+ <div class="card-responsive-status-title">
209
+ Locked
210
+ </div>
211
+ <div :title="formatTitle(job.job.lockedAt)">
212
+ {{ formatDate(job.job.lockedAt) || "-" }}
213
+ </div>
214
+ </div>
215
+ </div>
216
+ <div></div>
217
+ </div>
218
+ </div>
219
+ </div>
220
+ </div>
221
+ </div>
222
+
223
+
224
+ <div class="row">
225
+ <div class="col d-flex justify-content-center">
226
+ <nav aria-label="Page navigation example">
227
+ <ul class="pagination">
228
+ <li class="page-item" :class="pagenumber === 1 ? 'disabled': ''"><a class="page-link" @click="$emit('pagechange', 'prev')">Previous</a></li>
229
+ <!-- <li class="page-item" :class="pagenumber === 1 ? 'disabled': ''"><a class="page-link" @click="$emit('pagechange', 'prev')">{{pagenumber -1}}</a></li>
230
+ <li class="page-item active"><a class="page-link">{{pagenumber}}</a></li>
231
+ <li class="page-item"><a style="cursor:pointer;" class="page-link" @click="$emit('pagechange', 'next')">{{pagenumber +1}}</a></li> -->
232
+ <li class="page-item"><a style="cursor:pointer;" class="page-link" @click="$emit('pagechange', 'next')">Next</a></li>
233
+ </ul>
234
+ </nav>
235
+ </div>
236
+ </div>
237
+ </div>
238
+ `,
239
+ });