@sneat/dto 0.1.1
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.
- package/eslint.config.js +7 -0
- package/ng-package.json +7 -0
- package/package.json +14 -0
- package/project.json +38 -0
- package/src/index.ts +1 -0
- package/src/lib/by-user.ts +4 -0
- package/src/lib/dto-app.ts +9 -0
- package/src/lib/dto-brief.ts +4 -0
- package/src/lib/dto-commune.ts +158 -0
- package/src/lib/dto-constants.spec.ts +7 -0
- package/src/lib/dto-constants.ts +2 -0
- package/src/lib/dto-models.ts +105 -0
- package/src/lib/dto-pricing.ts +5 -0
- package/src/lib/dto-team-brief.ts +20 -0
- package/src/lib/dto-team-info.ts +15 -0
- package/src/lib/dto-team-meeting.ts +6 -0
- package/src/lib/dto-team-metric.ts +29 -0
- package/src/lib/dto-team.ts +27 -0
- package/src/lib/dto-user.ts +85 -0
- package/src/lib/dto-with-modified.ts +32 -0
- package/src/lib/index.ts +18 -0
- package/src/lib/period.ts +1 -0
- package/src/lib/types.ts +34 -0
- package/src/lib/ui-state.ts +5 -0
- package/src/lib/with-related.ts +180 -0
- package/src/test-setup.ts +1 -0
- package/tsconfig.json +13 -0
- package/tsconfig.lib.json +19 -0
- package/tsconfig.lib.prod.json +7 -0
- package/tsconfig.spec.json +31 -0
- package/vite.config.mts +10 -0
package/eslint.config.js
ADDED
package/ng-package.json
ADDED
package/package.json
ADDED
package/project.json
ADDED
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "dto",
|
|
3
|
+
"$schema": "../../node_modules/nx/schemas/project-schema.json",
|
|
4
|
+
"projectType": "library",
|
|
5
|
+
"sourceRoot": "libs/dto/src",
|
|
6
|
+
"prefix": "sneat",
|
|
7
|
+
"targets": {
|
|
8
|
+
"build": {
|
|
9
|
+
"executor": "@nx/angular:ng-packagr-lite",
|
|
10
|
+
"outputs": [
|
|
11
|
+
"{workspaceRoot}/dist/libs/dto"
|
|
12
|
+
],
|
|
13
|
+
"options": {
|
|
14
|
+
"project": "libs/dto/ng-package.json",
|
|
15
|
+
"tsConfig": "libs/dto/tsconfig.lib.json"
|
|
16
|
+
},
|
|
17
|
+
"configurations": {
|
|
18
|
+
"production": {
|
|
19
|
+
"tsConfig": "libs/dto/tsconfig.lib.prod.json"
|
|
20
|
+
},
|
|
21
|
+
"development": {}
|
|
22
|
+
},
|
|
23
|
+
"defaultConfiguration": "production"
|
|
24
|
+
},
|
|
25
|
+
"test": {
|
|
26
|
+
"executor": "@nx/vitest:test",
|
|
27
|
+
"outputs": [
|
|
28
|
+
"{workspaceRoot}/coverage/libs/dto"
|
|
29
|
+
],
|
|
30
|
+
"options": {
|
|
31
|
+
"tsConfig": "libs/dto/tsconfig.spec.json"
|
|
32
|
+
}
|
|
33
|
+
},
|
|
34
|
+
"lint": {
|
|
35
|
+
"executor": "@nx/eslint:lint"
|
|
36
|
+
}
|
|
37
|
+
}
|
|
38
|
+
}
|
package/src/index.ts
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export * from './lib';
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
// import { IAuthStateRecord } from '../../auth/interfaces';
|
|
2
|
+
import { ITitledRecord } from './dto-models';
|
|
3
|
+
|
|
4
|
+
export interface DtoApp {
|
|
5
|
+
/*extends IRecord*/ users?: ITitledRecord[];
|
|
6
|
+
currentLanguage?: string;
|
|
7
|
+
// authState?: IAuthStateRecord;
|
|
8
|
+
isInDemoMode?: boolean;
|
|
9
|
+
}
|
|
@@ -0,0 +1,158 @@
|
|
|
1
|
+
import { IDemoRecord, ITitledRecord, ITotalsHolder } from './dto-models';
|
|
2
|
+
import { CommuneType, CountryId } from './types';
|
|
3
|
+
|
|
4
|
+
export const enum SpaceCounter {
|
|
5
|
+
activities = 'activities',
|
|
6
|
+
assets = 'assets',
|
|
7
|
+
assetGroups = 'assetGroups',
|
|
8
|
+
contacts = 'contacts',
|
|
9
|
+
documents = 'documents',
|
|
10
|
+
liabilities = 'liabilities',
|
|
11
|
+
members = 'members',
|
|
12
|
+
// membersByRole = 'membersByRole',
|
|
13
|
+
memberGroups = 'memberGroups',
|
|
14
|
+
overdues = 'overdues',
|
|
15
|
+
pupils = 'pupils',
|
|
16
|
+
regularTasks = 'regularTasks',
|
|
17
|
+
staff = 'staff',
|
|
18
|
+
todos = 'todos',
|
|
19
|
+
upcomings = 'upcomings',
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
// type TeamCounters = {
|
|
23
|
+
// [P in EnumAsUnionOfKeys<typeof TeamCounter>]: number;
|
|
24
|
+
// };
|
|
25
|
+
|
|
26
|
+
export interface SpaceCounts {
|
|
27
|
+
[SpaceCounter.activities]?: number;
|
|
28
|
+
[SpaceCounter.assets]?: number;
|
|
29
|
+
[SpaceCounter.assetGroups]?: number;
|
|
30
|
+
[SpaceCounter.contacts]?: number;
|
|
31
|
+
[SpaceCounter.documents]?: number;
|
|
32
|
+
[SpaceCounter.regularTasks]?: number;
|
|
33
|
+
[SpaceCounter.liabilities]?: number;
|
|
34
|
+
[SpaceCounter.pupils]?: number;
|
|
35
|
+
[SpaceCounter.staff]?: number;
|
|
36
|
+
[SpaceCounter.members]?: number;
|
|
37
|
+
[SpaceCounter.memberGroups]?: number;
|
|
38
|
+
[SpaceCounter.overdues]?: number;
|
|
39
|
+
[SpaceCounter.todos]?: number;
|
|
40
|
+
[SpaceCounter.upcomings]?: number;
|
|
41
|
+
// [CommuneCounter.membersByRole]?: { [role: string]: number }; this does not make sense here
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
export function incrementNumberOf<
|
|
45
|
+
NumberOf,
|
|
46
|
+
Dbo extends { numberOf?: NumberOf },
|
|
47
|
+
>(dto: Dbo, init: () => NumberOf, counter: keyof NumberOf, v = 1): Dbo {
|
|
48
|
+
const current: number = ((dto.numberOf && dto.numberOf[counter]) ||
|
|
49
|
+
0) as unknown as number;
|
|
50
|
+
return {
|
|
51
|
+
...dto,
|
|
52
|
+
numberOf: {
|
|
53
|
+
...(dto.numberOf || init()),
|
|
54
|
+
[counter]: current + v,
|
|
55
|
+
},
|
|
56
|
+
};
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
export function newSpaceCounts(numberOf?: SpaceCounts): SpaceCounts {
|
|
60
|
+
numberOf = numberOf || {};
|
|
61
|
+
return {
|
|
62
|
+
activities: numberOf.activities || 0,
|
|
63
|
+
assets: numberOf.assets || 0,
|
|
64
|
+
assetGroups: numberOf.assetGroups || 0,
|
|
65
|
+
contacts: numberOf.contacts || 0,
|
|
66
|
+
documents: numberOf.documents || 0,
|
|
67
|
+
members: numberOf.members || 0,
|
|
68
|
+
memberGroups: numberOf.memberGroups || 0,
|
|
69
|
+
liabilities: numberOf.liabilities || 0,
|
|
70
|
+
overdues: numberOf.overdues || 0,
|
|
71
|
+
regularTasks: numberOf.regularTasks || 0,
|
|
72
|
+
todos: numberOf.todos || 0,
|
|
73
|
+
upcomings: numberOf.upcomings || 0,
|
|
74
|
+
};
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
export interface ICommuneDbo extends IDemoRecord, ITitledRecord, ITotalsHolder {
|
|
78
|
+
readonly countryId?: CountryId;
|
|
79
|
+
readonly type: CommuneType;
|
|
80
|
+
readonly desc?: string;
|
|
81
|
+
readonly userID: string;
|
|
82
|
+
readonly order?: number;
|
|
83
|
+
readonly numberOf?: SpaceCounts;
|
|
84
|
+
readonly membersCountByRole?: Record<string, number>;
|
|
85
|
+
readonly noContactRoles?: string[];
|
|
86
|
+
// readonly groups?: ICommuneDtoMemberGroupInfo[];
|
|
87
|
+
// readonly members?: readonly ITeamMemberInfo[];
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
export function isCommuneShouldHoldMembersInfo(type: CommuneType): boolean {
|
|
91
|
+
return type === 'family' || type === 'cohabit';
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
// export function findCommuneMemberInfo(
|
|
95
|
+
// members: readonly ITeamMemberInfo[],
|
|
96
|
+
// member: { readonly id?: string; readonly userID?: string },
|
|
97
|
+
// ): ITeamMemberInfo | undefined {
|
|
98
|
+
// return (
|
|
99
|
+
// members &&
|
|
100
|
+
// members.find(
|
|
101
|
+
// (m) =>
|
|
102
|
+
// (m && !!m.id && m.id === member.id) ||
|
|
103
|
+
// (!!m.userID && m.userID === member.userID),
|
|
104
|
+
// )
|
|
105
|
+
// );
|
|
106
|
+
// }
|
|
107
|
+
|
|
108
|
+
export const personalCommuneIdPrefix = 'u_';
|
|
109
|
+
|
|
110
|
+
export function getUserPersonalCommuneID(userID?: string): string {
|
|
111
|
+
if (!userID) {
|
|
112
|
+
throw new Error('userID is required parameter');
|
|
113
|
+
}
|
|
114
|
+
return personalCommuneIdPrefix + userID;
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
export function isPersonalCommuneId(id: string): boolean {
|
|
118
|
+
return !!id && id.startsWith(personalCommuneIdPrefix);
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
export function isUserPersonalCommune(
|
|
122
|
+
communeID: string,
|
|
123
|
+
userID?: string,
|
|
124
|
+
): boolean {
|
|
125
|
+
return !!userID && communeID === getUserPersonalCommuneID(userID);
|
|
126
|
+
}
|
|
127
|
+
|
|
128
|
+
// export const CommuneModel = {
|
|
129
|
+
// getListInfoByRealId: (dto: ITeamDto, listId: string) => {
|
|
130
|
+
// const result =
|
|
131
|
+
// dto.listGroups &&
|
|
132
|
+
// dto.listGroups
|
|
133
|
+
// .map((lg) => lg.lists && lg.lists.find((l) => l.id === listId))
|
|
134
|
+
// .find((l) => !!l);
|
|
135
|
+
// console.log(
|
|
136
|
+
// `CommuneModel.getListInfoByRealId(${listId})`,
|
|
137
|
+
// dto.listGroups,
|
|
138
|
+
// ' => ',
|
|
139
|
+
// result,
|
|
140
|
+
// );
|
|
141
|
+
// return result;
|
|
142
|
+
// },
|
|
143
|
+
//
|
|
144
|
+
// getListInfoByShortId: (dto: ICommuneDto, shortListId: string) => {
|
|
145
|
+
// console.log(
|
|
146
|
+
// `CommuneModel.getListInfoByShortId(${shortListId})`,
|
|
147
|
+
// dto.listGroups,
|
|
148
|
+
// );
|
|
149
|
+
// return (
|
|
150
|
+
// dto.listGroups &&
|
|
151
|
+
// dto.listGroups
|
|
152
|
+
// .map(
|
|
153
|
+
// (lg) => lg.lists && lg.lists.find((l) => l.shortId === shortListId),
|
|
154
|
+
// )
|
|
155
|
+
// .find((l) => !!l)
|
|
156
|
+
// );
|
|
157
|
+
// },
|
|
158
|
+
// };
|
|
@@ -0,0 +1,105 @@
|
|
|
1
|
+
import { Restriction } from './types';
|
|
2
|
+
|
|
3
|
+
// import {IPersonSize} from '../ui/dto-sizechart';
|
|
4
|
+
|
|
5
|
+
export interface ITitled {
|
|
6
|
+
title: string;
|
|
7
|
+
}
|
|
8
|
+
|
|
9
|
+
export interface ITitledRecord {
|
|
10
|
+
id?: string;
|
|
11
|
+
title?: string;
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
export interface ITitledRecordInfo {
|
|
15
|
+
id: string;
|
|
16
|
+
title: string;
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
// | MemberRoleWork;
|
|
20
|
+
|
|
21
|
+
export interface DtoTotal {
|
|
22
|
+
count: number;
|
|
23
|
+
day?: number;
|
|
24
|
+
week?: number;
|
|
25
|
+
month?: number;
|
|
26
|
+
quarter?: number;
|
|
27
|
+
year?: number;
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
export interface IDemoRecord {
|
|
31
|
+
id?: string;
|
|
32
|
+
isDemo?: boolean;
|
|
33
|
+
isLocal?: boolean;
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
export interface ITotalsHolder {
|
|
37
|
+
totals?: DtoTotals;
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
export interface DtoTotals {
|
|
41
|
+
incomes?: DtoTotal;
|
|
42
|
+
expenses?: DtoTotal;
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
// export interface Category extends ITitledRecord {
|
|
46
|
+
// id: string;
|
|
47
|
+
// }
|
|
48
|
+
|
|
49
|
+
export interface IWithSpaceIDs {
|
|
50
|
+
spaceIDs?: string[];
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
export interface IWithTag {
|
|
54
|
+
tags?: string[];
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
export interface IWithAssetIDs {
|
|
58
|
+
assetIDs?: string[];
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
export interface IWithMemberIDs {
|
|
62
|
+
memberIDs?: string[];
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
export interface IRealEstate {
|
|
66
|
+
leaseExpires?: string;
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
export interface IWithRestrictions {
|
|
70
|
+
restrictions?: Restriction[];
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
function newTotal(): DtoTotal {
|
|
74
|
+
return { count: 0, day: 0, week: 0, month: 0, quarter: 0, year: 0 };
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
export function newTotals(): DtoTotals {
|
|
78
|
+
// TODO: Rename to ITotalsDto
|
|
79
|
+
return { incomes: newTotal(), expenses: newTotal() };
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
export function zeroIfEmptyTotals(totals: DtoTotals): DtoTotals {
|
|
83
|
+
const f = (t?: DtoTotal) =>
|
|
84
|
+
t
|
|
85
|
+
? {
|
|
86
|
+
count: t.count || 0,
|
|
87
|
+
day: t.day || 0,
|
|
88
|
+
week: t.week || 0,
|
|
89
|
+
month: t.month || 0,
|
|
90
|
+
quarter: t.quarter || 0,
|
|
91
|
+
year: t.year || 0,
|
|
92
|
+
}
|
|
93
|
+
: newTotal();
|
|
94
|
+
totals.incomes = f(totals.incomes);
|
|
95
|
+
totals.expenses = f(totals.expenses);
|
|
96
|
+
return totals;
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
export interface IVerification {
|
|
100
|
+
isVerified?: boolean;
|
|
101
|
+
verification?: {
|
|
102
|
+
confirmedBy?: string[]; // List of ID of users who confirmed validity of the member
|
|
103
|
+
rejectedBy?: string[]; // List of ID of users who questions validity of the member
|
|
104
|
+
};
|
|
105
|
+
}
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
import { SpaceType } from '@sneat/core';
|
|
2
|
+
|
|
3
|
+
export interface ISpaceBrief {
|
|
4
|
+
readonly title: string;
|
|
5
|
+
readonly type: SpaceType;
|
|
6
|
+
readonly parentSpaceID?: string;
|
|
7
|
+
readonly roles?: string[];
|
|
8
|
+
}
|
|
9
|
+
|
|
10
|
+
export const equalSpaceBriefs = (
|
|
11
|
+
v1?: ISpaceBrief | null | undefined,
|
|
12
|
+
v2?: ISpaceBrief | null | undefined,
|
|
13
|
+
): boolean => {
|
|
14
|
+
if (v1 === v2) return true;
|
|
15
|
+
return v1?.parentSpaceID === v2?.parentSpaceID && v1?.title === v2?.title;
|
|
16
|
+
};
|
|
17
|
+
|
|
18
|
+
export const isSpaceSupportsMemberGroups = (t: SpaceType): boolean => {
|
|
19
|
+
return t === 'educator' || t === 'sport_club' || t === 'cohabit';
|
|
20
|
+
};
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
import { SpaceType } from '@sneat/core';
|
|
2
|
+
import { IUserCommuneInfo } from './dto-user';
|
|
3
|
+
|
|
4
|
+
export interface IShortSpaceInfo {
|
|
5
|
+
type: SpaceType;
|
|
6
|
+
id?: string;
|
|
7
|
+
// shortId?: CommuneShortId;
|
|
8
|
+
title?: string;
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
export function createShortCommuneInfoFromUserCommuneInfo(
|
|
12
|
+
v: IUserCommuneInfo,
|
|
13
|
+
): IShortSpaceInfo {
|
|
14
|
+
return { id: v.id, title: v.title, /*shortId: v.shortId,*/ type: v.type };
|
|
15
|
+
}
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
export type MetricColor =
|
|
2
|
+
| 'primary'
|
|
3
|
+
| 'secondary'
|
|
4
|
+
| 'tertiary'
|
|
5
|
+
| 'success'
|
|
6
|
+
| 'danger'
|
|
7
|
+
| 'warning';
|
|
8
|
+
|
|
9
|
+
export interface IBoolMetricVal {
|
|
10
|
+
label: string;
|
|
11
|
+
color: MetricColor;
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
export interface IBoolMetric {
|
|
15
|
+
true: IBoolMetricVal;
|
|
16
|
+
false: IBoolMetricVal;
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
export interface ISpaceMetric {
|
|
20
|
+
id?: string;
|
|
21
|
+
title: string;
|
|
22
|
+
colorTrue?: MetricColor;
|
|
23
|
+
colorFalse?: MetricColor;
|
|
24
|
+
type: 'bool' | 'integer' | 'options';
|
|
25
|
+
mode: 'personal' | 'team';
|
|
26
|
+
bool?: IBoolMetric;
|
|
27
|
+
min?: number;
|
|
28
|
+
max?: number;
|
|
29
|
+
}
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
import { ITotalsHolder } from './dto-models';
|
|
2
|
+
import { ISpaceBrief } from './dto-team-brief';
|
|
3
|
+
import { ISpaceMetric } from './dto-team-metric';
|
|
4
|
+
|
|
5
|
+
// export interface ITeamMeetings {
|
|
6
|
+
// scrum?: IMemberBrief;
|
|
7
|
+
// retrospective?: IMeetingInfo;
|
|
8
|
+
// }
|
|
9
|
+
|
|
10
|
+
export interface ISpaceDbo extends ISpaceBrief, ITotalsHolder {
|
|
11
|
+
readonly countryID: string;
|
|
12
|
+
readonly userIDs: string[];
|
|
13
|
+
// readonly members?: IMemberBrief[];
|
|
14
|
+
// readonly assets?: IAssetBrief[];
|
|
15
|
+
// readonly contacts?: IContactBrief[];
|
|
16
|
+
// readonly numberOf?: TeamCounts;
|
|
17
|
+
// readonly recurringHappenings?: { [id: string]: IHappeningBrief }; // TODO: Move to ISchedulusTeamDto
|
|
18
|
+
|
|
19
|
+
metrics: ISpaceMetric[];
|
|
20
|
+
// active?: ITeamMeetings;
|
|
21
|
+
// last?: ITeamMeetings;
|
|
22
|
+
upcomingRetro?: {
|
|
23
|
+
itemsByUserAndType?: Record<string, Record<string, number>>;
|
|
24
|
+
};
|
|
25
|
+
|
|
26
|
+
noContactRoles?: string[]; // lists roles that have no contacts. For example families that have no plumber contacts.
|
|
27
|
+
}
|
|
@@ -0,0 +1,85 @@
|
|
|
1
|
+
// import { MemberRelationship } from './dto-member';
|
|
2
|
+
import { ITitledRecord } from './dto-models';
|
|
3
|
+
import { ICommuneDbo } from './dto-commune';
|
|
4
|
+
import { CommuneType, CountryId } from './types';
|
|
5
|
+
|
|
6
|
+
export interface IUserCommuneInfo {
|
|
7
|
+
id?: string;
|
|
8
|
+
shortId?: string;
|
|
9
|
+
title?: string;
|
|
10
|
+
type: CommuneType;
|
|
11
|
+
// members?: {
|
|
12
|
+
// [id: string]: {
|
|
13
|
+
// relatedAs: MemberRelationship;
|
|
14
|
+
// // title?: string;
|
|
15
|
+
// };
|
|
16
|
+
// };
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
export function createUserCommuneInfoFromCommuneDto(
|
|
20
|
+
communeDto: ICommuneDbo,
|
|
21
|
+
shortId?: string,
|
|
22
|
+
): IUserCommuneInfo {
|
|
23
|
+
return {
|
|
24
|
+
id: communeDto.id,
|
|
25
|
+
shortId,
|
|
26
|
+
title: communeDto.title,
|
|
27
|
+
type: communeDto.type,
|
|
28
|
+
};
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
export interface IUserDbo extends ITitledRecord {
|
|
32
|
+
countryIds?: CountryId[];
|
|
33
|
+
created: {
|
|
34
|
+
hostOrApp: string;
|
|
35
|
+
at: string;
|
|
36
|
+
};
|
|
37
|
+
readonly communes?: readonly IUserCommuneInfo[]; // Returns real ID and Title, find by shortId
|
|
38
|
+
communesCount?: number;
|
|
39
|
+
isAnonymous: boolean;
|
|
40
|
+
isDemo?: boolean;
|
|
41
|
+
autogeneratedPassword?: string;
|
|
42
|
+
emailVerified?: boolean;
|
|
43
|
+
email?: string;
|
|
44
|
+
emailOriginal?: string;
|
|
45
|
+
gender?: 'male' | 'female' | 'undisclosed';
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
export function addCommuneToUserDto(
|
|
49
|
+
userDto: IUserDbo,
|
|
50
|
+
communeInfo: IUserCommuneInfo,
|
|
51
|
+
): IUserDbo {
|
|
52
|
+
const communes = userDto.communes || [];
|
|
53
|
+
if (
|
|
54
|
+
communeInfo.id &&
|
|
55
|
+
communes.find((c) => !!c.id && !!communeInfo.id && c.id === communeInfo.id)
|
|
56
|
+
) {
|
|
57
|
+
throw new Error(`Can't add commune with duplicate id: ${communeInfo.id}`);
|
|
58
|
+
}
|
|
59
|
+
return {
|
|
60
|
+
...userDto,
|
|
61
|
+
communes: [...communes, communeInfo],
|
|
62
|
+
communesCount: communes.length + 1,
|
|
63
|
+
};
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
export class UserModel {
|
|
67
|
+
public static getCommuneInfoByShortId(
|
|
68
|
+
id: string,
|
|
69
|
+
communes?: readonly IUserCommuneInfo[],
|
|
70
|
+
): IUserCommuneInfo | undefined {
|
|
71
|
+
return communes && communes.find((c) => c.shortId === id);
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
public static getCommuneInfoById(
|
|
75
|
+
id: string,
|
|
76
|
+
communes: readonly IUserCommuneInfo[],
|
|
77
|
+
): IUserCommuneInfo | undefined {
|
|
78
|
+
const ci =
|
|
79
|
+
communes && communes.find((c) => c.shortId === id || c.id === id);
|
|
80
|
+
if (!ci && id === 'family') {
|
|
81
|
+
return { type: 'family', shortId: 'family', title: 'Family' };
|
|
82
|
+
}
|
|
83
|
+
return ci;
|
|
84
|
+
}
|
|
85
|
+
}
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
export interface timestamp {
|
|
2
|
+
seconds: number;
|
|
3
|
+
nanoseconds: number;
|
|
4
|
+
}
|
|
5
|
+
export const emptyTimestamp: timestamp = { seconds: 0, nanoseconds: 0 };
|
|
6
|
+
|
|
7
|
+
export interface IWithCreatedShort {
|
|
8
|
+
readonly at: string;
|
|
9
|
+
readonly by: string;
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
export interface IWithCreatedOn {
|
|
13
|
+
readonly createdOn: string;
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
export interface IWithCreated {
|
|
17
|
+
readonly createdAt: timestamp;
|
|
18
|
+
readonly createdBy: string;
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
export interface IWithUpdated {
|
|
22
|
+
readonly updatedAt: timestamp;
|
|
23
|
+
readonly updatedBy: string;
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
export interface IWithDeleted {
|
|
27
|
+
readonly deletedAt?: timestamp;
|
|
28
|
+
readonly deletedBy?: string;
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
export interface IWithModified
|
|
32
|
+
extends IWithCreated, IWithUpdated, IWithDeleted {}
|
package/src/lib/index.ts
ADDED
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
export * from './ui-state';
|
|
2
|
+
export * from './dto-app';
|
|
3
|
+
export * from './dto-brief';
|
|
4
|
+
export * from './dto-commune';
|
|
5
|
+
export * from './dto-constants';
|
|
6
|
+
export * from './dto-models';
|
|
7
|
+
export * from './dto-pricing';
|
|
8
|
+
export * from './dto-team';
|
|
9
|
+
export * from './dto-team-brief';
|
|
10
|
+
export * from './dto-team-info';
|
|
11
|
+
export * from './dto-team-meeting';
|
|
12
|
+
export * from './dto-team-metric';
|
|
13
|
+
export * from './dto-user';
|
|
14
|
+
export * from './dto-with-modified';
|
|
15
|
+
export * from './by-user';
|
|
16
|
+
export * from './types';
|
|
17
|
+
export * from './with-related';
|
|
18
|
+
export * from './period';
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export type Period = 'day' | 'week' | 'month' | 'quarter' | 'year';
|
package/src/lib/types.ts
ADDED
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
import { SpaceType } from '@sneat/core';
|
|
2
|
+
|
|
3
|
+
export { SpaceMemberType } from '@sneat/auth-models';
|
|
4
|
+
|
|
5
|
+
export type Restriction = 'adults_only' | 'personal' | string;
|
|
6
|
+
|
|
7
|
+
export type SneatRecordStatus = 'active' | 'archived' | 'deleted';
|
|
8
|
+
|
|
9
|
+
export type CommuneType = SpaceType;
|
|
10
|
+
|
|
11
|
+
export type CountryId =
|
|
12
|
+
| 'IE'
|
|
13
|
+
| 'US'
|
|
14
|
+
| 'UK'
|
|
15
|
+
| 'RU'
|
|
16
|
+
| 'FR'
|
|
17
|
+
| 'ES'
|
|
18
|
+
| 'AU'
|
|
19
|
+
| 'ZA'
|
|
20
|
+
| string;
|
|
21
|
+
|
|
22
|
+
export type CommuneItemCounter =
|
|
23
|
+
| 'activities'
|
|
24
|
+
| 'assets'
|
|
25
|
+
| 'assetGroups'
|
|
26
|
+
| 'contacts'
|
|
27
|
+
| 'documents'
|
|
28
|
+
| 'liabilities'
|
|
29
|
+
| 'members'
|
|
30
|
+
| 'memberGroups'
|
|
31
|
+
| 'overdues'
|
|
32
|
+
| 'regularTasks'
|
|
33
|
+
| 'todos'
|
|
34
|
+
| 'upcomings';
|
|
@@ -0,0 +1,180 @@
|
|
|
1
|
+
import { IWithCreatedShort } from './dto-with-modified';
|
|
2
|
+
|
|
3
|
+
export interface IRelatedItemKey {
|
|
4
|
+
readonly itemID: string;
|
|
5
|
+
readonly spaceID?: string;
|
|
6
|
+
}
|
|
7
|
+
|
|
8
|
+
export interface ISpaceModuleItemRef {
|
|
9
|
+
readonly module: string;
|
|
10
|
+
readonly collection: string;
|
|
11
|
+
readonly spaceID: string;
|
|
12
|
+
readonly itemID: string;
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
export interface IRelationshipRole {
|
|
16
|
+
readonly created: IWithCreatedShort;
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
export type WritableRelationshipRoles = Record<string, IRelationshipRole>;
|
|
20
|
+
// {
|
|
21
|
+
// // -readonly [K in keyof IRelationshipRoles]: IRelationshipRoles[K];
|
|
22
|
+
// };
|
|
23
|
+
export type IRelationshipRoles = Readonly<WritableRelationshipRoles>;
|
|
24
|
+
|
|
25
|
+
export interface IRelatedItem {
|
|
26
|
+
// readonly keys: readonly IRelatedItemKey[];
|
|
27
|
+
readonly rolesOfItem?: IRelationshipRoles; // if related item is a child of the current record, then rolesOfItem = {"child": ...}
|
|
28
|
+
readonly rolesToItem?: IRelationshipRoles; // if related item is a child of the current contact, then rolesToItem = {"parent": ...}
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
export type IRelatedItems = Readonly<Record<string, IRelatedItem>>;
|
|
32
|
+
export type IRelatedCollections = Readonly<Record<string, IRelatedItems>>;
|
|
33
|
+
export type IRelatedModules = Readonly<Record<string, IRelatedCollections>>;
|
|
34
|
+
|
|
35
|
+
export interface IRelatedTo extends IWithRelatedOnly {
|
|
36
|
+
readonly key: ISpaceModuleItemRef;
|
|
37
|
+
readonly title: string; // pass empty string if you don't want to display name
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
export function getRelatedItems(
|
|
41
|
+
moduleId: string,
|
|
42
|
+
collectionId: string,
|
|
43
|
+
related?: IRelatedModules,
|
|
44
|
+
): IRelatedItems {
|
|
45
|
+
return (related && related[moduleId]?.[collectionId]) || {};
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
export interface IWithRelatedOnly {
|
|
49
|
+
readonly related?: IRelatedModules;
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
export function validateRelated(related?: IRelatedModules): void {
|
|
53
|
+
if (!related) {
|
|
54
|
+
return;
|
|
55
|
+
}
|
|
56
|
+
Object.entries(related).forEach(([, collections]) => {
|
|
57
|
+
Object.entries(collections).forEach(([, items]) => {
|
|
58
|
+
if (!items) {
|
|
59
|
+
return;
|
|
60
|
+
}
|
|
61
|
+
Object.entries(items).forEach(([itemID]) => {
|
|
62
|
+
if (!itemID) {
|
|
63
|
+
throw new Error('ItemID is not set');
|
|
64
|
+
}
|
|
65
|
+
});
|
|
66
|
+
});
|
|
67
|
+
});
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
export interface IWithRelatedAndRelatedIDs extends IWithRelatedOnly {
|
|
71
|
+
readonly relatedIDs?: readonly string[];
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
export const addRelatedItem = (
|
|
75
|
+
related: IRelatedModules | undefined,
|
|
76
|
+
key: ISpaceModuleItemRef,
|
|
77
|
+
rolesOfItem?: IRelationshipRoles,
|
|
78
|
+
) => {
|
|
79
|
+
related = related || {};
|
|
80
|
+
let collectionRelated = related[key.module] || {};
|
|
81
|
+
let relatedItems = collectionRelated[key.collection] || {};
|
|
82
|
+
if (!hasRelated(related, key)) {
|
|
83
|
+
relatedItems = {
|
|
84
|
+
...relatedItems,
|
|
85
|
+
[key.itemID]: { rolesOfItem },
|
|
86
|
+
};
|
|
87
|
+
collectionRelated = {
|
|
88
|
+
...collectionRelated,
|
|
89
|
+
[key.collection]: relatedItems,
|
|
90
|
+
};
|
|
91
|
+
related = { ...related, [key.module]: collectionRelated };
|
|
92
|
+
}
|
|
93
|
+
return related;
|
|
94
|
+
};
|
|
95
|
+
|
|
96
|
+
export const removeRelatedItem = (
|
|
97
|
+
related: IRelatedModules | undefined,
|
|
98
|
+
key: ISpaceModuleItemRef,
|
|
99
|
+
) => {
|
|
100
|
+
if (!related) {
|
|
101
|
+
return related;
|
|
102
|
+
}
|
|
103
|
+
let collectionRelated = related[key.module];
|
|
104
|
+
if (!collectionRelated) {
|
|
105
|
+
return related;
|
|
106
|
+
}
|
|
107
|
+
const relatedItems = collectionRelated[key.collection];
|
|
108
|
+
if (!relatedItems) {
|
|
109
|
+
return related;
|
|
110
|
+
}
|
|
111
|
+
if (hasRelated(related, key)) {
|
|
112
|
+
const collectionItems = { ...relatedItems };
|
|
113
|
+
delete collectionItems[key.itemID];
|
|
114
|
+
collectionRelated = {
|
|
115
|
+
...collectionRelated,
|
|
116
|
+
[key.collection]: collectionItems,
|
|
117
|
+
};
|
|
118
|
+
related = { ...related, [key.module]: collectionRelated };
|
|
119
|
+
}
|
|
120
|
+
return related;
|
|
121
|
+
};
|
|
122
|
+
|
|
123
|
+
export const getLongRelatedItemID = (itemID: string, spaceID: string) =>
|
|
124
|
+
`${itemID}@${spaceID}`;
|
|
125
|
+
|
|
126
|
+
export const getRelatedItemByIDs = (
|
|
127
|
+
relatedItems: Readonly<Record<string, IRelatedItem>> | undefined,
|
|
128
|
+
itemID: string,
|
|
129
|
+
spaceID?: string,
|
|
130
|
+
) =>
|
|
131
|
+
relatedItems &&
|
|
132
|
+
(relatedItems[itemID] ||
|
|
133
|
+
(spaceID && relatedItems[getLongRelatedItemID(itemID, spaceID)]));
|
|
134
|
+
|
|
135
|
+
export const getRelatedItemByKey = (
|
|
136
|
+
related: IRelatedModules | undefined,
|
|
137
|
+
key: ISpaceModuleItemRef,
|
|
138
|
+
): IRelatedItem | undefined => {
|
|
139
|
+
const items = related?.[key.module]?.[key.collection];
|
|
140
|
+
const { itemID, spaceID } = key;
|
|
141
|
+
return getRelatedItemByIDs(items, itemID, spaceID);
|
|
142
|
+
};
|
|
143
|
+
|
|
144
|
+
export const getRelatedItemIDs = (
|
|
145
|
+
related: IRelatedModules | undefined,
|
|
146
|
+
module: string,
|
|
147
|
+
collection: string,
|
|
148
|
+
spaceID?: string,
|
|
149
|
+
): readonly string[] => {
|
|
150
|
+
if (!related) {
|
|
151
|
+
return [];
|
|
152
|
+
}
|
|
153
|
+
// console.log('getRelatedItemIDs', module, collection, spaceID, related);
|
|
154
|
+
const collectionRelated = (related || {})[module] || {};
|
|
155
|
+
const relatedItems = collectionRelated[collection];
|
|
156
|
+
const keys = Object.keys(relatedItems);
|
|
157
|
+
return spaceID
|
|
158
|
+
? keys.filter((k) => !k.includes('@') || k.endsWith(`@${spaceID}`))
|
|
159
|
+
: keys;
|
|
160
|
+
};
|
|
161
|
+
|
|
162
|
+
export const hasRelated = (
|
|
163
|
+
related: IRelatedModules | undefined,
|
|
164
|
+
key: ISpaceModuleItemRef,
|
|
165
|
+
): boolean => {
|
|
166
|
+
if (!related) {
|
|
167
|
+
return false;
|
|
168
|
+
}
|
|
169
|
+
const collectionRelated = (related || {})[key.module] || {};
|
|
170
|
+
const relatedItems = collectionRelated[key.collection];
|
|
171
|
+
return hasRelatedItem(relatedItems, key);
|
|
172
|
+
};
|
|
173
|
+
|
|
174
|
+
const hasRelatedItem = (
|
|
175
|
+
relatedItems: IRelatedItems,
|
|
176
|
+
itemKey: IRelatedItemKey,
|
|
177
|
+
): boolean => {
|
|
178
|
+
const { itemID, spaceID } = itemKey;
|
|
179
|
+
return !!getRelatedItemByIDs(relatedItems, itemID, spaceID);
|
|
180
|
+
};
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
import '@sneat/core/testing-light';
|
package/tsconfig.json
ADDED
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
{
|
|
2
|
+
"extends": "../../tsconfig.lib.base.json",
|
|
3
|
+
"exclude": [
|
|
4
|
+
"vite.config.ts",
|
|
5
|
+
"vite.config.mts",
|
|
6
|
+
"vitest.config.ts",
|
|
7
|
+
"vitest.config.mts",
|
|
8
|
+
"src/**/*.test.ts",
|
|
9
|
+
"src/**/*.spec.ts",
|
|
10
|
+
"src/**/*.test.tsx",
|
|
11
|
+
"src/**/*.spec.tsx",
|
|
12
|
+
"src/**/*.test.js",
|
|
13
|
+
"src/**/*.spec.js",
|
|
14
|
+
"src/**/*.test.jsx",
|
|
15
|
+
"src/**/*.spec.jsx",
|
|
16
|
+
"src/test-setup.ts",
|
|
17
|
+
"src/lib/testing/**/*"
|
|
18
|
+
]
|
|
19
|
+
}
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
{
|
|
2
|
+
"extends": "./tsconfig.json",
|
|
3
|
+
"compilerOptions": {
|
|
4
|
+
"outDir": "../../dist/out-tsc",
|
|
5
|
+
"types": [
|
|
6
|
+
"vitest/globals",
|
|
7
|
+
"vitest/importMeta",
|
|
8
|
+
"vite/client",
|
|
9
|
+
"node",
|
|
10
|
+
"vitest"
|
|
11
|
+
]
|
|
12
|
+
},
|
|
13
|
+
"include": [
|
|
14
|
+
"vite.config.ts",
|
|
15
|
+
"vite.config.mts",
|
|
16
|
+
"vitest.config.ts",
|
|
17
|
+
"vitest.config.mts",
|
|
18
|
+
"src/**/*.test.ts",
|
|
19
|
+
"src/**/*.spec.ts",
|
|
20
|
+
"src/**/*.test.tsx",
|
|
21
|
+
"src/**/*.spec.tsx",
|
|
22
|
+
"src/**/*.test.js",
|
|
23
|
+
"src/**/*.spec.js",
|
|
24
|
+
"src/**/*.test.jsx",
|
|
25
|
+
"src/**/*.spec.jsx",
|
|
26
|
+
"src/**/*.d.ts"
|
|
27
|
+
],
|
|
28
|
+
"files": [
|
|
29
|
+
"src/test-setup.ts"
|
|
30
|
+
]
|
|
31
|
+
}
|
package/vite.config.mts
ADDED