diff --git a/calsub/http.go b/calsub/http.go index 235cdb4c5c..811f7633f4 100644 --- a/calsub/http.go +++ b/calsub/http.go @@ -32,29 +32,58 @@ func (s *Store) ServeICalData(w http.ResponseWriter, req *http.Request) { return } - // filter out other users - filtered := shifts[:0] - for _, s := range shifts { - if s.UserID != info.UserID.String() { - continue - } - filtered = append(filtered, s) - } - var subCfg SubscriptionConfig err = json.Unmarshal(info.Config, &subCfg) if errutil.HTTPError(ctx, w, err) { return } + if !subCfg.FullSchedule { + // filter out other users + filtered := shifts[:0] + for _, s := range shifts { + if s.UserID != info.UserID.String() { + continue + } + filtered = append(filtered, s) + } + shifts = filtered + } + data := renderData{ ApplicationName: cfg.ApplicationName(), ScheduleID: info.ScheduleID, ScheduleName: info.ScheduleName, - Shifts: filtered, + Shifts: shifts, ReminderMinutes: subCfg.ReminderMinutes, Version: version.GitVersion(), GeneratedAt: info.Now, + FullSchedule: subCfg.FullSchedule, + } + + if subCfg.FullSchedule { + // When rendering the full schedule, we need to fetch the names of all users. + data.UserNames = make(map[string]string) + var uniqueIDs []uuid.UUID + for _, s := range shifts { + + // We'll use the map to track which IDs we've already seen. + // That way we don't ask the DB for the same user multiple times. + if _, ok := data.UserNames[s.UserID]; ok { + continue + } + data.UserNames[s.UserID] = "Unknown User" + uniqueIDs = append(uniqueIDs, uuid.MustParse(s.UserID)) + } + + users, err := gadb.New(s.db).CalSubUserNames(ctx, uniqueIDs) + if errutil.HTTPError(ctx, w, err) { + return + } + + for _, u := range users { + data.UserNames[u.ID.String()] = u.Name + } } calData, err := data.renderICal() diff --git a/calsub/queries.sql b/calsub/queries.sql index 6c291ccafc..560ee1cd65 100644 --- a/calsub/queries.sql +++ b/calsub/queries.sql @@ -1,13 +1,25 @@ -- name: FindOneCalSub :one -SELECT id, +SELECT + id, NAME, user_id, disabled, schedule_id, config, last_access -FROM user_calendar_subscriptions -WHERE id = $1; +FROM + user_calendar_subscriptions +WHERE + id = $1; + +-- name: CalSubUserNames :many +SELECT + id, + name +FROM + users +WHERE + id = ANY ($1::uuid[]); -- name: CalSubRenderInfo :one SELECT @@ -23,59 +35,70 @@ WHERE sub.id = $1; -- name: FindOneCalSubForUpdate :one -SELECT id, +SELECT + id, NAME, user_id, disabled, schedule_id, config, last_access -FROM user_calendar_subscriptions -WHERE id = $1 FOR -UPDATE; +FROM + user_calendar_subscriptions +WHERE + id = $1 +FOR UPDATE; -- name: FindManyCalSubByUser :many -SELECT id, +SELECT + id, NAME, user_id, disabled, schedule_id, config, last_access -FROM user_calendar_subscriptions -WHERE user_id = $1; +FROM + user_calendar_subscriptions +WHERE + user_id = $1; -- name: DeleteManyCalSub :exec DELETE FROM user_calendar_subscriptions -WHERE id = ANY($1::uuid [ ]) +WHERE id = ANY ($1::uuid[]) AND user_id = $2; -- name: CreateCalSub :one -INSERT INTO user_calendar_subscriptions ( - id, - NAME, - user_id, - disabled, - schedule_id, - config - ) -VALUES ($1, $2, $3, $4, $5, $6) RETURNING created_at; +INSERT INTO user_calendar_subscriptions(id, NAME, user_id, disabled, schedule_id, config) + VALUES ($1, $2, $3, $4, $5, $6) +RETURNING + created_at; -- name: Now :one -SELECT now()::timestamptz; +SELECT + now()::timestamptz; -- name: CalSubAuthUser :one -UPDATE user_calendar_subscriptions -SET last_access = now() -WHERE NOT disabled +UPDATE + user_calendar_subscriptions +SET + last_access = now() +WHERE + NOT disabled AND id = $1 - AND date_trunc('second', created_at) = $2 RETURNING user_id; + AND date_trunc('second', created_at) = $2 +RETURNING + user_id; -- name: UpdateCalSub :exec -UPDATE user_calendar_subscriptions -SET NAME = $1, +UPDATE + user_calendar_subscriptions +SET + NAME = $1, disabled = $2, config = $3, last_update = now() -WHERE id = $4 +WHERE + id = $4 AND user_id = $5; + diff --git a/calsub/renderdata.go b/calsub/renderdata.go index 2237bf1a1b..480f0ec7f6 100644 --- a/calsub/renderdata.go +++ b/calsub/renderdata.go @@ -15,4 +15,6 @@ type renderData struct { ReminderMinutes []int Version string GeneratedAt time.Time + FullSchedule bool + UserNames map[string]string } diff --git a/calsub/renderical.go b/calsub/renderical.go index 5e3dbf471a..d5ddd30a2b 100644 --- a/calsub/renderical.go +++ b/calsub/renderical.go @@ -23,7 +23,7 @@ METHOD:PUBLISH {{- range $i, $s := .Shifts}} BEGIN:VEVENT UID:{{index $eventUIDs $i}} -SUMMARY:On-Call ({{$.ApplicationName}}: {{$.ScheduleName}}){{if $s.Truncated}} Begins* +SUMMARY:{{if $.FullSchedule}}{{index $.UserNames $s.UserID}} {{end}}On-Call ({{$.ApplicationName}}: {{$.ScheduleName}}){{if $s.Truncated}} Begins* DESCRIPTION:The end time of this shift is unknown and will continue beyond what is displayed. {{- end }} DTSTAMP:{{$genTime.UTC.Format "20060102T150405Z"}} diff --git a/calsub/subscriptionconfig.go b/calsub/subscriptionconfig.go index 46b2c00799..d69200f365 100644 --- a/calsub/subscriptionconfig.go +++ b/calsub/subscriptionconfig.go @@ -10,6 +10,7 @@ import ( // SubscriptionConfig is the configuration for a calendar subscription. type SubscriptionConfig struct { ReminderMinutes []int + FullSchedule bool } var ( diff --git a/gadb/queries.sql.go b/gadb/queries.sql.go index 7844031e7f..7a7043cb7d 100644 --- a/gadb/queries.sql.go +++ b/gadb/queries.sql.go @@ -617,11 +617,16 @@ func (q *Queries) AuthLinkUseReq(ctx context.Context, id uuid.UUID) (AuthLinkUse } const calSubAuthUser = `-- name: CalSubAuthUser :one -UPDATE user_calendar_subscriptions -SET last_access = now() -WHERE NOT disabled +UPDATE + user_calendar_subscriptions +SET + last_access = now() +WHERE + NOT disabled AND id = $1 - AND date_trunc('second', created_at) = $2 RETURNING user_id + AND date_trunc('second', created_at) = $2 +RETURNING + user_id ` type CalSubAuthUserParams struct { @@ -671,16 +676,49 @@ func (q *Queries) CalSubRenderInfo(ctx context.Context, id uuid.UUID) (CalSubRen return i, err } +const calSubUserNames = `-- name: CalSubUserNames :many +SELECT + id, + name +FROM + users +WHERE + id = ANY ($1::uuid[]) +` + +type CalSubUserNamesRow struct { + ID uuid.UUID + Name string +} + +func (q *Queries) CalSubUserNames(ctx context.Context, dollar_1 []uuid.UUID) ([]CalSubUserNamesRow, error) { + rows, err := q.db.QueryContext(ctx, calSubUserNames, pq.Array(dollar_1)) + if err != nil { + return nil, err + } + defer rows.Close() + var items []CalSubUserNamesRow + for rows.Next() { + var i CalSubUserNamesRow + if err := rows.Scan(&i.ID, &i.Name); err != nil { + return nil, err + } + items = append(items, i) + } + if err := rows.Close(); err != nil { + return nil, err + } + if err := rows.Err(); err != nil { + return nil, err + } + return items, nil +} + const createCalSub = `-- name: CreateCalSub :one -INSERT INTO user_calendar_subscriptions ( - id, - NAME, - user_id, - disabled, - schedule_id, - config - ) -VALUES ($1, $2, $3, $4, $5, $6) RETURNING created_at +INSERT INTO user_calendar_subscriptions(id, NAME, user_id, disabled, schedule_id, config) + VALUES ($1, $2, $3, $4, $5, $6) +RETURNING + created_at ` type CreateCalSubParams struct { @@ -708,7 +746,7 @@ func (q *Queries) CreateCalSub(ctx context.Context, arg CreateCalSubParams) (tim const deleteManyCalSub = `-- name: DeleteManyCalSub :exec DELETE FROM user_calendar_subscriptions -WHERE id = ANY($1::uuid [ ]) +WHERE id = ANY ($1::uuid[]) AND user_id = $2 ` @@ -723,15 +761,18 @@ func (q *Queries) DeleteManyCalSub(ctx context.Context, arg DeleteManyCalSubPara } const findManyCalSubByUser = `-- name: FindManyCalSubByUser :many -SELECT id, +SELECT + id, NAME, user_id, disabled, schedule_id, config, last_access -FROM user_calendar_subscriptions -WHERE user_id = $1 +FROM + user_calendar_subscriptions +WHERE + user_id = $1 ` type FindManyCalSubByUserRow struct { @@ -776,15 +817,18 @@ func (q *Queries) FindManyCalSubByUser(ctx context.Context, userID uuid.UUID) ([ } const findOneCalSub = `-- name: FindOneCalSub :one -SELECT id, +SELECT + id, NAME, user_id, disabled, schedule_id, config, last_access -FROM user_calendar_subscriptions -WHERE id = $1 +FROM + user_calendar_subscriptions +WHERE + id = $1 ` type FindOneCalSubRow struct { @@ -813,16 +857,19 @@ func (q *Queries) FindOneCalSub(ctx context.Context, id uuid.UUID) (FindOneCalSu } const findOneCalSubForUpdate = `-- name: FindOneCalSubForUpdate :one -SELECT id, +SELECT + id, NAME, user_id, disabled, schedule_id, config, last_access -FROM user_calendar_subscriptions -WHERE id = $1 FOR -UPDATE +FROM + user_calendar_subscriptions +WHERE + id = $1 +FOR UPDATE ` type FindOneCalSubForUpdateRow struct { @@ -1037,7 +1084,8 @@ func (q *Queries) NoticeUnackedAlertsByService(ctx context.Context, dollar_1 uui } const now = `-- name: Now :one -SELECT now()::timestamptz +SELECT + now()::timestamptz ` func (q *Queries) Now(ctx context.Context) (time.Time, error) { @@ -1455,12 +1503,15 @@ func (q *Queries) StatusMgrUpdateSub(ctx context.Context, arg StatusMgrUpdateSub } const updateCalSub = `-- name: UpdateCalSub :exec -UPDATE user_calendar_subscriptions -SET NAME = $1, +UPDATE + user_calendar_subscriptions +SET + NAME = $1, disabled = $2, config = $3, last_update = now() -WHERE id = $4 +WHERE + id = $4 AND user_id = $5 ` diff --git a/graphql2/generated.go b/graphql2/generated.go index 0729170b76..4c2f6e7ba5 100644 --- a/graphql2/generated.go +++ b/graphql2/generated.go @@ -635,6 +635,7 @@ type ComplexityRoot struct { UserCalendarSubscription struct { Disabled func(childComplexity int) int + FullSchedule func(childComplexity int) int ID func(childComplexity int) int LastAccess func(childComplexity int) int Name func(childComplexity int) int @@ -900,6 +901,7 @@ type UserResolver interface { } type UserCalendarSubscriptionResolver interface { ReminderMinutes(ctx context.Context, obj *calsub.Subscription) ([]int, error) + FullSchedule(ctx context.Context, obj *calsub.Subscription) (bool, error) Schedule(ctx context.Context, obj *calsub.Subscription) (*schedule.Schedule, error) @@ -3870,6 +3872,13 @@ func (e *executableSchema) Complexity(typeName, field string, childComplexity in return e.complexity.UserCalendarSubscription.Disabled(childComplexity), true + case "UserCalendarSubscription.fullSchedule": + if e.complexity.UserCalendarSubscription.FullSchedule == nil { + break + } + + return e.complexity.UserCalendarSubscription.FullSchedule(childComplexity), true + case "UserCalendarSubscription.id": if e.complexity.UserCalendarSubscription.ID == nil { break @@ -13674,6 +13683,8 @@ func (ec *executionContext) fieldContext_Mutation_createUserCalendarSubscription return ec.fieldContext_UserCalendarSubscription_name(ctx, field) case "reminderMinutes": return ec.fieldContext_UserCalendarSubscription_reminderMinutes(ctx, field) + case "fullSchedule": + return ec.fieldContext_UserCalendarSubscription_fullSchedule(ctx, field) case "scheduleID": return ec.fieldContext_UserCalendarSubscription_scheduleID(ctx, field) case "schedule": @@ -16950,6 +16961,8 @@ func (ec *executionContext) fieldContext_Query_userCalendarSubscription(ctx cont return ec.fieldContext_UserCalendarSubscription_name(ctx, field) case "reminderMinutes": return ec.fieldContext_UserCalendarSubscription_reminderMinutes(ctx, field) + case "fullSchedule": + return ec.fieldContext_UserCalendarSubscription_fullSchedule(ctx, field) case "scheduleID": return ec.fieldContext_UserCalendarSubscription_scheduleID(ctx, field) case "schedule": @@ -23669,6 +23682,8 @@ func (ec *executionContext) fieldContext_User_calendarSubscriptions(ctx context. return ec.fieldContext_UserCalendarSubscription_name(ctx, field) case "reminderMinutes": return ec.fieldContext_UserCalendarSubscription_reminderMinutes(ctx, field) + case "fullSchedule": + return ec.fieldContext_UserCalendarSubscription_fullSchedule(ctx, field) case "scheduleID": return ec.fieldContext_UserCalendarSubscription_scheduleID(ctx, field) case "schedule": @@ -24070,6 +24085,50 @@ func (ec *executionContext) fieldContext_UserCalendarSubscription_reminderMinute return fc, nil } +func (ec *executionContext) _UserCalendarSubscription_fullSchedule(ctx context.Context, field graphql.CollectedField, obj *calsub.Subscription) (ret graphql.Marshaler) { + fc, err := ec.fieldContext_UserCalendarSubscription_fullSchedule(ctx, field) + if err != nil { + return graphql.Null + } + ctx = graphql.WithFieldContext(ctx, fc) + defer func() { + if r := recover(); r != nil { + ec.Error(ctx, ec.Recover(ctx, r)) + ret = graphql.Null + } + }() + resTmp, err := ec.ResolverMiddleware(ctx, func(rctx context.Context) (interface{}, error) { + ctx = rctx // use context from middleware stack in children + return ec.resolvers.UserCalendarSubscription().FullSchedule(rctx, obj) + }) + if err != nil { + ec.Error(ctx, err) + return graphql.Null + } + if resTmp == nil { + if !graphql.HasFieldError(ctx, fc) { + ec.Errorf(ctx, "must not be null") + } + return graphql.Null + } + res := resTmp.(bool) + fc.Result = res + return ec.marshalNBoolean2bool(ctx, field.Selections, res) +} + +func (ec *executionContext) fieldContext_UserCalendarSubscription_fullSchedule(ctx context.Context, field graphql.CollectedField) (fc *graphql.FieldContext, err error) { + fc = &graphql.FieldContext{ + Object: "UserCalendarSubscription", + Field: field, + IsMethod: true, + IsResolver: true, + Child: func(ctx context.Context, field graphql.CollectedField) (*graphql.FieldContext, error) { + return nil, errors.New("field of type Boolean does not have child fields") + }, + } + return fc, nil +} + func (ec *executionContext) _UserCalendarSubscription_scheduleID(ctx context.Context, field graphql.CollectedField, obj *calsub.Subscription) (ret graphql.Marshaler) { fc, err := ec.fieldContext_UserCalendarSubscription_scheduleID(ctx, field) if err != nil { @@ -28738,7 +28797,7 @@ func (ec *executionContext) unmarshalInputCreateUserCalendarSubscriptionInput(ct asMap[k] = v } - fieldsInOrder := [...]string{"name", "reminderMinutes", "scheduleID", "disabled"} + fieldsInOrder := [...]string{"name", "reminderMinutes", "scheduleID", "disabled", "fullSchedule"} for _, k := range fieldsInOrder { v, ok := asMap[k] if !ok { @@ -28781,6 +28840,15 @@ func (ec *executionContext) unmarshalInputCreateUserCalendarSubscriptionInput(ct return it, err } it.Disabled = data + case "fullSchedule": + var err error + + ctx := graphql.WithPathContext(ctx, graphql.NewPathWithField("fullSchedule")) + data, err := ec.unmarshalOBoolean2ᚖbool(ctx, v) + if err != nil { + return it, err + } + it.FullSchedule = data } } @@ -31281,7 +31349,7 @@ func (ec *executionContext) unmarshalInputUpdateUserCalendarSubscriptionInput(ct asMap[k] = v } - fieldsInOrder := [...]string{"id", "name", "reminderMinutes", "disabled"} + fieldsInOrder := [...]string{"id", "name", "reminderMinutes", "disabled", "fullSchedule"} for _, k := range fieldsInOrder { v, ok := asMap[k] if !ok { @@ -31324,6 +31392,15 @@ func (ec *executionContext) unmarshalInputUpdateUserCalendarSubscriptionInput(ct return it, err } it.Disabled = data + case "fullSchedule": + var err error + + ctx := graphql.WithPathContext(ctx, graphql.NewPathWithField("fullSchedule")) + data, err := ec.unmarshalOBoolean2ᚖbool(ctx, v) + if err != nil { + return it, err + } + it.FullSchedule = data } } @@ -38284,6 +38361,42 @@ func (ec *executionContext) _UserCalendarSubscription(ctx context.Context, sel a continue } + out.Concurrently(i, func(ctx context.Context) graphql.Marshaler { return innerFunc(ctx, out) }) + case "fullSchedule": + field := field + + innerFunc := func(ctx context.Context, fs *graphql.FieldSet) (res graphql.Marshaler) { + defer func() { + if r := recover(); r != nil { + ec.Error(ctx, ec.Recover(ctx, r)) + } + }() + res = ec._UserCalendarSubscription_fullSchedule(ctx, field, obj) + if res == graphql.Null { + atomic.AddUint32(&fs.Invalids, 1) + } + return res + } + + if field.Deferrable != nil { + dfs, ok := deferred[field.Deferrable.Label] + di := 0 + if ok { + dfs.AddField(field) + di = len(dfs.Values) - 1 + } else { + dfs = graphql.NewFieldSet([]graphql.CollectedField{field}) + deferred[field.Deferrable.Label] = dfs + } + dfs.Concurrently(di, func(ctx context.Context) graphql.Marshaler { + return innerFunc(ctx, dfs) + }) + + // don't run the out.Concurrently() call below + out.Values[i] = graphql.Null + continue + } + out.Concurrently(i, func(ctx context.Context) graphql.Marshaler { return innerFunc(ctx, out) }) case "scheduleID": out.Values[i] = ec._UserCalendarSubscription_scheduleID(ctx, field, obj) diff --git a/graphql2/graphqlapp/calendarsubscription.go b/graphql2/graphqlapp/calendarsubscription.go index b6b3647cca..f05fc12fb6 100644 --- a/graphql2/graphqlapp/calendarsubscription.go +++ b/graphql2/graphqlapp/calendarsubscription.go @@ -21,6 +21,9 @@ func (a *App) UserCalendarSubscription() graphql2.UserCalendarSubscriptionResolv func (a *UserCalendarSubscription) ReminderMinutes(ctx context.Context, obj *calsub.Subscription) ([]int, error) { return obj.Config.ReminderMinutes, nil } +func (a *UserCalendarSubscription) FullSchedule(ctx context.Context, obj *calsub.Subscription) (bool, error) { + return obj.Config.FullSchedule, nil +} func (a *UserCalendarSubscription) Schedule(ctx context.Context, obj *calsub.Subscription) (*schedule.Schedule, error) { return a.ScheduleStore.FindOne(ctx, obj.ScheduleID) @@ -55,6 +58,9 @@ func (m *Mutation) CreateUserCalendarSubscription(ctx context.Context, input gra cs.Disabled = *input.Disabled } cs.Config.ReminderMinutes = input.ReminderMinutes + if input.FullSchedule != nil { + cs.Config.FullSchedule = *input.FullSchedule + } err = withContextTx(ctx, m.DB, func(ctx context.Context, tx *sql.Tx) error { var err error cs, err = m.CalSubStore.CreateTx(ctx, tx, cs) @@ -80,6 +86,9 @@ func (m *Mutation) UpdateUserCalendarSubscription(ctx context.Context, input gra if input.ReminderMinutes != nil { cs.Config.ReminderMinutes = input.ReminderMinutes } + if input.FullSchedule != nil { + cs.Config.FullSchedule = *input.FullSchedule + } return m.CalSubStore.UpdateTx(ctx, tx, cs) }) diff --git a/graphql2/models_gen.go b/graphql2/models_gen.go index 73297e3b89..ae5fc135b1 100644 --- a/graphql2/models_gen.go +++ b/graphql2/models_gen.go @@ -196,6 +196,7 @@ type CreateUserCalendarSubscriptionInput struct { ReminderMinutes []int `json:"reminderMinutes,omitempty"` ScheduleID string `json:"scheduleID"` Disabled *bool `json:"disabled,omitempty"` + FullSchedule *bool `json:"fullSchedule,omitempty"` } type CreateUserContactMethodInput struct { @@ -669,6 +670,7 @@ type UpdateUserCalendarSubscriptionInput struct { Name *string `json:"name,omitempty"` ReminderMinutes []int `json:"reminderMinutes,omitempty"` Disabled *bool `json:"disabled,omitempty"` + FullSchedule *bool `json:"fullSchedule,omitempty"` } type UpdateUserContactMethodInput struct { diff --git a/graphql2/schema.graphql b/graphql2/schema.graphql index 4124fcf970..2be17f6c15 100644 --- a/graphql2/schema.graphql +++ b/graphql2/schema.graphql @@ -684,17 +684,20 @@ input CreateUserCalendarSubscriptionInput { reminderMinutes: [Int!] scheduleID: ID! disabled: Boolean + fullSchedule: Boolean } input UpdateUserCalendarSubscriptionInput { id: ID! name: String reminderMinutes: [Int!] disabled: Boolean + fullSchedule: Boolean } type UserCalendarSubscription { id: ID! name: String! reminderMinutes: [Int!]! + fullSchedule: Boolean! scheduleID: ID! schedule: Schedule lastAccess: ISOTimestamp! diff --git a/web/src/app/schedules/calendar-subscribe/CalendarSubscribeCreateDialog.js b/web/src/app/schedules/calendar-subscribe/CalendarSubscribeCreateDialog.js index e236dde074..0ab6cfb8a9 100644 --- a/web/src/app/schedules/calendar-subscribe/CalendarSubscribeCreateDialog.js +++ b/web/src/app/schedules/calendar-subscribe/CalendarSubscribeCreateDialog.js @@ -54,6 +54,7 @@ export default function CalendarSubscribeCreateDialog(props) { name: '', scheduleID: props.scheduleID || null, reminderMinutes: [], + fullSchedule: false, }) const [createSubscription, status] = useMutation(mutation, { @@ -63,6 +64,7 @@ export default function CalendarSubscribeCreateDialog(props) { name: value.name, reminderMinutes: [0], // default reminder at shift start time disabled: false, + fullSchedule: value.fullSchedule, }, }, }) diff --git a/web/src/app/schedules/calendar-subscribe/CalendarSubscribeEditDialog.js b/web/src/app/schedules/calendar-subscribe/CalendarSubscribeEditDialog.js index 51a10e3957..f635c58869 100644 --- a/web/src/app/schedules/calendar-subscribe/CalendarSubscribeEditDialog.js +++ b/web/src/app/schedules/calendar-subscribe/CalendarSubscribeEditDialog.js @@ -14,6 +14,7 @@ const query = gql` id name scheduleID + fullSchedule } } ` @@ -31,6 +32,7 @@ export function CalendarSubscribeEditDialogContent(props) { const [value, setValue] = useState({ name: _.get(data, 'name', ''), scheduleID: _.get(data, 'scheduleID', null), + fullSchedule: _.get(data, 'fullSchedule', false), }) // setup the mutation @@ -39,6 +41,7 @@ export function CalendarSubscribeEditDialogContent(props) { input: { id: props.data.id, name: value.name, + fullSchedule: value.fullSchedule, }, }, onCompleted: () => props.onClose(), diff --git a/web/src/app/schedules/calendar-subscribe/CalendarSubscribeForm.js b/web/src/app/schedules/calendar-subscribe/CalendarSubscribeForm.js index 8f1de5acfb..163d9af59a 100644 --- a/web/src/app/schedules/calendar-subscribe/CalendarSubscribeForm.js +++ b/web/src/app/schedules/calendar-subscribe/CalendarSubscribeForm.js @@ -1,7 +1,7 @@ import React from 'react' import { PropTypes as p } from 'prop-types' import { FormContainer, FormField } from '../../forms' -import { Grid, TextField } from '@mui/material' +import { Checkbox, FormControlLabel, Grid, TextField } from '@mui/material' import { ScheduleSelect } from '../../selection' export default function CalendarSubscribeForm(props) { @@ -35,6 +35,15 @@ export default function CalendarSubscribeForm(props) { name='scheduleID' /> + + + } + label='Include on-call shifts of other users' + labelPlacement='end' + /> + ) diff --git a/web/src/schema.d.ts b/web/src/schema.d.ts index be8db7a1e2..0043b4020a 100644 --- a/web/src/schema.d.ts +++ b/web/src/schema.d.ts @@ -513,6 +513,7 @@ export interface CreateUserCalendarSubscriptionInput { reminderMinutes?: null | number[] scheduleID: string disabled?: null | boolean + fullSchedule?: null | boolean } export interface UpdateUserCalendarSubscriptionInput { @@ -520,12 +521,14 @@ export interface UpdateUserCalendarSubscriptionInput { name?: null | string reminderMinutes?: null | number[] disabled?: null | boolean + fullSchedule?: null | boolean } export interface UserCalendarSubscription { id: string name: string reminderMinutes: number[] + fullSchedule: boolean scheduleID: string schedule?: null | Schedule lastAccess: ISOTimestamp