Skip to content

Commit

Permalink
Merge pull request #28 from Amsterdam/feature/122885-zaakhistorie-com…
Browse files Browse the repository at this point in the history
…ponent

122885 Added case events history
  • Loading branch information
remyvdwereld authored Aug 9, 2024
2 parents 2926ef3 + 2afdb59 commit 4fb3ae3
Show file tree
Hide file tree
Showing 13 changed files with 163 additions and 5 deletions.
11 changes: 11 additions & 0 deletions src/__external__/CaseEvent.d.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
type EventType = "CASE" | "GENERIC_TASK"

type CaseEvent = {
id: number
event_values: Record<string, string>
event_variables: Record<string, string>
date_created: string // date-time
type: EventType
emitter_id: number
case: number
}
2 changes: 1 addition & 1 deletion src/app/components/PageHeading/PageHeading.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@ const Wrapper = styled.div<{ $isBorder: boolean }>`
display: flex;
padding-bottom: 8px;
margin-bottom: 8px;
border-bottom: ${ ({ $isBorder }) => $isBorder ? "1px solid rgb(180, 180, 180)" : "none" };
border-bottom: ${ ({ $isBorder }) => $isBorder ? "1px solid #b4b4b4" : "none" };
`

const StyledIcon = styled(Icon)`
Expand Down
40 changes: 40 additions & 0 deletions src/app/components/TimelineEvents/TimelineEvent.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
import React from "react"
import { DescriptionList } from "@amsterdam/design-system-react"
import { getEventValueName } from "./dictionaries"
import { isValidDate, formatDate } from "app/utils/dates"
import { styled } from "styled-components"


type Props = {
event: CaseEvent
}

const Wrapper = styled.div`
padding: 25px;
background-color: #E6E6E6;
border: 0.5px solid #d4d2d2;
border-radius: 4px;
box-shadow:
0px 6px 16px -8px rgba(0, 0, 0, 0.08),
0px 9px 28px 0px rgba(0, 0, 0, 0.05),
0px 12px 48px 16px rgba(0, 0, 0, 0.03);
`

export const TimelineEvent: React.FC<Props> = ({ event }) => {
const { event_values } = event
return (
<Wrapper>
<DescriptionList>
{ Object.entries(event_values).map(([key, value], index) => (
<React.Fragment key={ `event_values-${ index }` }>
<DescriptionList.Term>
{ getEventValueName(key) }
</DescriptionList.Term><DescriptionList.Details>
{ isValidDate(value) ? formatDate(value, true) : value }
</DescriptionList.Details>
</React.Fragment>
))}
</DescriptionList>
</Wrapper>
)
}
23 changes: 23 additions & 0 deletions src/app/components/TimelineEvents/TimelineEvents.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
import { Accordion } from "@amsterdam/design-system-react"
import { getEventTitle } from "./dictionaries"
import { TimelineEvent } from "./TimelineEvent"


export const TimelineEvents: React.FC<{ events: CaseEvent[] }> = ({ events }) => (
<>
<Accordion
headingLevel={1}
sectionAs="div"
>
{ events.map((event, index) => (
<Accordion.Section
key={ event.id }
label={ getEventTitle(event) }
expanded={ index === 0 }
>
<TimelineEvent event={ event }/>
</Accordion.Section>
))}
</Accordion>
</>
)
27 changes: 27 additions & 0 deletions src/app/components/TimelineEvents/dictionaries.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
// Event types
export const EVENT_TYPES: Record<CaseEvent["type"], string> = {
"CASE": "Aanleiding",
"GENERIC_TASK": "GENERIC_TASK"
}


export const getEventTitle = (event: CaseEvent): string => {
const { type, event_values } = event
if (type === EVENT_TYPES.GENERIC_TASK) {
return event_values.description
}
if (EVENT_TYPES[type]) {
return EVENT_TYPES[type]
}
return type
}

export const EVENT_VALUES: Record<string, string> = {
"date_added": "Datum",
"author": "Medewerker",
"description": "Toelichting"
}

export const getEventValueName = (value: string): string => (
EVENT_VALUES[value] ?? value
)
1 change: 1 addition & 0 deletions src/app/components/index.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
export * from "./DefaultLayout/DefaultLayout"
export * from "./DetailsList/DetailsList"
export * from "./TimelineEvents/TimelineEvents"
export * from "./icons/icons"
export * from "./Modal/Modal"
export * from "./Modal/hooks/useModal"
Expand Down
2 changes: 2 additions & 0 deletions src/app/pages/CaseDetailsPage/CaseDetailsPage.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ import { DocumentIcon } from "@amsterdam/design-system-react-icons"
import { useCase } from "app/state/rest"
import { PageHeading, PageSpinner, DetailsList } from "app/components"
import Workflows from "./Workflows/Workflows"
import CaseHistory from "./CaseHistory/CaseHistory"


export const CaseDetailsPage: React.FC = () => {
Expand All @@ -25,6 +26,7 @@ export const CaseDetailsPage: React.FC = () => {
<br />
<br />
{ data?.id && <Workflows caseId={ data?.id } /> }
{ data?.id && <CaseHistory caseId={ data?.id } /> }
</>
)
}
Expand Down
25 changes: 25 additions & 0 deletions src/app/pages/CaseDetailsPage/CaseHistory/CaseHistory.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
import { useCaseEvents } from "app/state/rest"
import { SmallSkeleton, PageHeading, TimelineEvents } from "app/components"


type Props = {
caseId: Components.Schemas.Case["id"]
}

export const CaseHistory: React.FC<Props> = ({ caseId }) => {
const [data, { isBusy }] = useCaseEvents(caseId)
const events = data ? [...data]?.reverse() : []

if (isBusy) {
return <SmallSkeleton height={ 4 } />
}
return (
<>
<PageHeading label="Zaakhistorie" level={ 4 } border/>
<TimelineEvents events={ events }/>
</>
)
}

export default CaseHistory

2 changes: 2 additions & 0 deletions src/app/pages/CaseDetailsPage/Workflows/Workflows.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,8 @@ export const Workflows: React.FC<Props> = ({ caseId }) => {
</Wrapper>
))
) : <></>}
<br />
<br />
</>
)
}
Expand Down
4 changes: 2 additions & 2 deletions src/app/pages/CaseDetailsPage/Workflows/columns.tsx
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import { Icon } from "@amsterdam/design-system-react"
import dayjs from "dayjs"
import { ColumnType, TaskOutlined } from "app/components"
import TaskButton from "../tasks/TaskButton/TaskButton"
import { formatDate } from "app/utils/dates"

const getColumns = (caseId: Components.Schemas.Case["id"]): ColumnType<Components.Schemas.CaseUserTask>[] => ([
{
Expand All @@ -14,7 +14,7 @@ const getColumns = (caseId: Components.Schemas.Case["id"]): ColumnType<Component
}, {
header: "Slotdatum",
dataIndex: "due_date",
render: (text) => dayjs(text).format("DD-MM-YYYY")
render: (text) => formatDate(text)
}, {
header: "Verwerking taak",
dataIndex: "id",
Expand Down
4 changes: 2 additions & 2 deletions src/app/pages/TasksPage/TaskPage.tsx
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import dayjs from "dayjs"
import { ColumnType, LinkButton, PageHeading, Table } from "app/components"
import { useTasks } from "app/state/rest"
import { formatDate } from "app/utils/dates"


const columns: ColumnType<Components.Schemas.CaseUserTask>[] = [
Expand All @@ -21,7 +21,7 @@ const columns: ColumnType<Components.Schemas.CaseUserTask>[] = [
sorter: (a: Components.Schemas.CaseUserTask, b: Components.Schemas.CaseUserTask) => (
a.created.localeCompare(b.created)
),
render: (text) => dayjs(text).format("DD-MM-YYYY HH:mm")
render: (text) => formatDate(text, true)
}, {
header: "",
dataIndex: "case",
Expand Down
12 changes: 12 additions & 0 deletions src/app/state/rest/cases.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ import type { Options } from "./"
import { makeApiUrl, useErrorHandler } from "./hooks/utils"
import useApiRequest from "./hooks/useApiRequest"


type CaseApiResponse = Components.Schemas.Case[] | Components.Schemas.Case;

export const useCases = (options?: Options) => {
Expand Down Expand Up @@ -47,3 +48,14 @@ export const useTaskComplete = (options?: Options) => {
isProtected: true
})
}

export const useCaseEvents = (id: Components.Schemas.Case["id"] ,options?: Options) => {
const handleError = useErrorHandler()
return useApiRequest<CaseEvent[]>({
...options,
url: `${ makeApiUrl("cases", id, "events") }`,
groupName: "cases",
handleError,
isProtected: true
})
}
15 changes: 15 additions & 0 deletions src/app/utils/dates.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
import dayjs from "dayjs"

export const isValidDate = (dateString: string): boolean => {
// Parse the date string with dayjs
const date = dayjs(dateString)
// Check if the parsed date is valid
return date.isValid()
}

const DEFAULT_DATE_FORMAT = "DD-MM-YYYY"
const DEFAULT_DATE_FORMAT_TIME = "DD-MM-YYYY HH:mm"

export const formatDate = (dateString: string, includeTime: boolean = false): string => (
dayjs(dateString).format(includeTime ? DEFAULT_DATE_FORMAT_TIME : DEFAULT_DATE_FORMAT)
)

0 comments on commit 4fb3ae3

Please sign in to comment.