@sideline/domain 0.10.0 → 0.12.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.
- package/dist/cjs/api/ActivityLogApi.js +126 -0
- package/dist/cjs/api/ActivityLogApi.js.map +1 -0
- package/dist/cjs/api/ActivityStatsApi.js +37 -0
- package/dist/cjs/api/ActivityStatsApi.js.map +1 -0
- package/dist/cjs/api/EventApi.js +15 -2
- package/dist/cjs/api/EventApi.js.map +1 -1
- package/dist/cjs/api/EventRsvpApi.js +3 -1
- package/dist/cjs/api/EventRsvpApi.js.map +1 -1
- package/dist/cjs/api/EventSeriesApi.js +19 -2
- package/dist/cjs/api/EventSeriesApi.js.map +1 -1
- package/dist/cjs/api/RoleApi.js +9 -3
- package/dist/cjs/api/RoleApi.js.map +1 -1
- package/dist/cjs/api/Roster.js +9 -3
- package/dist/cjs/api/Roster.js.map +1 -1
- package/dist/cjs/api/TrainingTypeApi.js +14 -4
- package/dist/cjs/api/TrainingTypeApi.js.map +1 -1
- package/dist/cjs/index.js +15 -1
- package/dist/cjs/models/ActivityLog.js +24 -0
- package/dist/cjs/models/ActivityLog.js.map +1 -0
- package/dist/cjs/models/ActivityStats.js +120 -0
- package/dist/cjs/models/ActivityStats.js.map +1 -0
- package/dist/cjs/models/ActivityType.js +10 -0
- package/dist/cjs/models/ActivityType.js.map +1 -0
- package/dist/cjs/models/Event.js +3 -0
- package/dist/cjs/models/Event.js.map +1 -1
- package/dist/cjs/models/EventSeries.js +3 -0
- package/dist/cjs/models/EventSeries.js.map +1 -1
- package/dist/cjs/models/TrainingType.js +2 -1
- package/dist/cjs/models/TrainingType.js.map +1 -1
- package/dist/cjs/rpc/SyncRpcs.js +2 -1
- package/dist/cjs/rpc/SyncRpcs.js.map +1 -1
- package/dist/cjs/rpc/activity/ActivityRpcGroup.js +29 -0
- package/dist/cjs/rpc/activity/ActivityRpcGroup.js.map +1 -0
- package/dist/cjs/rpc/activity/ActivityRpcModels.js +31 -0
- package/dist/cjs/rpc/activity/ActivityRpcModels.js.map +1 -0
- package/dist/cjs/rpc/event/EventRpcGroup.js +8 -2
- package/dist/cjs/rpc/event/EventRpcGroup.js.map +1 -1
- package/dist/cjs/rpc/event/EventRpcModels.js +9 -1
- package/dist/cjs/rpc/event/EventRpcModels.js.map +1 -1
- package/dist/dts/api/ActivityLogApi.d.ts +163 -0
- package/dist/dts/api/ActivityLogApi.d.ts.map +1 -0
- package/dist/dts/api/ActivityStatsApi.d.ts +58 -0
- package/dist/dts/api/ActivityStatsApi.d.ts.map +1 -0
- package/dist/dts/api/AgeThresholdApi.d.ts +2 -2
- package/dist/dts/api/EventApi.d.ts +44 -4
- package/dist/dts/api/EventApi.d.ts.map +1 -1
- package/dist/dts/api/EventRsvpApi.d.ts +1 -1
- package/dist/dts/api/EventSeriesApi.d.ts +56 -0
- package/dist/dts/api/EventSeriesApi.d.ts.map +1 -1
- package/dist/dts/api/GroupApi.d.ts +1 -1
- package/dist/dts/api/RoleApi.d.ts +18 -1
- package/dist/dts/api/RoleApi.d.ts.map +1 -1
- package/dist/dts/api/Roster.d.ts +18 -1
- package/dist/dts/api/Roster.d.ts.map +1 -1
- package/dist/dts/api/TrainingTypeApi.d.ts +48 -16
- package/dist/dts/api/TrainingTypeApi.d.ts.map +1 -1
- package/dist/dts/index.d.ts +7 -0
- package/dist/dts/index.d.ts.map +1 -1
- package/dist/dts/models/ActivityLog.d.ts +122 -0
- package/dist/dts/models/ActivityLog.d.ts.map +1 -0
- package/dist/dts/models/ActivityStats.d.ts +28 -0
- package/dist/dts/models/ActivityStats.d.ts.map +1 -0
- package/dist/dts/models/ActivityType.d.ts +6 -0
- package/dist/dts/models/ActivityType.d.ts.map +1 -0
- package/dist/dts/models/ChannelSyncEvent.d.ts +4 -4
- package/dist/dts/models/Event.d.ts +28 -2
- package/dist/dts/models/Event.d.ts.map +1 -1
- package/dist/dts/models/EventRsvp.d.ts +4 -4
- package/dist/dts/models/EventSeries.d.ts +26 -0
- package/dist/dts/models/EventSeries.d.ts.map +1 -1
- package/dist/dts/models/RoleSyncEvent.d.ts +4 -4
- package/dist/dts/models/TrainingType.d.ts +24 -11
- package/dist/dts/models/TrainingType.d.ts.map +1 -1
- package/dist/dts/rpc/SyncRpcs.d.ts +15 -3
- package/dist/dts/rpc/SyncRpcs.d.ts.map +1 -1
- package/dist/dts/rpc/activity/ActivityRpcGroup.d.ts +14 -0
- package/dist/dts/rpc/activity/ActivityRpcGroup.d.ts.map +1 -0
- package/dist/dts/rpc/activity/ActivityRpcModels.d.ts +67 -0
- package/dist/dts/rpc/activity/ActivityRpcModels.d.ts.map +1 -0
- package/dist/dts/rpc/event/EventRpcGroup.d.ts +6 -3
- package/dist/dts/rpc/event/EventRpcGroup.d.ts.map +1 -1
- package/dist/dts/rpc/event/EventRpcModels.d.ts +18 -0
- package/dist/dts/rpc/event/EventRpcModels.d.ts.map +1 -1
- package/dist/esm/api/ActivityLogApi.js +108 -0
- package/dist/esm/api/ActivityLogApi.js.map +1 -0
- package/dist/esm/api/ActivityStatsApi.js +27 -0
- package/dist/esm/api/ActivityStatsApi.js.map +1 -0
- package/dist/esm/api/EventApi.js +15 -2
- package/dist/esm/api/EventApi.js.map +1 -1
- package/dist/esm/api/EventRsvpApi.js +3 -1
- package/dist/esm/api/EventRsvpApi.js.map +1 -1
- package/dist/esm/api/EventSeriesApi.js +19 -2
- package/dist/esm/api/EventSeriesApi.js.map +1 -1
- package/dist/esm/api/RoleApi.js +7 -2
- package/dist/esm/api/RoleApi.js.map +1 -1
- package/dist/esm/api/Roster.js +7 -2
- package/dist/esm/api/Roster.js.map +1 -1
- package/dist/esm/api/TrainingTypeApi.js +14 -4
- package/dist/esm/api/TrainingTypeApi.js.map +1 -1
- package/dist/esm/index.js +7 -0
- package/dist/esm/index.js.map +1 -1
- package/dist/esm/models/ActivityLog.js +17 -0
- package/dist/esm/models/ActivityLog.js.map +1 -0
- package/dist/esm/models/ActivityStats.js +111 -0
- package/dist/esm/models/ActivityStats.js.map +1 -0
- package/dist/esm/models/ActivityType.js +4 -0
- package/dist/esm/models/ActivityType.js.map +1 -0
- package/dist/esm/models/Event.js +3 -0
- package/dist/esm/models/Event.js.map +1 -1
- package/dist/esm/models/EventSeries.js +3 -0
- package/dist/esm/models/EventSeries.js.map +1 -1
- package/dist/esm/models/TrainingType.js +2 -1
- package/dist/esm/models/TrainingType.js.map +1 -1
- package/dist/esm/rpc/SyncRpcs.js +2 -1
- package/dist/esm/rpc/SyncRpcs.js.map +1 -1
- package/dist/esm/rpc/activity/ActivityRpcGroup.js +23 -0
- package/dist/esm/rpc/activity/ActivityRpcGroup.js.map +1 -0
- package/dist/esm/rpc/activity/ActivityRpcModels.js +21 -0
- package/dist/esm/rpc/activity/ActivityRpcModels.js.map +1 -0
- package/dist/esm/rpc/event/EventRpcGroup.js +10 -4
- package/dist/esm/rpc/event/EventRpcGroup.js.map +1 -1
- package/dist/esm/rpc/event/EventRpcModels.js +6 -0
- package/dist/esm/rpc/event/EventRpcModels.js.map +1 -1
- package/package.json +2 -2
- package/src/api/ActivityLogApi.ts +135 -0
- package/src/api/ActivityStatsApi.ts +42 -0
- package/src/api/EventApi.ts +9 -0
- package/src/api/EventRsvpApi.ts +1 -1
- package/src/api/EventSeriesApi.ts +13 -0
- package/src/api/RoleApi.ts +7 -1
- package/src/api/Roster.ts +7 -1
- package/src/api/TrainingTypeApi.ts +10 -4
- package/src/index.ts +12 -0
- package/src/models/ActivityLog.ts +21 -0
- package/src/models/ActivityStats.ts +131 -0
- package/src/models/ActivityType.ts +7 -0
- package/src/models/Event.ts +3 -0
- package/src/models/EventSeries.ts +3 -0
- package/src/models/TrainingType.ts +2 -1
- package/src/rpc/SyncRpcs.ts +2 -0
- package/src/rpc/activity/ActivityRpcGroup.ts +31 -0
- package/src/rpc/activity/ActivityRpcModels.ts +32 -0
- package/src/rpc/event/EventRpcGroup.ts +14 -2
- package/src/rpc/event/EventRpcModels.ts +11 -0
package/src/api/EventApi.ts
CHANGED
|
@@ -5,6 +5,7 @@ import { AuthMiddleware } from '~/api/Auth.js';
|
|
|
5
5
|
import { Snowflake } from '~/models/Discord.js';
|
|
6
6
|
import { EventId, EventStatus, EventType } from '~/models/Event.js';
|
|
7
7
|
import { EventSeriesId } from '~/models/EventSeries.js';
|
|
8
|
+
import { GroupId } from '~/models/GroupModel.js';
|
|
8
9
|
import { TeamId } from '~/models/Team.js';
|
|
9
10
|
import { TrainingTypeId } from '~/models/TrainingType.js';
|
|
10
11
|
|
|
@@ -39,6 +40,10 @@ export class EventDetail extends Schema.Class<EventDetail>('EventDetail')({
|
|
|
39
40
|
seriesId: Schema.OptionFromNullOr(EventSeriesId),
|
|
40
41
|
seriesModified: Schema.Boolean,
|
|
41
42
|
discordChannelId: Schema.OptionFromNullOr(Snowflake),
|
|
43
|
+
ownerGroupId: Schema.OptionFromNullOr(GroupId),
|
|
44
|
+
ownerGroupName: Schema.OptionFromNullOr(Schema.String),
|
|
45
|
+
memberGroupId: Schema.OptionFromNullOr(GroupId),
|
|
46
|
+
memberGroupName: Schema.OptionFromNullOr(Schema.String),
|
|
42
47
|
}) {}
|
|
43
48
|
|
|
44
49
|
export class EventListResponse extends Schema.Class<EventListResponse>('EventListResponse')({
|
|
@@ -55,6 +60,8 @@ export class CreateEventRequest extends Schema.Class<CreateEventRequest>('Create
|
|
|
55
60
|
endAt: Schema.OptionFromNullOr(Schemas.DateTimeFromIsoString),
|
|
56
61
|
location: Schema.OptionFromNullOr(Schema.String),
|
|
57
62
|
discordChannelId: Schema.OptionFromNullOr(Snowflake),
|
|
63
|
+
ownerGroupId: Schema.OptionFromNullOr(GroupId),
|
|
64
|
+
memberGroupId: Schema.OptionFromNullOr(GroupId),
|
|
58
65
|
}) {}
|
|
59
66
|
|
|
60
67
|
export class UpdateEventRequest extends Schema.Class<UpdateEventRequest>('UpdateEventRequest')({
|
|
@@ -68,6 +75,8 @@ export class UpdateEventRequest extends Schema.Class<UpdateEventRequest>('Update
|
|
|
68
75
|
}),
|
|
69
76
|
location: Schema.optionalWith(Schema.OptionFromNullOr(Schema.String), { as: 'Option' }),
|
|
70
77
|
discordChannelId: Schema.optionalWith(Schema.OptionFromNullOr(Snowflake), { as: 'Option' }),
|
|
78
|
+
ownerGroupId: Schema.optionalWith(Schema.OptionFromNullOr(GroupId), { as: 'Option' }),
|
|
79
|
+
memberGroupId: Schema.optionalWith(Schema.OptionFromNullOr(GroupId), { as: 'Option' }),
|
|
71
80
|
}) {}
|
|
72
81
|
|
|
73
82
|
export class EventNotFound extends Schema.TaggedError<EventNotFound>()(
|
package/src/api/EventRsvpApi.ts
CHANGED
|
@@ -71,7 +71,7 @@ export class EventRsvpApiGroup extends HttpApiGroup.make('eventRsvp')
|
|
|
71
71
|
)
|
|
72
72
|
.add(
|
|
73
73
|
HttpApiEndpoint.put('submitRsvp', '/teams/:teamId/events/:eventId/rsvp')
|
|
74
|
-
.addSuccess(
|
|
74
|
+
.addSuccess(Schema.Void, { status: 204 })
|
|
75
75
|
.addError(Forbidden, { status: 403 })
|
|
76
76
|
.addError(EventNotFound, { status: 404 })
|
|
77
77
|
.addError(RsvpDeadlinePassed, { status: 400 })
|
|
@@ -10,6 +10,7 @@ import {
|
|
|
10
10
|
EventSeriesStatus,
|
|
11
11
|
RecurrenceFrequency,
|
|
12
12
|
} from '~/models/EventSeries.js';
|
|
13
|
+
import { GroupId } from '~/models/GroupModel.js';
|
|
13
14
|
import { TeamId } from '~/models/Team.js';
|
|
14
15
|
import { TrainingTypeId } from '~/models/TrainingType.js';
|
|
15
16
|
|
|
@@ -28,6 +29,10 @@ export class EventSeriesInfo extends Schema.Class<EventSeriesInfo>('EventSeriesI
|
|
|
28
29
|
endTime: Schema.OptionFromNullOr(Schema.String),
|
|
29
30
|
location: Schema.OptionFromNullOr(Schema.String),
|
|
30
31
|
discordChannelId: Schema.OptionFromNullOr(Snowflake),
|
|
32
|
+
ownerGroupId: Schema.OptionFromNullOr(GroupId),
|
|
33
|
+
ownerGroupName: Schema.OptionFromNullOr(Schema.String),
|
|
34
|
+
memberGroupId: Schema.OptionFromNullOr(GroupId),
|
|
35
|
+
memberGroupName: Schema.OptionFromNullOr(Schema.String),
|
|
31
36
|
}) {}
|
|
32
37
|
|
|
33
38
|
export class EventSeriesDetail extends Schema.Class<EventSeriesDetail>('EventSeriesDetail')({
|
|
@@ -46,6 +51,10 @@ export class EventSeriesDetail extends Schema.Class<EventSeriesDetail>('EventSer
|
|
|
46
51
|
endTime: Schema.OptionFromNullOr(Schema.String),
|
|
47
52
|
location: Schema.OptionFromNullOr(Schema.String),
|
|
48
53
|
discordChannelId: Schema.OptionFromNullOr(Snowflake),
|
|
54
|
+
ownerGroupId: Schema.OptionFromNullOr(GroupId),
|
|
55
|
+
ownerGroupName: Schema.OptionFromNullOr(Schema.String),
|
|
56
|
+
memberGroupId: Schema.OptionFromNullOr(GroupId),
|
|
57
|
+
memberGroupName: Schema.OptionFromNullOr(Schema.String),
|
|
49
58
|
canEdit: Schema.Boolean,
|
|
50
59
|
canCancel: Schema.Boolean,
|
|
51
60
|
}) {}
|
|
@@ -64,6 +73,8 @@ export class CreateEventSeriesRequest extends Schema.Class<CreateEventSeriesRequ
|
|
|
64
73
|
endTime: Schema.OptionFromNullOr(Schema.String),
|
|
65
74
|
location: Schema.OptionFromNullOr(Schema.String),
|
|
66
75
|
discordChannelId: Schema.OptionFromNullOr(Snowflake),
|
|
76
|
+
ownerGroupId: Schema.OptionFromNullOr(GroupId),
|
|
77
|
+
memberGroupId: Schema.OptionFromNullOr(GroupId),
|
|
67
78
|
}) {}
|
|
68
79
|
|
|
69
80
|
export class UpdateEventSeriesRequest extends Schema.Class<UpdateEventSeriesRequest>(
|
|
@@ -80,6 +91,8 @@ export class UpdateEventSeriesRequest extends Schema.Class<UpdateEventSeriesRequ
|
|
|
80
91
|
as: 'Option',
|
|
81
92
|
}),
|
|
82
93
|
discordChannelId: Schema.optionalWith(Schema.OptionFromNullOr(Snowflake), { as: 'Option' }),
|
|
94
|
+
ownerGroupId: Schema.optionalWith(Schema.OptionFromNullOr(GroupId), { as: 'Option' }),
|
|
95
|
+
memberGroupId: Schema.optionalWith(Schema.OptionFromNullOr(GroupId), { as: 'Option' }),
|
|
83
96
|
}) {}
|
|
84
97
|
|
|
85
98
|
export class EventSeriesNotFound extends Schema.TaggedError<EventSeriesNotFound>()(
|
package/src/api/RoleApi.ts
CHANGED
|
@@ -13,12 +13,18 @@ export class RoleInfo extends Schema.Class<RoleInfo>('RoleInfo')({
|
|
|
13
13
|
permissionCount: Schema.Number,
|
|
14
14
|
}) {}
|
|
15
15
|
|
|
16
|
+
export class RoleListResponse extends Schema.Class<RoleListResponse>('RoleListResponse')({
|
|
17
|
+
canManage: Schema.Boolean,
|
|
18
|
+
roles: Schema.Array(RoleInfo),
|
|
19
|
+
}) {}
|
|
20
|
+
|
|
16
21
|
export class RoleDetail extends Schema.Class<RoleDetail>('RoleDetail')({
|
|
17
22
|
roleId: RoleId,
|
|
18
23
|
teamId: TeamId,
|
|
19
24
|
name: Schema.String,
|
|
20
25
|
isBuiltIn: Schema.Boolean,
|
|
21
26
|
permissions: Schema.Array(Permission),
|
|
27
|
+
canManage: Schema.Boolean,
|
|
22
28
|
}) {}
|
|
23
29
|
|
|
24
30
|
export class CreateRoleRequest extends Schema.Class<CreateRoleRequest>('CreateRoleRequest')({
|
|
@@ -74,7 +80,7 @@ export class RoleNameAlreadyTaken extends Schema.TaggedError<RoleNameAlreadyTake
|
|
|
74
80
|
export class RoleApiGroup extends HttpApiGroup.make('role')
|
|
75
81
|
.add(
|
|
76
82
|
HttpApiEndpoint.get('listRoles', '/teams/:teamId/roles')
|
|
77
|
-
.addSuccess(
|
|
83
|
+
.addSuccess(RoleListResponse)
|
|
78
84
|
.addError(Forbidden, { status: 403 })
|
|
79
85
|
.setPath(Schema.Struct({ teamId: TeamId }))
|
|
80
86
|
.middleware(AuthMiddleware),
|
package/src/api/Roster.ts
CHANGED
|
@@ -55,6 +55,11 @@ export class RosterInfo extends Schema.Class<RosterInfo>('RosterInfo')({
|
|
|
55
55
|
createdAt: Schema.String,
|
|
56
56
|
}) {}
|
|
57
57
|
|
|
58
|
+
export class RosterListResponse extends Schema.Class<RosterListResponse>('RosterListResponse')({
|
|
59
|
+
canManage: Schema.Boolean,
|
|
60
|
+
rosters: Schema.Array(RosterInfo),
|
|
61
|
+
}) {}
|
|
62
|
+
|
|
58
63
|
export class RosterDetail extends Schema.Class<RosterDetail>('RosterDetail')({
|
|
59
64
|
rosterId: RosterId,
|
|
60
65
|
teamId: TeamId,
|
|
@@ -62,6 +67,7 @@ export class RosterDetail extends Schema.Class<RosterDetail>('RosterDetail')({
|
|
|
62
67
|
active: Schema.Boolean,
|
|
63
68
|
createdAt: Schema.String,
|
|
64
69
|
members: Schema.Array(RosterPlayer),
|
|
70
|
+
canManage: Schema.Boolean,
|
|
65
71
|
}) {}
|
|
66
72
|
|
|
67
73
|
export class CreateRosterRequest extends Schema.Class<CreateRosterRequest>('CreateRosterRequest')({
|
|
@@ -114,7 +120,7 @@ export class RosterApiGroup extends HttpApiGroup.make('roster')
|
|
|
114
120
|
)
|
|
115
121
|
.add(
|
|
116
122
|
HttpApiEndpoint.get('listRosters', '/teams/:teamId/rosters')
|
|
117
|
-
.addSuccess(
|
|
123
|
+
.addSuccess(RosterListResponse)
|
|
118
124
|
.addError(Forbidden, { status: 403 })
|
|
119
125
|
.setPath(Schema.Struct({ teamId: TeamId }))
|
|
120
126
|
.middleware(AuthMiddleware),
|
|
@@ -10,15 +10,18 @@ export class TrainingTypeInfo extends Schema.Class<TrainingTypeInfo>('TrainingTy
|
|
|
10
10
|
trainingTypeId: TrainingTypeId,
|
|
11
11
|
teamId: TeamId,
|
|
12
12
|
name: Schema.String,
|
|
13
|
-
|
|
13
|
+
ownerGroupName: Schema.OptionFromNullOr(Schema.String),
|
|
14
|
+
memberGroupName: Schema.OptionFromNullOr(Schema.String),
|
|
14
15
|
}) {}
|
|
15
16
|
|
|
16
17
|
export class TrainingTypeDetail extends Schema.Class<TrainingTypeDetail>('TrainingTypeDetail')({
|
|
17
18
|
trainingTypeId: TrainingTypeId,
|
|
18
19
|
teamId: TeamId,
|
|
19
20
|
name: Schema.String,
|
|
20
|
-
|
|
21
|
-
|
|
21
|
+
ownerGroupId: Schema.OptionFromNullOr(GroupId),
|
|
22
|
+
ownerGroupName: Schema.OptionFromNullOr(Schema.String),
|
|
23
|
+
memberGroupId: Schema.OptionFromNullOr(GroupId),
|
|
24
|
+
memberGroupName: Schema.OptionFromNullOr(Schema.String),
|
|
22
25
|
discordChannelId: Schema.OptionFromNullOr(Snowflake),
|
|
23
26
|
canAdmin: Schema.Boolean,
|
|
24
27
|
}) {}
|
|
@@ -34,7 +37,8 @@ export class CreateTrainingTypeRequest extends Schema.Class<CreateTrainingTypeRe
|
|
|
34
37
|
'CreateTrainingTypeRequest',
|
|
35
38
|
)({
|
|
36
39
|
name: Schema.NonEmptyString,
|
|
37
|
-
|
|
40
|
+
ownerGroupId: Schema.OptionFromNullOr(GroupId),
|
|
41
|
+
memberGroupId: Schema.OptionFromNullOr(GroupId),
|
|
38
42
|
discordChannelId: Schema.OptionFromNullOr(Snowflake),
|
|
39
43
|
}) {}
|
|
40
44
|
|
|
@@ -42,6 +46,8 @@ export class UpdateTrainingTypeRequest extends Schema.Class<UpdateTrainingTypeRe
|
|
|
42
46
|
'UpdateTrainingTypeRequest',
|
|
43
47
|
)({
|
|
44
48
|
name: Schema.NonEmptyString,
|
|
49
|
+
ownerGroupId: Schema.optionalWith(Schema.OptionFromNullOr(GroupId), { as: 'Option' }),
|
|
50
|
+
memberGroupId: Schema.optionalWith(Schema.OptionFromNullOr(GroupId), { as: 'Option' }),
|
|
45
51
|
discordChannelId: Schema.optionalWith(Schema.OptionFromNullOr(Snowflake), { as: 'Option' }),
|
|
46
52
|
}) {}
|
|
47
53
|
|
package/src/index.ts
CHANGED
|
@@ -1,5 +1,9 @@
|
|
|
1
1
|
export * as ApiGroup from './ApiGroup.js';
|
|
2
2
|
|
|
3
|
+
export * as ActivityLogApi from './api/ActivityLogApi.js';
|
|
4
|
+
|
|
5
|
+
export * as ActivityStatsApi from './api/ActivityStatsApi.js';
|
|
6
|
+
|
|
3
7
|
export * as AgeThresholdApi from './api/AgeThresholdApi.js';
|
|
4
8
|
|
|
5
9
|
export * as Auth from './api/Auth.js';
|
|
@@ -26,6 +30,12 @@ export * as TeamSettingsApi from './api/TeamSettingsApi.js';
|
|
|
26
30
|
|
|
27
31
|
export * as TrainingTypeApi from './api/TrainingTypeApi.js';
|
|
28
32
|
|
|
33
|
+
export * as ActivityLog from './models/ActivityLog.js';
|
|
34
|
+
|
|
35
|
+
export * as ActivityStats from './models/ActivityStats.js';
|
|
36
|
+
|
|
37
|
+
export * as ActivityType from './models/ActivityType.js';
|
|
38
|
+
|
|
29
39
|
export * as AgeThresholdRule from './models/AgeThresholdRule.js';
|
|
30
40
|
|
|
31
41
|
export * as ChannelSyncEvent from './models/ChannelSyncEvent.js';
|
|
@@ -75,6 +85,8 @@ export * as TeamSettings from './models/TeamSettings.js';
|
|
|
75
85
|
export * as TrainingType from './models/TrainingType.js';
|
|
76
86
|
|
|
77
87
|
export * as User from './models/User.js';
|
|
88
|
+
export * as ActivityRpcGroup from './rpc/activity/ActivityRpcGroup.js';
|
|
89
|
+
export * as ActivityRpcModels from './rpc/activity/ActivityRpcModels.js';
|
|
78
90
|
export * as ChannelRpcEvents from './rpc/channel/ChannelRpcEvents.js';
|
|
79
91
|
export * as ChannelRpcGroup from './rpc/channel/ChannelRpcGroup.js';
|
|
80
92
|
export * as ChannelRpcModels from './rpc/channel/ChannelRpcModels.js';
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
import { Model } from '@effect/sql';
|
|
2
|
+
import { Schema } from 'effect';
|
|
3
|
+
import { ActivityTypeId } from '~/models/ActivityType.js';
|
|
4
|
+
import { TeamMemberId } from '~/models/TeamMember.js';
|
|
5
|
+
|
|
6
|
+
export const ActivityLogId = Schema.String.pipe(Schema.brand('ActivityLogId'));
|
|
7
|
+
export type ActivityLogId = typeof ActivityLogId.Type;
|
|
8
|
+
|
|
9
|
+
export const ActivitySource = Schema.Literal('manual', 'auto');
|
|
10
|
+
export type ActivitySource = typeof ActivitySource.Type;
|
|
11
|
+
|
|
12
|
+
export class ActivityLog extends Model.Class<ActivityLog>('ActivityLog')({
|
|
13
|
+
id: Model.Generated(ActivityLogId),
|
|
14
|
+
team_member_id: TeamMemberId,
|
|
15
|
+
activity_type_id: ActivityTypeId,
|
|
16
|
+
logged_at: Model.DateTimeInsertFromDate,
|
|
17
|
+
duration_minutes: Schema.OptionFromNullOr(Schema.Int.pipe(Schema.between(1, 1440))),
|
|
18
|
+
note: Schema.OptionFromNullOr(Schema.String),
|
|
19
|
+
source: ActivitySource,
|
|
20
|
+
created_at: Model.DateTimeInsertFromDate,
|
|
21
|
+
}) {}
|
|
@@ -0,0 +1,131 @@
|
|
|
1
|
+
import { Option } from 'effect';
|
|
2
|
+
|
|
3
|
+
export interface StreakResult {
|
|
4
|
+
readonly currentStreak: number;
|
|
5
|
+
readonly longestStreak: number;
|
|
6
|
+
}
|
|
7
|
+
|
|
8
|
+
export interface StatsResult {
|
|
9
|
+
readonly currentStreak: number;
|
|
10
|
+
readonly longestStreak: number;
|
|
11
|
+
readonly totalActivities: number;
|
|
12
|
+
readonly totalDurationMinutes: number;
|
|
13
|
+
readonly counts: ReadonlyArray<{
|
|
14
|
+
readonly activityTypeId: string;
|
|
15
|
+
readonly activityTypeName: string;
|
|
16
|
+
readonly count: number;
|
|
17
|
+
}>;
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
/** Calculate current and longest streaks from a list of activity dates (ISO date strings like "2026-03-25"). */
|
|
21
|
+
export const calculateStreaks = (dates: ReadonlyArray<string>, today: string): StreakResult => {
|
|
22
|
+
const uniqueDates = [...new Set(dates)].sort();
|
|
23
|
+
if (uniqueDates.length === 0) return { currentStreak: 0, longestStreak: 0 };
|
|
24
|
+
|
|
25
|
+
// Calculate longest streak by walking sorted dates forward
|
|
26
|
+
let longestStreak = 1;
|
|
27
|
+
let currentRun = 1;
|
|
28
|
+
for (let i = 1; i < uniqueDates.length; i++) {
|
|
29
|
+
const prev = uniqueDates[i - 1];
|
|
30
|
+
const curr = uniqueDates[i];
|
|
31
|
+
if (prev !== undefined && curr !== undefined && daysBetween(prev, curr) === 1) {
|
|
32
|
+
currentRun++;
|
|
33
|
+
longestStreak = Math.max(longestStreak, currentRun);
|
|
34
|
+
} else {
|
|
35
|
+
currentRun = 1;
|
|
36
|
+
}
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
// Calculate current streak: walk backwards from today or yesterday
|
|
40
|
+
const lastDate = uniqueDates[uniqueDates.length - 1];
|
|
41
|
+
if (lastDate === undefined) return { currentStreak: 0, longestStreak: 0 };
|
|
42
|
+
const gapFromToday = daysBetween(lastDate, today);
|
|
43
|
+
|
|
44
|
+
// If most recent activity is more than 1 day ago, current streak is 0
|
|
45
|
+
if (gapFromToday > 1) return { currentStreak: 0, longestStreak };
|
|
46
|
+
|
|
47
|
+
let currentStreak = 1;
|
|
48
|
+
for (let i = uniqueDates.length - 2; i >= 0; i--) {
|
|
49
|
+
const curr = uniqueDates[i];
|
|
50
|
+
const next = uniqueDates[i + 1];
|
|
51
|
+
if (curr !== undefined && next !== undefined && daysBetween(curr, next) === 1) {
|
|
52
|
+
currentStreak++;
|
|
53
|
+
} else {
|
|
54
|
+
break;
|
|
55
|
+
}
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
return { currentStreak, longestStreak };
|
|
59
|
+
};
|
|
60
|
+
|
|
61
|
+
/** Calculate full stats from activity rows. */
|
|
62
|
+
export const calculateStats = (
|
|
63
|
+
rows: ReadonlyArray<{
|
|
64
|
+
readonly activity_type_id: string;
|
|
65
|
+
readonly activity_type_name: string;
|
|
66
|
+
readonly logged_at_date: string;
|
|
67
|
+
readonly duration_minutes: Option.Option<number>;
|
|
68
|
+
}>,
|
|
69
|
+
today: string,
|
|
70
|
+
): StatsResult => {
|
|
71
|
+
if (rows.length === 0) {
|
|
72
|
+
return {
|
|
73
|
+
currentStreak: 0,
|
|
74
|
+
longestStreak: 0,
|
|
75
|
+
totalActivities: 0,
|
|
76
|
+
totalDurationMinutes: 0,
|
|
77
|
+
counts: [],
|
|
78
|
+
};
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
const dates = rows.map((r) => r.logged_at_date);
|
|
82
|
+
const { currentStreak, longestStreak } = calculateStreaks(dates, today);
|
|
83
|
+
|
|
84
|
+
let totalDurationMinutes = 0;
|
|
85
|
+
const countMap = new Map<string, { activityTypeName: string; count: number }>();
|
|
86
|
+
|
|
87
|
+
for (const row of rows) {
|
|
88
|
+
totalDurationMinutes += Option.getOrElse(row.duration_minutes, () => 0);
|
|
89
|
+
const existing = countMap.get(row.activity_type_id);
|
|
90
|
+
if (existing) {
|
|
91
|
+
existing.count++;
|
|
92
|
+
} else {
|
|
93
|
+
countMap.set(row.activity_type_id, { activityTypeName: row.activity_type_name, count: 1 });
|
|
94
|
+
}
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
const counts = Array.from(countMap.entries()).map(
|
|
98
|
+
([activityTypeId, { activityTypeName, count }]) => ({
|
|
99
|
+
activityTypeId,
|
|
100
|
+
activityTypeName,
|
|
101
|
+
count,
|
|
102
|
+
}),
|
|
103
|
+
);
|
|
104
|
+
|
|
105
|
+
return {
|
|
106
|
+
currentStreak,
|
|
107
|
+
longestStreak,
|
|
108
|
+
totalActivities: rows.length,
|
|
109
|
+
totalDurationMinutes,
|
|
110
|
+
counts,
|
|
111
|
+
};
|
|
112
|
+
};
|
|
113
|
+
|
|
114
|
+
/** Returns today's date as an ISO string in the Europe/Prague timezone. */
|
|
115
|
+
export const todayInPrague = (): string => {
|
|
116
|
+
const parts = new Intl.DateTimeFormat('en-CA', {
|
|
117
|
+
timeZone: 'Europe/Prague',
|
|
118
|
+
year: 'numeric',
|
|
119
|
+
month: '2-digit',
|
|
120
|
+
day: '2-digit',
|
|
121
|
+
}).formatToParts(new Date());
|
|
122
|
+
|
|
123
|
+
const get = (type: string) => parts.find((p) => p.type === type)?.value ?? '';
|
|
124
|
+
return `${get('year')}-${get('month')}-${get('day')}`;
|
|
125
|
+
};
|
|
126
|
+
|
|
127
|
+
/** Returns the number of days between two ISO date strings. Always positive. Inputs must be date-only strings (UTC midnight). */
|
|
128
|
+
const daysBetween = (a: string, b: string): number => {
|
|
129
|
+
const msPerDay = 86400000;
|
|
130
|
+
return Math.round(Math.abs(new Date(b).getTime() - new Date(a).getTime()) / msPerDay);
|
|
131
|
+
};
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
import { Schema } from 'effect';
|
|
2
|
+
|
|
3
|
+
export const ActivityTypeId = Schema.String.pipe(Schema.brand('ActivityTypeId'));
|
|
4
|
+
export type ActivityTypeId = typeof ActivityTypeId.Type;
|
|
5
|
+
|
|
6
|
+
export const ActivityTypeSlug = Schema.Literal('gym', 'running', 'stretching', 'training');
|
|
7
|
+
export type ActivityTypeSlug = typeof ActivityTypeSlug.Type;
|
package/src/models/Event.ts
CHANGED
|
@@ -2,6 +2,7 @@ import { Model } from '@effect/sql';
|
|
|
2
2
|
import * as Schemas from '@sideline/effect-lib/Schemas';
|
|
3
3
|
import { Schema } from 'effect';
|
|
4
4
|
import { EventSeriesId } from '~/models/EventSeries.js';
|
|
5
|
+
import { GroupId } from '~/models/GroupModel.js';
|
|
5
6
|
import { TeamId } from '~/models/Team.js';
|
|
6
7
|
import { TeamMemberId } from '~/models/TeamMember.js';
|
|
7
8
|
import { TrainingTypeId } from '~/models/TrainingType.js';
|
|
@@ -32,6 +33,8 @@ export class Event extends Model.Class<Event>('Event')({
|
|
|
32
33
|
start_at: Schemas.DateTimeFromDate,
|
|
33
34
|
end_at: Schema.OptionFromNullOr(Schemas.DateTimeFromDate),
|
|
34
35
|
location: Schema.OptionFromNullOr(Schema.String),
|
|
36
|
+
owner_group_id: Schema.OptionFromNullOr(GroupId),
|
|
37
|
+
member_group_id: Schema.OptionFromNullOr(GroupId),
|
|
35
38
|
series_id: Schema.OptionFromNullOr(EventSeriesId),
|
|
36
39
|
series_modified: Schema.Boolean,
|
|
37
40
|
status: Model.FieldExcept('update')(EventStatus),
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import { Model } from '@effect/sql';
|
|
2
2
|
import { Schema } from 'effect';
|
|
3
|
+
import { GroupId } from '~/models/GroupModel.js';
|
|
3
4
|
import { TeamId } from '~/models/Team.js';
|
|
4
5
|
import { TeamMemberId } from '~/models/TeamMember.js';
|
|
5
6
|
import { TrainingTypeId } from '~/models/TrainingType.js';
|
|
@@ -32,6 +33,8 @@ export class EventSeries extends Model.Class<EventSeries>('EventSeries')({
|
|
|
32
33
|
days_of_week: DaysOfWeek,
|
|
33
34
|
start_date: Schema.DateFromSelf,
|
|
34
35
|
end_date: Schema.OptionFromNullOr(Schema.DateFromSelf),
|
|
36
|
+
owner_group_id: Schema.OptionFromNullOr(GroupId),
|
|
37
|
+
member_group_id: Schema.OptionFromNullOr(GroupId),
|
|
35
38
|
status: Model.FieldExcept('update')(EventSeriesStatus),
|
|
36
39
|
created_by: TeamMemberId,
|
|
37
40
|
created_at: Model.DateTimeInsertFromDate,
|
|
@@ -10,6 +10,7 @@ export class TrainingType extends Model.Class<TrainingType>('TrainingType')({
|
|
|
10
10
|
id: Model.Generated(TrainingTypeId),
|
|
11
11
|
team_id: TeamId,
|
|
12
12
|
name: Schema.String,
|
|
13
|
-
|
|
13
|
+
owner_group_id: Schema.OptionFromNullOr(GroupId),
|
|
14
|
+
member_group_id: Schema.OptionFromNullOr(GroupId),
|
|
14
15
|
created_at: Model.DateTimeInsertFromDate,
|
|
15
16
|
}) {}
|
package/src/rpc/SyncRpcs.ts
CHANGED
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { RpcGroup } from '@effect/rpc';
|
|
2
|
+
import { ActivityRpcGroup } from './activity/ActivityRpcGroup.js';
|
|
2
3
|
import { ChannelRpcGroup } from './channel/ChannelRpcGroup.js';
|
|
3
4
|
import { EventRpcGroup } from './event/EventRpcGroup.js';
|
|
4
5
|
import { GuildRpcGroup } from './guild/GuildRpcGroup.js';
|
|
@@ -9,4 +10,5 @@ export class SyncRpcs extends RpcGroup.make().merge(
|
|
|
9
10
|
ChannelRpcGroup,
|
|
10
11
|
GuildRpcGroup,
|
|
11
12
|
EventRpcGroup,
|
|
13
|
+
ActivityRpcGroup,
|
|
12
14
|
) {}
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
import { Rpc, RpcGroup } from '@effect/rpc';
|
|
2
|
+
import { Schema } from 'effect';
|
|
3
|
+
import { Discord } from '~/index.js';
|
|
4
|
+
import {
|
|
5
|
+
ActivityGuildNotFound,
|
|
6
|
+
ActivityMemberNotFound,
|
|
7
|
+
GetStatsResult,
|
|
8
|
+
LogActivityResult,
|
|
9
|
+
} from './ActivityRpcModels.js';
|
|
10
|
+
|
|
11
|
+
export const ActivityRpcGroup = RpcGroup.make(
|
|
12
|
+
Rpc.make('LogActivity', {
|
|
13
|
+
payload: {
|
|
14
|
+
guild_id: Discord.Snowflake,
|
|
15
|
+
discord_user_id: Discord.Snowflake,
|
|
16
|
+
activity_type: Schema.String,
|
|
17
|
+
duration_minutes: Schema.OptionFromNullOr(Schema.Int.pipe(Schema.between(1, 1440))),
|
|
18
|
+
note: Schema.OptionFromNullOr(Schema.String),
|
|
19
|
+
},
|
|
20
|
+
success: LogActivityResult,
|
|
21
|
+
error: Schema.Union(ActivityMemberNotFound, ActivityGuildNotFound),
|
|
22
|
+
}),
|
|
23
|
+
Rpc.make('GetStats', {
|
|
24
|
+
payload: {
|
|
25
|
+
guild_id: Discord.Snowflake,
|
|
26
|
+
discord_user_id: Discord.Snowflake,
|
|
27
|
+
},
|
|
28
|
+
success: GetStatsResult,
|
|
29
|
+
error: Schema.Union(ActivityMemberNotFound, ActivityGuildNotFound),
|
|
30
|
+
}),
|
|
31
|
+
).prefix('Activity/');
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
import { Schema } from 'effect';
|
|
2
|
+
import { ActivityLogId } from '~/models/ActivityLog.js';
|
|
3
|
+
|
|
4
|
+
export class LogActivityResult extends Schema.Class<LogActivityResult>('LogActivityResult')({
|
|
5
|
+
id: ActivityLogId,
|
|
6
|
+
activity_type_id: Schema.String,
|
|
7
|
+
logged_at: Schema.String,
|
|
8
|
+
}) {}
|
|
9
|
+
|
|
10
|
+
export class ActivityMemberNotFound extends Schema.TaggedError<ActivityMemberNotFound>()(
|
|
11
|
+
'ActivityMemberNotFound',
|
|
12
|
+
{},
|
|
13
|
+
) {}
|
|
14
|
+
|
|
15
|
+
export class ActivityGuildNotFound extends Schema.TaggedError<ActivityGuildNotFound>()(
|
|
16
|
+
'ActivityGuildNotFound',
|
|
17
|
+
{},
|
|
18
|
+
) {}
|
|
19
|
+
|
|
20
|
+
export class GetStatsResult extends Schema.Class<GetStatsResult>('GetStatsResult')({
|
|
21
|
+
current_streak: Schema.Int,
|
|
22
|
+
longest_streak: Schema.Int,
|
|
23
|
+
total_activities: Schema.Int,
|
|
24
|
+
total_duration_minutes: Schema.Int,
|
|
25
|
+
counts: Schema.Array(
|
|
26
|
+
Schema.Struct({
|
|
27
|
+
activity_type_id: Schema.String,
|
|
28
|
+
activity_type_name: Schema.String,
|
|
29
|
+
count: Schema.Int,
|
|
30
|
+
}),
|
|
31
|
+
),
|
|
32
|
+
}) {}
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { Rpc, RpcGroup } from '@effect/rpc';
|
|
2
2
|
import { Schema } from 'effect';
|
|
3
|
-
import { Discord, Event, EventRsvp, Team } from '~/index.js';
|
|
3
|
+
import { Discord, Event, EventRsvp, Team, TrainingType } from '~/index.js';
|
|
4
4
|
import { UnprocessedEventSyncEvent } from './EventRpcEvents.js';
|
|
5
5
|
import {
|
|
6
6
|
ChannelEventEntry,
|
|
@@ -17,7 +17,9 @@ import {
|
|
|
17
17
|
RsvpDeadlinePassed,
|
|
18
18
|
RsvpEventNotFound,
|
|
19
19
|
RsvpMemberNotFound,
|
|
20
|
+
RsvpNotGroupMember,
|
|
20
21
|
RsvpReminderSummary,
|
|
22
|
+
TrainingTypeChoice,
|
|
21
23
|
} from './EventRpcModels.js';
|
|
22
24
|
|
|
23
25
|
export const EventRpcGroup = RpcGroup.make(
|
|
@@ -51,7 +53,12 @@ export const EventRpcGroup = RpcGroup.make(
|
|
|
51
53
|
message: Schema.OptionFromNullOr(Schema.String),
|
|
52
54
|
},
|
|
53
55
|
success: RsvpCountsResult,
|
|
54
|
-
error: Schema.Union(
|
|
56
|
+
error: Schema.Union(
|
|
57
|
+
RsvpMemberNotFound,
|
|
58
|
+
RsvpDeadlinePassed,
|
|
59
|
+
RsvpEventNotFound,
|
|
60
|
+
RsvpNotGroupMember,
|
|
61
|
+
),
|
|
55
62
|
}),
|
|
56
63
|
Rpc.make('GetRsvpCounts', {
|
|
57
64
|
payload: { event_id: Event.EventId },
|
|
@@ -82,6 +89,10 @@ export const EventRpcGroup = RpcGroup.make(
|
|
|
82
89
|
success: GuildEventListResult,
|
|
83
90
|
error: GuildNotFound,
|
|
84
91
|
}),
|
|
92
|
+
Rpc.make('GetTrainingTypesByGuild', {
|
|
93
|
+
payload: { guild_id: Discord.Snowflake },
|
|
94
|
+
success: Schema.Array(TrainingTypeChoice),
|
|
95
|
+
}),
|
|
85
96
|
Rpc.make('CreateEvent', {
|
|
86
97
|
payload: {
|
|
87
98
|
guild_id: Discord.Snowflake,
|
|
@@ -92,6 +103,7 @@ export const EventRpcGroup = RpcGroup.make(
|
|
|
92
103
|
end_at: Schema.OptionFromNullOr(Schema.String),
|
|
93
104
|
location: Schema.OptionFromNullOr(Schema.String),
|
|
94
105
|
description: Schema.OptionFromNullOr(Schema.String),
|
|
106
|
+
training_type_id: Schema.OptionFromNullOr(TrainingType.TrainingTypeId),
|
|
95
107
|
},
|
|
96
108
|
success: CreateEventResult,
|
|
97
109
|
error: Schema.Union(CreateEventNotMember, CreateEventForbidden, CreateEventInvalidDate),
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import * as Schemas from '@sideline/effect-lib/Schemas';
|
|
2
2
|
import { Schema } from 'effect';
|
|
3
3
|
import { Snowflake } from '~/models/Discord.js';
|
|
4
|
+
import { TrainingTypeId } from '~/models/TrainingType.js';
|
|
4
5
|
|
|
5
6
|
export class EventDiscordMessage extends Schema.Class<EventDiscordMessage>('EventDiscordMessage')({
|
|
6
7
|
discord_channel_id: Snowflake,
|
|
@@ -51,6 +52,11 @@ export class RsvpEventNotFound extends Schema.TaggedError<RsvpEventNotFound>()(
|
|
|
51
52
|
{},
|
|
52
53
|
) {}
|
|
53
54
|
|
|
55
|
+
export class RsvpNotGroupMember extends Schema.TaggedError<RsvpNotGroupMember>()(
|
|
56
|
+
'RsvpNotGroupMember',
|
|
57
|
+
{},
|
|
58
|
+
) {}
|
|
59
|
+
|
|
54
60
|
export class CreateEventNotMember extends Schema.TaggedError<CreateEventNotMember>()(
|
|
55
61
|
'CreateEventNotMember',
|
|
56
62
|
{},
|
|
@@ -119,3 +125,8 @@ export class RsvpReminderSummary extends Schema.Class<RsvpReminderSummary>('Rsvp
|
|
|
119
125
|
maybeCount: Schema.Number,
|
|
120
126
|
nonResponders: Schema.Array(NonResponderRpcEntry),
|
|
121
127
|
}) {}
|
|
128
|
+
|
|
129
|
+
export class TrainingTypeChoice extends Schema.Class<TrainingTypeChoice>('TrainingTypeChoice')({
|
|
130
|
+
id: TrainingTypeId,
|
|
131
|
+
name: Schema.String,
|
|
132
|
+
}) {}
|