diff --git a/app/components/report-review-warning/component.js b/app/components/report-review-warning/component.js
index b44f974c4..405728753 100644
--- a/app/components/report-review-warning/component.js
+++ b/app/components/report-review-warning/component.js
@@ -1,14 +1,13 @@
-import { tagName } from "@ember-decorators/component";
import Component from "@ember/component";
import { inject as service } from "@ember/service";
-import classic from "ember-classic-decorator";
-@classic
-@tagName("")
export default class ReportReviewWarning extends Component {
@service
session;
@service
unverifiedReports;
+
+ @service
+ rejectedReports;
}
diff --git a/app/components/report-review-warning/template.hbs b/app/components/report-review-warning/template.hbs
index 07aca80b9..b2054f9ae 100644
--- a/app/components/report-review-warning/template.hbs
+++ b/app/components/report-review-warning/template.hbs
@@ -1,25 +1,53 @@
-{{#if this.unverifiedReports.hasReports}}
-
-
-
- {{this.unverifiedReports.amountReports}}
-
-
-{{/if}}
+
+ {{#if this.unverifiedReports.hasReports}}
+
+
+
+ {{this.unverifiedReports.amountReports}}
+
+
+ {{/if}}
+ {{#if this.rejectedReports.hasReports}}
+
+
+
+ {{this.rejectedReports.amountReports}}
+
+
+ {{/if}}
+
diff --git a/app/components/user-selection/component.js b/app/components/user-selection/component.js
index 545282754..cd874a5db 100644
--- a/app/components/user-selection/component.js
+++ b/app/components/user-selection/component.js
@@ -17,18 +17,7 @@ export default class UserSelection extends Component {
constructor(...args) {
super(...args);
- this.fetchUsers();
- }
-
- async fetchUsers() {
- try {
- await this.tracking.users.perform();
- } catch (e) {
- if (e.taskInstance && e.taskInstance.isCanceling) {
- return;
- }
- throw e;
- }
+ this.tracking.users.perform();
}
@restartableTask
diff --git a/app/models/report-intersection.js b/app/models/report-intersection.js
index e3185ae71..7fa4f79b5 100644
--- a/app/models/report-intersection.js
+++ b/app/models/report-intersection.js
@@ -3,6 +3,7 @@ import Model, { attr, belongsTo } from "@ember-data/model";
export default Model.extend({
comment: attr("string"),
notBillable: attr("boolean", { allowNull: true, defaultValue: null }),
+ rejected: attr("boolean", { allowNull: true, defaultValue: null }),
review: attr("boolean", { allowNull: true, defaultValue: null }),
billed: attr("boolean", { allowNull: true, defaultValue: null }),
verified: attr("boolean", { allowNull: true, defaultValue: null }),
diff --git a/app/models/report.js b/app/models/report.js
index 7c09df072..a755ec489 100644
--- a/app/models/report.js
+++ b/app/models/report.js
@@ -56,6 +56,14 @@ export default class Report extends Model {
*/
@attr("boolean", { defaultValue: false }) review;
+ /**
+ * Whether the report got rejected by the reviewer
+ *
+ * @property {Boolean} reject
+ * @public
+ */
+ @attr("boolean", { defaultValue: false }) rejected;
+
/**
* Whether the report is not billable
*
diff --git a/app/services/rejected-reports.js b/app/services/rejected-reports.js
new file mode 100644
index 000000000..6a0570cc7
--- /dev/null
+++ b/app/services/rejected-reports.js
@@ -0,0 +1,64 @@
+import Service, { inject as service } from "@ember/service";
+import { macroCondition, isTesting } from "@embroider/macros";
+import { tracked } from "@glimmer/tracking";
+import moment from "moment";
+
+const INTERVAL_DELAY = 10 * 60000; // 10 Minutes
+
+export default class RejectedReportsService extends Service {
+ @service
+ store;
+
+ @service
+ session;
+
+ @service
+ notify;
+
+ @tracked amountReports = 0;
+ @tracked intervalId;
+
+ get hasReports() {
+ return this.amountReports > 0;
+ }
+
+ get reportsToDate() {
+ return moment().subtract(1, "month").endOf("month");
+ }
+
+ constructor(...args) {
+ super(...args);
+
+ this.pollReports();
+
+ /* instanbul ignore next */
+ if (macroCondition(isTesting())) {
+ this.intervalId = null;
+ } else {
+ this.intervalId = setInterval(
+ this.pollReports.bind(this),
+ INTERVAL_DELAY
+ );
+ }
+ }
+
+ async pollReports() {
+ try {
+ const reports = await this.store.query("report", {
+ to_date: this.reportsToDate.format("YYYY-MM-DD"),
+ reviewer: this.session.data.user.id,
+ editable: 1,
+ rejected: 1,
+ page: { number: 1, size: 1 },
+ });
+
+ this.amountReports = reports.meta.pagination.count;
+ } catch (e) {
+ this.notify.error("Error while polling reports");
+ }
+ }
+
+ willDestroy() {
+ clearInterval(this.intervalId);
+ }
+}
diff --git a/app/services/tracking.js b/app/services/tracking.js
index 46ed05742..8fdea1cf9 100644
--- a/app/services/tracking.js
+++ b/app/services/tracking.js
@@ -258,7 +258,7 @@ export default class TrackingService extends Service {
* @property {EmberConcurrency.Task} users
* @public
*/
- @task
+ @dropTask
*users() {
return yield this.store.query("user", {});
}
@@ -288,7 +288,7 @@ export default class TrackingService extends Service {
* @param {Number} customer The customer id to filter by
* @public
*/
- @task
+ @dropTask
*projects(customer) {
if (!customer) {
// We can't test this because the UI prevents it
@@ -305,7 +305,7 @@ export default class TrackingService extends Service {
* @param {Number} project The project id to filter by
* @public
*/
- @task
+ @dropTask
*tasks(project) {
if (!project) {
// We can't test this because the UI prevents it
diff --git a/app/styles/analysis.scss b/app/styles/analysis.scss
index bed5a8fb6..e04bdd774 100644
--- a/app/styles/analysis.scss
+++ b/app/styles/analysis.scss
@@ -25,7 +25,7 @@
&:nth-child(4),
&:nth-child(5),
&:nth-child(6) {
- width: 11%;
+ width: 10%;
}
&:nth-child(8) {
@@ -34,12 +34,13 @@
&:nth-child(9),
&:nth-child(10),
- &:nth-child(11){
- width: 6%;
+ &:nth-child(11),
+ &:nth-child(12) {
+ width: 5%;
}
&:nth-child(7) {
- width: 20%;
+ width: 21%;
}
}
@@ -50,7 +51,7 @@
.analysis-scrollable-container::after,
.analysis-scrollable-container::before {
- content: '';
+ content: "";
position: absolute;
z-index: 1;
left: 0;
diff --git a/app/styles/app.scss b/app/styles/app.scss
index 4b8654732..d9e40c809 100644
--- a/app/styles/app.scss
+++ b/app/styles/app.scss
@@ -55,6 +55,10 @@ strong {
font-weight: bold;
}
+.flex {
+ display: flex;
+}
+
.pointer {
cursor: pointer;
}
@@ -322,6 +326,7 @@ strong {
color: darken($color-border, 25%);
height: 100%;
display: flex;
+ flex-direction: column;
align-items: center;
justify-content: center;
text-align: center;
diff --git a/tests/unit/services/rejected-reports-test.js b/tests/unit/services/rejected-reports-test.js
new file mode 100644
index 000000000..c30a50dcd
--- /dev/null
+++ b/tests/unit/services/rejected-reports-test.js
@@ -0,0 +1,12 @@
+import { setupTest } from "ember-qunit";
+import { module, test } from "qunit";
+
+module("Unit | Service | rejectedReports", function (hooks) {
+ setupTest(hooks);
+
+ // TODO: Replace this with your real tests.
+ test("it exists", function (assert) {
+ const service = this.owner.lookup("service:rejected-reports");
+ assert.ok(service);
+ });
+});