sowell-models 1.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 (39) hide show
  1. package/.editorconfig +15 -0
  2. package/.github/workflows/publish.yml +24 -0
  3. package/.github/workflows/remove-stale-branches.yml +19 -0
  4. package/.prettierrc +6 -0
  5. package/LICENSE +201 -0
  6. package/README.md +20 -0
  7. package/eslint.config.mjs +43 -0
  8. package/package.json +47 -0
  9. package/src/main.ts +26 -0
  10. package/src/models/Agency/index.ts +48 -0
  11. package/src/models/ApplicationRecord/index.ts +10 -0
  12. package/src/models/AreaItem/index.ts +80 -0
  13. package/src/models/Assignation/index.ts +21 -0
  14. package/src/models/Author/index.ts +16 -0
  15. package/src/models/Checklist/index.ts +62 -0
  16. package/src/models/Checkpoint/index.ts +57 -0
  17. package/src/models/Company/index.ts +26 -0
  18. package/src/models/Export/index.ts +27 -0
  19. package/src/models/Family/index.ts +41 -0
  20. package/src/models/IssueReport/index.ts +169 -0
  21. package/src/models/Location/index.ts +23 -0
  22. package/src/models/Origin/index.ts +44 -0
  23. package/src/models/Place/index.ts +75 -0
  24. package/src/models/Provider/index.ts +44 -0
  25. package/src/models/Reason/index.ts +41 -0
  26. package/src/models/Residence/index.ts +50 -0
  27. package/src/models/Sector/index.ts +56 -0
  28. package/src/models/Spot/index.ts +49 -0
  29. package/src/models/UnseenIssue/index.ts +17 -0
  30. package/src/models/User/index.ts +62 -0
  31. package/src/models/VisitProp/index.ts +38 -0
  32. package/src/models/VisitReport/index.ts +127 -0
  33. package/src/models/VisitSchedule/index.ts +58 -0
  34. package/src/models/category/index.ts +54 -0
  35. package/src/models/interfaces.ts +8 -0
  36. package/src/models/types.ts +10 -0
  37. package/src/utils/arrayDiffBy.ts +14 -0
  38. package/tsconfig.json +13 -0
  39. package/tsconfig.release.json +9 -0
