From 6467331c8e1c1e54da253399d3584eca51daa780 Mon Sep 17 00:00:00 2001 From: Joosa Kurvinen Date: Wed, 29 Nov 2023 15:51:59 +0200 Subject: [PATCH] assigned employee --- frontend/src/App.tsx | 2 +- frontend/src/api-client.ts | 3 +- frontend/src/auth/UserContext.tsx | 6 +- frontend/src/auth/auth-status.ts | 10 +--- frontend/src/employees/api.ts | 17 ++++++ frontend/src/students/CreateStudentPage.tsx | 12 +++- frontend/src/students/StudentCaseForm.tsx | 57 +++++++++++++----- frontend/src/students/StudentPage.tsx | 14 ++++- frontend/src/students/StudentsSearchPage.tsx | 32 ++++++---- frontend/src/students/api.ts | 6 +- .../espoo/oppivelvollisuus/AppController.kt | 5 ++ .../espoo/oppivelvollisuus/DevDataInserter.kt | 4 +- .../espoo/oppivelvollisuus/EmployeeBasics.kt | 60 +++++++++++++++++++ .../fi/espoo/oppivelvollisuus/Student.kt | 15 +++-- .../fi/espoo/oppivelvollisuus/StudentCase.kt | 17 ++++-- .../oppivelvollisuus/SystemController.kt | 45 -------------- .../V005__student_case_assigned_to.sql | 2 + 17 files changed, 206 insertions(+), 101 deletions(-) create mode 100644 frontend/src/employees/api.ts create mode 100644 service/src/main/kotlin/fi/espoo/oppivelvollisuus/EmployeeBasics.kt create mode 100644 service/src/main/resources/db/migration/V005__student_case_assigned_to.sql diff --git a/frontend/src/App.tsx b/frontend/src/App.tsx index bd77e71..1972c7d 100755 --- a/frontend/src/App.tsx +++ b/frontend/src/App.tsx @@ -42,7 +42,7 @@ function App() {
-

Espoon kaupunki - Oppivelvollisuuden valvonta

+

Espoon kaupunki - Oppivelvollisuuden seuranta

diff --git a/frontend/src/api-client.ts b/frontend/src/api-client.ts index 7183826..711ea8a 100644 --- a/frontend/src/api-client.ts +++ b/frontend/src/api-client.ts @@ -1,5 +1,6 @@ import axios from 'axios' export const apiClient = axios.create({ - baseURL: '/api' + baseURL: '/api', + xsrfCookieName: 'oppivelvollisuus.xsrf' }) diff --git a/frontend/src/auth/UserContext.tsx b/frontend/src/auth/UserContext.tsx index 9b21822..72b678f 100644 --- a/frontend/src/auth/UserContext.tsx +++ b/frontend/src/auth/UserContext.tsx @@ -1,9 +1,9 @@ import React, { createContext, useMemo } from 'react' -import { User } from './auth-status' +import { EmployeeUser } from '../employees/api' export interface UserState { - user: User | null + user: EmployeeUser | null } export const UserContext = createContext({ @@ -15,7 +15,7 @@ export const UserContextProvider = React.memo(function UserContextProvider({ user }: { children: React.JSX.Element - user: User | null + user: EmployeeUser | null }) { const value = useMemo( () => ({ diff --git a/frontend/src/auth/auth-status.ts b/frontend/src/auth/auth-status.ts index 255fd44..6b1ad08 100644 --- a/frontend/src/auth/auth-status.ts +++ b/frontend/src/auth/auth-status.ts @@ -1,18 +1,12 @@ import { useEffect, useState } from 'react' import { apiClient } from '../api-client' +import { EmployeeUser } from '../employees/api' import { JsonOf } from '../shared/api-utils' -export interface User { - externalId: string - firstName: string - lastName: string - email?: string | null -} - export interface AuthStatus { loggedIn: boolean - user?: User + user?: EmployeeUser apiVersion: string } diff --git a/frontend/src/employees/api.ts b/frontend/src/employees/api.ts new file mode 100644 index 0000000..2ef6edb --- /dev/null +++ b/frontend/src/employees/api.ts @@ -0,0 +1,17 @@ +import { apiClient } from '../api-client' +import { JsonOf } from '../shared/api-utils' + +export interface EmployeeUser { + externalId: string + firstName: string + lastName: string + email?: string | null +} + +export interface EmployeeBasics { + id: string + name: string +} + +export const apiGetEmployees = (): Promise => + apiClient.get>('/employees').then((res) => res.data) diff --git a/frontend/src/students/CreateStudentPage.tsx b/frontend/src/students/CreateStudentPage.tsx index c1ef8b9..2ea1eb6 100644 --- a/frontend/src/students/CreateStudentPage.tsx +++ b/frontend/src/students/CreateStudentPage.tsx @@ -1,6 +1,7 @@ -import React, { useState } from 'react' +import React, { useEffect, useState } from 'react' import { Link, useNavigate } from 'react-router-dom' +import { apiGetEmployees, EmployeeUser } from '../employees/api' import { VerticalGap } from '../shared/layout' import { H2, H3 } from '../shared/typography' @@ -11,11 +12,18 @@ import { apiPostStudent, StudentCaseInput, StudentInput } from './api' export const CreateStudentPage = React.memo(function CreateStudentPage() { const navigate = useNavigate() + const [employees, setEmployees] = useState(null) + useEffect(() => { + void apiGetEmployees().then(setEmployees) + }, []) + const [studentInput, setStudentInput] = useState(null) const [studentCaseInput, setStudentCaseInput] = useState(null) const [submitting, setSubmitting] = useState(false) + if (!employees) return
...
+ return (
Takaisin @@ -31,7 +39,7 @@ export const CreateStudentPage = React.memo(function CreateStudentPage() {

Ilmoituksen tiedot

- +
diff --git a/frontend/src/students/api.ts b/frontend/src/students/api.ts index 6557694..df84479 100644 --- a/frontend/src/students/api.ts +++ b/frontend/src/students/api.ts @@ -1,6 +1,7 @@ import { formatISO, parseISO } from 'date-fns' import { apiClient } from '../api-client' +import { EmployeeBasics } from '../employees/api' import { JsonOf } from '../shared/api-utils' export interface StudentInput { @@ -54,6 +55,7 @@ export interface StudentSummary { firstName: string lastName: string openedAt: Date | null + assignedTo: EmployeeBasics | null } export const apiGetStudents = (): Promise => @@ -99,11 +101,13 @@ export const apiGetStudent = (id: string): Promise => export interface StudentCaseInput { openedAt: Date info: string + assignedTo: string | null } -export interface StudentCase extends StudentCaseInput { +export interface StudentCase extends Omit { id: string studentId: string + assignedTo: EmployeeBasics | null } export const apiPostStudentCase = ( diff --git a/service/src/main/kotlin/fi/espoo/oppivelvollisuus/AppController.kt b/service/src/main/kotlin/fi/espoo/oppivelvollisuus/AppController.kt index f78d41c..e75dda5 100644 --- a/service/src/main/kotlin/fi/espoo/oppivelvollisuus/AppController.kt +++ b/service/src/main/kotlin/fi/espoo/oppivelvollisuus/AppController.kt @@ -79,4 +79,9 @@ class AppController { tx.updateStudentCase(id = id, studentId = studentId, data = body) } } + + @GetMapping("/employees") + fun getEmployees(): List { + return jdbi.inTransactionUnchecked { it.getEmployees() } + } } diff --git a/service/src/main/kotlin/fi/espoo/oppivelvollisuus/DevDataInserter.kt b/service/src/main/kotlin/fi/espoo/oppivelvollisuus/DevDataInserter.kt index 7ada739..4c2f2e5 100644 --- a/service/src/main/kotlin/fi/espoo/oppivelvollisuus/DevDataInserter.kt +++ b/service/src/main/kotlin/fi/espoo/oppivelvollisuus/DevDataInserter.kt @@ -20,7 +20,9 @@ class DevDataInserter(jdbi: Jdbi) { tx.createUpdate( """ INSERT INTO employees (external_id, first_name, last_name) -VALUES ('12345678-0000-0000-0000-000000000000', 'Sanna', 'Suunnittelija') +VALUES ('12345678-0000-0000-0000-000000000000', 'Sanna', 'Suunnittelija'); +INSERT INTO employees (external_id, first_name, last_name) +VALUES ('12345678-0000-0000-0000-000000000001', 'Olli', 'Ohjaaja'); """ ).execute() } diff --git a/service/src/main/kotlin/fi/espoo/oppivelvollisuus/EmployeeBasics.kt b/service/src/main/kotlin/fi/espoo/oppivelvollisuus/EmployeeBasics.kt new file mode 100644 index 0000000..23c9cc1 --- /dev/null +++ b/service/src/main/kotlin/fi/espoo/oppivelvollisuus/EmployeeBasics.kt @@ -0,0 +1,60 @@ +package fi.espoo.oppivelvollisuus + +import org.jdbi.v3.core.Handle +import org.jdbi.v3.core.kotlin.bindKotlin +import org.jdbi.v3.core.kotlin.mapTo +import org.jdbi.v3.core.mapper.PropagateNull +import kotlin.jvm.optionals.getOrNull + +data class EmployeeUser( + val externalId: String, + val firstName: String, + val lastName: String, + val email: String? +) + +typealias EmployeeLoginRequest = EmployeeUser + +data class EmployeeBasics( + @PropagateNull val id: String, + val name: String +) + +fun Handle.upsertEmployee(employee: EmployeeLoginRequest) = + createQuery( + // language=SQL + """ +INSERT INTO employees (external_id, first_name, last_name, email) +VALUES (:externalId, :firstName, :lastName, :email) +ON CONFLICT (external_id) DO UPDATE +SET updated = now(), first_name = :firstName, last_name = :lastName, email = :email +RETURNING external_id, first_name, last_name, email + """ + .trimIndent() + ) + .bindKotlin(employee) + .mapTo() + .findOne() + .get() + +fun Handle.getEmployees(): List = createQuery( + """ + SELECT external_id, first_name, last_name, email + FROM employees +""" +).mapTo().list() + +fun Handle.getEmployee(id: String) = + createQuery( + // language=SQL + """ +SELECT external_id, first_name, last_name, email +FROM employees +WHERE external_id = :id + """ + .trimIndent() + ) + .bind("id", id) + .mapTo() + .findOne() + .getOrNull() diff --git a/service/src/main/kotlin/fi/espoo/oppivelvollisuus/Student.kt b/service/src/main/kotlin/fi/espoo/oppivelvollisuus/Student.kt index 17642c5..9ccac73 100644 --- a/service/src/main/kotlin/fi/espoo/oppivelvollisuus/Student.kt +++ b/service/src/main/kotlin/fi/espoo/oppivelvollisuus/Student.kt @@ -3,6 +3,7 @@ package fi.espoo.oppivelvollisuus import org.jdbi.v3.core.Handle import org.jdbi.v3.core.kotlin.bindKotlin import org.jdbi.v3.core.kotlin.mapTo +import org.jdbi.v3.core.mapper.Nested import java.time.LocalDate import java.util.* import kotlin.jvm.optionals.getOrNull @@ -37,20 +38,24 @@ data class StudentSummary( val id: UUID, val firstName: String, val lastName: String, - val openedAt: LocalDate? + val openedAt: LocalDate?, + @Nested("assignedTo") val assignedTo: EmployeeBasics? ) fun Handle.getStudentSummaries(): List = createQuery( """ -SELECT id, first_name, last_name, sc.opened_at -FROM students +SELECT s.id, s.first_name, s.last_name, sc.opened_at, + assignee.external_id AS assigned_to_id, + assignee.first_name || ' ' || assignee.last_name AS assigned_to_name +FROM students s LEFT JOIN LATERAL ( - SELECT opened_at + SELECT opened_at, assigned_to FROM student_cases - WHERE student_id = students.id + WHERE student_id = s.id ORDER BY opened_at DESC LIMIT 1 ) sc ON true +LEFT JOIN employees assignee ON sc.assigned_to = assignee.external_id ORDER BY opened_at DESC, first_name, last_name """ ) diff --git a/service/src/main/kotlin/fi/espoo/oppivelvollisuus/StudentCase.kt b/service/src/main/kotlin/fi/espoo/oppivelvollisuus/StudentCase.kt index 3eb85a4..f0b2c62 100644 --- a/service/src/main/kotlin/fi/espoo/oppivelvollisuus/StudentCase.kt +++ b/service/src/main/kotlin/fi/espoo/oppivelvollisuus/StudentCase.kt @@ -3,12 +3,14 @@ package fi.espoo.oppivelvollisuus import org.jdbi.v3.core.Handle import org.jdbi.v3.core.kotlin.bindKotlin import org.jdbi.v3.core.kotlin.mapTo +import org.jdbi.v3.core.mapper.Nested import java.time.LocalDate import java.util.* data class StudentCaseInput( val openedAt: LocalDate, - val info: String + val info: String, + val assignedTo: String? ) fun Handle.insertStudentCase( @@ -32,14 +34,20 @@ data class StudentCase( val id: UUID, val studentId: UUID, val openedAt: LocalDate, - val info: String + val info: String, + @Nested("assignedTo") val assignedTo: EmployeeBasics? ) fun Handle.getStudentCasesByStudent(studentId: UUID): List = createQuery( """ -SELECT id, student_id, opened_at, info +SELECT + id, student_id, opened_at, info, + assignee.external_id AS assigned_to_id, + assignee.first_name || ' ' || assignee.last_name AS assigned_to_name FROM student_cases +LEFT JOIN employees assignee ON student_cases.assigned_to = assignee.external_id WHERE student_id = :studentId +ORDER BY opened_at DESC, student_cases.created DESC """ ) .bind("studentId", studentId) @@ -52,7 +60,8 @@ fun Handle.updateStudentCase(id: UUID, studentId: UUID, data: StudentCaseInput) UPDATE student_cases SET opened_at = :openedAt, - info = :info + info = :info, + assigned_to = :assignedTo WHERE id = :id AND student_id = :studentId """ ) diff --git a/service/src/main/kotlin/fi/espoo/oppivelvollisuus/SystemController.kt b/service/src/main/kotlin/fi/espoo/oppivelvollisuus/SystemController.kt index f035162..2bc01ed 100644 --- a/service/src/main/kotlin/fi/espoo/oppivelvollisuus/SystemController.kt +++ b/service/src/main/kotlin/fi/espoo/oppivelvollisuus/SystemController.kt @@ -1,10 +1,7 @@ package fi.espoo.oppivelvollisuus -import org.jdbi.v3.core.Handle import org.jdbi.v3.core.Jdbi -import org.jdbi.v3.core.kotlin.bindKotlin import org.jdbi.v3.core.kotlin.inTransactionUnchecked -import org.jdbi.v3.core.kotlin.mapTo import org.springframework.beans.factory.annotation.Autowired import org.springframework.web.bind.annotation.GetMapping import org.springframework.web.bind.annotation.PathVariable @@ -12,7 +9,6 @@ import org.springframework.web.bind.annotation.PostMapping import org.springframework.web.bind.annotation.RequestBody import org.springframework.web.bind.annotation.RequestMapping import org.springframework.web.bind.annotation.RestController -import kotlin.jvm.optionals.getOrNull /** * Controller for "system" endpoints intended to be only called from api-gateway @@ -34,44 +30,3 @@ class SystemController { return jdbi.inTransactionUnchecked { it.getEmployee(id) } } } - -data class EmployeeUser( - val externalId: String, - val firstName: String, - val lastName: String, - val email: String? -) - -typealias EmployeeLoginRequest = EmployeeUser - -fun Handle.upsertEmployee(employee: EmployeeLoginRequest) = - createQuery( - // language=SQL - """ -INSERT INTO employees (external_id, first_name, last_name, email) -VALUES (:externalId, :firstName, :lastName, :email) -ON CONFLICT (external_id) DO UPDATE -SET updated = now(), first_name = :firstName, last_name = :lastName, email = :email -RETURNING external_id, first_name, last_name, email - """ - .trimIndent() - ) - .bindKotlin(employee) - .mapTo() - .findOne() - .get() - -fun Handle.getEmployee(id: String) = - createQuery( - // language=SQL - """ -SELECT external_id, first_name, last_name, email -FROM employees -WHERE external_id = :id - """ - .trimIndent() - ) - .bind("id", id) - .mapTo() - .findOne() - .getOrNull() diff --git a/service/src/main/resources/db/migration/V005__student_case_assigned_to.sql b/service/src/main/resources/db/migration/V005__student_case_assigned_to.sql new file mode 100644 index 0000000..38259af --- /dev/null +++ b/service/src/main/resources/db/migration/V005__student_case_assigned_to.sql @@ -0,0 +1,2 @@ +ALTER TABLE student_cases + ADD COLUMN assigned_to text REFERENCES employees(external_id);