Skip to content

Commit

Permalink
Allow starting childs as possible recipients
Browse files Browse the repository at this point in the history
  • Loading branch information
terolaakso committed Jan 27, 2025
1 parent 548fbc1 commit 326eed3
Show file tree
Hide file tree
Showing 13 changed files with 182 additions and 33 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -216,6 +216,7 @@ export default React.memo(function MessageEditor({
receiversAsSelectorNode(
defaultSender.value,
availableReceivers,
i18n.messages.receiverSelection.starters,
draftContent?.recipientIds
)
)
Expand Down Expand Up @@ -312,13 +313,20 @@ export default React.memo(function MessageEditor({

const accountReceivers = receiversAsSelectorNode(
sender.value,
availableReceivers
availableReceivers,
i18n.messages.receiverSelection.starters
)
if (accountReceivers) {
setReceiverTree(accountReceivers)
}
},
[availableReceivers, message.type, selectedReceivers, updateMessage]
[
availableReceivers,
i18n.messages.receiverSelection.starters,
message.type,
selectedReceivers,
updateMessage
]
)
const handleRecipientChange = useCallback(
(recipients: SelectorNode[]) => {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,7 @@ import { UpdatableDraftContent } from 'lib-common/generated/api-types/messaging'
import { client } from '../../api/client'
import { createUrlSearchParams } from 'lib-common/api'
import { deserializeJsonDraftContent } from 'lib-common/generated/api-types/messaging'
import { deserializeJsonMessageReceiversResponse } from 'lib-common/generated/api-types/messaging'
import { deserializeJsonMessageThread } from 'lib-common/generated/api-types/messaging'
import { deserializeJsonPagedMessageCopies } from 'lib-common/generated/api-types/messaging'
import { deserializeJsonPagedMessageThreads } from 'lib-common/generated/api-types/messaging'
Expand Down Expand Up @@ -208,7 +209,7 @@ export async function getReceiversForNewMessage(): Promise<MessageReceiversRespo
url: uri`/employee/messages/receivers`.toString(),
method: 'GET'
})
return json
return json.map(e => deserializeJsonMessageReceiversResponse(e))
}


Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,7 @@ import { UpdatableDraftContent } from 'lib-common/generated/api-types/messaging'
import { client } from '../../client'
import { createUrlSearchParams } from 'lib-common/api'
import { deserializeJsonDraftContent } from 'lib-common/generated/api-types/messaging'
import { deserializeJsonMessageReceiversResponse } from 'lib-common/generated/api-types/messaging'
import { deserializeJsonMessageThread } from 'lib-common/generated/api-types/messaging'
import { deserializeJsonPagedMessageThreads } from 'lib-common/generated/api-types/messaging'
import { deserializeJsonPagedSentMessages } from 'lib-common/generated/api-types/messaging'
Expand Down Expand Up @@ -149,7 +150,7 @@ export async function getReceiversForNewMessage(): Promise<MessageReceiversRespo
url: uri`/employee-mobile/messages/receivers`.toString(),
method: 'GET'
})
return json
return json.map(e => deserializeJsonMessageReceiversResponse(e))
}


Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -149,14 +149,19 @@ export default React.memo(function MessageEditor({
recipients: receiversAsSelectorNode(
accountId,
availableRecipients,
i18n.messages.messageEditor.starters,
draft.recipientIds
),
urgent: draft.urgent,
title: draft.title,
content: draft.content
}
: {
recipients: receiversAsSelectorNode(accountId, availableRecipients),
recipients: receiversAsSelectorNode(
accountId,
availableRecipients,
i18n.messages.messageEditor.starters
),
urgent: false,
title: '',
content: ''
Expand Down
66 changes: 62 additions & 4 deletions frontend/src/lib-common/api-types/messaging.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,12 +2,15 @@
//
// SPDX-License-Identifier: LGPL-2.1-or-later

import { JsonOf } from 'lib-common/json'
import LocalDate from 'lib-common/local-date'
import { UUID } from 'lib-common/types'

import { MessageRecipientType } from '../generated/api-types/messaging'

export type MessageReceiver =
| MessageReceiverBase // unit in area, or child, or citizen
| MessageReceiverBase // unit in area, or citizen
| MessageReceiverChild
| MessageReceiverArea
| MessageReceiverUnit
| MessageReceiverGroup
Expand All @@ -18,6 +21,10 @@ interface MessageReceiverBase {
type: MessageRecipientType
}

interface MessageReceiverChild extends MessageReceiverBase {
startDate: LocalDate | null
}

interface MessageReceiverArea extends MessageReceiverBase {
receivers: MessageReceiverUnitInArea[]
}
Expand All @@ -26,10 +33,12 @@ type MessageReceiverUnitInArea = MessageReceiverBase

interface MessageReceiverUnit extends MessageReceiverBase {
receivers: MessageReceiverGroup[]
hasStarters: boolean
}

interface MessageReceiverGroup extends MessageReceiverBase {
receivers: MessageReceiverBase[]
hasStarters: boolean
}

export const sortReceivers = (
Expand All @@ -47,6 +56,55 @@ export const sortReceivers = (
}
: receiver
)
.sort((a, b) =>
a.name.toLocaleLowerCase().localeCompare(b.name.toLocaleLowerCase())
)
.sort((a, b) => {
const aStarter = messageReceiverIsStarter(a) ? 1 : 0
const bStarter = messageReceiverIsStarter(b) ? 1 : 0

if (aStarter !== bStarter) {
return aStarter - bStarter
}

const aDate = messageReceiverStartDate(a)
const bDate = messageReceiverStartDate(b)

if (aDate && bDate) {
if (aDate.isBefore(bDate)) return -1
if (aDate.isAfter(bDate)) return 1
} else if (aDate && !bDate) {
return -1
} else if (!aDate && bDate) {
return 1
}
return a.name
.toLocaleLowerCase()
.localeCompare(b.name.toLocaleLowerCase())
})

export function messageReceiverStartDate(
receiver: MessageReceiver
): LocalDate | null {
return 'startDate' in receiver ? receiver.startDate : null
}

export function messageReceiverIsStarter(receiver: MessageReceiver): boolean {
return (
('hasStarters' in receiver && receiver.hasStarters) ||
messageReceiverStartDate(receiver) !== null
)
}

export function deserializeMessageReceiver(
json: JsonOf<MessageReceiver>
): MessageReceiver {
const result: MessageReceiver = {
...json,
...('startDate' in json && {
startDate:
json.startDate !== null ? LocalDate.parseIso(json.startDate) : null
}),
...('receivers' in json && {
receivers: json.receivers.map(deserializeMessageReceiver)
})
}
return result
}
9 changes: 9 additions & 0 deletions frontend/src/lib-common/generated/api-types/messaging.ts
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@ import { MessageId } from './shared'
import { MessageReceiver } from '../../api-types/messaging'
import { MessageThreadId } from './shared'
import { PersonId } from './shared'
import { deserializeMessageReceiver } from '../../api-types/messaging'

/**
* Generated from fi.espoo.evaka.messaging.AccountType
Expand Down Expand Up @@ -471,6 +472,14 @@ export function deserializeJsonMessageCopy(json: JsonOf<MessageCopy>): MessageCo
}


export function deserializeJsonMessageReceiversResponse(json: JsonOf<MessageReceiversResponse>): MessageReceiversResponse {
return {
...json,
receivers: json.receivers.map(e => deserializeMessageReceiver(e))
}
}


export function deserializeJsonMessageThread(json: JsonOf<MessageThread>): MessageThread {
return {
...json,
Expand Down
45 changes: 31 additions & 14 deletions frontend/src/lib-components/messages/SelectorNode.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,11 @@
//
// SPDX-License-Identifier: LGPL-2.1-or-later

import { MessageReceiver } from 'lib-common/api-types/messaging'
import {
MessageReceiver,
messageReceiverIsStarter,
messageReceiverStartDate
} from 'lib-common/api-types/messaging'
import {
MessageReceiversResponse,
MessageRecipient
Expand All @@ -27,6 +31,7 @@ export interface SelectorNode extends TreeNode {
export const receiversAsSelectorNode = (
accountId: UUID,
receivers: MessageReceiversResponse[],
starterTranslation: string,
checkedIds: UUID[] = []
): SelectorNode[] => {
const accountReceivers = receivers.find(
Expand All @@ -36,9 +41,9 @@ export const receiversAsSelectorNode = (
if (!accountReceivers) {
return []
}

const selectorNodes = accountReceivers.map(receiverAsSelectorNode)

const selectorNodes = accountReceivers.map((r) =>
receiverAsSelectorNode(r, starterTranslation)
)
if (selectorNodes.length === 1 && selectorNodes[0].children.length === 0) {
return selectorNodes.map((node) => ({ ...node, checked: true }))
}
Expand Down Expand Up @@ -67,16 +72,28 @@ function checkAll(node: SelectorNode): SelectorNode {
return { ...node, checked: true, children: node.children.map(checkAll) }
}

const receiverAsSelectorNode = (receiver: MessageReceiver): SelectorNode => ({
key: receiver.id,
checked: false,
text: receiver.name,
messageRecipient: { type: receiver.type, id: receiver.id },
children:
'receivers' in receiver
? receiver.receivers.map(receiverAsSelectorNode)
: []
})
function receiverAsSelectorNode(
receiver: MessageReceiver,
starterTranslation: string
): SelectorNode {
const startDate = messageReceiverStartDate(receiver)
const isStarter = messageReceiverIsStarter(receiver)
const nameWithStarterIndication = isStarter
? `${receiver.name} (${startDate?.format() ?? starterTranslation})`
: receiver.name
return {
key: `${receiver.id}-${isStarter}`,
checked: false,
text: nameWithStarterIndication,
messageRecipient: { type: receiver.type, id: receiver.id },
children:
'receivers' in receiver
? receiver.receivers.map((r) =>
receiverAsSelectorNode(r, starterTranslation)
)
: []
}
}

export type SelectedNode = {
key: UUID
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -444,6 +444,7 @@ export const fi = {
sender: 'Lähettäjä',
receivers: 'Vastaanottajat',
recipientsPlaceholder: 'Valitse...',
starters: 'aloittavat',
subject: {
heading: 'Otsikko',
placeholder: 'Kirjoita...'
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4587,7 +4587,8 @@ export const fi = {
childName: 'Nimi',
childDob: 'Syntymäaika',
receivers: 'Vastaanottajat',
confirmText: 'Lähetä viesti valituille'
confirmText: 'Lähetä viesti valituille',
starters: 'aloittavat'
},
noTitle: 'Ei otsikkoa',
notSent: 'Ei lähetetty',
Expand Down
6 changes: 5 additions & 1 deletion service/codegen/src/main/kotlin/evaka/codegen/api/Config.kt
Original file line number Diff line number Diff line change
Expand Up @@ -61,6 +61,8 @@ object Imports {
val vasuQuestion = TsImport.Named(LibCommon / "api-types/vasu.ts", "VasuQuestion")
val mapVasuQuestion = TsImport.Named(LibCommon / "api-types/vasu.ts", "mapVasuQuestion")
val messageReceiver = TsImport.Named(LibCommon / "api-types/messaging.ts", "MessageReceiver")
val deserializeMessageReceiver =
TsImport.Named(LibCommon / "api-types/messaging.ts", "deserializeMessageReceiver")
val uuid = TsImport.Named(LibCommon / "types.d.ts", "UUID")
val action = TsImport.Named(LibCommon / "generated/action.ts", "Action")
val jsonOf = TsImport.Named(LibCommon / "json.d.ts", "JsonOf")
Expand Down Expand Up @@ -184,7 +186,9 @@ val defaultMetadata =
TsExternalTypeRef(
"MessageReceiver",
keyRepresentation = null,
deserializeJson = null,
deserializeJson = { json ->
TsCode { "${ref(Imports.deserializeMessageReceiver)}(${inline(json)})" }
},
serializePathVariable = null,
serializeRequestParam = null,
Imports.messageReceiver,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -128,17 +128,20 @@ class MessageReceiversIntegrationTest : FullApplicationTest(resetDbBeforeEach =
MessageReceiver.Unit(
id = daycare1.id,
name = daycare1.name,
hasStarters = false,
receivers =
listOf(
MessageReceiver.Group(
id = group1.id,
name = group1.name,
hasStarters = false,
receivers =
listOf(
MessageReceiver.Child(
id = child1.id,
name =
"${child1.lastName} ${child2.firstName}",
startDate = null,
)
),
)
Expand All @@ -153,11 +156,13 @@ class MessageReceiversIntegrationTest : FullApplicationTest(resetDbBeforeEach =
MessageReceiver.Group(
id = group1.id,
name = group1.name,
hasStarters = false,
receivers =
listOf(
MessageReceiver.Child(
id = child1.id,
name = "${child1.lastName} ${child1.firstName}",
startDate = null,
)
),
)
Expand All @@ -182,17 +187,20 @@ class MessageReceiversIntegrationTest : FullApplicationTest(resetDbBeforeEach =
MessageReceiver.Unit(
id = daycare2.id,
name = daycare2.name,
hasStarters = false,
receivers =
listOf(
MessageReceiver.Group(
id = group2.id,
name = group2.name,
hasStarters = false,
receivers =
listOf(
MessageReceiver.Child(
id = child2.id,
name =
"${child2.lastName} ${child2.firstName}",
startDate = null,
)
),
)
Expand Down
Loading

0 comments on commit 326eed3

Please sign in to comment.