@@ -0,0 +1,49 @@
1
+ import { SPAreaItem, IArea } from "../AreaItem/index"
2
+ import { Model, Attr, BelongsTo } from "spraypaint"
3
+ import { orderBy } from "lodash"
4
+ import { PouchCollection, PouchORM } from "pouchorm"
5
+ import { IModel } from "pouchorm"
6
+ import { IPlace, SPPlaceItem } from "../Place"
7
+ import { ICompany, SPCompany } from "../Company"
8
+ import { CollectionFindOptions, IClearable } from "../interfaces"
9
+ import { ApplicationRecord } from "../ApplicationRecord"
10
+
11
+ export interface ISpot extends IModel {
12
+ name: string
13
+ placeId?: string
14
+ place?: IPlace
15
+ area?: IArea
16
+ company?: ICompany
17
+ }
18
+
19
+ export class SpotCollection
20
+ extends PouchCollection<ISpot>
21
+ implements IClearable {
22
+ async beforeInit(): Promise<void> {
23
+ // NOTE: none
24
+ }
25
+
26
+ async clear(): Promise<void> {
27
+ await PouchORM.clearDatabase("spots")
28
+ }
29
+
30
+ async find(
31
+ selector?: Record<string, any> | Partial<ISpot> | undefined,
32
+ opts?: CollectionFindOptions
33
+ ) {
34
+ const result = await super.find(selector, opts)
35
+ return orderBy(result, ["name"], ["asc"])
36
+ }
37
+ }
38
+
39
+ @Model()
40
+ export class SPSpotItem extends ApplicationRecord implements ISpot {
41
+ static jsonapiType = "spots"
42
+ @Attr() name!: string
43
+ @BelongsTo() place!: SPPlaceItem
44
+ @BelongsTo() area!: SPAreaItem
45
+ @BelongsTo() company!: SPCompany
46
+ get _id(): string | undefined {
47
+ return this.id
48
+ }
49
+ }
@@ -0,0 +1,17 @@
1
+ import { BelongsTo, Model } from "spraypaint"
2
+ import { ApplicationRecord } from "../ApplicationRecord"
3
+ import { IUser, SPUserItem } from "../User"
4
+ import { IIssueReport, SPIssueReportItem } from "../IssueReport"
5
+ import { IModel } from "pouchorm"
6
+
7
+ export interface IUnseenIssue extends IModel {
8
+ user?: IUser
9
+ issueReport?: IIssueReport
10
+ }
11
+
12
+ @Model()
13
+ export class SPUnseenIssue extends ApplicationRecord implements IUnseenIssue {
14
+ static jsonapiType = "unseen_issues"
15
+ @BelongsTo() user!: SPUserItem
16
+ @BelongsTo() issueReport!: SPIssueReportItem
17
+ }
@@ -0,0 +1,62 @@
1
+ import { Attr, BelongsTo, HasMany, Model } from "spraypaint"
2
+ import { ICompany, SPCompany } from "../Company"
3
+
4
+ import { ApplicationRecord } from "../ApplicationRecord"
5
+ import { IModel, PouchCollection, PouchORM } from "pouchorm"
6
+ import { IAssignation, SPAssignation } from "../Assignation"
7
+ import arrayDiffBy from "../../utils/arrayDiffBy"
8
+ import { SPUnseenIssue } from "../UnseenIssue"
9
+
10
+ export type PhoneNumber = {
11
+ msisdn: string
12
+ }
13
+
14
+ export interface IUser extends IModel {
15
+ id?: string
16
+ fname?: string
17
+ lname?: string
18
+ fullName?: string
19
+ email?: string
20
+ company?: ICompany
21
+ img?: string
22
+ password?: string
23
+ assignationsCount?: number | null
24
+ status?: string
25
+ recipients?: PhoneNumber[]
26
+ assignations?: IAssignation[]
27
+ canUpdateIssueReports?: boolean
28
+ }
29
+
30
+ export class UserCollection extends PouchCollection<IUser> {
31
+ async beforeInit(): Promise<void> {
32
+ await this.addIndex(["id"])
33
+ }
34
+
35
+ async clear(): Promise<void> {
36
+ await PouchORM.clearDatabase("users")
37
+ }
38
+
39
+ async bulkUpsertIfNotExists(items: IUser[]): Promise<IUser[]> {
40
+ const existingItems = await super.find({})
41
+ const diff = arrayDiffBy(existingItems, items, "id")
42
+ const upsertedIssueReport = await super.bulkUpsert(diff.missingLeft)
43
+ return upsertedIssueReport
44
+ }
45
+ }
46
+
47
+ @Model()
48
+ export class SPUserItem extends ApplicationRecord implements IUser {
49
+ static jsonapiType = "users"
50
+ @Attr() fname!: string
51
+ @Attr() lname!: string
52
+ @Attr() email!: string
53
+ @Attr() fullName!: string
54
+ @Attr() password!: string
55
+ @Attr() status!: string
56
+ @Attr() recipients?: PhoneNumber[]
57
+ @Attr() assignationsCount!: number | null
58
+ @Attr() canUpdateIssueReports!: boolean
59
+ @BelongsTo() company!: SPCompany
60
+ @HasMany() assignations!: SPAssignation[]
61
+ @HasMany() unseenIssues!: SPUnseenIssue[]
62
+ }
@@ -0,0 +1,38 @@
1
+ import { Model, Attr, BelongsTo } from "spraypaint"
2
+ import { IModel } from "pouchorm"
3
+
4
+ import { ApplicationRecord } from "../ApplicationRecord"
5
+ import { IPlace, SPPlaceItem } from "../Place"
6
+ import { ICheckpoint, SPCheckpointItem } from "../Checkpoint"
7
+ import { IResidence, SPResidenceItem } from "../Residence"
8
+ import { ISpot, SPSpotItem } from "../Spot"
9
+
10
+ export type CleanerType = "reporter" | "tenant" | "provider" | "others"
11
+ export interface IVisitProp extends IModel {
12
+ _id?: string
13
+ status?: "pending" | "missing"
14
+ place?: IPlace
15
+ placeId?: number
16
+ checkpoint?: ICheckpoint
17
+ checkpointId?: number
18
+ residence?: IResidence
19
+ residenceId?: number
20
+ spot?: ISpot
21
+ spotId?: number
22
+ cleaner?: CleanerType
23
+ }
24
+
25
+ @Model()
26
+ export class SPVisitPropItem extends ApplicationRecord implements IVisitProp {
27
+ static jsonapiType = "visit_props"
28
+ @Attr() status!: "pending" | "missing"
29
+ @Attr() cleaner!: CleanerType
30
+ @Attr() placeId!: number
31
+ @Attr() checkpointId!: number
32
+ @Attr() residenceId!: number
33
+ @Attr() spotId!: number
34
+ @BelongsTo() place!: SPPlaceItem
35
+ @BelongsTo() checkpoint!: SPCheckpointItem
36
+ @BelongsTo() residence!: SPResidenceItem
37
+ @BelongsTo() spot!: SPSpotItem
38
+ }
@@ -0,0 +1,127 @@
1
+ import { Model, Attr, BelongsTo, HasMany } from "spraypaint"
2
+ import { IModel } from "pouchorm"
3
+
4
+ import { IUser, SPUserItem } from "../User"
5
+ import { IIssueReport, SPIssueReportItem } from "../IssueReport"
6
+ import { ApplicationRecord } from "../ApplicationRecord"
7
+ import { IVisitSchedule, SPVisitSchedule } from "../VisitSchedule"
8
+ import { ICheckpoint } from "../Checkpoint"
9
+ import * as yup from "yup"
10
+ import { IChecklist, SPChecklistItem } from "../Checklist"
11
+ import { IPlace, SPPlaceItem } from "../Place"
12
+ import { IResidence, SPResidenceItem } from "../Residence"
13
+ import { ISpot, SPSpotItem } from "../Spot"
14
+
15
+ export enum CheckpointStatus {
16
+ NONE = "NONE",
17
+ OK = "OK",
18
+ KO = "KO",
19
+ MISSING = "MISSING",
20
+ PENDING = "PENDING"
21
+ }
22
+
23
+ export interface VisitReportPayload {
24
+ visitSchedule?: string
25
+ checkpoints: CheckpointProps[]
26
+ residence?: string
27
+ place?: string
28
+ spot?: string
29
+ checklist?: string
30
+ isContradictory?: boolean
31
+ assistedBy?: {
32
+ id: string
33
+ fullName: string
34
+ }
35
+ comment?: string
36
+ validate?: (payload: VisitReportPayload) => string
37
+ }
38
+
39
+ export interface CheckpointProps {
40
+ _id?: string
41
+ id?: string
42
+ label?: string
43
+ status: CheckpointStatus
44
+ issue?: {
45
+ description: string
46
+ priority: string
47
+ imgs?: {
48
+ base64: string[]
49
+ }
50
+ }
51
+ category?: number
52
+ comment?: string
53
+ cleaner?: "tenant" | "provider" | "reporter" | "others"
54
+ coefficient?: number
55
+ }
56
+
57
+ export interface IVisitReport extends IModel {
58
+ id?: string
59
+ createdAt?: Date
60
+ checkpoints: ICheckpoint[] | CheckpointProps[]
61
+ visitSchedule?: IVisitSchedule
62
+ checklist?: IChecklist
63
+ residence?: IResidence
64
+ place?: IPlace
65
+ spot?: ISpot
66
+ author?: IUser
67
+ issueReports?: IIssueReport[]
68
+ isContradictory?: boolean
69
+ assistedBy?: {
70
+ id: string
71
+ full_name: string
72
+ }
73
+ score?: number
74
+ validate?: (payload: VisitReportPayload) => string
75
+ }
76
+
77
+ @Model()
78
+ export class SPVisitReport extends ApplicationRecord implements IVisitReport {
79
+ static jsonapiType = "visit_reports"
80
+ @Attr() checkpoints!: CheckpointProps[]
81
+ @Attr() createdAt!: Date
82
+ @Attr() score!: number
83
+ @Attr() isContradictory!: boolean
84
+ @Attr() assistedBy!: {
85
+ id: string
86
+ full_name: string
87
+ }
88
+ @Attr() comment!: string
89
+ @BelongsTo() author!: SPUserItem
90
+ @BelongsTo() visitSchedule!: SPVisitSchedule
91
+ @BelongsTo() checklist!: SPChecklistItem
92
+ @BelongsTo() residence!: SPResidenceItem
93
+ @BelongsTo() place!: SPPlaceItem
94
+ @BelongsTo() spot!: SPSpotItem
95
+ @HasMany() issueReports!: SPIssueReportItem[]
96
+
97
+ static async validate(payload: VisitReportPayload) {
98
+ const schema = yup
99
+ .object()
100
+ .required()
101
+ .shape({
102
+ checkpoints: yup.array().of(
103
+ yup.object().shape({
104
+ id: yup.string().required(),
105
+ status: yup.string().required()
106
+ })
107
+ ),
108
+ visitSchedule: yup.string(),
109
+ checklist: yup.string(),
110
+ residence: yup.string(),
111
+ place: yup.string(),
112
+ spot: yup.string(),
113
+ comment: yup.string()
114
+ })
115
+
116
+ const isValid = await schema.isValid(payload)
117
+
118
+ if (!isValid) {
119
+ try {
120
+ await schema.validate(payload)
121
+ } catch (error: any) {
122
+ return error.message
123
+ }
124
+ }
125
+ return "VALID"
126
+ }
127
+ }
@@ -0,0 +1,58 @@
1
+ import { Model, Attr, BelongsTo } from "spraypaint"
2
+ import { IModel, PouchCollection, PouchORM } from "pouchorm"
3
+ import { orderBy } from "lodash"
4
+
5
+ import { ApplicationRecord } from "../ApplicationRecord"
6
+ import { IPlace, SPPlaceItem } from "../Place"
7
+ import { IChecklist, SPChecklistItem } from "../Checklist"
8
+ import { CollectionFindOptions, IClearable } from "../interfaces"
9
+ import { IResidence, SPResidenceItem } from "../Residence"
10
+ import { ISpot, SPSpotItem } from "../Spot"
11
+
12
+ export interface IVisitSchedule extends IModel {
13
+ _id?: string
14
+ dueAt: Date
15
+ checklistId?: string
16
+ residence?: IResidence
17
+ place?: IPlace
18
+ spot?: ISpot
19
+ checklist?: IChecklist
20
+ }
21
+
22
+ export class VisitSchedulesCollection
23
+ extends PouchCollection<IVisitSchedule>
24
+ implements IClearable
25
+ {
26
+ async beforeInit(): Promise<void> {
27
+ // NOTE: none
28
+ }
29
+
30
+ async clear(): Promise<void> {
31
+ await PouchORM.clearDatabase("visit_schedules")
32
+ }
33
+
34
+ async find(
35
+ selector?: Record<string, any> | Partial<IVisitSchedule> | undefined,
36
+ opts?: CollectionFindOptions
37
+ ) {
38
+ const result = await super.find(selector, opts)
39
+ return orderBy(result, ["dueAt"], ["asc"])
40
+ }
41
+ }
42
+
43
+ @Model()
44
+ export class SPVisitSchedule
45
+ extends ApplicationRecord
46
+ implements IVisitSchedule
47
+ {
48
+ static jsonapiType = "visit_schedules"
49
+ @Attr() dueAt!: Date
50
+ @Attr() checklistId!: string
51
+ @BelongsTo() residence!: SPResidenceItem
52
+ @BelongsTo() place!: SPPlaceItem
53
+ @BelongsTo() spot!: SPSpotItem
54
+ @BelongsTo() checklist!: SPChecklistItem
55
+ get _id(): string | undefined {
56
+ return this.id
57
+ }
58
+ }
@@ -0,0 +1,54 @@
1
+ import { Model, Attr, BelongsTo, HasMany } from "spraypaint"
2
+ import { IModel, PouchCollection, PouchORM } from "pouchorm"
3
+ import { orderBy } from "lodash"
4
+
5
+ import { ApplicationRecord } from "../ApplicationRecord"
6
+ import { ICompany, SPCompany } from "../Company"
7
+ import { IArea, SPAreaItem } from "../AreaItem"
8
+ import { SPCheckpointItem } from "../Checkpoint"
9
+ import { CollectionFindOptions, IClearable } from "../interfaces"
10
+ import { IReason, SPReasonItem } from "../Reason"
11
+
12
+ export interface ICategory extends IModel {
13
+ _id?: string
14
+ name: string
15
+ iconUrl?: string
16
+ company?: ICompany
17
+ areaId?: string
18
+ area?: IArea
19
+ reasons?: IReason[]
20
+ }
21
+
22
+ export class CategoryCollection
23
+ extends PouchCollection<ICategory>
24
+ implements IClearable {
25
+ async beforeInit(): Promise<void> {
26
+ // NOTE: none
27
+ }
28
+
29
+ async clear(): Promise<void> {
30
+ await PouchORM.clearDatabase("categories")
31
+ }
32
+
33
+ async find(
34
+ selector?: Record<string, any> | Partial<ICategory> | undefined,
35
+ opts?: CollectionFindOptions
36
+ ) {
37
+ const result = await super.find(selector, opts)
38
+ return orderBy(result, ["name"], ["asc"])
39
+ }
40
+ }
41
+
42
+ @Model()
43
+ export class SPCategoryItem extends ApplicationRecord implements ICategory {
44
+ static jsonapiType = "categories"
45
+ @Attr() name!: string
46
+ @Attr() iconUrl!: string
47
+ @BelongsTo() company!: SPCompany
48
+ @BelongsTo() area!: SPAreaItem
49
+ get _id(): string | undefined {
50
+ return this.id
51
+ }
52
+ @HasMany() checkpoints!: SPCheckpointItem[]
53
+ @HasMany() reasons!: SPReasonItem[]
54
+ }
@@ -0,0 +1,8 @@
1
+ export interface IClearable {
2
+ clear(): Promise<void>
3
+ }
4
+
5
+ export interface CollectionFindOptions {
6
+ sort?: string[] | undefined
7
+ limit?: number | undefined
8
+ }
@@ -0,0 +1,10 @@
1
+ import { intersectionBy, differenceBy, ValueIteratee } from "lodash"
2
+
3
+ export default function arrayDiffBy<T> (array1: T[], array2: T[], iteratee: ValueIteratee<T>) {
4
+ const result = {
5
+ matched: intersectionBy(array1, array2, iteratee),
6
+ missingRight: differenceBy(array1, array2, iteratee),
7
+ missingLeft: differenceBy(array2, array1, iteratee)
8
+ }
9
+ return result
10
+ }
@@ -0,0 +1,14 @@
1
+ import { intersectionBy, differenceBy, ValueIteratee } from "lodash"
2
+
3
+ export default function arrayDiffBy<T>(
4
+ array1: T[],
5
+ array2: T[],
6
+ iteratee: ValueIteratee<T>
7
+ ) {
8
+ const result = {
9
+ matched: intersectionBy(array1, array2, iteratee),
10
+ missingRight: differenceBy(array1, array2, iteratee),
11
+ missingLeft: differenceBy(array2, array1, iteratee)
12
+ }
13
+ return result
14
+ }
package/tsconfig.json ADDED
@@ -0,0 +1,13 @@
1
+ {
2
+ "compilerOptions": {
3
+ "experimentalDecorators": true,
4
+ "allowJs": true,
5
+ "target": "es6",
6
+ "moduleResolution": "node",
7
+ "esModuleInterop": true,
8
+ "skipLibCheck": true,
9
+ "forceConsistentCasingInFileNames": true,
10
+ "resolveJsonModule": true,
11
+ "types": ["node"]
12
+ }
13
+ }
@@ -0,0 +1,9 @@
1
+ {
2
+ "extends": "./tsconfig.json",
3
+ "compilerOptions": {
4
+ "sourceMap": false,
5
+ "removeComments": true
6
+ },
7
+ "include": ["src"],
8
+ "exclude": ["**/*.test.ts", "**/*.spec.ts"]
9
+ }