-
Notifications
You must be signed in to change notification settings - Fork 3
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Add new observation form for custom observations #70
base: main
Are you sure you want to change the base?
Changes from 25 commits
ae6a40f
3e6d97a
52e2837
b7fb855
96a505e
446f236
b7e7935
fd6d756
ba5d2fb
40f9a78
5d3d94b
c6bc33f
7d78ddb
6cd31a2
bf75392
6abcf19
766fa4a
577bc24
54ecf89
aa9e077
9398157
b634111
3600e80
56962f4
0d8ad1b
b548b11
c28293d
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,6 +1,10 @@ | ||
name: Release docker image | ||
|
||
on: | ||
push: | ||
branches: | ||
- main | ||
- new_observation_types | ||
release: | ||
types: [created] | ||
|
||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1 @@ | ||
declare module '@bhch/react-json-form'; |
Original file line number | Diff line number | Diff line change | ||||
---|---|---|---|---|---|---|
@@ -0,0 +1,126 @@ | ||||||
import { JSONSchema } from 'json-schema-yup-transformer/dist/schema'; | ||||||
|
||||||
import { Attachement } from './settings'; | ||||||
|
||||||
export type Observation = { | ||||||
id: number; | ||||||
label: string; | ||||||
description: string; | ||||||
json_schema_form: JSONSchema; | ||||||
stations: number[]; | ||||||
password_required?: boolean; | ||||||
}; | ||||||
|
||||||
export type ObservationDetails = { | ||||||
values: { id: string; value: any; label?: string }[]; | ||||||
id: string; | ||||||
contributedAt: string; | ||||||
label?: string; | ||||||
description?: string; | ||||||
attachments?: Attachement[]; | ||||||
geometry?: { | ||||||
coordinates: number[]; | ||||||
}; | ||||||
}; | ||||||
|
||||||
type ObservationListItem = { | ||||||
id: number; | ||||||
contributed_at: string; | ||||||
attachments: Attachement[]; | ||||||
}; | ||||||
|
||||||
async function fetchObservations(): Promise<Observation[]> { | ||||||
const res = await fetch( | ||||||
`${process.env.apiHost}/api/portal/fr/${process.env.portal}/custom-contribution-types/`, | ||||||
{ | ||||||
next: { revalidate: 5 * 60, tags: ['admin', 'contributions'] }, | ||||||
headers: { | ||||||
Accept: 'application/json', | ||||||
}, | ||||||
}, | ||||||
); | ||||||
if (res.status < 200 || res.status > 299) { | ||||||
return []; | ||||||
} | ||||||
return res.json(); | ||||||
} | ||||||
|
||||||
async function fetchObservation(id: string): Promise<Observation | null> { | ||||||
const res = await fetch( | ||||||
`${process.env.apiHost}/api/portal/fr/${process.env.portal}/custom-contribution-types/${id}`, | ||||||
{ | ||||||
next: { revalidate: 5 * 60, tags: ['admin', 'contributions'] }, | ||||||
headers: { | ||||||
Accept: 'application/json', | ||||||
}, | ||||||
}, | ||||||
); | ||||||
if (res.status < 200 || res.status > 299) { | ||||||
return null; | ||||||
} | ||||||
return res.json(); | ||||||
} | ||||||
|
||||||
async function fetchObservationDetails( | ||||||
id: string, | ||||||
): Promise<ObservationListItem[] | null> { | ||||||
const res = await fetch( | ||||||
`${process.env.apiHost}/api/portal/fr/${process.env.portal}/custom-contribution-types/${id}/contributions`, | ||||||
{ | ||||||
next: { revalidate: 5 * 60, tags: ['admin', 'contributions'] }, | ||||||
headers: { | ||||||
Accept: 'application/json', | ||||||
}, | ||||||
}, | ||||||
); | ||||||
if (res.status < 200 || res.status > 299) { | ||||||
return null; | ||||||
} | ||||||
return res.json(); | ||||||
} | ||||||
|
||||||
export async function getObservationDetails( | ||||||
type: string, | ||||||
id: string, | ||||||
): Promise<ObservationDetails | null> { | ||||||
const schema = await fetchObservation(type); | ||||||
const detailsList = await fetchObservationDetails(type); | ||||||
Comment on lines
+86
to
+87
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Maybe you can use a |
||||||
const values = detailsList?.find(detail => detail.id === parseInt(id)); | ||||||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
Suggested change
or |
||||||
|
||||||
if (!values) return null; | ||||||
|
||||||
const details = { | ||||||
values: Object.entries(values) | ||||||
.filter(([key]) => schema?.json_schema_form.properties?.[key]) | ||||||
.map(([key, value]) => ({ | ||||||
id: key, | ||||||
value, | ||||||
label: (schema?.json_schema_form.properties?.[key] as any)?.title, | ||||||
})), | ||||||
id, | ||||||
contributedAt: values?.contributed_at, | ||||||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Optional chaining seems useless with the early return line 90 |
||||||
label: schema?.label, | ||||||
description: schema?.description, | ||||||
attachments: values?.attachments, | ||||||
}; | ||||||
|
||||||
return details; | ||||||
} | ||||||
|
||||||
export async function getObservation(id: string) { | ||||||
const observation = await fetchObservation(id); | ||||||
return { | ||||||
...observation, | ||||||
json_schema_form: { | ||||||
...observation?.json_schema_form, | ||||||
properties: { | ||||||
...observation?.json_schema_form?.properties, | ||||||
}, | ||||||
}, | ||||||
}; | ||||||
Comment on lines
+111
to
+120
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Maybe it's more readable to remove all optional chaining by using an early return if |
||||||
} | ||||||
|
||||||
export async function getObservations() { | ||||||
const observations = await fetchObservations(); | ||||||
return observations; | ||||||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,56 @@ | ||
async function postObservation( | ||
props: { [key: string]: string | Blob }, | ||
id: string, | ||
formData: FormData, | ||
) { | ||
const body = new FormData(); | ||
|
||
Object.entries(props).forEach(([key, value]) => { | ||
// `null` in formData is converted to `'null'` | ||
// We convert them to an empty string to avoid it | ||
const nextValue = value !== null ? value : ''; | ||
body.append(key, nextValue); | ||
}); | ||
|
||
Array.from({ length: 5 }).forEach((_, index) => { | ||
const number = index + 1; | ||
const file = formData.get(`file${number}-file`) as File; | ||
if (file && file.size > 0) { | ||
body.append(`file${number}`, file); | ||
} | ||
}); | ||
|
||
try { | ||
const res = await fetch( | ||
`${process.env.apiHost}/api/portal/fr/${process.env.portal}/custom-contribution-types/${id}/contributions/`, | ||
{ | ||
method: 'POST', | ||
body, | ||
}, | ||
).catch(errorServer => { | ||
throw Error(errorServer); | ||
}); | ||
if (res.status > 499) { | ||
throw Error(res.statusText); | ||
} | ||
const json = await res.json(); | ||
|
||
if (res.status < 200 || res.status > 299) { | ||
return { error: true, message: json }; | ||
} | ||
return { error: false, message: json }; | ||
} catch (error) { | ||
let message = 'Unknown Error'; | ||
if (error instanceof Error) message = error.message; | ||
return { error: true, message }; | ||
} | ||
} | ||
|
||
export async function handleSubmitCustomObservation( | ||
body: { [key: string]: string | Blob }, | ||
id: string, | ||
formData: FormData, | ||
) { | ||
'use server'; | ||
return await postObservation(body, id, formData); | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Useful?