Skip to content

Commit

Permalink
Add staff attendance modified metadata
Browse files Browse the repository at this point in the history
  • Loading branch information
patari committed Dec 31, 2024
1 parent 707f4cb commit 93a0f28
Show file tree
Hide file tree
Showing 27 changed files with 1,131 additions and 253 deletions.
2 changes: 2 additions & 0 deletions frontend/src/e2e-test/dev-api/fixtures.ts
Original file line number Diff line number Diff line change
Expand Up @@ -998,6 +998,8 @@ export class Fixture {
occupancyCoefficient: 7,
type: 'PRESENT',
departedAutomatically: false,
modifiedAt: HelsinkiDateTime.now(),
modifiedBy: systemInternalUser.id,
...initial
})
}
Expand Down
5 changes: 4 additions & 1 deletion frontend/src/e2e-test/generated/api-types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -939,6 +939,8 @@ export interface DevStaffAttendance {
employeeId: EmployeeId
groupId: GroupId | null
id: StaffAttendanceRealtimeId
modifiedAt: HelsinkiDateTime | null
modifiedBy: EvakaUserId | null
occupancyCoefficient: number
type: StaffAttendanceType
}
Expand Down Expand Up @@ -1573,7 +1575,8 @@ export function deserializeJsonDevStaffAttendance(json: JsonOf<DevStaffAttendanc
return {
...json,
arrived: HelsinkiDateTime.parseIso(json.arrived),
departed: (json.departed != null) ? HelsinkiDateTime.parseIso(json.departed) : null
departed: (json.departed != null) ? HelsinkiDateTime.parseIso(json.departed) : null,
modifiedAt: (json.modifiedAt != null) ? HelsinkiDateTime.parseIso(json.modifiedAt) : null
}
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -282,16 +282,31 @@ export class UnitStaffAttendancesTable extends Element {
)
}

