Skip to content

Commit

Permalink
Merge pull request #6336 from espoon-voltti/acl-end-dates
Browse files Browse the repository at this point in the history
Luvituksen ennalta asetettu loppupäivä
  • Loading branch information
Joosakur authored Feb 6, 2025
2 parents 5ca4976 + 3d3b858 commit 4e73968
Show file tree
Hide file tree
Showing 22 changed files with 440 additions and 161 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@
import React from 'react'

import { scopedRoles } from 'lib-common/api-types/employee-auth'
import { boolean, string } from 'lib-common/form/fields'
import { boolean, localDate, string } from 'lib-common/form/fields'
import {
array,
object,
Expand All @@ -25,12 +25,14 @@ import {
EmployeeId,
UserRole
} from 'lib-common/generated/api-types/shared'
import LocalDate from 'lib-common/local-date'
import { SelectF } from 'lib-components/atoms/dropdowns/Select'
import TreeDropdown, {
sortTreeByText,
TreeNode
} from 'lib-components/atoms/dropdowns/TreeDropdown'
import { FixedSpaceColumn } from 'lib-components/layout/flex-helpers'
import { DatePickerF } from 'lib-components/molecules/date-picker/DatePicker'
import { MutateFormModal } from 'lib-components/molecules/modals/FormModal'
import { Label } from 'lib-components/typography'

Expand All @@ -54,7 +56,8 @@ const treeNode = (): Form<DaycareTreeNode, never, DaycareTreeNode, unknown> =>
const form = transformed(
object({
daycareTree: array(treeNode()),
role: required(oneOf<UserRole>())
role: required(oneOf<UserRole>()),
endDate: localDate()
}),
(res) => {
const daycareIds = res.daycareTree
Expand All @@ -66,7 +69,8 @@ const form = transformed(

return ValidationSuccess.of<UpsertEmployeeDaycareRolesRequest>({
daycareIds,
role: res.role
role: res.role,
endDate: res.endDate ?? null
})
}
)
Expand All @@ -80,7 +84,8 @@ export default React.memo(function DaycareRolesModal({
units: Daycare[]
onClose: () => void
}) {
const { i18n } = useTranslation()
const { i18n, lang } = useTranslation()

const boundForm = useForm(
form,
() => ({
Expand Down Expand Up @@ -113,12 +118,15 @@ export default React.memo(function DaycareRolesModal({
domValue: r,
label: i18n.roles.adRoles[r]
}))
}
},
endDate: localDate.fromDate(null, {
minDate: LocalDate.todayInHelsinkiTz()
})
}),
i18n.validationErrors
)

const { daycareTree, role } = useFormFields(boundForm)
const { daycareTree, role, endDate } = useFormFields(boundForm)

return (
<MutateFormModal
Expand All @@ -144,6 +152,10 @@ export default React.memo(function DaycareRolesModal({
<Label>{i18n.employees.editor.unitRoles.role}</Label>
<SelectF bind={role} />
</FixedSpaceColumn>
<FixedSpaceColumn spacing="xs">
<Label>{i18n.employees.editor.unitRoles.endDate}</Label>
<DatePickerF bind={endDate} locale={lang} />
</FixedSpaceColumn>
</FixedSpaceColumn>
</MutateFormModal>
)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -180,16 +180,18 @@ const EmployeePage = React.memo(function EmployeePage({
<Tr>
<Th>{i18n.employees.editor.unitRoles.unit}</Th>
<Th>{i18n.employees.editor.unitRoles.role}</Th>
<Th>{i18n.employees.editor.unitRoles.endDate}</Th>
<Th />
</Tr>
</Thead>
<Tbody>
{sortedRoles.map(({ daycareId, daycareName, role }) => (
{sortedRoles.map(({ daycareId, daycareName, role, endDate }) => (
<Tr key={`${daycareId}/${role}`}>
<Td>
<Link to={`/units/${daycareId}`}>{daycareName}</Link>
</Td>
<Td>{i18n.roles.adRoles[role]}</Td>
<Td>{endDate?.format() ?? '-'}</Td>
<Td>
<ConfirmedMutation
buttonStyle="ICON"
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -175,6 +175,7 @@ function AclRow({
<GroupListing unitGroups={unitGroups} groupIds={row.groupIds} />
</Td>
)}
<Td>{row.endDate?.format()}</Td>
<Td>
<FixedSpaceRow justifyContent="flex-end">
{isEditable && (
Expand Down Expand Up @@ -288,6 +289,7 @@ function AclTable({
<Th>{i18n.unit.accessControl.role}</Th>
<Th>{i18n.common.form.name}</Th>
{unitGroups && <GroupsTh>{i18n.unit.accessControl.groups}</GroupsTh>}
<Th>{i18n.unit.accessControl.aclEndDate}</Th>
<ActionsTh />
</Tr>
</Thead>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -9,11 +9,13 @@ import { Action } from 'lib-common/generated/action'
import { DaycareGroupResponse } from 'lib-common/generated/api-types/daycare'
import { Employee } from 'lib-common/generated/api-types/pis'
import { DaycareId, EmployeeId } from 'lib-common/generated/api-types/shared'
import LocalDate from 'lib-common/local-date'
import { cancelMutation } from 'lib-components/atoms/buttons/MutateButton'
import Combobox from 'lib-components/atoms/dropdowns/Combobox'
import Checkbox from 'lib-components/atoms/form/Checkbox'
import MultiSelect from 'lib-components/atoms/form/MultiSelect'
import { FixedSpaceColumn } from 'lib-components/layout/flex-helpers'
import DatePicker from 'lib-components/molecules/date-picker/DatePicker'
import { MutateFormModal } from 'lib-components/molecules/modals/FormModal'
import { Label } from 'lib-components/typography'

Expand Down Expand Up @@ -41,6 +43,7 @@ type FormState = {
selectedEmployee: EmployeeOption | null
selectedGroups: DaycareGroupResponse[] | null
hasStaffOccupancyEffect: boolean | null
endDate: LocalDate | null
}

export default React.memo(function AddAclModal({
Expand All @@ -50,7 +53,7 @@ export default React.memo(function AddAclModal({
employees,
permittedActions
}: Props) {
const { i18n } = useTranslation()
const { i18n, lang } = useTranslation()

const [formData, setFormData] = useState<FormState>({
role: 'STAFF',
Expand All @@ -60,7 +63,8 @@ export default React.memo(function AddAclModal({
'UPSERT_STAFF_OCCUPANCY_COEFFICIENTS'
)
? false
: null
: null,
endDate: null
})

const roles: DaycareAclRole[] = useMemo(
Expand Down Expand Up @@ -118,7 +122,8 @@ export default React.memo(function AddAclModal({
'UPSERT_STAFF_OCCUPANCY_COEFFICIENTS'
)
? formData.hasStaffOccupancyEffect
: null
: null,
endDate: formData.endDate
}
}
}
Expand Down Expand Up @@ -185,6 +190,7 @@ export default React.memo(function AddAclModal({
/>
</FixedSpaceColumn>
)}

{permittedActions.includes('UPSERT_STAFF_OCCUPANCY_COEFFICIENTS') && (
<Checkbox
data-qa="coefficient-checkbox"
Expand All @@ -199,6 +205,19 @@ export default React.memo(function AddAclModal({
}}
/>
)}

<FixedSpaceColumn spacing="xs">
<Label>{`${i18n.unit.accessControl.aclEndDate}`}</Label>
<DatePicker
data-qa="end-date"
date={formData.endDate}
onChange={(date) =>
setFormData((prev) => ({ ...prev, endDate: date }))
}
locale={lang}
minDate={LocalDate.todayInHelsinkiTz()}
/>
</FixedSpaceColumn>
</FixedSpaceColumn>
</MutateFormModal>
)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,9 +7,11 @@ import React, { useState } from 'react'
import { Action } from 'lib-common/generated/action'
import { DaycareGroupResponse } from 'lib-common/generated/api-types/daycare'
import { DaycareAclRow, DaycareId } from 'lib-common/generated/api-types/shared'
import LocalDate from 'lib-common/local-date'
import Checkbox from 'lib-components/atoms/form/Checkbox'
import MultiSelect from 'lib-components/atoms/form/MultiSelect'
import { FixedSpaceColumn } from 'lib-components/layout/flex-helpers'
import DatePicker from 'lib-components/molecules/date-picker/DatePicker'
import { MutateFormModal } from 'lib-components/molecules/modals/FormModal'
import { Label } from 'lib-components/typography'

Expand All @@ -30,6 +32,7 @@ interface Props {
type FormState = {
selectedGroups: DaycareGroupResponse[] | null
hasStaffOccupancyEffect: boolean | null
endDate: LocalDate | null
}

export default React.memo(function EditAclModal({
Expand All @@ -39,7 +42,7 @@ export default React.memo(function EditAclModal({
groups,
row
}: Props) {
const { i18n } = useTranslation()
const { i18n, lang } = useTranslation()
const { employee, role } = row
const groupOptions = useGroupOptions(groups)

Expand All @@ -51,7 +54,8 @@ export default React.memo(function EditAclModal({
'UPSERT_STAFF_OCCUPANCY_COEFFICIENTS'
)
? employee.hasStaffOccupancyEffect
: null
: null,
endDate: row.endDate
})

return (
Expand All @@ -63,7 +67,8 @@ export default React.memo(function EditAclModal({
employeeId: row.employee.id,
body: {
groupIds: formData.selectedGroups?.map(({ id }) => id) ?? null,
hasStaffOccupancyEffect: formData.hasStaffOccupancyEffect
hasStaffOccupancyEffect: formData.hasStaffOccupancyEffect,
endDate: formData.endDate
}
})}
resolveLabel={i18n.common.save}
Expand Down Expand Up @@ -99,6 +104,7 @@ export default React.memo(function EditAclModal({
/>
</FixedSpaceColumn>
)}

{permittedActions.includes('READ_STAFF_OCCUPANCY_COEFFICIENTS') && (
<Checkbox
data-qa="coefficient-checkbox"
Expand All @@ -115,6 +121,19 @@ export default React.memo(function EditAclModal({
}}
/>
)}

<FixedSpaceColumn spacing="xs">
<Label>{`${i18n.unit.accessControl.aclEndDate}`}</Label>
<DatePicker
data-qa="end-date"
date={formData.endDate}
onChange={(date) =>
setFormData((prev) => ({ ...prev, endDate: date }))
}
locale={lang}
minDate={LocalDate.todayInHelsinkiTz()}
/>
</FixedSpaceColumn>
</FixedSpaceColumn>
</MutateFormModal>
)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -54,6 +54,7 @@ import { deserializeJsonCaretakersResponse } from 'lib-common/generated/api-type
import { deserializeJsonChildResponse } from 'lib-common/generated/api-types/daycare'
import { deserializeJsonClubTerm } from 'lib-common/generated/api-types/daycare'
import { deserializeJsonDaycare } from 'lib-common/generated/api-types/daycare'
import { deserializeJsonDaycareAclRow } from 'lib-common/generated/api-types/shared'
import { deserializeJsonDaycareGroup } from 'lib-common/generated/api-types/daycare'
import { deserializeJsonDaycareResponse } from 'lib-common/generated/api-types/daycare'
import { deserializeJsonEmployee } from 'lib-common/generated/api-types/pis'
Expand Down Expand Up @@ -853,7 +854,7 @@ export async function getDaycareAcl(
url: uri`/employee/daycares/${request.unitId}/acl`.toString(),
method: 'GET'
})
return json
return json.map(e => deserializeJsonDaycareAclRow(e))
}


Expand Down
17 changes: 17 additions & 0 deletions frontend/src/lib-common/generated/api-types/daycare.ts
Original file line number Diff line number Diff line change
Expand Up @@ -46,6 +46,7 @@ import { deserializeJsonUnitBackupCare } from './backupcare'
* Generated from fi.espoo.evaka.daycare.controllers.UnitAclController.AclUpdate
*/
export interface AclUpdate {
endDate: LocalDate | null
groupIds: GroupId[] | null
hasStaffOccupancyEffect: boolean | null
}
Expand Down Expand Up @@ -570,6 +571,14 @@ export interface VisitingAddress {
}


export function deserializeJsonAclUpdate(json: JsonOf<AclUpdate>): AclUpdate {
return {
...json,
endDate: (json.endDate != null) ? LocalDate.parseIso(json.endDate) : null
}
}


export function deserializeJsonCaretakerAmount(json: JsonOf<CaretakerAmount>): CaretakerAmount {
return {
...json,
Expand Down Expand Up @@ -705,6 +714,14 @@ export function deserializeJsonDaycareResponse(json: JsonOf<DaycareResponse>): D
}


export function deserializeJsonFullAclInfo(json: JsonOf<FullAclInfo>): FullAclInfo {
return {
...json,
update: deserializeJsonAclUpdate(json.update)
}
}


export function deserializeJsonGroupOccupancies(json: JsonOf<GroupOccupancies>): GroupOccupancies {
return {
...json,
Expand Down
19 changes: 19 additions & 0 deletions frontend/src/lib-common/generated/api-types/pis.ts
Original file line number Diff line number Diff line change
Expand Up @@ -135,6 +135,7 @@ export interface DaycareGroupRole {
export interface DaycareRole {
daycareId: DaycareId
daycareName: string
endDate: LocalDate | null
role: UserRole
}

Expand Down Expand Up @@ -734,6 +735,7 @@ export interface UpdateWeakLoginCredentialsRequest {
*/
export interface UpsertEmployeeDaycareRolesRequest {
daycareIds: DaycareId[]
endDate: LocalDate | null
role: UserRole
}

Expand Down Expand Up @@ -764,6 +766,14 @@ export function deserializeJsonCreationModificationMetadata(json: JsonOf<Creatio
}


export function deserializeJsonDaycareRole(json: JsonOf<DaycareRole>): DaycareRole {
return {
...json,
endDate: (json.endDate != null) ? LocalDate.parseIso(json.endDate) : null
}
}


export function deserializeJsonEmailVerification(json: JsonOf<EmailVerification>): EmailVerification {
return {
...json,
Expand Down Expand Up @@ -794,6 +804,7 @@ export function deserializeJsonEmployeeWithDaycareRoles(json: JsonOf<EmployeeWit
return {
...json,
created: HelsinkiDateTime.parseIso(json.created),
daycareRoles: json.daycareRoles.map(e => deserializeJsonDaycareRole(e)),
lastLogin: (json.lastLogin != null) ? HelsinkiDateTime.parseIso(json.lastLogin) : null,
updated: (json.updated != null) ? HelsinkiDateTime.parseIso(json.updated) : null
}
Expand Down Expand Up @@ -984,3 +995,11 @@ export function deserializeJsonRestrictedDetails(json: JsonOf<RestrictedDetails>
endDate: (json.endDate != null) ? LocalDate.parseIso(json.endDate) : null
}
}


export function deserializeJsonUpsertEmployeeDaycareRolesRequest(json: JsonOf<UpsertEmployeeDaycareRolesRequest>): UpsertEmployeeDaycareRolesRequest {
return {
...json,
endDate: (json.endDate != null) ? LocalDate.parseIso(json.endDate) : null
}
}
Loading

0 comments on commit 4e73968

Please sign in to comment.