@sneat/contactus-core 0.1.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.
@@ -0,0 +1,7 @@
1
+ const baseConfig = require('../../../eslint.config.js');
2
+ const { sneatLibConfig } = require('../../../eslint.lib.config.js');
3
+
4
+ module.exports = [
5
+ ...baseConfig,
6
+ ...sneatLibConfig(__dirname),
7
+ ];
@@ -0,0 +1,7 @@
1
+ {
2
+ "$schema": "../../../node_modules/ng-packagr/ng-package.schema.json",
3
+ "dest": "../../../dist/libs/contactus/core",
4
+ "lib": {
5
+ "entryFile": "src/index.ts"
6
+ }
7
+ }
package/package.json ADDED
@@ -0,0 +1,15 @@
1
+ {
2
+ "name": "@sneat/contactus-core",
3
+ "version": "0.1.0",
4
+ "publishConfig": {
5
+ "access": "public"
6
+ },
7
+ "peerDependencies": {
8
+ "@angular/common": ">=21.0.0",
9
+ "@angular/core": ">=21.0.0"
10
+ },
11
+ "dependencies": {
12
+ "tslib": "2.8.1"
13
+ },
14
+ "sideEffects": false
15
+ }
package/project.json ADDED
@@ -0,0 +1,38 @@
1
+ {
2
+ "name": "contactus-core",
3
+ "$schema": "../../../node_modules/nx/schemas/project-schema.json",
4
+ "projectType": "library",
5
+ "sourceRoot": "libs/contactus/core/src",
6
+ "prefix": "sneat",
7
+ "targets": {
8
+ "build": {
9
+ "executor": "@nx/angular:package",
10
+ "outputs": [
11
+ "{workspaceRoot}/dist/libs/contactus/core"
12
+ ],
13
+ "options": {
14
+ "project": "libs/contactus/core/ng-package.json",
15
+ "tsConfig": "libs/contactus/core/tsconfig.lib.json"
16
+ },
17
+ "configurations": {
18
+ "production": {
19
+ "tsConfig": "libs/contactus/core/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/contactus/core"
29
+ ],
30
+ "options": {
31
+ "tsConfig": "libs/contactus/core/tsconfig.spec.json"
32
+ }
33
+ },
34
+ "lint": {
35
+ "executor": "@nx/eslint:lint"
36
+ }
37
+ }
38
+ }
package/src/index.ts ADDED
@@ -0,0 +1,3 @@
1
+ export * from './lib/dto';
2
+ export * from './lib/contexts';
3
+ export * from './lib/apidto';
@@ -0,0 +1,3 @@
1
+ // TODO: move to services?
2
+ export * from './requests';
3
+ export * from './responses';
@@ -0,0 +1,131 @@
1
+ import { mustHaveAtLeastOneName } from '@sneat/auth-models';
2
+ import { validateRelated } from '@sneat/dto';
3
+ import {
4
+ ContactType,
5
+ IAddress,
6
+ IContact2Asset,
7
+ IContact2ContactInRequest,
8
+ ICreatePeronRequest,
9
+ IMemberBrief,
10
+ IRelatedPerson,
11
+ MemberContactType,
12
+ } from '../dto';
13
+ import { IMemberContext } from '../contexts';
14
+ import { ISpaceRequest, SpaceMemberStatus } from '@sneat/space-models';
15
+
16
+ export interface ICreateSpaceMemberRequest
17
+ extends ISpaceRequest, IRelatedPerson {
18
+ readonly type: MemberContactType;
19
+ readonly status: SpaceMemberStatus;
20
+ readonly countryID: string;
21
+ readonly roles: readonly string[];
22
+ readonly message?: string;
23
+ }
24
+
25
+ export function validateCreateSpaceMemberRequest(
26
+ request: ICreateSpaceMemberRequest,
27
+ ): void {
28
+ mustHaveAtLeastOneName(request.names);
29
+ validateRelated(request.related);
30
+ }
31
+
32
+ export interface ICreateContactBaseRequest extends ISpaceRequest {
33
+ readonly status: 'active' | 'draft';
34
+ readonly type: ContactType;
35
+ // countryID: string;
36
+ readonly parentContactID?: string;
37
+ readonly roles?: readonly string[];
38
+ readonly relatedToAssets?: readonly IContact2Asset[];
39
+ readonly relatedToContacts?: Readonly<
40
+ Record<string, IContact2ContactInRequest>
41
+ >;
42
+ }
43
+
44
+ export interface ICreateContactPersonRequest extends ICreateContactBaseRequest {
45
+ readonly type: 'person';
46
+ readonly person?: ICreatePeronRequest;
47
+ }
48
+
49
+ export interface ICreateContactLocationRequest extends ICreateContactBaseRequest {
50
+ readonly type: 'location';
51
+ readonly location?: ICreateLocationRequest;
52
+ }
53
+
54
+ export interface IBasicContactRequest {
55
+ // type: ContactType;
56
+ // relationship?: string;
57
+ // message?: string;
58
+ readonly title: string;
59
+ // parentContactID?: string;
60
+ }
61
+
62
+ export interface ICreateContactBasicRequest extends ICreateContactBaseRequest {
63
+ readonly basic: IBasicContactRequest;
64
+ }
65
+
66
+ export interface ICreateLocationBaseRequest {
67
+ readonly title: string;
68
+ readonly address: IAddress;
69
+ }
70
+
71
+ export type ICreateLocationRequest = ICreateLocationBaseRequest;
72
+ export type ICreateCompanyRequest = ICreateLocationBaseRequest;
73
+
74
+ export interface ICreateContactCompanyRequest extends ICreateContactBaseRequest {
75
+ company?: ICreateCompanyRequest;
76
+ }
77
+
78
+ export type ICreateContactRequest =
79
+ | ICreateContactPersonRequest
80
+ | ICreateContactCompanyRequest
81
+ | ICreateContactLocationRequest
82
+ | ICreateContactBasicRequest;
83
+
84
+ export interface IBy {
85
+ readonly memberID?: string;
86
+ readonly userID?: string;
87
+ readonly title: string;
88
+ }
89
+
90
+ interface IInvite {
91
+ readonly message?: string;
92
+ }
93
+
94
+ export interface IInviteFromContact {
95
+ readonly memberID: string;
96
+ readonly userID?: string;
97
+ readonly title?: string;
98
+ }
99
+
100
+ export type InviteChannel = 'email' | 'sms' | 'link';
101
+
102
+ export interface IInviteToContact {
103
+ readonly channel: InviteChannel;
104
+ readonly address?: string;
105
+ readonly memberID?: string;
106
+ readonly title?: string;
107
+ }
108
+
109
+ export interface IPersonalInvite extends IInvite {
110
+ readonly space: { readonly id: string; readonly title: string };
111
+ readonly memberID: string;
112
+ readonly from: IInviteFromContact;
113
+ readonly to: IInviteToContact;
114
+ }
115
+
116
+ export interface IAddSpaceMemberResponse {
117
+ readonly member: IMemberContext;
118
+ }
119
+
120
+ export interface IAcceptPersonalInviteRequest extends ISpaceRequest {
121
+ readonly inviteID: string;
122
+ readonly pin: string; // Do not make number as we can lose leading 0's
123
+ readonly member?: IMemberBrief;
124
+ // fullName?: string;
125
+ // email?: string;
126
+ }
127
+
128
+ export interface ICreatePersonalInviteRequest extends ISpaceRequest {
129
+ readonly to: IInviteToContact;
130
+ readonly message: string;
131
+ }
@@ -0,0 +1,21 @@
1
+ import { IInviteSpace } from '@sneat/space-models';
2
+ import { IMemberBrief } from '../dto';
3
+ import { IInviteFromContact, IInviteToContact } from './requests';
4
+
5
+ export interface IJoinSpaceInfoResponse {
6
+ space: IInviteSpace;
7
+ invite: {
8
+ id: string;
9
+ pin: string;
10
+ status: string;
11
+ created: string;
12
+ from: IInviteFromContact;
13
+ to: IInviteToContact;
14
+ message?: string;
15
+ };
16
+ member: IMemberBrief;
17
+ }
18
+
19
+ export interface ICreatePersonalInviteResponse {
20
+ invite: { id: string; pin?: string };
21
+ }
@@ -0,0 +1,22 @@
1
+ import { IIdAndDboWithSpaceRef, ISpaceRef } from '@sneat/core';
2
+ import { IContactBase, IContactBrief, IContactDbo } from '../dto';
3
+ import { ISpaceItemNavContext } from '@sneat/space-models';
4
+
5
+ export interface IContactContext extends ISpaceItemNavContext<
6
+ IContactBrief,
7
+ IContactDbo
8
+ > {
9
+ parentContact?: IContactContext;
10
+ }
11
+
12
+ export type ContactDboWithSpaceRef = {
13
+ readonly dbo: IContactDbo;
14
+ readonly space: ISpaceRef;
15
+ };
16
+
17
+ export type ContactIdAndDboWithSpaceRef = IIdAndDboWithSpaceRef<IContactDbo>;
18
+
19
+ export type NewContactBaseDboAndSpaceRef = {
20
+ readonly dbo: IContactBase;
21
+ readonly space: ISpaceRef;
22
+ };
@@ -0,0 +1,2 @@
1
+ export * from './contact-context';
2
+ export * from './person-context';
@@ -0,0 +1,35 @@
1
+ import {
2
+ DtoGroupTerms,
3
+ IContactGroupBrief,
4
+ IContactGroupDbo,
5
+ IContactGroupDtoCounts,
6
+ } from '../dto';
7
+ import { IMemberGroupContext } from '.';
8
+
9
+ export class MemberGroup {
10
+ public readonly id: string;
11
+ public readonly dto?: IContactGroupDbo | null;
12
+ public readonly brief?: IContactGroupBrief | null;
13
+
14
+ constructor(memberGroup: IMemberGroupContext) {
15
+ this.id = memberGroup.id;
16
+ this.brief = memberGroup.brief;
17
+ this.dto = memberGroup.dbo;
18
+ }
19
+
20
+ public get numberOf(): IContactGroupDtoCounts {
21
+ const n = this.dto?.numberOf || {};
22
+ if (!n.members) {
23
+ n.members = 0;
24
+ }
25
+ return n;
26
+ }
27
+
28
+ public get title(): string {
29
+ return this.brief?.title || '';
30
+ }
31
+
32
+ public get terms(): DtoGroupTerms {
33
+ return this.dto?.terms || {};
34
+ }
35
+ }
@@ -0,0 +1,41 @@
1
+ import { ISpaceItemWithBriefAndDbo } from '@sneat/core';
2
+ import {
3
+ IContactGroupBrief,
4
+ IContactGroupDbo,
5
+ IMemberBrief,
6
+ IMemberDbo,
7
+ } from '../dto';
8
+ import { ISpaceItemNavContext, Totals } from '@sneat/space-models';
9
+ import { IPerson, IPersonBrief } from '../dto';
10
+
11
+ export type IMemberContext = ISpaceItemNavContext<IMemberBrief, IMemberDbo>;
12
+ export type IPersonContext = ISpaceItemWithBriefAndDbo<IPersonBrief, IPerson>;
13
+
14
+ export type IMemberGroupContext = ISpaceItemNavContext<
15
+ IContactGroupBrief,
16
+ IContactGroupDbo
17
+ >;
18
+
19
+ export class Member {
20
+ // TODO - document usage or remove
21
+ public readonly totals: Totals;
22
+
23
+ constructor(
24
+ public member: IMemberContext,
25
+ public isChecked = false,
26
+ ) {
27
+ this.totals = new Totals(member.dbo?.totals);
28
+ }
29
+
30
+ public get id(): string {
31
+ return this.member.id;
32
+ }
33
+
34
+ public get title(): string {
35
+ return this.member.brief?.title || this.member.id;
36
+ }
37
+
38
+ public get emoji(): string {
39
+ return this.member.dbo?.ageGroup === 'child' ? '🧒' : '🧑';
40
+ }
41
+ }
@@ -0,0 +1,26 @@
1
+ export interface IAddress {
2
+ readonly countryID: string;
3
+ readonly zipCode?: string;
4
+ readonly state?: string;
5
+ readonly city?: string;
6
+ readonly lines?: string;
7
+ }
8
+
9
+ export function validateAddress(
10
+ address?: IAddress,
11
+ requires?: { city?: boolean; lines?: boolean },
12
+ ): IAddress {
13
+ if (!address) {
14
+ throw new Error('Address is required');
15
+ }
16
+ if (address.countryID === '') {
17
+ throw new Error('Country is required');
18
+ }
19
+ if (requires?.city && !address?.city?.trim()) {
20
+ throw new Error('Address city is required');
21
+ }
22
+ if (requires?.lines && !address?.lines?.trim()) {
23
+ throw new Error('Address text is required');
24
+ }
25
+ return address;
26
+ }
@@ -0,0 +1,47 @@
1
+ import { IAvatar, IPersonNames } from '@sneat/auth-models';
2
+ import { AgeGroupID, Gender } from '@sneat/core';
3
+ import { IWithRelatedOnly } from '@sneat/dto';
4
+ import { IAddress } from './address';
5
+ import { ContactType } from './contact-types';
6
+ import { PetKind } from './pet-kind';
7
+
8
+ export type ContactCommChannelType = 'email' | 'phone';
9
+
10
+ export interface IContactCommChannelProps {
11
+ type: 'work' | 'personal';
12
+ isPrimary?: boolean;
13
+ isVerified?: boolean;
14
+ original?: string;
15
+ }
16
+
17
+ // Originally was in IPerson but also required by company
18
+ // and also if in IPerson troubles with Person Wizard component
19
+ export interface IContactChannels {
20
+ readonly email?: string; // TODO: Document how email is different from emails
21
+ readonly emails?: Readonly<Record<string, IContactCommChannelProps>>;
22
+
23
+ readonly phone?: string; // TODO: Document how phone is different from phones
24
+ readonly phones?: Readonly<Record<string, IContactCommChannelProps>>;
25
+ }
26
+
27
+ // This is used to pass to create_contact API endpoint
28
+ // as IContactDbo have required fields
29
+ // that are not needed at client side at time of creation.
30
+ export interface IContactBase extends IWithRelatedOnly, IContactChannels {
31
+ readonly type: ContactType;
32
+ readonly title?: string;
33
+ readonly shortTitle?: string;
34
+ readonly names?: IPersonNames;
35
+ readonly countryID?: string;
36
+ readonly userID?: string;
37
+ readonly gender?: Gender; // is required for contact "person" and "animal" type, for others it should be `undefined`
38
+ readonly ageGroup?: AgeGroupID;
39
+ readonly petKind?: PetKind;
40
+ readonly petBreed?: string;
41
+ readonly address?: IAddress;
42
+ readonly avatar?: IAvatar;
43
+ readonly roles?: readonly string[];
44
+ readonly groupIDs?: readonly string[];
45
+ readonly invitesCount?: string;
46
+ readonly dob?: string; // Date of birth
47
+ }
@@ -0,0 +1,8 @@
1
+ import { IIdAndBrief } from '@sneat/core';
2
+
3
+ export interface IContactGroupBrief {
4
+ readonly emoji?: string;
5
+ readonly title: string;
6
+ }
7
+
8
+ export type ContactGroupWithIdAndBrief = IIdAndBrief<IContactGroupBrief>;
@@ -0,0 +1,19 @@
1
+ import { IContactGroupBrief } from './contact-group-brief';
2
+ import { IContactRoleWithIdAndBrief } from './contact-roles';
3
+
4
+ import { MembersVisibility } from './member-types';
5
+ import { DtoGroupTerms } from './term';
6
+
7
+ export interface IContactGroupDtoCounts {
8
+ members?: number;
9
+ }
10
+
11
+ export interface IContactGroupDbo extends IContactGroupBrief {
12
+ // spaceID: string; This is part of a key
13
+ readonly desc?: string;
14
+ readonly timetable?: string;
15
+ readonly membersVisibility?: MembersVisibility;
16
+ readonly numberOf?: IContactGroupDtoCounts;
17
+ readonly terms?: DtoGroupTerms;
18
+ readonly roles?: readonly IContactRoleWithIdAndBrief[];
19
+ }
@@ -0,0 +1,99 @@
1
+ export const ContactRoleEmployee = 'employee';
2
+ export const ContactRoleInsurer = 'insurer';
3
+
4
+ export const ContactRolePet = 'pet';
5
+ export const ContactRoleFriend = 'friend';
6
+ export const ContactRoleRelative = 'relative';
7
+
8
+ // export type ContactRoleFriend = 'friend';
9
+ export const ContactRoleParentOfFriend = 'parent_of_friend';
10
+ export const ContactRoleDriver = 'driver';
11
+ export const ContactRoleLocation = 'location';
12
+ export type ContactRoleDwellingRelated =
13
+ | typeof ContactRoleInsurer
14
+ | 'cleaner'
15
+ | 'gardener'
16
+ | 'plumber'
17
+ | 'handyman'
18
+ | 'gp'
19
+ | 'landlord'
20
+ | 'tenant'
21
+ | 'realtor';
22
+ export type ContactRoleVehicle =
23
+ | typeof ContactRoleInsurer
24
+ | 'mechanic'
25
+ | 'electrician'
26
+ | 'handyman'
27
+ | typeof ContactRoleDriver;
28
+ export type ContactRoleMedRelated = 'GP' | 'med_specialist';
29
+ export type ContactRoleFamilyRelated =
30
+ | typeof RoleSpaceMember
31
+ | typeof ContactRolePet
32
+ | typeof ContactRoleFriend
33
+ | typeof ContactRoleRelative;
34
+ export type ContactRoleWorkRelated =
35
+ | typeof ContactRoleEmployee
36
+ | 'client'
37
+ | 'supplier';
38
+ export type ContactRoleKidRelated =
39
+ | typeof ContactRoleFriend
40
+ | typeof ContactRoleParentOfFriend
41
+ | 'teacher'
42
+ | 'babysitter';
43
+ export const ContactRoleShip = 'ship';
44
+ export type ContactRoleLogistSubContact =
45
+ | typeof ContactRoleShip
46
+ | typeof ContactRoleLocation;
47
+ export type ContactRoleLogistParentContact = 'shipper' | 'dispatcher';
48
+ import { MemberRole, RoleSpaceMember } from './member-types';
49
+
50
+ export type LogistOrderContactRole =
51
+ | ContactRoleLogistParentContact
52
+ | ContactRoleLogistSubContact
53
+ | 'consignee'
54
+ | 'dispatch_point'
55
+ | 'receive_point'
56
+ | 'dispatch_agent'
57
+ | 'receive_agent'
58
+ | 'buyer'
59
+ | 'courier'
60
+ | 'freight_agent'
61
+ | 'notify_party'
62
+ | 'port'
63
+ | 'port_from'
64
+ | 'port_from_location'
65
+ | 'port_to'
66
+ | 'port_to_location'
67
+ | 'shipping_line'
68
+ | 'truck'
69
+ | 'trucker'
70
+ | 'warehouse';
71
+ export type ContactRole =
72
+ | MemberRole
73
+ | typeof RoleSpaceMember
74
+ | ContactRoleFamilyRelated
75
+ | ContactRoleWorkRelated
76
+ | ContactRoleKidRelated
77
+ | ContactRoleMedRelated
78
+ | ContactRoleDwellingRelated
79
+ | ContactRoleVehicle
80
+ | LogistOrderContactRole
81
+ | 'applicant';
82
+
83
+ export interface IContactRoleBrief {
84
+ readonly title: string;
85
+ readonly titlePlural?: string;
86
+ readonly emoji?: string;
87
+ readonly finder?: string;
88
+ }
89
+
90
+ export interface IContactRoleWithIdAndBrief {
91
+ readonly id: ContactRole;
92
+ readonly brief: IContactRoleBrief;
93
+ readonly hideInNewContactForm?: boolean;
94
+ }
95
+
96
+ export interface IContactRoleWithIdAndOptionalBrief {
97
+ readonly id: ContactRole;
98
+ readonly brief?: IContactRoleBrief;
99
+ }
@@ -0,0 +1,41 @@
1
+ import { SpaceMemberType } from '@sneat/auth-models';
2
+ import { IFormField } from '@sneat/core';
3
+
4
+ export const ContactTypePerson = 'person',
5
+ ContactTypeCompany = 'company',
6
+ ContactTypeLocation = 'location',
7
+ ContactTypeAnimal = 'animal',
8
+ ContactTypeVehicle = 'vehicle';
9
+
10
+ export type ContactType =
11
+ | SpaceMemberType
12
+ | typeof ContactTypePerson
13
+ | typeof ContactTypeCompany
14
+ | typeof ContactTypeLocation
15
+ | typeof ContactTypeAnimal
16
+ | typeof ContactTypeVehicle
17
+ | 'landlord'
18
+ | 'tenant';
19
+ export type MemberContactType =
20
+ | typeof ContactTypePerson
21
+ | typeof ContactTypeAnimal;
22
+
23
+ export interface IEmail {
24
+ readonly type: 'work' | 'personal';
25
+ readonly address: string;
26
+ }
27
+
28
+ export interface IPhone {
29
+ readonly type: 'work' | 'mobile' | 'personal' | 'fax';
30
+ readonly number: string;
31
+ }
32
+
33
+ export interface IPersonRequirements {
34
+ readonly lastName?: IFormField;
35
+ readonly ageGroup?: IFormField;
36
+ readonly gender?: IFormField;
37
+ readonly phone?: IFormField;
38
+ readonly email?: IFormField;
39
+ readonly relatedAs?: IFormField;
40
+ readonly roles?: IFormField;
41
+ }
@@ -0,0 +1,85 @@
1
+ import {
2
+ IIdAndBrief,
3
+ IIdAndBriefAndDbo,
4
+ IIdAndBriefAndOptionalDbo,
5
+ ISpaceItemBriefWithSpace,
6
+ } from '@sneat/core';
7
+ import { IWithRelatedAndRelatedIDs } from '@sneat/dto';
8
+ import { ISpaceRef } from '@sneat/core';
9
+ import { IContactBase } from './contact-base';
10
+ import { ContactRole } from './contact-roles';
11
+ import { IContact2Asset } from './contact2item';
12
+ import { IPersonRecord } from './person';
13
+
14
+ // // Default value: 'optional'
15
+ export type RequirementOption = 'required' | 'optional' | 'excluded';
16
+
17
+ export interface IContactBrief extends IContactBase {
18
+ readonly parentID?: string;
19
+ }
20
+
21
+ export type IContactWithBrief = IIdAndBrief<IContactBrief>;
22
+
23
+ export function filterContactsByTextAndRole( // TODO(help-wanted): add test
24
+ contacts: readonly IContactWithCheck[] | undefined,
25
+ text: string,
26
+ role?: ContactRole,
27
+ ): readonly IContactWithCheck[] | undefined {
28
+ return !text && !role
29
+ ? contacts
30
+ : contacts?.filter((c) => {
31
+ return isContactPassFilter(c, text, role);
32
+ });
33
+ }
34
+
35
+ export function isContactPassFilter(
36
+ c: IContactWithBrief,
37
+ text: string,
38
+ role?: ContactRole,
39
+ ): boolean {
40
+ const { brief } = c;
41
+ const names = brief?.names;
42
+ return (
43
+ (!role || (!!c.brief?.roles && c?.brief.roles.includes(role))) &&
44
+ (!text ||
45
+ brief?.title?.toLowerCase().includes(text) ||
46
+ (!!names &&
47
+ (!!names.firstName?.toLowerCase().includes(text) ||
48
+ !!names.lastName?.toLowerCase().includes(text) ||
49
+ !!names.nickName?.toLowerCase().includes(text) ||
50
+ !!names.middleName?.toLowerCase().includes(text) ||
51
+ !!names.fullName?.toLowerCase().includes(text))))
52
+ );
53
+ }
54
+
55
+ export interface IContactWithBriefAndSpace extends IContactWithBrief {
56
+ readonly space: ISpaceRef;
57
+ }
58
+
59
+ export function addSpace<IBrief>(
60
+ space: ISpaceRef,
61
+ ): (item: IIdAndBrief<IBrief>) => ISpaceItemBriefWithSpace<IBrief> {
62
+ return (item) => ({ ...item, space });
63
+ }
64
+
65
+ export interface IContactDbo
66
+ extends IContactBase, IPersonRecord, IWithRelatedAndRelatedIDs {
67
+ readonly assets?: IContact2Asset[]; // TODO: Remove as it can be replaced with IWithRelatedItems?
68
+ }
69
+
70
+ export type IContactWithDbo = IIdAndBriefAndDbo<IContactBrief, IContactDbo>;
71
+
72
+ export type IContactWithOptionalDbo = IIdAndBriefAndOptionalDbo<
73
+ IContactBrief,
74
+ IContactDbo
75
+ >;
76
+
77
+ export interface IContactWithDboAndSpace
78
+ extends IContactWithDbo, IContactWithBriefAndSpace {}
79
+
80
+ export interface IContactWithOptionalDboAndSpace
81
+ extends IContactWithOptionalDbo, IContactWithBriefAndSpace {}
82
+
83
+ export interface IContactWithCheck extends IContactWithOptionalDboAndSpace {
84
+ readonly isChecked?: boolean;
85
+ }
@@ -0,0 +1,23 @@
1
+ import { IWithIdAndTitle } from '@sneat/dto';
2
+
3
+ export type ContactToContactRelation =
4
+ | 'undefined'
5
+ | 'parent'
6
+ | 'mother'
7
+ | 'father'
8
+ | 'sibling'
9
+ | 'childminder'
10
+ | 'friend'
11
+ | 'child';
12
+
13
+ export type ContactToAssetRelation = string;
14
+
15
+ // This is a DTO object to be used in request and NOT to be used in DB records
16
+ export interface IContact2ContactInRequest {
17
+ relation: ContactToContactRelation;
18
+ }
19
+
20
+ export interface IContact2Asset extends IWithIdAndTitle {
21
+ // TODO: Remove deprecated
22
+ relation: ContactToAssetRelation;
23
+ }
@@ -0,0 +1,8 @@
1
+ import { IIdAndOptionalDbo } from '@sneat/core';
2
+ import { IContactBrief } from './contact';
3
+
4
+ export interface IContactusSpaceDbo {
5
+ contacts: Readonly<Record<string, IContactBrief>>;
6
+ }
7
+
8
+ export type IContactusSpaceDboAndID = IIdAndOptionalDbo<IContactusSpaceDbo>;
@@ -0,0 +1,14 @@
1
+ export * from './address';
2
+ export * from './contact';
3
+ export * from './contact-base';
4
+ export * from './contact-group';
5
+ export * from './contact-roles';
6
+ export * from './contactus-space';
7
+ export * from './member';
8
+ export * from './person';
9
+ export * from './pet-kind';
10
+ export * from './term';
11
+ export * from './contact2item';
12
+ export * from './contact-group-brief';
13
+ export * from './contact-types';
14
+ export * from './member-types';
@@ -0,0 +1,29 @@
1
+ export type MembersVisibility = 'private' | 'protected' | 'public';
2
+ export const RoleSpaceMember = 'member';
3
+ export const MemberRoleContributor = 'contributor';
4
+ export const MemberRoleSpectator = 'spectator';
5
+ export const MemberRoleParish = 'pastor';
6
+ export type MemberRoleEducation =
7
+ | 'administrator'
8
+ | 'principal'
9
+ | 'pupil'
10
+ | 'teacher';
11
+ export type MemberRoleRealtor = 'administrator' | 'agent';
12
+ export type MemberRole =
13
+ | typeof MemberRoleContributor
14
+ | typeof MemberRoleSpectator
15
+ | MemberRoleEducation
16
+ | MemberRoleRealtor
17
+ | typeof MemberRoleParish;
18
+
19
+ export enum FamilyMemberRelation {
20
+ child = 'child',
21
+ cousin = 'cousin',
22
+ grandparent = 'grandparent',
23
+ grandparentInLaw = 'grandparent_in_law',
24
+ parent = 'parent',
25
+ parentInLaw = 'parent_in_law',
26
+ partner = 'partner',
27
+ sibling = 'sibling',
28
+ spouse = 'spouse',
29
+ }
@@ -0,0 +1,87 @@
1
+ import { IAvatar, SpaceMemberType } from '@sneat/auth-models';
2
+ import { EnumAsUnionOfKeys, excludeUndefined } from '@sneat/core';
3
+ import { ITitledRecordInfo, ITotalsHolder, IVerification } from '@sneat/dto';
4
+ import { IContactBrief } from './contact';
5
+ import { IContactGroupBrief } from './contact-group-brief';
6
+ import { FamilyMemberRelation, MemberRole } from './member-types';
7
+ import { IPersonRecord, ISpaceMemberInfo } from './person';
8
+
9
+ export function relationshipTitle(id: string): string {
10
+ // TODO: Needs fix to replace all _ with space
11
+ return id ? id[0].toUpperCase() + id.substring(1).replace(/_+/g, ' ') : '';
12
+ }
13
+
14
+ export type FamilyMemberRelations = EnumAsUnionOfKeys<
15
+ typeof FamilyMemberRelation
16
+ >;
17
+
18
+ export const MemberRelationshipOther = 'other';
19
+ export const MemberRelationshipUndisclosed = 'undisclosed';
20
+
21
+ export type MemberRelationship =
22
+ | FamilyMemberRelations
23
+ | typeof MemberRelationshipOther
24
+ | typeof MemberRelationshipUndisclosed;
25
+
26
+ export interface IMemberBase
27
+ extends IPersonRecord, IVerification, ITotalsHolder {
28
+ readonly type: SpaceMemberType;
29
+ readonly title?: string;
30
+ readonly userID?: string; // User ID
31
+ readonly roles?: readonly MemberRole[];
32
+ readonly avatar?: IAvatar;
33
+ }
34
+
35
+ export type IMemberBrief = IContactBrief;
36
+
37
+ export interface IMemberDbo extends IMemberBase {
38
+ position?: string;
39
+ groups?: IContactGroupBrief[];
40
+ }
41
+
42
+ export interface IWithContactGroups {
43
+ groupIDs?: readonly string[];
44
+ groups: Record<string, IContactGroupBrief>;
45
+ }
46
+
47
+ // export function newCommuneMemberInfo(id: string, m: IMemberDto): ITeamMemberInfo {
48
+ // return excludeUndefined({
49
+ // id: id,
50
+ // userID: m.uid,
51
+ // title: m.title && m.uid && m.title === m.uid ? undefined : m.title,
52
+ // ageGroup: m.ageGroup,
53
+ // roles: m.roles,
54
+ // gender: m.gender,
55
+ // groupIds: m.groupIDs,
56
+ // });
57
+ // }
58
+
59
+ export function memberDtoFromMemberInfo(
60
+ memberInfo: ISpaceMemberInfo,
61
+ spaceID: string,
62
+ title: string,
63
+ ): IMemberDbo {
64
+ const memberType: SpaceMemberType = 'member';
65
+ return excludeUndefined({
66
+ ...memberInfo,
67
+ spaceID,
68
+ title,
69
+ type: memberType,
70
+ });
71
+ }
72
+
73
+ // Deprecated
74
+ export interface ICommuneDtoMemberGroupInfo extends ITitledRecordInfo {
75
+ members: number;
76
+ }
77
+
78
+ export const MemberGroupTypeAdults = 'adults';
79
+ export const MemberGroupTypeKids = 'kids';
80
+ export const MemberGroupTypePets = 'pets';
81
+ export const MemberGroupTypeOther = 'other';
82
+
83
+ export type MemberGroupType =
84
+ | typeof MemberGroupTypeAdults
85
+ | typeof MemberGroupTypeKids
86
+ | typeof MemberGroupTypePets
87
+ | typeof MemberGroupTypeOther;
@@ -0,0 +1,92 @@
1
+ import { isNameEmpty } from '@sneat/auth-models';
2
+ import { excludeUndefined } from '@sneat/core';
3
+ import { IWithRelatedOnly, IWithSpaceIDs } from '@sneat/dto';
4
+ import { IContactBase } from './contact-base';
5
+ import {
6
+ ContactType,
7
+ IPersonRequirements,
8
+ MemberContactType,
9
+ } from './contact-types';
10
+
11
+ import { MemberRole } from './member-types';
12
+
13
+ export type IPersonBrief = IContactBase;
14
+
15
+ export type IPerson = IContactBase;
16
+
17
+ export interface ISpaceMemberInfo extends IPerson {
18
+ readonly id?: string;
19
+ readonly userID?: string;
20
+ readonly title?: string;
21
+ readonly groupIDs?: readonly string[];
22
+ readonly roles?: readonly MemberRole[];
23
+ }
24
+
25
+ export interface IRelatedPerson extends IPerson, IWithRelatedOnly {
26
+ // relatedAs to current user or a specific contact
27
+ // readonly roles?: string[]; // Either member roles or contact roles
28
+ }
29
+
30
+ export interface IMemberPerson extends IRelatedPerson {
31
+ type: MemberContactType;
32
+ }
33
+
34
+ export function relatedPersonToPerson(v: IRelatedPerson): IPerson {
35
+ const v2 = { ...excludeUndefined(v) };
36
+ delete v2['related'];
37
+ return v2 as IPerson;
38
+ }
39
+
40
+ export interface IRelatedPersonContact extends IRelatedPerson {
41
+ readonly type: 'person';
42
+ }
43
+
44
+ export interface ICreatePeronRequest extends IRelatedPersonContact {
45
+ readonly status: 'active' | 'draft';
46
+ }
47
+
48
+ export function isPersonNotReady(
49
+ p: IPerson,
50
+ requires: IPersonRequirements,
51
+ ): boolean {
52
+ const nameIsEmpty = isNameEmpty(p.names);
53
+ const isMissingRequiredFields =
54
+ (!!requires.lastName?.required && !p.names?.lastName) ||
55
+ (!!requires.ageGroup?.required && !p.ageGroup) ||
56
+ (!!requires.gender?.required && !p.gender);
57
+ return nameIsEmpty || isMissingRequiredFields;
58
+ }
59
+
60
+ export function isPersonReady(
61
+ p: IPerson,
62
+ requires: IPersonRequirements,
63
+ ): boolean {
64
+ return !isPersonNotReady(p, requires);
65
+ }
66
+
67
+ export function isRelatedPersonNotReady(
68
+ p: IRelatedPerson,
69
+ requires: IPersonRequirements,
70
+ ): boolean {
71
+ return (
72
+ isPersonNotReady(p, requires) ||
73
+ (p.type !== 'animal' && !!requires.relatedAs?.required && !p.related)
74
+ );
75
+ }
76
+
77
+ export function isRelatedPersonReady(
78
+ p: IPerson,
79
+ requires: IPersonRequirements,
80
+ ): boolean {
81
+ return !isRelatedPersonNotReady(p, requires);
82
+ }
83
+
84
+ export interface IPersonRecord extends IWithSpaceIDs, IPerson {
85
+ /*, IPersonSize*/
86
+ }
87
+
88
+ export const emptyContactBase: IContactBase = {
89
+ type: undefined as unknown as ContactType,
90
+ };
91
+
92
+ export const emptyMemberPerson = emptyContactBase as IMemberPerson;
@@ -0,0 +1,16 @@
1
+ export enum PetKinds {
2
+ dog = 'dog',
3
+ cat = 'cat',
4
+ hamster = 'hamster',
5
+ rabbit = 'rabbit',
6
+ bird = 'bird',
7
+ fish = 'fish',
8
+ turtle = 'turtle',
9
+ snake = 'snake',
10
+ lizard = 'lizard',
11
+ horse = 'horse',
12
+ pig = 'pig',
13
+ cow = 'cow',
14
+ }
15
+
16
+ export type PetKind = keyof typeof PetKinds;
@@ -0,0 +1 @@
1
+ export const empty = {};
@@ -0,0 +1,28 @@
1
+ import { IPrice, ITitledRecord, IWithSpaceIDs } from '@sneat/dto';
2
+
3
+ export interface DtoTerm extends IWithSpaceIDs, ITitledRecord {
4
+ status: 'active' | 'archived';
5
+ startsOn: string;
6
+ endsOn: string;
7
+ weeks?: number;
8
+ classesTotal?: number;
9
+ classesPerWeek?: number;
10
+ prices?: ITermPrice[];
11
+ }
12
+
13
+ export interface DtoGroupTerms {
14
+ current?: DtoTerm;
15
+ next?: DtoTerm;
16
+ prev?: DtoTerm;
17
+ }
18
+
19
+ export interface ITermPrice extends IPrice {
20
+ title: string;
21
+ description?: string;
22
+ numberOfClasses?: number; // -1 => unlimited
23
+ }
24
+
25
+ export interface ITermCustomer {
26
+ memberId: string;
27
+ paid: boolean;
28
+ }
@@ -0,0 +1,5 @@
1
+ describe('libs/contactus/core sanity', () => {
2
+ it('should pass sanity check', () => {
3
+ expect(true).toBe(true);
4
+ });
5
+ });
@@ -0,0 +1,3 @@
1
+ import { setupTestEnvironment } from '@sneat/core/testing';
2
+
3
+ setupTestEnvironment();
package/tsconfig.json ADDED
@@ -0,0 +1,13 @@
1
+ {
2
+ "extends": "../../../tsconfig.angular.json",
3
+ "files": [],
4
+ "include": [],
5
+ "references": [
6
+ {
7
+ "path": "./tsconfig.lib.json"
8
+ },
9
+ {
10
+ "path": "./tsconfig.spec.json"
11
+ }
12
+ ]
13
+ }
@@ -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,7 @@
1
+ {
2
+ "extends": "./tsconfig.lib.json",
3
+ "compilerOptions": {
4
+ "declarationMap": false
5
+ },
6
+ "angularCompilerOptions": {}
7
+ }
@@ -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
+ }
@@ -0,0 +1,10 @@
1
+ /// <reference types='vitest' />
2
+ import { defineConfig } from 'vitest/config';
3
+ import { createBaseViteConfig } from '../../../vite.config.base';
4
+
5
+ export default defineConfig(() =>
6
+ createBaseViteConfig({
7
+ dirname: __dirname,
8
+ name: 'contactus-core',
9
+ }),
10
+ );