async assertTooltip(
async assertArrivalTimeTooltip(
row: number,
date: LocalDate,
expectedTooltipText: string
): Promise<void> {
const cell = this.#attendanceCell(date, row)
await cell.hover()
const arrivalTime = cell.findByDataQa('arrival-time')
await arrivalTime.hover()

await arrivalTime
.findByDataQa('arrival-time-tooltip')
.assertTextEquals(expectedTooltipText)
}

async assertDepartureTimeTooltip(
row: number,
date: LocalDate,
expectedTooltipText: string
): Promise<void> {
const cell = this.#attendanceCell(date, row)
const departureTime = cell.findByDataQa('departure-time')
await departureTime.hover()

await cell
.findByDataQa('attendance-tooltip')
await departureTime
.findByDataQa('departure-time-tooltip')
.assertTextEquals(expectedTooltipText)
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -302,7 +302,9 @@ describe('Realtime staff attendances', () => {
type: 'OVERTIME',
arrived: yesterday.toHelsinkiDateTime(LocalTime.of(7, 0)),
departed: yesterday.toHelsinkiDateTime(LocalTime.of(20, 0)),
departedAutomatically: true
departedAutomatically: true,
modifiedAt: null,
modifiedBy: null
}).save()

await Fixture.realtimeStaffAttendance({
Expand All @@ -311,7 +313,9 @@ describe('Realtime staff attendances', () => {
type: 'OVERTIME',
arrived: yesterday.toHelsinkiDateTime(LocalTime.of(8, 0)),
departed: yesterday.toHelsinkiDateTime(LocalTime.of(16, 0)),
departedAutomatically: false
departedAutomatically: false,
modifiedAt: null,
modifiedBy: null
}).save()

await Fixture.realtimeStaffAttendance({
Expand All @@ -320,7 +324,9 @@ describe('Realtime staff attendances', () => {
type: 'OVERTIME',
arrived: yesterday.subDays(1).toHelsinkiDateTime(LocalTime.of(9, 0)),
departed: yesterday.subDays(1).toHelsinkiDateTime(LocalTime.of(15, 0)),
departedAutomatically: false
departedAutomatically: false,
modifiedAt: null,
modifiedBy: null
}).save()

await Fixture.staffAttendancePlan({
Expand Down Expand Up @@ -348,7 +354,11 @@ describe('Realtime staff attendances', () => {
attendances: [['07:00', '20:00*']]
})

await staffAttendances.assertTooltip(0, yesterday, 'Automaattikatkaistu')
await staffAttendances.assertDepartureTimeTooltip(
0,
yesterday,
'Automaattikatkaistu'
)

const modal = await staffAttendances.openDetails(0, yesterday)
await modal.setDepartureTime(0, '15:00')
Expand All @@ -359,6 +369,11 @@ describe('Realtime staff attendances', () => {
name: staffName(groupStaff),
attendances: [['07:00', '15:00']]
})
await staffAttendances.assertDepartureTimeTooltip(
0,
yesterday,
'Muokattu 30.03.2022 18:00, Esimies Essi'
)
})
})
describe('Details modal', () => {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -29,13 +29,15 @@ import {
DevEmployee,
StaffAttendancePlanId
} from '../../generated/api-types'
import { UnitPage } from '../../pages/employee/units/unit'
import MobileNav from '../../pages/mobile/mobile-nav'
import {
StaffAttendanceEditPage,
StaffAttendancePage
} from '../../pages/mobile/staff-page'
import { pairMobileDevice } from '../../utils/mobile'
import { Page } from '../../utils/page'
import { employeeLogin } from '../../utils/user'

let page: Page
let nav: MobileNav
Expand Down Expand Up @@ -125,7 +127,8 @@ const initPages = async (

describe('Realtime staff attendance page', () => {
test('Staff member can be marked as arrived and departed', async () => {
await initPages(HelsinkiDateTime.of(2022, 5, 5, 6, 0))
const date = LocalDate.of(2022, 5, 5)
await initPages(HelsinkiDateTime.fromLocal(date, LocalTime.of(6, 0)))
const arrivalTime = '05:59'
const departureTime = '12:45'

Expand All @@ -145,7 +148,7 @@ describe('Realtime staff attendance page', () => {
`Paikalla ${arrivalTime}–`
])

await initPages(HelsinkiDateTime.of(2022, 5, 5, 13, 30))
await initPages(HelsinkiDateTime.fromLocal(date, LocalTime.of(13, 30)))
await staffAttendancePage.assertPresentStaffCount(1)

await staffAttendancePage.selectTab('present')
Expand All @@ -158,6 +161,33 @@ describe('Realtime staff attendance page', () => {
])
await staffAttendancePage.goBackFromMemberPage()
await staffAttendancePage.assertPresentStaffCount(0)

const desktopPage = await Page.open({
mockedTime: HelsinkiDateTime.fromLocal(date, LocalTime.of(13, 30))
})
await employeeLogin(desktopPage, staffFixture)
const unitPage = new UnitPage(desktopPage)
await unitPage.navigateToUnit(testDaycare2.id)
const calendarPage = await unitPage.openCalendarPage()
await calendarPage.selectGroup(testDaycareGroup.id)
const staffAttendances = calendarPage.staffAttendances

await staffAttendances.assertTableRow({
rowIx: 0,
nth: date.getIsoDayOfWeek() - 1,
name: `${staffFixture.lastName} ${staffFixture.firstName}`,
attendances: [[arrivalTime, departureTime]]
})
await staffAttendances.assertArrivalTimeTooltip(
0,
date,
'Merkintä luotu 05.05.2022 06:00, testMobileDevice'
)
await staffAttendances.assertDepartureTimeTooltip(
0,
date,
'Merkintä luotu 05.05.2022 13:30, testMobileDevice'
)
})

test('Occupancy effect can not be unchecked on arrival if it has been given permanently but can be edited', async () => {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@

import classNames from 'classnames'
import groupBy from 'lodash/groupBy'
import isEqual from 'lodash/isEqual'
import mapValues from 'lodash/mapValues'
import sortBy from 'lodash/sortBy'
import uniq from 'lodash/uniq'
Expand Down Expand Up @@ -32,6 +33,7 @@ import {
StaffAttendanceExternalId,
StaffAttendanceRealtimeId
} from 'lib-common/generated/api-types/shared'
import { EvakaUser } from 'lib-common/generated/api-types/user'
import HelsinkiDateTime from 'lib-common/helsinki-date-time'
import LocalDate from 'lib-common/local-date'
import LocalTime from 'lib-common/local-time'
Expand All @@ -43,7 +45,7 @@ import AddButton from 'lib-components/atoms/buttons/AddButton'
import { IconOnlyButton } from 'lib-components/atoms/buttons/IconOnlyButton'
import { Table, Tbody } from 'lib-components/layout/Table'
import { FixedSpaceRow } from 'lib-components/layout/flex-helpers'
import { fontWeights } from 'lib-components/typography'
import { fontWeights, Italic, P } from 'lib-components/typography'
import { BaseProps } from 'lib-components/utils'
import { defaultMargins } from 'lib-components/white-space'
import { colors } from 'lib-customizations/common'
Expand Down Expand Up @@ -202,7 +204,17 @@ export default React.memo(function StaffAttendanceTable({
}
name={row.name}
operationalDays={operationalDays}
attendances={row.attendances}
attendances={row.attendances.map((attendance) => ({
...attendance,
arrivedAddedAt: null,
arrivedAddedBy: null,
arrivedModifiedAt: null,
arrivedModifiedBy: null,
departedAddedAt: null,
departedAddedBy: null,
departedModifiedAt: null,
departedModifiedBy: null
}))}
groupFilter={groupFilter}
openDetails={(date: LocalDate) => {
setDetailsModalConfig({
Expand Down Expand Up @@ -497,6 +509,14 @@ interface AttendanceRowAttendance {
groupId: GroupId | null
departedAutomatically: boolean
type: StaffAttendanceType
arrivedAddedAt: HelsinkiDateTime | null
arrivedAddedBy: EvakaUser | null
arrivedModifiedAt: HelsinkiDateTime | null
arrivedModifiedBy: EvakaUser | null
departedAddedAt: HelsinkiDateTime | null
departedAddedBy: EvakaUser | null
departedModifiedAt: HelsinkiDateTime | null
departedModifiedBy: EvakaUser | null
}

interface AttendanceRowProps extends BaseProps {
Expand Down Expand Up @@ -527,12 +547,63 @@ const AttendanceRow = React.memo(function AttendanceRow({
const { i18n } = useTranslation()
const today = LocalDate.todayInHelsinkiTz()

const attendanceTooltipText = (attendance: AttendanceRowAttendance) =>
attendance.departedAutomatically ? (
const timeTooltipText = ({
addedAt,
addedBy,
modifiedAt,
modifiedBy
}: {
addedAt: HelsinkiDateTime | null
addedBy: EvakaUser | null
modifiedAt: HelsinkiDateTime | null
modifiedBy: EvakaUser | null
}) => {
if (
addedAt === null &&
addedBy === null &&
modifiedAt === null &&
modifiedBy === null
) {
return undefined
}
return (
<>
{addedAt !== null && addedBy !== null && (
<P>
{i18n.unit.staffAttendance.addedAt} {addedAt.format()},{' '}
<Italic>{addedBy.name}</Italic>
</P>
)}
{modifiedAt !== null &&
modifiedBy !== null &&
(addedAt === null ||
!addedAt.isEqual(modifiedAt) ||
!isEqual(addedBy, modifiedBy)) && (
<P>
{i18n.unit.staffAttendance.modifiedAt} {modifiedAt.format()},{' '}
<Italic>{modifiedBy.name}</Italic>
</P>
)}
</>
)
}
const departureTooltipText = (attendance: AttendanceRowAttendance) => {
const timeTooltip = timeTooltipText({
addedAt: attendance.departedAddedAt,
addedBy: attendance.departedAddedBy,
modifiedAt: attendance.departedModifiedAt,
modifiedBy: attendance.departedModifiedBy
})

return timeTooltip !== undefined || attendance.departedAutomatically ? (
<div>
<div>{i18n.unit.staffAttendance.departedAutomatically}</div>
{timeTooltip !== undefined && timeTooltip}
{attendance.departedAutomatically && (
<P>{i18n.unit.staffAttendance.departedAutomatically}</P>
)}
</div>
) : undefined
}

return (
<DayTr data-qa={`attendance-row-${rowIndex}`}>
Expand Down Expand Up @@ -621,23 +692,31 @@ const AttendanceRow = React.memo(function AttendanceRow({
<AttendanceTimes data-qa="attendance-day">
{attendancesForToday.length > 0 ? (
attendancesForToday.map((attendance, i) => {
const tooltip = attendanceTooltipText(attendance)
return (
<Tooltip
key={i}
tooltip={tooltip}
data-qa="attendance-tooltip"
>
<AttendanceCell>
<AttendanceTime data-qa="arrival-time">
<AttendanceCell key={i}>
<AttendanceTime data-qa="arrival-time">
<Tooltip
tooltip={timeTooltipText({
addedAt: attendance.arrivedAddedAt,
addedBy: attendance.arrivedAddedBy,
modifiedAt: attendance.arrivedModifiedAt,
modifiedBy: attendance.arrivedModifiedBy
})}
data-qa="arrival-time-tooltip"
>
{renderTime(attendance.arrived, date)}
</AttendanceTime>
<AttendanceTime data-qa="departure-time">
</Tooltip>
</AttendanceTime>
<AttendanceTime data-qa="departure-time">
<Tooltip
tooltip={departureTooltipText(attendance)}
data-qa="departure-time-tooltip"
>
{renderTime(attendance.departed, date)}
{tooltip ? `*` : ''}
</AttendanceTime>
</AttendanceCell>
</Tooltip>
{attendance.departedAutomatically ? `*` : ''}
</Tooltip>
</AttendanceTime>
</AttendanceCell>
)
})
) : (
Expand Down
Loading

0 comments on commit 93a0f28

Please sign in to comment.