diff --git a/frontend/app/adapters/activity-block.js b/frontend/app/adapters/activity-block.ts similarity index 86% rename from frontend/app/adapters/activity-block.js rename to frontend/app/adapters/activity-block.ts index 84f076928..8ae348561 100644 --- a/frontend/app/adapters/activity-block.js +++ b/frontend/app/adapters/activity-block.ts @@ -41,3 +41,9 @@ export default class ActivityBlockAdapter extends ApplicationAdapter { return `${super.urlForCreateRecord(...args)}?include=activity`; } } + +declare module "ember-data/types/registries/adapter" { + export default interface AdapterRegistry { + "activity-block": ActivityBlockAdapter; + } +} diff --git a/frontend/app/adapters/application.js b/frontend/app/adapters/application.ts similarity index 100% rename from frontend/app/adapters/application.js rename to frontend/app/adapters/application.ts diff --git a/frontend/app/config/environment.d.ts b/frontend/app/config/environment.d.ts index 131cb0a93..be4017478 100644 --- a/frontend/app/config/environment.d.ts +++ b/frontend/app/config/environment.d.ts @@ -6,9 +6,9 @@ declare const config: { environment: string; modulePrefix: string; podModulePrefix: string; - locationType: 'history' | 'hash' | 'none'; + locationType: "history" | "hash" | "none"; rootURL: string; APP: Record; }; -export default config; \ No newline at end of file +export default config; diff --git a/frontend/app/models/absence-balance.js b/frontend/app/models/absence-balance.js deleted file mode 100644 index ad425b658..000000000 --- a/frontend/app/models/absence-balance.js +++ /dev/null @@ -1,12 +0,0 @@ -import Model, { attr, belongsTo, hasMany } from "@ember-data/model"; - -export default class AbsenceBalance extends Model { - @attr("number") credit; - @attr("number") usedDays; - @attr("django-duration") usedDuration; - @attr("number") balance; - @belongsTo("user", { async: false, inverse: "absenceBalances" }) user; - @belongsTo("absence-type", { async: false, inverse: "absenceBalances" }) - absenceType; - @hasMany("absence-credit", { async: true, inverse: null }) absenceCredits; -} diff --git a/frontend/app/models/absence-balance.ts b/frontend/app/models/absence-balance.ts new file mode 100644 index 000000000..6528f3d26 --- /dev/null +++ b/frontend/app/models/absence-balance.ts @@ -0,0 +1,29 @@ +import type { AsyncHasMany } from "@ember-data/model"; +import Model, { attr, belongsTo, hasMany } from "@ember-data/model"; +import type { Duration } from "moment"; +import type AbsenceCredit from "timed/models/absence-credit"; +import type AbsenceType from "timed/models/absence-type"; +import type User from "timed/models/user"; + +export default class AbsenceBalance extends Model { + @attr("number") + declare credit?: number; + @attr("number") + declare usedDays?: number; + @attr("django-duration") + declare usedDuration?: Duration; + @attr("number") + declare balance?: number; + @belongsTo("user", { async: false, inverse: "absenceBalances" }) + declare user: User; + @belongsTo("absence-type", { async: false, inverse: "absenceBalances" }) + declare absenceType: AbsenceType; + @hasMany("absence-credit", { async: true, inverse: null }) + declare absenceCredits: AsyncHasMany; +} + +declare module "ember-data/types/registries/model" { + export default interface ModelRegistry { + "absence-balance": AbsenceBalance; + } +} diff --git a/frontend/app/models/absence-credit.js b/frontend/app/models/absence-credit.ts similarity index 54% rename from frontend/app/models/absence-credit.js rename to frontend/app/models/absence-credit.ts index 417c3960c..2dc9969d1 100644 --- a/frontend/app/models/absence-credit.js +++ b/frontend/app/models/absence-credit.ts @@ -1,9 +1,12 @@ +import Model, { attr, belongsTo } from "@ember-data/model"; +import type { Moment } from "moment"; +import type AbsenceType from "timed/models/absence-type"; +import type User from "timed/models/user"; /** * @module timed * @submodule timed-models * @public */ -import Model, { attr, belongsTo } from "@ember-data/model"; /** * The absence credit model @@ -19,7 +22,8 @@ export default class AbsenceCredit extends Model { * @property {Number} days * @public */ - @attr("number") days; + @attr("number") + declare days?: number; /** * The date @@ -27,7 +31,8 @@ export default class AbsenceCredit extends Model { * @property {moment} date * @public */ - @attr("django-date") date; + @attr("django-date") + declare date?: Moment; /** * The comment @@ -35,7 +40,8 @@ export default class AbsenceCredit extends Model { * @property {String} comment * @public */ - @attr("string", { defaultValue: "" }) comment; + @attr("string", { defaultValue: "" }) + declare comment: string; /** * The absence type for which this credit counts @@ -43,7 +49,8 @@ export default class AbsenceCredit extends Model { * @property {AbsenceType} absenceType * @public */ - @belongsTo("absence-type", { async: false, inverse: null }) absenceType; + @belongsTo("absence-type", { async: false, inverse: null }) + declare absenceType: AbsenceType; /** * The user to which this credit belongs to @@ -51,5 +58,12 @@ export default class AbsenceCredit extends Model { * @property {User} user * @public */ - @belongsTo("user", { async: false, inverse: null }) user; + @belongsTo("user", { async: false, inverse: null }) + declare user: User; +} + +declare module "ember-data/types/registries/model" { + export default interface ModelRegistry { + "absence-credit": AbsenceCredit; + } } diff --git a/frontend/app/models/absence-type.js b/frontend/app/models/absence-type.ts similarity index 63% rename from frontend/app/models/absence-type.js rename to frontend/app/models/absence-type.ts index 21b97249e..313aa9c63 100644 --- a/frontend/app/models/absence-type.js +++ b/frontend/app/models/absence-type.ts @@ -1,9 +1,11 @@ +import type { AsyncHasMany } from "@ember-data/model"; /** * @module timed * @submodule timed-models * @public */ import Model, { attr, hasMany } from "@ember-data/model"; +import type AbsenceBalance from "timed/models/absence-balance"; /** * The absence type model @@ -21,7 +23,8 @@ export default class AbsenceType extends Model { * @property {String} name * @public */ - @attr("string") name; + @attr("string") + declare name?: string; /** * Whether the absence type only fills the worktime @@ -29,7 +32,8 @@ export default class AbsenceType extends Model { * @property {Boolean} fillWorktime * @public */ - @attr("boolean") fillWorktime; + @attr("boolean") + declare fillWorktime?: boolean; /** * The balances for this type @@ -38,5 +42,11 @@ export default class AbsenceType extends Model { * @public */ @hasMany("absence-balance", { async: true, inverse: "absenceType" }) - absenceBalances; + declare absenceBalances: AsyncHasMany; +} + +declare module "ember-data/types/registries/model" { + export default interface ModelRegistry { + "absence-type": AbsenceType; + } } diff --git a/frontend/app/models/absence.js b/frontend/app/models/absence.ts similarity index 53% rename from frontend/app/models/absence.js rename to frontend/app/models/absence.ts index 630f1b91c..e315788d7 100644 --- a/frontend/app/models/absence.js +++ b/frontend/app/models/absence.ts @@ -1,10 +1,14 @@ +import type { AsyncBelongsTo } from "@ember-data/model"; +import Model, { attr, belongsTo } from "@ember-data/model"; +import type { Moment, Duration } from "moment"; /** * @module timed * @submodule timed-models * @public */ -import Model, { attr, belongsTo } from "@ember-data/model"; import moment from "moment"; +import type AbsenceType from "timed/models/absence-type"; +import type User from "timed/models/user"; /** * The report model @@ -20,7 +24,8 @@ export default class Absence extends Model { * @property {String} comment * @public */ - @attr("string", { defaultValue: "" }) comment; + @attr("string", { defaultValue: "" }) + declare comment: string; /** * The duration @@ -28,7 +33,8 @@ export default class Absence extends Model { * @property {moment.duration} duration * @public */ - @attr("django-duration", { defaultValue: () => moment.duration() }) duration; + @attr("django-duration", { defaultValue: () => moment.duration() }) + declare duration: Duration; /** * The date @@ -36,7 +42,8 @@ export default class Absence extends Model { * @property {moment} date * @public */ - @attr("django-date") date; + @attr("django-date") + declare date?: Moment; /** * The type of the absence @@ -44,7 +51,8 @@ export default class Absence extends Model { * @property {AbsenceType} absenceType * @public */ - @belongsTo("absence-type", { async: false, inverse: null }) absenceType; + @belongsTo("absence-type", { async: false, inverse: null }) + declare absenceType: AbsenceType; /** * The user @@ -52,5 +60,12 @@ export default class Absence extends Model { * @property {User} user * @public */ - @belongsTo("user", { async: true, inverse: null }) user; + @belongsTo("user", { async: true, inverse: null }) + declare user: AsyncBelongsTo; +} + +declare module "ember-data/types/registries/model" { + export default interface ModelRegistry { + absence: Absence; + } } diff --git a/frontend/app/models/activity.js b/frontend/app/models/activity.ts similarity index 71% rename from frontend/app/models/activity.js rename to frontend/app/models/activity.ts index e92782c43..82d58f175 100644 --- a/frontend/app/models/activity.js +++ b/frontend/app/models/activity.ts @@ -1,21 +1,36 @@ import { service } from "@ember/service"; +import type { AsyncBelongsTo } from "@ember-data/model"; import Model, { attr, belongsTo } from "@ember-data/model"; +import type StoreService from "@ember-data/store"; +import type NotifyService from "ember-notify"; +import type { Moment } from "moment"; import moment from "moment"; import { all } from "rsvp"; +import type Task from "timed/models/task"; +import type User from "timed/models/user"; export default class Activity extends Model { - @attr("django-time") fromTime; - @attr("django-time") toTime; - @attr("string", { defaultValue: "" }) comment; - @attr("django-date") date; - @attr("boolean", { defaultValue: false }) transferred; - @attr("boolean", { defaultValue: false }) review; - @attr("boolean", { defaultValue: false }) notBillable; - @belongsTo("task", { async: true, inverse: null }) task; - @belongsTo("user", { async: true, inverse: null }) user; - - @service notify; - @service store; + @attr("django-time") + declare fromTime?: Moment; + @attr("django-time") + declare toTime?: Moment; + @attr("string", { defaultValue: "" }) + declare comment: string; + @attr("django-date") + declare date?: Moment; + @attr("boolean", { defaultValue: false }) + declare transferred: boolean; + @attr("boolean", { defaultValue: false }) + declare review: boolean; + @attr("boolean", { defaultValue: false }) + declare notBillable: boolean; + @belongsTo("task", { async: true, inverse: null }) + declare task: AsyncBelongsTo; + @belongsTo("user", { async: true, inverse: null }) + declare user: AsyncBelongsTo; + + @service declare notify: NotifyService; + @service declare store: StoreService; get active() { return !this.toTime && !!this.id; @@ -137,3 +152,9 @@ export default class Activity extends Model { } } } + +declare module "ember-data/types/registries/model" { + export default interface ModelRegistry { + activity: Activity; + } +} diff --git a/frontend/app/models/attendance.js b/frontend/app/models/attendance.ts similarity index 67% rename from frontend/app/models/attendance.js rename to frontend/app/models/attendance.ts index 34a6e8acc..848fc3280 100644 --- a/frontend/app/models/attendance.js +++ b/frontend/app/models/attendance.ts @@ -1,10 +1,13 @@ +import type { AsyncBelongsTo } from "@ember-data/model"; /** * @module timed * @submodule timed-models * @public */ import Model, { attr, belongsTo } from "@ember-data/model"; +import type { Moment } from "moment"; import moment from "moment"; +import type User from "timed/models/user"; /** * The attendance model @@ -20,7 +23,8 @@ export default class Attendance extends Model { * @property {moment} date * @public */ - @attr("django-date") date; + @attr("django-date") + declare date?: Moment; /** * The start time @@ -28,7 +32,8 @@ export default class Attendance extends Model { * @property {moment} from * @public */ - @attr("django-time") from; + @attr("django-time") + declare from?: Moment; /** * The end time @@ -36,7 +41,8 @@ export default class Attendance extends Model { * @property {moment} to * @public */ - @attr("django-time") to; + @attr("django-time") + declare to?: Moment; /** * The user @@ -45,7 +51,8 @@ export default class Attendance extends Model { * @type {User} * @public */ - @belongsTo("user", { async: true, inverse: null }) user; + @belongsTo("user", { async: true, inverse: null }) + declare user: AsyncBelongsTo; /** * The duration between start and end time @@ -65,3 +72,9 @@ export default class Attendance extends Model { return moment.duration(calcTo.diff(this.from)); } } + +declare module "ember-data/types/registries/model" { + export default interface ModelRegistry { + attendance: Attendance; + } +} diff --git a/frontend/app/models/billing-type.js b/frontend/app/models/billing-type.ts similarity index 64% rename from frontend/app/models/billing-type.js rename to frontend/app/models/billing-type.ts index de59d7548..b98463638 100644 --- a/frontend/app/models/billing-type.js +++ b/frontend/app/models/billing-type.ts @@ -19,5 +19,12 @@ export default class BillingType extends Model { * @property {String} name * @public */ - @attr("string") name; + @attr("string") + declare name?: string; +} + +declare module "ember-data/types/registries/model" { + export default interface ModelRegistry { + "billing-type": BillingType; + } } diff --git a/frontend/app/models/cost-center.js b/frontend/app/models/cost-center.js deleted file mode 100644 index a1b80fb56..000000000 --- a/frontend/app/models/cost-center.js +++ /dev/null @@ -1,6 +0,0 @@ -import Model, { attr } from "@ember-data/model"; - -export default class CostCenter extends Model { - @attr("string") name; - @attr("string") reference; -} diff --git a/frontend/app/models/cost-center.ts b/frontend/app/models/cost-center.ts new file mode 100644 index 000000000..55a69c05a --- /dev/null +++ b/frontend/app/models/cost-center.ts @@ -0,0 +1,14 @@ +import Model, { attr } from "@ember-data/model"; + +export default class CostCenter extends Model { + @attr("string") + declare name?: string; + @attr("string") + declare reference?: string; +} + +declare module "ember-data/types/registries/model" { + export default interface ModelRegistry { + "cost-center": CostCenter; + } +} diff --git a/frontend/app/models/customer-assignee.js b/frontend/app/models/customer-assignee.ts similarity index 53% rename from frontend/app/models/customer-assignee.js rename to frontend/app/models/customer-assignee.ts index 678ea0ef4..aa10c5924 100644 --- a/frontend/app/models/customer-assignee.js +++ b/frontend/app/models/customer-assignee.ts @@ -1,9 +1,12 @@ +import type { AsyncBelongsTo } from "@ember-data/model"; /** * @module timed * @submodule timed-models * @public */ import Model, { attr, belongsTo } from "@ember-data/model"; +import type Customer from "timed/models/customer"; +import type User from "timed/models/user"; /** * The customer assignee model @@ -20,7 +23,8 @@ export default class CustomerAssignee extends Model { * @type {Customer} * @public */ - @belongsTo("customer", { async: true, inverse: null }) customer; + @belongsTo("customer", { async: true, inverse: null }) + declare customer: AsyncBelongsTo; /** * The user * @@ -28,7 +32,8 @@ export default class CustomerAssignee extends Model { * @type {User} * @public */ - @belongsTo("user", { async: true, inverse: null }) user; + @belongsTo("user", { async: true, inverse: null }) + declare user: AsyncBelongsTo; /** * Whether the assignee is a reviewer @@ -37,7 +42,8 @@ export default class CustomerAssignee extends Model { * @type {Boolean} * @public */ - @attr("boolean", { defaultValue: false }) isReviewer; + @attr("boolean", { defaultValue: false }) + declare isReviewer: boolean; /** * Whether the assignee is a manager @@ -46,7 +52,8 @@ export default class CustomerAssignee extends Model { * @type {Boolean} * @public */ - @attr("boolean", { defaultValue: false }) isManager; + @attr("boolean", { defaultValue: false }) + declare isManager: boolean; /** * Whether the assignee is a resource @@ -55,5 +62,12 @@ export default class CustomerAssignee extends Model { * @type {Boolean} * @public */ - @attr("boolean", { defaultValue: false }) isResource; + @attr("boolean", { defaultValue: false }) + declare isResource: boolean; +} + +declare module "ember-data/types/registries/model" { + export default interface ModelRegistry { + "customer-assignee": CustomerAssignee; + } } diff --git a/frontend/app/models/customer-statistic.js b/frontend/app/models/customer-statistic.js deleted file mode 100644 index 315e0a950..000000000 --- a/frontend/app/models/customer-statistic.js +++ /dev/null @@ -1,6 +0,0 @@ -import Model, { attr } from "@ember-data/model"; - -export default class CustomerStatistics extends Model { - @attr("django-duration") duration; - @attr name; -} diff --git a/frontend/app/models/customer-statistic.ts b/frontend/app/models/customer-statistic.ts new file mode 100644 index 000000000..9228708b8 --- /dev/null +++ b/frontend/app/models/customer-statistic.ts @@ -0,0 +1,15 @@ +import Model, { attr } from "@ember-data/model"; +import type { Duration } from "moment"; + +export default class CustomerStatistics extends Model { + @attr("django-duration") + declare duration?: Duration; + @attr + declare name?: string; +} + +declare module "ember-data/types/registries/model" { + export default interface ModelRegistry { + "customer-statistic": CustomerStatistics; + } +} diff --git a/frontend/app/models/customer.js b/frontend/app/models/customer.ts similarity index 56% rename from frontend/app/models/customer.js rename to frontend/app/models/customer.ts index 49444cf6f..1494dc9a4 100644 --- a/frontend/app/models/customer.js +++ b/frontend/app/models/customer.ts @@ -1,9 +1,12 @@ +import type { AsyncHasMany } from "@ember-data/model"; +import Model, { attr, hasMany } from "@ember-data/model"; +import type CustomerAssignee from "timed/models/customer-assignee"; +import type Project from "timed/models/project"; /** * @module timed * @submodule timed-models * @public */ -import Model, { attr, hasMany } from "@ember-data/model"; /** * The customer model @@ -20,7 +23,8 @@ export default class Customer extends Model { * @type {String} * @public */ - @attr("string", { defaultValue: "" }) name; + @attr("string", { defaultValue: "" }) + declare name: string; /** * Whether the project is archived @@ -29,7 +33,8 @@ export default class Customer extends Model { * @type {Boolean} * @public */ - @attr("boolean", { defaultValue: false }) archived; + @attr("boolean", { defaultValue: false }) + declare archived: boolean; /** * The projects @@ -38,7 +43,8 @@ export default class Customer extends Model { * @type {Project[]} * @public */ - @hasMany("project", { async: true, inverse: "customer" }) projects; + @hasMany("project", { async: true, inverse: "customer" }) + declare projects: AsyncHasMany; /** * Long name - alias for name, used for filtering in the customer box @@ -57,5 +63,12 @@ export default class Customer extends Model { * @type {CustomerAssignee[]} * @public */ - @hasMany("customer-assignee", { async: true, inverse: null }) assignees; + @hasMany("customer-assignee", { async: true, inverse: null }) + declare assignees: AsyncHasMany; +} + +declare module "ember-data/types/registries/model" { + export default interface ModelRegistry { + customer: Customer; + } } diff --git a/frontend/app/models/employment.js b/frontend/app/models/employment.ts similarity index 52% rename from frontend/app/models/employment.js rename to frontend/app/models/employment.ts index 560ce4013..e1894bf97 100644 --- a/frontend/app/models/employment.js +++ b/frontend/app/models/employment.ts @@ -1,9 +1,13 @@ +import type { AsyncBelongsTo } from "@ember-data/model"; +import Model, { attr, belongsTo } from "@ember-data/model"; +import type { Moment, Duration } from "moment"; +import type Location from "timed/models/location"; /** * @module timed * @submodule timed-models * @public */ -import Model, { attr, belongsTo } from "@ember-data/model"; +import type User from "timed/models/user"; /** * The employment model @@ -19,7 +23,8 @@ export default class Employment extends Model { * @property {Number} percentage * @public */ - @attr("number") percentage; + @attr("number") + declare percentage?: number; /** * The time the user has to work every day @@ -27,7 +32,8 @@ export default class Employment extends Model { * @property {moment.duration} worktimePerDay * @public */ - @attr("django-duration") worktimePerDay; + @attr("django-duration") + declare worktimePerDay?: Duration; /** * The start date @@ -35,7 +41,8 @@ export default class Employment extends Model { * @property {moment} start * @public */ - @attr("django-date") start; + @attr("django-date") + declare start?: Moment; /** * Whether the employment is of an external employee @@ -43,7 +50,8 @@ export default class Employment extends Model { * @property {Boolean} isExternal * @public */ - @attr("boolean", { defaultValue: false }) isExternal; + @attr("boolean", { defaultValue: false }) + declare isExternal: boolean; /** * The end date @@ -51,7 +59,8 @@ export default class Employment extends Model { * @property {moment} end * @public */ - @attr("django-date") end; + @attr("django-date") + declare end?: Moment; /** * The employed user @@ -59,7 +68,8 @@ export default class Employment extends Model { * @property {User} user * @public */ - @belongsTo("user", { async: true, inverse: "employments" }) user; + @belongsTo("user", { async: true, inverse: "employments" }) + declare user: AsyncBelongsTo; /** * The work location @@ -67,5 +77,12 @@ export default class Employment extends Model { * @property {Location} location * @public */ - @belongsTo("location", { async: true, inverse: null }) location; + @belongsTo("location", { async: true, inverse: null }) + declare location: AsyncBelongsTo; +} + +declare module "ember-data/types/registries/model" { + export default interface ModelRegistry { + employment: Employment; + } } diff --git a/frontend/app/models/location.js b/frontend/app/models/location.ts similarity index 66% rename from frontend/app/models/location.js rename to frontend/app/models/location.ts index fe7c35580..8ce571d0e 100644 --- a/frontend/app/models/location.js +++ b/frontend/app/models/location.ts @@ -19,7 +19,8 @@ export default class Location extends Model { * @property {String} name * @public */ - @attr("string") name; + @attr("string") + declare name?: string; /** * The days on which users in this location need to work @@ -27,5 +28,12 @@ export default class Location extends Model { * @property {Number[]} workdays * @public */ - @attr("django-workdays") workdays; + @attr("django-workdays") + declare workdays?: number[]; +} + +declare module "ember-data/types/registries/model" { + export default interface ModelRegistry { + location: Location; + } } diff --git a/frontend/app/models/month-statistic.js b/frontend/app/models/month-statistic.js deleted file mode 100644 index bd36d4ea6..000000000 --- a/frontend/app/models/month-statistic.js +++ /dev/null @@ -1,7 +0,0 @@ -import Model, { attr } from "@ember-data/model"; - -export default class MonthStatistic extends Model { - @attr("number") year; - @attr("number") month; - @attr("django-duration") duration; -} diff --git a/frontend/app/models/month-statistic.ts b/frontend/app/models/month-statistic.ts new file mode 100644 index 000000000..785afce8f --- /dev/null +++ b/frontend/app/models/month-statistic.ts @@ -0,0 +1,17 @@ +import Model, { attr } from "@ember-data/model"; +import type { Duration } from "moment"; + +export default class MonthStatistic extends Model { + @attr("number") + declare year?: number; + @attr("number") + declare month?: number; + @attr("django-duration") + declare duration?: Duration; +} + +declare module "ember-data/types/registries/model" { + export default interface ModelRegistry { + "month-statistic": MonthStatistic; + } +} diff --git a/frontend/app/models/overtime-credit.js b/frontend/app/models/overtime-credit.js deleted file mode 100644 index ba06c6308..000000000 --- a/frontend/app/models/overtime-credit.js +++ /dev/null @@ -1,8 +0,0 @@ -import Model, { attr, belongsTo } from "@ember-data/model"; - -export default class OvertimeCredit extends Model { - @attr("django-date") date; - @attr("django-duration") duration; - @attr("string", { defaultValue: "" }) comment; - @belongsTo("user", { async: false, inverse: null }) user; -} diff --git a/frontend/app/models/overtime-credit.ts b/frontend/app/models/overtime-credit.ts new file mode 100644 index 000000000..c5ecc905e --- /dev/null +++ b/frontend/app/models/overtime-credit.ts @@ -0,0 +1,20 @@ +import Model, { attr, belongsTo } from "@ember-data/model"; +import type { Moment, Duration } from "moment"; +import type User from "timed/models/user"; + +export default class OvertimeCredit extends Model { + @attr("django-date") + declare date?: Moment; + @attr("django-duration") + declare duration?: Duration; + @attr("string", { defaultValue: "" }) + declare comment: string; + @belongsTo("user", { async: false, inverse: null }) + declare user: User; +} + +declare module "ember-data/types/registries/model" { + export default interface ModelRegistry { + "overtime-credit": OvertimeCredit; + } +} diff --git a/frontend/app/models/project-assignee.js b/frontend/app/models/project-assignee.ts similarity index 53% rename from frontend/app/models/project-assignee.js rename to frontend/app/models/project-assignee.ts index 58a3b844e..efca09400 100644 --- a/frontend/app/models/project-assignee.js +++ b/frontend/app/models/project-assignee.ts @@ -1,9 +1,12 @@ +import type { AsyncBelongsTo } from "@ember-data/model"; /** * @module timed * @submodule timed-models * @public */ import Model, { attr, belongsTo } from "@ember-data/model"; +import type Project from "timed/models/project"; +import type User from "timed/models/user"; /** * The project assignee model @@ -20,7 +23,8 @@ export default class ProjectAssignee extends Model { * @type {Project} * @public */ - @belongsTo("project", { async: true, inverse: null }) project; + @belongsTo("project", { async: true, inverse: null }) + declare project: AsyncBelongsTo; /** * The user @@ -29,7 +33,8 @@ export default class ProjectAssignee extends Model { * @type {User} * @public */ - @belongsTo("user", { async: true, inverse: null }) user; + @belongsTo("user", { async: true, inverse: null }) + declare user: AsyncBelongsTo; /** * Whether the assignee is a reviewer @@ -38,7 +43,8 @@ export default class ProjectAssignee extends Model { * @type {Boolean} * @public */ - @attr("boolean", { defaultValue: false }) isReviewer; + @attr("boolean", { defaultValue: false }) + declare isReviewer: boolean; /** * Whether the assignee is a manager @@ -47,7 +53,8 @@ export default class ProjectAssignee extends Model { * @type {Boolean} * @public */ - @attr("boolean", { defaultValue: false }) isManager; + @attr("boolean", { defaultValue: false }) + declare isManager: boolean; /** * Whether the assignee is a resource @@ -56,5 +63,12 @@ export default class ProjectAssignee extends Model { * @type {Boolean} * @public */ - @attr("boolean", { defaultValue: false }) isResource; + @attr("boolean", { defaultValue: false }) + declare isResource: boolean; +} + +declare module "ember-data/types/registries/model" { + export default interface ModelRegistry { + "project-assignee": ProjectAssignee; + } } diff --git a/frontend/app/models/project-statistic.js b/frontend/app/models/project-statistic.js deleted file mode 100644 index 12a607a84..000000000 --- a/frontend/app/models/project-statistic.js +++ /dev/null @@ -1,9 +0,0 @@ -import Model, { attr, belongsTo } from "@ember-data/model"; - -export default class ProjectStatistics extends Model { - @attr name; - @attr("django-duration") estimatedTime; - @attr("django-duration") duration; - @attr("django-duration") totalRemainingEffort; - @belongsTo("customer", { async: true, inverse: null }) customer; -} diff --git a/frontend/app/models/project-statistic.ts b/frontend/app/models/project-statistic.ts new file mode 100644 index 000000000..0b9c5c10e --- /dev/null +++ b/frontend/app/models/project-statistic.ts @@ -0,0 +1,23 @@ +import type { AsyncBelongsTo } from "@ember-data/model"; +import Model, { attr, belongsTo } from "@ember-data/model"; +import type { Duration } from "moment"; +import type Customer from "timed/models/customer"; + +export default class ProjectStatistics extends Model { + @attr + declare name?: string; + @attr("django-duration") + declare estimatedTime?: Duration; + @attr("django-duration") + declare duration?: Duration; + @attr("django-duration") + declare totalRemainingEffort?: Duration; + @belongsTo("customer", { async: true, inverse: null }) + declare customer: AsyncBelongsTo; +} + +declare module "ember-data/types/registries/model" { + export default interface ModelRegistry { + "project-statistic": ProjectStatistics; + } +} diff --git a/frontend/app/models/project.js b/frontend/app/models/project.ts similarity index 51% rename from frontend/app/models/project.js rename to frontend/app/models/project.ts index a5a0bb80f..1a9c52335 100644 --- a/frontend/app/models/project.js +++ b/frontend/app/models/project.ts @@ -1,9 +1,15 @@ +import type { AsyncBelongsTo, AsyncHasMany } from "@ember-data/model"; +import Model, { attr, belongsTo, hasMany } from "@ember-data/model"; +import type { Duration } from "moment"; +import type BillingType from "timed/models/billing-type"; +import type Customer from "timed/models/customer"; +import type ProjectAssignee from "timed/models/project-assignee"; +import type Task from "timed/models/task"; /** * @module timed * @submodule timed-models * @public */ -import Model, { attr, belongsTo, hasMany } from "@ember-data/model"; /** * The project model @@ -20,7 +26,8 @@ export default class Project extends Model { * @type {String} * @public */ - @attr("string", { defaultValue: "" }) name; + @attr("string", { defaultValue: "" }) + declare name: string; /** * Whether the project is archived @@ -29,7 +36,8 @@ export default class Project extends Model { * @type {Boolean} * @public */ - @attr("boolean", { defaultValue: false }) archived; + @attr("boolean", { defaultValue: false }) + declare archived: boolean; /** * The estimated time for this project @@ -37,7 +45,8 @@ export default class Project extends Model { * @property {moment.duration} estimatedTime * @public */ - @attr("django-duration") estimatedTime; + @attr("django-duration") + declare estimatedTime?: Duration; /** * Boolean indicating if the remainig effort should be trackable @@ -45,14 +54,16 @@ export default class Project extends Model { * @type {Boolean} * @public */ - @attr("boolean", { defaultValue: false }) remainingEffortTracking; + @attr("boolean", { defaultValue: false }) + declare remainingEffortTracking: boolean; /** * Total remainig effort for this project * @property {moment.duration} estimatedtime * @public */ - @attr("django-duration") totalRemainingEffort; + @attr("django-duration") + declare totalRemainingEffort?: Duration; /** * The customer @@ -61,7 +72,8 @@ export default class Project extends Model { * @type {Customer} * @public */ - @belongsTo("customer", { async: true, inverse: "projects" }) customer; + @belongsTo("customer", { async: true, inverse: "projects" }) + declare customer: AsyncBelongsTo; /** * Whether the project's comments are visible to the customer @@ -70,7 +82,8 @@ export default class Project extends Model { * @type {Boolean} * @public */ - @attr("boolean", { defaultValue: false }) customerVisible; + @attr("boolean", { defaultValue: false }) + declare customerVisible: boolean; /** * The billing @@ -78,7 +91,8 @@ export default class Project extends Model { * @property {BillingType} billingType * @public */ - @belongsTo("billing-type", { async: false, inverse: null }) billingType; + @belongsTo("billing-type", { async: false, inverse: null }) + declare billingType: BillingType; /** * The tasks @@ -87,7 +101,8 @@ export default class Project extends Model { * @type {Task[]} * @public */ - @hasMany("task", { async: true, inverse: "project" }) tasks; + @hasMany("task", { async: true, inverse: "project" }) + declare tasks: AsyncHasMany; /** * Assigned users to this project @@ -96,5 +111,12 @@ export default class Project extends Model { * @type {ProjectAssignee[]} * @public */ - @hasMany("project-assignee", { async: true, inverse: null }) assignees; + @hasMany("project-assignee", { async: true, inverse: null }) + declare assignees: AsyncHasMany; +} + +declare module "ember-data/types/registries/model" { + export default interface ModelRegistry { + project: Project; + } } diff --git a/frontend/app/models/public-holiday.js b/frontend/app/models/public-holiday.ts similarity index 51% rename from frontend/app/models/public-holiday.js rename to frontend/app/models/public-holiday.ts index 2bca8babc..3d7c10902 100644 --- a/frontend/app/models/public-holiday.js +++ b/frontend/app/models/public-holiday.ts @@ -1,9 +1,12 @@ +import type { AsyncBelongsTo } from "@ember-data/model"; /** * @module timed * @submodule timed-models * @public */ import Model, { attr, belongsTo } from "@ember-data/model"; +import type { Moment } from "moment"; +import type Location from "timed/models/location"; /** * The public holiday model @@ -19,7 +22,8 @@ export default class PublicHoliday extends Model { * @property {String} name * @public */ - @attr("string") name; + @attr("string") + declare name?: string; /** * The date @@ -27,7 +31,8 @@ export default class PublicHoliday extends Model { * @property {moment} date * @public */ - @attr("django-date") date; + @attr("django-date") + declare date?: Moment; /** * The location @@ -35,5 +40,12 @@ export default class PublicHoliday extends Model { * @property {Location} location * @public */ - @belongsTo("location", { async: true, inverse: null }) location; + @belongsTo("location", { async: true, inverse: null }) + declare location: AsyncBelongsTo; +} + +declare module "ember-data/types/registries/model" { + export default interface ModelRegistry { + "public-holiday": PublicHoliday; + } } diff --git a/frontend/app/models/report-intersection.js b/frontend/app/models/report-intersection.js deleted file mode 100644 index 8b3f2eb5c..000000000 --- a/frontend/app/models/report-intersection.js +++ /dev/null @@ -1,15 +0,0 @@ -import Model, { attr, belongsTo } from "@ember-data/model"; - -export default class ReportIntersection extends Model { - @attr("string") comment; - @attr("boolean", { allowNull: true, defaultValue: null }) notBillable; - @attr("boolean", { allowNull: true, defaultValue: false }) rejected; - @attr("boolean", { allowNull: true, defaultValue: null }) review; - @attr("boolean", { allowNull: true, defaultValue: null }) billed; - @attr("boolean", { allowNull: true, defaultValue: null }) verified; - - @belongsTo("customer", { async: true, inverse: null }) customer; - @belongsTo("project", { async: true, inverse: null }) project; - @belongsTo("task", { async: true, inverse: null }) task; - @belongsTo("user", { async: true, inverse: null }) user; -} diff --git a/frontend/app/models/report-intersection.ts b/frontend/app/models/report-intersection.ts new file mode 100644 index 000000000..cb91c7c9e --- /dev/null +++ b/frontend/app/models/report-intersection.ts @@ -0,0 +1,36 @@ +import type { AsyncBelongsTo } from "@ember-data/model"; +import Model, { attr, belongsTo } from "@ember-data/model"; +import type Customer from "timed/models/customer"; +import type Project from "timed/models/project"; +import type Task from "timed/models/task"; +import type User from "timed/models/user"; + +export default class ReportIntersection extends Model { + @attr("string") + declare comment?: string; + @attr("boolean", { allowNull: true, defaultValue: null }) + declare notBillable: boolean | null; + @attr("boolean", { allowNull: true, defaultValue: false }) + declare rejected: boolean | null; + @attr("boolean", { allowNull: true, defaultValue: null }) + declare review: boolean | null; + @attr("boolean", { allowNull: true, defaultValue: null }) + declare billed: boolean | null; + @attr("boolean", { allowNull: true, defaultValue: null }) + declare verified: boolean | null; + + @belongsTo("customer", { async: true, inverse: null }) + declare customer: AsyncBelongsTo; + @belongsTo("project", { async: true, inverse: null }) + declare project: AsyncBelongsTo; + @belongsTo("task", { async: true, inverse: null }) + declare task: AsyncBelongsTo; + @belongsTo("user", { async: true, inverse: null }) + declare user: AsyncBelongsTo; +} + +declare module "ember-data/types/registries/model" { + export default interface ModelRegistry { + "report-intersection": ReportIntersection; + } +} diff --git a/frontend/app/models/report.js b/frontend/app/models/report.ts similarity index 58% rename from frontend/app/models/report.js rename to frontend/app/models/report.ts index 1407ecbff..2d46b3057 100644 --- a/frontend/app/models/report.js +++ b/frontend/app/models/report.ts @@ -1,10 +1,14 @@ +import type { AsyncBelongsTo } from "@ember-data/model"; +import Model, { attr, belongsTo } from "@ember-data/model"; +import type { Moment, Duration } from "moment"; /** * @module timed * @submodule timed-models * @public */ -import Model, { attr, belongsTo } from "@ember-data/model"; import moment from "moment"; +import type Task from "timed/models/task"; +import type User from "timed/models/user"; /** * The report model @@ -20,7 +24,8 @@ export default class Report extends Model { * @property {String} comment * @public */ - @attr("string", { defaultValue: "" }) comment; + @attr("string", { defaultValue: "" }) + declare comment: string; /** * The date @@ -28,7 +33,8 @@ export default class Report extends Model { * @property {moment} date * @public */ - @attr("django-date") date; + @attr("django-date") + declare date?: Moment; /** * The duration @@ -36,7 +42,8 @@ export default class Report extends Model { * @property {moment.duration} duration * @public */ - @attr("django-duration", { defaultValue: () => moment.duration() }) duration; + @attr("django-duration", { defaultValue: () => moment.duration() }) + declare duration: Duration; /** * The remaining effort for the underlying task @@ -46,7 +53,7 @@ export default class Report extends Model { */ @attr("django-duration", { defaultValue: () => moment.duration() }) - remainingEffort; + declare remainingEffort: Duration; /** * Whether the report needs to be reviewed @@ -54,7 +61,8 @@ export default class Report extends Model { * @property {Boolean} review * @public */ - @attr("boolean", { defaultValue: false }) review; + @attr("boolean", { defaultValue: false }) + declare review: boolean; /** * Whether the report got rejected by the reviewer @@ -62,7 +70,8 @@ export default class Report extends Model { * @property {Boolean} reject * @public */ - @attr("boolean", { defaultValue: false }) rejected; + @attr("boolean", { defaultValue: false }) + declare rejected: boolean; /** * Whether the report is not billable @@ -70,7 +79,8 @@ export default class Report extends Model { * @property {Boolean} notBillable * @public */ - @attr("boolean", { defaultValue: false }) notBillable; + @attr("boolean", { defaultValue: false }) + declare notBillable: boolean; /** * Whether the report has been marked as billed @@ -78,7 +88,8 @@ export default class Report extends Model { * @property {Boolean} billed * @public */ - @attr("boolean", { defaultValue: false }) billed; + @attr("boolean", { defaultValue: false }) + declare billed: boolean; /** * The task @@ -86,7 +97,8 @@ export default class Report extends Model { * @property {Task} task * @public */ - @belongsTo("task", { async: false, inverse: null }) task; + @belongsTo("task", { async: false, inverse: null }) + declare task: Task; /** * The user @@ -94,7 +106,8 @@ export default class Report extends Model { * @property {User} user * @public */ - @belongsTo("user", { async: false, inverse: null }) user; + @belongsTo("user", { async: false, inverse: null }) + declare user: User; /** * The user which verified this report @@ -102,5 +115,12 @@ export default class Report extends Model { * @property {User} verifiedBy * @public */ - @belongsTo("user", { async: true, inverse: null }) verifiedBy; + @belongsTo("user", { async: true, inverse: null }) + declare verifiedBy: AsyncBelongsTo; +} + +declare module "ember-data/types/registries/model" { + export default interface ModelRegistry { + report: Report; + } } diff --git a/frontend/app/models/task-assignee.js b/frontend/app/models/task-assignee.ts similarity index 53% rename from frontend/app/models/task-assignee.js rename to frontend/app/models/task-assignee.ts index 4f8a58bbe..b0aa65f5c 100644 --- a/frontend/app/models/task-assignee.js +++ b/frontend/app/models/task-assignee.ts @@ -1,9 +1,12 @@ +import type { AsyncBelongsTo } from "@ember-data/model"; /** * @module timed * @submodule timed-models * @public */ import Model, { attr, belongsTo } from "@ember-data/model"; +import type Task from "timed/models/task"; +import type User from "timed/models/user"; /** * The task assignee model @@ -20,7 +23,8 @@ export default class TaskAssignee extends Model { * @type {Task} * @public */ - @belongsTo("task", { async: true, inverse: "assignees" }) task; + @belongsTo("task", { async: true, inverse: "assignees" }) + declare task: AsyncBelongsTo; /** * The user * @@ -28,7 +32,8 @@ export default class TaskAssignee extends Model { * @type {User} * @public */ - @belongsTo("user", { async: true, inverse: null }) user; + @belongsTo("user", { async: true, inverse: null }) + declare user: AsyncBelongsTo; /** * Whether the assignee is a reviewer @@ -37,7 +42,8 @@ export default class TaskAssignee extends Model { * @type {Boolean} * @public */ - @attr("boolean", { defaultValue: false }) isReviewer; + @attr("boolean", { defaultValue: false }) + declare isReviewer: boolean; /** * Whether the assignee is a manager @@ -46,7 +52,8 @@ export default class TaskAssignee extends Model { * @type {Boolean} * @public */ - @attr("boolean", { defaultValue: false }) isManager; + @attr("boolean", { defaultValue: false }) + declare isManager: boolean; /** * Whether the assignee is a resource @@ -55,5 +62,12 @@ export default class TaskAssignee extends Model { * @type {Boolean} * @public */ - @attr("boolean", { defaultValue: false }) isResource; + @attr("boolean", { defaultValue: false }) + declare isResource: boolean; +} + +declare module "ember-data/types/registries/model" { + export default interface ModelRegistry { + "task-assignee": TaskAssignee; + } } diff --git a/frontend/app/models/task-statistic.js b/frontend/app/models/task-statistic.js deleted file mode 100644 index b9a234e25..000000000 --- a/frontend/app/models/task-statistic.js +++ /dev/null @@ -1,9 +0,0 @@ -import Model, { attr, belongsTo } from "@ember-data/model"; - -export default class TaskStatistics extends Model { - @attr name; - @attr("django-duration") duration; - @attr("django-duration") estimatedTime; - @attr("django-duration") mostRecentRemainingEffort; - @belongsTo("project", { async: true, inverse: null }) project; -} diff --git a/frontend/app/models/task-statistic.ts b/frontend/app/models/task-statistic.ts new file mode 100644 index 000000000..f29748873 --- /dev/null +++ b/frontend/app/models/task-statistic.ts @@ -0,0 +1,23 @@ +import type { AsyncBelongsTo } from "@ember-data/model"; +import Model, { attr, belongsTo } from "@ember-data/model"; +import type { Duration } from "moment"; +import type Project from "timed/models/project"; + +export default class TaskStatistics extends Model { + @attr + declare name?: string; + @attr("django-duration") + declare duration?: Duration; + @attr("django-duration") + declare estimatedTime?: Duration; + @attr("django-duration") + declare mostRecentRemainingEffort?: Duration; + @belongsTo("project", { async: true, inverse: null }) + declare project: AsyncBelongsTo; +} + +declare module "ember-data/types/registries/model" { + export default interface ModelRegistry { + "task-statistic": TaskStatistics; + } +} diff --git a/frontend/app/models/task.js b/frontend/app/models/task.js deleted file mode 100644 index 9882f902b..000000000 --- a/frontend/app/models/task.js +++ /dev/null @@ -1,40 +0,0 @@ -import Model, { attr, belongsTo, hasMany } from "@ember-data/model"; - -export default class Task extends Model { - @attr("string", { defaultValue: "" }) name; - @attr("django-duration") estimatedTime; - @attr("django-duration") mostRecentRemainingEffort; - @attr("boolean", { defaultValue: false }) archived; - @attr("string", { defaultValue: "" }) reference; - - @belongsTo("project", { - async: true, - inverse: "tasks", - }) - project; - @hasMany("task-assignee", { - async: true, - inverse: "task", - }) - assignees; - - /** - * Flag saying that this is a task. - * Used in /app/customer-suggestion/template.hbs - * We're using this as a workaround for the fact that one - * can't seem to use helpers like "(eq" in inline templates - * - * @property project - * @type {Project} - * @public - */ - isTask = true; - - get longName() { - const taskName = this.name; - const projectName = this.project.get("name"); - const customerName = this.project.get("customer.name"); - - return `${customerName} > ${projectName} > ${taskName}`; - } -} diff --git a/frontend/app/models/task.ts b/frontend/app/models/task.ts new file mode 100644 index 000000000..ef4174f54 --- /dev/null +++ b/frontend/app/models/task.ts @@ -0,0 +1,60 @@ +import Model, { + attr, + belongsTo, + hasMany, + type AsyncBelongsTo, + type AsyncHasMany, +} from "@ember-data/model"; +import type { Duration } from "moment"; +import type Project from "timed/models/project"; +import type TaskAssignee from "timed/models/task-assignee"; + +export default class Task extends Model { + @attr("string", { defaultValue: "" }) + declare name: string; + @attr("django-duration") + declare estimatedTime?: Duration; + @attr("django-duration") + declare mostRecentRemainingEffort?: Duration; + @attr("boolean", { defaultValue: false }) + declare archived: boolean; + @attr("string", { defaultValue: "" }) + declare reference: string; + + @belongsTo("project", { + async: true, + inverse: "tasks", + }) + declare project: AsyncBelongsTo; + @hasMany("task-assignee", { + async: true, + inverse: "task", + }) + declare assignees: AsyncHasMany; + + /** + * Flag saying that this is a task. + * Used in /app/customer-suggestion/template.hbs + * We're using this as a workaround for the fact that one + * can't seem to use helpers like "(eq" in inline templates + * + * @property project + * @type {Project} + * @public + */ + isTask = true; + + get longName() { + const taskName = this.name; + const projectName = this.project.get("name"); + const customerName = this.project.get("customer.name"); + + return `${customerName} > ${projectName} > ${taskName}`; + } +} + +declare module "ember-data/types/registries/model" { + export default interface ModelRegistry { + task: Task; + } +} diff --git a/frontend/app/models/user-statistic.js b/frontend/app/models/user-statistic.js deleted file mode 100644 index 1785551fe..000000000 --- a/frontend/app/models/user-statistic.js +++ /dev/null @@ -1,7 +0,0 @@ -import Model, { attr, belongsTo } from "@ember-data/model"; - -export default class UserStatistic extends Model { - @attr("django-duration") duration; - - @belongsTo("user", { async: true, inverse: null }) user; -} diff --git a/frontend/app/models/user-statistic.ts b/frontend/app/models/user-statistic.ts new file mode 100644 index 000000000..4fff5d017 --- /dev/null +++ b/frontend/app/models/user-statistic.ts @@ -0,0 +1,18 @@ +import type { AsyncBelongsTo } from "@ember-data/model"; +import Model, { attr, belongsTo } from "@ember-data/model"; +import type { Duration } from "moment"; +import type User from "timed/models/user"; + +export default class UserStatistic extends Model { + @attr("django-duration") + declare duration?: Duration; + + @belongsTo("user", { async: true, inverse: null }) + declare user: AsyncBelongsTo; +} + +declare module "ember-data/types/registries/model" { + export default interface ModelRegistry { + "user-statistic": UserStatistic; + } +} diff --git a/frontend/app/models/user.js b/frontend/app/models/user.ts similarity index 68% rename from frontend/app/models/user.js rename to frontend/app/models/user.ts index 3db1a1a6c..19f3eb5c2 100644 --- a/frontend/app/models/user.js +++ b/frontend/app/models/user.ts @@ -1,11 +1,16 @@ +import { service } from "@ember/service"; +import type { SyncHasMany, AsyncHasMany } from "@ember-data/model"; /** * @module timed * @submodule timed-models * @public */ -import { service } from "@ember/service"; import Model, { attr, hasMany } from "@ember-data/model"; +import type StoreService from "@ember-data/store"; import moment from "moment"; +import type AbsenceBalance from "timed/models/absence-balance"; +import type Employment from "timed/models/employment"; +import type WorktimeBalance from "timed/models/worktime-balance"; /** * The user model * @@ -14,14 +19,15 @@ import moment from "moment"; * @public */ export default class User extends Model { - @service store; + @service declare store: StoreService; /** * The username * * @property {String} username * @public */ - @attr("string") username; + @attr("string") + declare username?: string; /** * The first name @@ -29,7 +35,8 @@ export default class User extends Model { * @property {String} firstName * @public */ - @attr("string") firstName; + @attr("string") + declare firstName?: string; /** * The last name @@ -37,7 +44,8 @@ export default class User extends Model { * @property {String} lastName * @public */ - @attr("string") lastName; + @attr("string") + declare lastName?: string; /** * The email address @@ -45,7 +53,8 @@ export default class User extends Model { * @property {String} email * @public */ - @attr("string") email; + @attr("string") + declare email?: string; /** * Defines if the user is a superuser @@ -53,7 +62,8 @@ export default class User extends Model { * @property {Boolean} isSuperuser * @public */ - @attr("boolean") isSuperuser; + @attr("boolean") + declare isSuperuser?: boolean; /** * Whether a user is active @@ -61,7 +71,8 @@ export default class User extends Model { * @property {Boolean} isActive * @public */ - @attr("boolean") isActive; + @attr("boolean") + declare isActive?: boolean; /** * Whether the user is a reviewer in a project @@ -69,12 +80,14 @@ export default class User extends Model { * @property {Boolean} isReviewer * @public */ - @attr("boolean") isReviewer; + @attr("boolean") + declare isReviewer?: boolean; /** * Whether the user is an accountant */ - @attr("boolean", { defaultValue: false }) isAccountant; + @attr("boolean", { defaultValue: false }) + declare isAccountant: boolean; /** * Whether the user completed the app tour @@ -82,7 +95,8 @@ export default class User extends Model { * @property {Boolean} tourDone * @public */ - @attr("boolean") tourDone; + @attr("boolean") + declare tourDone?: boolean; /** * The users supervisors @@ -90,7 +104,8 @@ export default class User extends Model { * @property {User[]} supervisors * @public */ - @hasMany("user", { inverse: "supervisees", async: false }) supervisors; + @hasMany("user", { inverse: "supervisees", async: false }) + declare supervisors: SyncHasMany; /** * The users supervisees @@ -98,7 +113,8 @@ export default class User extends Model { * @property {User[]} supervisees * @public */ - @hasMany("user", { inverse: "supervisors", async: false }) supervisees; + @hasMany("user", { inverse: "supervisors", async: false }) + declare supervisees: SyncHasMany; /** * The users employments @@ -106,7 +122,8 @@ export default class User extends Model { * @property {Employment[]} employments * @public */ - @hasMany("employment", { async: true, inverse: "user" }) employments; + @hasMany("employment", { async: true, inverse: "user" }) + declare employments: AsyncHasMany; /** * The users worktime balances @@ -114,8 +131,8 @@ export default class User extends Model { * @property {WorktimeBalance[]} worktimeBalances * @public */ - @hasMany("worktime-balances", { async: true, inverse: "user" }) - worktimeBalances; + @hasMany("worktime-balance", { async: true, inverse: "user" }) + declare worktimeBalances: AsyncHasMany; /** * The users absence balances @@ -123,7 +140,8 @@ export default class User extends Model { * @property {AbsenceBalance[]} absenceBalances * @public */ - @hasMany("absence-balance", { async: true, inverse: "user" }) absenceBalances; + @hasMany("absence-balance", { async: true, inverse: "user" }) + declare absenceBalances: AsyncHasMany; /** * The full name @@ -200,3 +218,9 @@ export default class User extends Model { }; } } + +declare module "ember-data/types/registries/model" { + export default interface ModelRegistry { + user: User; + } +} diff --git a/frontend/app/models/worktime-balance.js b/frontend/app/models/worktime-balance.js deleted file mode 100644 index f7cb73ead..000000000 --- a/frontend/app/models/worktime-balance.js +++ /dev/null @@ -1,7 +0,0 @@ -import Model, { attr, belongsTo } from "@ember-data/model"; - -export default class WorktimeBalance extends Model { - @attr("django-date") date; - @attr("django-duration") balance; - @belongsTo("user", { async: true, inverse: "worktimeBalances" }) user; -} diff --git a/frontend/app/models/worktime-balance.ts b/frontend/app/models/worktime-balance.ts new file mode 100644 index 000000000..6c78a1bf5 --- /dev/null +++ b/frontend/app/models/worktime-balance.ts @@ -0,0 +1,19 @@ +import type { AsyncBelongsTo } from "@ember-data/model"; +import Model, { attr, belongsTo } from "@ember-data/model"; +import type { Moment, Duration } from "moment"; +import type User from "timed/models/user"; + +export default class WorktimeBalance extends Model { + @attr("django-date") + declare date?: Moment; + @attr("django-duration") + declare balance?: Duration; + @belongsTo("user", { async: true, inverse: "worktimeBalances" }) + declare user: AsyncBelongsTo; +} + +declare module "ember-data/types/registries/model" { + export default interface ModelRegistry { + "worktime-balance": WorktimeBalance; + } +} diff --git a/frontend/app/models/year-statistic.js b/frontend/app/models/year-statistic.js deleted file mode 100644 index 54108d33d..000000000 --- a/frontend/app/models/year-statistic.js +++ /dev/null @@ -1,6 +0,0 @@ -import Model, { attr } from "@ember-data/model"; - -export default class YearStatistic extends Model { - @attr("number") year; - @attr("django-duration") duration; -} diff --git a/frontend/app/models/year-statistic.ts b/frontend/app/models/year-statistic.ts new file mode 100644 index 000000000..efc322677 --- /dev/null +++ b/frontend/app/models/year-statistic.ts @@ -0,0 +1,15 @@ +import Model, { attr } from "@ember-data/model"; +import type { Duration } from "moment"; + +export default class YearStatistic extends Model { + @attr("number") + declare year?: number; + @attr("django-duration") + declare duration?: Duration; +} + +declare module "ember-data/types/registries/model" { + export default interface ModelRegistry { + "year-statistic": YearStatistic; + } +} diff --git a/frontend/app/serializers/application.js b/frontend/app/serializers/application.ts similarity index 100% rename from frontend/app/serializers/application.js rename to frontend/app/serializers/application.ts diff --git a/frontend/app/serializers/attendance.js b/frontend/app/serializers/attendance.ts similarity index 80% rename from frontend/app/serializers/attendance.js rename to frontend/app/serializers/attendance.ts index 1ec78c342..2eb787613 100644 --- a/frontend/app/serializers/attendance.js +++ b/frontend/app/serializers/attendance.ts @@ -29,3 +29,9 @@ export default class AttendanceSerializer extends ApplicationSerializer { to: "to-time", }; } + +declare module "ember-data/types/registries/serializer" { + export default interface SerializerRegistry { + attendance: AttendanceSerializer; + } +} diff --git a/frontend/app/serializers/employment.js b/frontend/app/serializers/employment.ts similarity index 80% rename from frontend/app/serializers/employment.js rename to frontend/app/serializers/employment.ts index 4b6761adc..17ad424e9 100644 --- a/frontend/app/serializers/employment.js +++ b/frontend/app/serializers/employment.ts @@ -29,3 +29,9 @@ export default class EmploymentSerializer extends ApplicationSerializer { end: "end-date", }; } + +declare module "ember-data/types/registries/serializer" { + export default interface SerializerRegistry { + employment: EmploymentSerializer; + } +} diff --git a/frontend/app/services/autostart-tour.js b/frontend/app/services/autostart-tour.ts similarity index 88% rename from frontend/app/services/autostart-tour.js rename to frontend/app/services/autostart-tour.ts index 65e326a6b..cbb4d433e 100644 --- a/frontend/app/services/autostart-tour.js +++ b/frontend/app/services/autostart-tour.ts @@ -37,3 +37,9 @@ export default class AutostartTourService extends Service { return this.undoneTours.length === 0; } } + +declare module "@ember/service" { + interface Registry { + "autostart-tour": AutostartTourService; + } +} diff --git a/frontend/app/services/current-user.js b/frontend/app/services/current-user.ts similarity index 62% rename from frontend/app/services/current-user.js rename to frontend/app/services/current-user.ts index d2e6524e2..2e5dc37f6 100644 --- a/frontend/app/services/current-user.js +++ b/frontend/app/services/current-user.ts @@ -1,11 +1,15 @@ +import type RouterService from "@ember/routing/router-service"; import Service, { service } from "@ember/service"; +import type StoreService from "@ember-data/store"; +import type SessionService from "ember-simple-auth-oidc/services/session"; import moment from "moment"; +import type FetchService from "timed/services/fetch"; export default class CurrentUserService extends Service { - @service session; - @service fetch; - @service store; - @service router; + @service declare session: SessionService; + @service declare fetch: FetchService; + @service declare store: StoreService; + @service declare router: RouterService; async load() { if (!this.session.isAuthenticated) { @@ -38,3 +42,9 @@ export default class CurrentUserService extends Service { this.user = usermodel; } } + +declare module "@ember/service" { + interface Registry { + "current-user": CurrentUserService; + } +} diff --git a/frontend/app/services/fetch.js b/frontend/app/services/fetch.ts similarity index 92% rename from frontend/app/services/fetch.js rename to frontend/app/services/fetch.ts index e950f900f..46e2f3ccb 100644 --- a/frontend/app/services/fetch.js +++ b/frontend/app/services/fetch.ts @@ -2,6 +2,7 @@ import Service, { service } from "@ember/service"; import { isEmpty } from "@ember/utils"; import { isUnauthorizedResponse } from "ember-fetch/errors"; import { handleUnauthorized } from "ember-simple-auth-oidc"; +import type SessionService from "ember-simple-auth-oidc/services/session"; import fetch from "fetch"; const CONTENT_TYPE = "application/vnd.api+json"; @@ -40,7 +41,7 @@ const stringifyBodyData = (obj) => { }; export default class FetchService extends Service { - @service session; + @service declare session: SessionService; async fetch(resource, init = {}) { await this.session.refreshAuthentication.perform(); @@ -97,3 +98,9 @@ export default class FetchService extends Service { }; } } + +declare module "@ember/service" { + interface Registry { + fetch: FetchService; + } +} diff --git a/frontend/app/services/metadata-fetcher.js b/frontend/app/services/metadata-fetcher.ts similarity index 92% rename from frontend/app/services/metadata-fetcher.js rename to frontend/app/services/metadata-fetcher.ts index eeb9f7429..7312781fa 100644 --- a/frontend/app/services/metadata-fetcher.js +++ b/frontend/app/services/metadata-fetcher.ts @@ -1,6 +1,7 @@ import Service, { service } from "@ember/service"; import { camelize, capitalize, dasherize } from "@ember/string"; import { restartableTask } from "ember-concurrency"; +import type FetchService from "timed/services/fetch"; import DjangoDurationTransform from "timed/transforms/django-duration"; const DJANGO_DURATION_TRANSFORM = DjangoDurationTransform.create(); @@ -45,7 +46,7 @@ export default class MetadataFetcherService extends Service { * @property {Emberfetch} fetch * @public */ - @service fetch; + @service declare fetch: FetchService; /** * Task to fetch a single records metadata @@ -95,3 +96,9 @@ export default class MetadataFetcherService extends Service { return { ...attributesValues, ...metaValues }; } } + +declare module "@ember/service" { + interface Registry { + "metadata-fetcher": MetadataFetcherService; + } +} diff --git a/frontend/app/services/rejected-reports.js b/frontend/app/services/rejected-reports.ts similarity index 73% rename from frontend/app/services/rejected-reports.js rename to frontend/app/services/rejected-reports.ts index 88197b3ae..06bbb9dc4 100644 --- a/frontend/app/services/rejected-reports.js +++ b/frontend/app/services/rejected-reports.ts @@ -1,15 +1,18 @@ import Service, { service } from "@ember/service"; +import type StoreService from "@ember-data/store"; import { macroCondition, isTesting } from "@embroider/macros"; import { tracked } from "@glimmer/tracking"; +import type NotifyService from "ember-notify"; +import type CurrentUserService from "timed/services/current-user"; const INTERVAL_DELAY = 10 * 60000; // 10 Minutes export default class RejectedReportsService extends Service { - @service store; + @service declare store: StoreService; - @service currentUser; + @service declare currentUser: CurrentUserService; - @service notify; + @service declare notify: NotifyService; @tracked amountReports = 0; @tracked intervalId; @@ -53,3 +56,9 @@ export default class RejectedReportsService extends Service { clearInterval(this.intervalId); } } + +declare module "@ember/service" { + interface Registry { + "rejected-reports": RejectedReportsService; + } +} diff --git a/frontend/app/services/tour.js b/frontend/app/services/tour.ts similarity index 87% rename from frontend/app/services/tour.js rename to frontend/app/services/tour.ts index d57f0ac1f..2d4f32200 100644 --- a/frontend/app/services/tour.js +++ b/frontend/app/services/tour.ts @@ -1,17 +1,21 @@ +import type RouterService from "@ember/routing/router-service"; import { schedule, later } from "@ember/runloop"; import { service } from "@ember/service"; import { waitFor } from "@ember/test-waiters"; import { isTesting, macroCondition } from "@embroider/macros"; import { tracked } from "@glimmer/tracking"; +import type NotifyService from "ember-notify"; import Tour from "ember-shepherd/services/tour"; +import type AutostartTourService from "timed/services/autostart-tour"; +import type MediaService from "timed/services/media"; import TOURS from "timed/tours"; import { cached } from "tracked-toolbox"; export default class TourService extends Tour { - @service notify; - @service media; - @service router; - @service autostartTour; + @service declare notify: NotifyService; + @service declare media: MediaService; + @service declare router: RouterService; + @service declare autostartTour: AutostartTourService; @tracked model; constructor(...args) { @@ -51,8 +55,7 @@ export default class TourService extends Tour { super.willDestroy(...args); } - @cached - get routeName() { + @cached get routeName() { return this.router.currentRouteName.replace(/\.index$/, ""); } @@ -132,8 +135,7 @@ export default class TourService extends Tour { } } - @waitFor - async startTour() { + @waitFor async startTour() { if (this._wantsTour && this.hasTourForRoute) { await this.prepareTourForCurrentRoute(); schedule("afterRender", this, () => { @@ -172,3 +174,9 @@ export default class TourService extends Tour { } } } + +declare module "@ember/service" { + interface Registry { + tour: TourService; + } +} diff --git a/frontend/app/services/tracking.js b/frontend/app/services/tracking.ts similarity index 96% rename from frontend/app/services/tracking.js rename to frontend/app/services/tracking.ts index 0df09deef..9f9569e5f 100644 --- a/frontend/app/services/tracking.js +++ b/frontend/app/services/tracking.ts @@ -2,9 +2,11 @@ import { getOwner } from "@ember/application"; import { scheduleOnce } from "@ember/runloop"; import Service, { service } from "@ember/service"; import { camelize, capitalize } from "@ember/string"; +import type StoreService from "@ember-data/store"; import { isTesting, macroCondition } from "@embroider/macros"; import { tracked } from "@glimmer/tracking"; import { dropTask, task, timeout } from "ember-concurrency"; +import type NotifyService from "ember-notify"; import { trackedTask } from "ember-resources/util/ember-concurrency"; import moment from "moment"; import formatDuration from "timed/utils/format-duration"; @@ -25,7 +27,7 @@ export default class TrackingService extends Service { * @property {Ember.Store} store * @public */ - @service store; + @service declare store: StoreService; /** * The notify service @@ -33,7 +35,7 @@ export default class TrackingService extends Service { * @property {EmberNotify.NotifyService} notify * @public */ - @service notify; + @service declare notify: NotifyService; /** * Flag indicating if the tracking reports is currently generated. @@ -363,3 +365,9 @@ export default class TrackingService extends Service { return; } } + +declare module "@ember/service" { + interface Registry { + tracking: TrackingService; + } +} diff --git a/frontend/app/services/unverified-reports.js b/frontend/app/services/unverified-reports.ts similarity index 77% rename from frontend/app/services/unverified-reports.js rename to frontend/app/services/unverified-reports.ts index b320b70e8..b6579c8d2 100644 --- a/frontend/app/services/unverified-reports.js +++ b/frontend/app/services/unverified-reports.ts @@ -1,7 +1,10 @@ import Service, { service } from "@ember/service"; +import type StoreService from "@ember-data/store"; import { isTesting, macroCondition } from "@embroider/macros"; import { tracked } from "@glimmer/tracking"; +import type NotifyService from "ember-notify"; import moment from "moment"; +import type CurrentUserService from "timed/services/current-user"; const INTERVAL_DELAY = 10 * 60000; // 10 Minutes @@ -16,11 +19,11 @@ const INTERVAL_DELAY = 10 * 60000; // 10 Minutes * @public */ export default class UnverifiedReportsService extends Service { - @service store; + @service declare store: StoreService; - @service currentUser; + @service declare currentUser: CurrentUserService; - @service notify; + @service declare notify: NotifyService; @tracked amountReports = 0; @@ -65,3 +68,9 @@ export default class UnverifiedReportsService extends Service { clearInterval(this.intervalId); } } + +declare module "@ember/service" { + interface Registry { + "unverified-reports": UnverifiedReportsService; + } +} diff --git a/frontend/app/transforms/django-date.ts b/frontend/app/transforms/django-date.ts new file mode 100644 index 000000000..1a0f042bc --- /dev/null +++ b/frontend/app/transforms/django-date.ts @@ -0,0 +1,26 @@ +import MomentTransform from "timed/transforms/moment"; + +/** + * The django date transform + * + * This transforms a django date into a moment date + * + * @class DjangoDateTransform + * @extends MomentTransform + * @public + */ +export default class DjangoDateTransform extends MomentTransform { + /** + * The date format + * + * @property {String} format + * @public + */ + format = "YYYY-MM-DD"; +} + +declare module "ember-data/types/registries/transform" { + export default interface TransformRegistry { + "django-date": DjangoDateTransform; + } +} diff --git a/frontend/app/transforms/django-datetime.ts b/frontend/app/transforms/django-datetime.ts new file mode 100644 index 000000000..dc4137ee4 --- /dev/null +++ b/frontend/app/transforms/django-datetime.ts @@ -0,0 +1,26 @@ +import MomentTransform from "timed/transforms/moment"; + +/** + * The django datetime transform + * + * This transforms a django datetime into a moment datetime + * + * @class DjangoDatetimeTransform + * @extends MomentTransform + * @public + */ +export default class DjangoDatetimeTransform extends MomentTransform { + /** + * The date format + * + * @property {String} format + * @public + */ + format = "YYYY-MM-DDTHH:mm:ss.SSSSZ"; +} + +declare module "ember-data/types/registries/transform" { + export default interface TransformRegistry { + "django-datetime": DjangoDatetimeTransform; + } +} diff --git a/frontend/app/transforms/django-duration.ts b/frontend/app/transforms/django-duration.ts new file mode 100644 index 000000000..4d0db9033 --- /dev/null +++ b/frontend/app/transforms/django-duration.ts @@ -0,0 +1,98 @@ +import Transform from "@ember-data/serializer/transform"; +import moment from "moment"; +import { pad2joincolon } from "timed/utils/pad"; +import parseDjangoDuration from "timed/utils/parse-django-duration"; + +const { round } = Math; + +/** + * The django duration transform + * + * This transforms a django duration into a moment duration + * + * Django formats the timedelta like this: `DD HH:MM:ss.uuuuuu`. However, + * days and microseconds are optional. + * + * @see http://www.django-rest-framework.org/api-guide/fields/#durationfield + * @see https://github.com/django/django/blob/main/django/utils/duration.py + * + * @class DjangoDurationTransform + * @extends DS.Transform + * @public + */ +export default class DjangoDurationTransform extends Transform { + /** + * Deserialize the django duration into a moment duration + * + * @method deserialize + * @param {String} serialized The django duration + * @return {moment.duration} The deserialized moment duration + * @public + */ + deserialize(serialized) { + return parseDjangoDuration(serialized); + } + + /** + * Get the duration components from the duration like pythons timedelta does + * it. + * + * This means that a duration of -1 hour becomes a duration of -1 day +23 + * hours, so we never have a negative hour, minute, second or millisecond. + * ONLY days can be negative! + * + * @method _getDurationComponentsTimedeltaLike + * @param {moment.duration} duration The duration to parse + * @returns {Object} An object containing all needed components as number + * @private + */ + _getDurationComponentsTimedeltaLike(duration) { + const days = Math.floor(duration.asDays()); + const milliseconds = Math.abs(moment.duration({ days }) - duration); + + const positiveDuration = moment.duration(milliseconds); + + return { + days, + hours: positiveDuration.hours(), + minutes: positiveDuration.minutes(), + seconds: positiveDuration.seconds(), + microseconds: round(positiveDuration.milliseconds() * 1000), + }; + } + + /** + * Serialize the moment duration into a django duration + * + * @method serialize + * @param {moment.duration} deserialized The moment duration + * @return {String} The serialized django duration + * @public + */ + serialize(deserialized) { + if (!moment.isDuration(deserialized)) { + return null; + } + + const { days, hours, minutes, seconds, microseconds } = + this._getDurationComponentsTimedeltaLike(deserialized); + + let string = pad2joincolon(hours, minutes, seconds); + + if (days) { + string = `${days} ${string}`; + } + + if (microseconds) { + string += `.${String(microseconds).padStart(6, "0")}`; + } + + return string; + } +} + +declare module "ember-data/types/registries/transform" { + export default interface TransformRegistry { + "django-duration": DjangoDurationTransform; + } +} diff --git a/frontend/app/transforms/django-time.ts b/frontend/app/transforms/django-time.ts new file mode 100644 index 000000000..86b26e98c --- /dev/null +++ b/frontend/app/transforms/django-time.ts @@ -0,0 +1,26 @@ +import MomentTransform from "timed/transforms/moment"; + +/** + * The django time transform + * + * This transforms a django time into a moment object + * + * @class DjangoTimeTransform + * @extends MomentTransform + * @public + */ +export default class DjangoTimeTransform extends MomentTransform { + /** + * The time format + * + * @property {String} format + * @public + */ + format = "HH:mm:ss"; +} + +declare module "ember-data/types/registries/transform" { + export default interface TransformRegistry { + "django-time": DjangoTimeTransform; + } +} diff --git a/frontend/app/transforms/django-workdays.ts b/frontend/app/transforms/django-workdays.ts new file mode 100644 index 000000000..a31ebb4a8 --- /dev/null +++ b/frontend/app/transforms/django-workdays.ts @@ -0,0 +1,42 @@ +import Transform from "@ember-data/serializer/transform"; + +/** + * Django worktime transform + * + * This transforms a string like '1,2,3' into an array of numbers + * + * @class DjangoWorktimeTransform + * @extends DS.Transform + * @public + */ +export default class DjangoWorkdaysTransform extends Transform { + /** + * Deserialize the string separated by comma into an array of numbers + * + * @method deserialize + * @param {String} serialized The string + * @return {Number[]} The deserialized array + * @public + */ + deserialize(serialized) { + return serialized.split(",").map(Number); + } + + /** + * Serialize the array of numbers into a string separated by comma + * + * @method serialize + * @param {Number[]} deserialized The number array + * @return {String} The serialized string + * @public + */ + serialize(deserialized) { + return deserialized.join(); + } +} + +declare module "ember-data/types/registries/transform" { + export default interface TransformRegistry { + "django-workdays": DjangoWorkdaysTransform; + } +} diff --git a/frontend/app/transforms/moment.ts b/frontend/app/transforms/moment.ts new file mode 100644 index 000000000..296ce5bca --- /dev/null +++ b/frontend/app/transforms/moment.ts @@ -0,0 +1,53 @@ +import Transform from "@ember-data/serializer/transform"; +import moment from "moment"; + +/** + * The moment transform + * + * This transforms any string into a moment object + * + * @class MomentTransform + * @extends DS.Transform + * @public + */ +export default class MomentTransform extends Transform { + /** + * The default date string format + * + * @property {String} format + * @public + */ + format = moment.defaultFormat; + + /** + * Deserialize the string into a moment object + * + * @method deserialize + * @param {String} serialized The date string + * @return {moment.duration} The deserialized moment object + * @public + */ + deserialize(serialized) { + return serialized ? moment(serialized, this.format) : null; + } + + /** + * Serialize the moment object into a string + * + * @method serialize + * @param {String} deserialized The moment object + * @return {moment.duration} The serialized date string + * @public + */ + serialize(deserialized) { + return deserialized && deserialized.isValid() + ? deserialized.format(this.format) + : null; + } +} + +declare module "ember-data/types/registries/transform" { + export default interface TransformRegistry { + moment: MomentTransform; + } +} diff --git a/frontend/types/ember-data/registries/model.d.ts b/frontend/types/ember-data/registries/model.d.ts index 6ec513ab7..bdd8c0f17 100644 --- a/frontend/types/ember-data/registries/model.d.ts +++ b/frontend/types/ember-data/registries/model.d.ts @@ -4,4 +4,4 @@ export default interface ModelRegistry { // eslint-disable-next-line @typescript-eslint/no-explicit-any [key: string]: any; -} \ No newline at end of file +} diff --git a/frontend/types/glint.d.ts b/frontend/types/glint.d.ts index 8fe909340..17bdace04 100644 --- a/frontend/types/glint.d.ts +++ b/frontend/types/glint.d.ts @@ -1,12 +1,12 @@ // Setup Glint Globals -import '@glint/environment-ember-loose'; -import '@glint/environment-ember-template-imports'; +import "@glint/environment-ember-loose"; +import "@glint/environment-ember-template-imports"; -declare module '@glint/environment-ember-loose/registry' { +declare module "@glint/environment-ember-loose/registry" { // Remove this once entries have been added! 👇 // eslint-disable-next-line @typescript-eslint/no-empty-interface export default interface Registry { // Add any registry entries from other addons here that your addon itself uses (in non-strict mode templates) // See https://typed-ember.gitbook.io/glint/using-glint/ember/using-addons } -} \ No newline at end of file +}