-
Notifications
You must be signed in to change notification settings - Fork 15
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
refactor: basic announcement setup extract api calls from ui componen…
…ts for future test purposes
- Loading branch information
1 parent
decff62
commit c9e98ac
Showing
12 changed files
with
276 additions
and
226 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,5 +1,7 @@ | ||
-- SPDX-FileCopyrightText: 2023 Christian Meeßen (GFZ) <[email protected]> | ||
-- SPDX-FileCopyrightText: 2023 Dusan Mijatovic (Netherlands eScience Center) | ||
-- SPDX-FileCopyrightText: 2023 Helmholtz Centre Potsdam - GFZ German Research Centre for Geosciences | ||
-- SPDX-FileCopyrightText: 2023 Netherlands eScience Center | ||
-- | ||
-- SPDX-License-Identifier: Apache-2.0 | ||
-- SPDX-License-Identifier: EUPL-1.2 | ||
|
@@ -38,6 +40,6 @@ CREATE TRIGGER sanitise_update_global_announcement BEFORE UPDATE ON global_annou | |
|
||
ALTER TABLE global_announcement ENABLE ROW LEVEL SECURITY; | ||
|
||
CREATE POLICY anyone_can_read ON global_announcement FOR SELECT TO rsd_web_anon, rsd_user USING (TRUE);´ | ||
CREATE POLICY anyone_can_read ON global_announcement FOR SELECT TO rsd_web_anon, rsd_user USING (TRUE); | ||
|
||
CREATE POLICY admin_all_rights ON global_announcement TO rsd_admin USING (TRUE) WITH CHECK (TRUE); |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,36 +1,37 @@ | ||
// SPDX-FileCopyrightText: 2023 Christian Meeßen (GFZ) <[email protected]> | ||
// SPDX-FileCopyrightText: 2023 Dusan Mijatovic (Netherlands eScience Center) | ||
// SPDX-FileCopyrightText: 2023 Helmholtz Centre Potsdam - GFZ German Research Centre for Geosciences | ||
// SPDX-FileCopyrightText: 2023 Netherlands eScience Center | ||
// | ||
// SPDX-License-Identifier: Apache-2.0 | ||
// SPDX-License-Identifier: EUPL-1.2 | ||
|
||
import Button from '@mui/material/Button' | ||
import {useState} from 'react' | ||
import IconButton from '@mui/material/IconButton' | ||
import CancelIcon from '@mui/icons-material/Cancel' | ||
import ErrorIcon from '@mui/icons-material/Error' | ||
import {useState} from 'react' | ||
|
||
export default function Announcement({announcement}: {announcement: string | null}) { | ||
const [open, setOpen] = useState(true) | ||
if (announcement === null || announcement === undefined) return null | ||
|
||
if (!open) return null | ||
// do not show if no content or close icon is clicked | ||
if (typeof(announcement) == 'undefined' || announcement === null || open===false) return null | ||
|
||
return ( | ||
<div | ||
className="fixed bottom-0 right-0 w-full bg-warning text-white font-extrabold flex" | ||
className="flex justify-center items-center fixed bottom-0 right-0 w-full bg-warning text-warning-content text-xl px-4" | ||
> | ||
<div className='my-auto pl-5'> | ||
<ErrorIcon className='align-middle' /> | ||
</div> | ||
<div className="w-full p-5 inline-block"> | ||
<span className='align-middle'>{announcement}</span> | ||
</div> | ||
<Button | ||
className='ml-auto' | ||
<ErrorIcon/> | ||
<span className='flex-1 py-8 ml-2'>{announcement}</span> | ||
<IconButton | ||
size='large' | ||
onClick={() => {setOpen(false)}} | ||
sx={{ | ||
color: 'warning.contrastText' | ||
}} | ||
> | ||
<CancelIcon className='text-white' /> | ||
</Button> | ||
<CancelIcon /> | ||
</IconButton> | ||
</div> | ||
) | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
173 changes: 79 additions & 94 deletions
173
frontend/components/admin/announcements/AnnouncementsForm.tsx
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,127 +1,112 @@ | ||
// SPDX-FileCopyrightText: 2023 Christian Meeßen (GFZ) <[email protected]> | ||
// SPDX-FileCopyrightText: 2023 Dusan Mijatovic (Netherlands eScience Center) | ||
// SPDX-FileCopyrightText: 2023 Dusan Mijatovic (dv4all) | ||
// SPDX-FileCopyrightText: 2023 Helmholtz Centre Potsdam - GFZ German Research Centre for Geosciences | ||
// SPDX-FileCopyrightText: 2023 Netherlands eScience Center | ||
// SPDX-FileCopyrightText: 2023 dv4all | ||
// | ||
// SPDX-License-Identifier: Apache-2.0 | ||
// SPDX-License-Identifier: EUPL-1.2 | ||
|
||
import Button from '@mui/material/Button' | ||
import AddIcon from '@mui/icons-material/Add' | ||
|
||
import {useForm} from 'react-hook-form' | ||
import {useSession} from '~/auth' | ||
import {Controller, SubmitHandler, UseControllerProps, useController, useForm} from 'react-hook-form' | ||
import AutosaveControlledTextField, {OnSaveProps} from '~/components/form/AutosaveControlledTextField' | ||
import {Input, Switch} from '@mui/material' | ||
import useSnackbar from '~/components/snackbar/useSnackbar' | ||
import SubmitButtonWithListener from '~/components/form/SubmitButtonWithListener' | ||
import {createJsonHeaders, extractReturnMessage, getBaseUrl} from '~/utils/fetchHelpers' | ||
import {GetServerSidePropsContext} from 'next' | ||
import logger from '~/utils/logger' | ||
import {useEffect, useState} from 'react' | ||
import ControlledSwitch from '~/components/form/ControlledSwitch' | ||
import ControlledTextField from '~/components/form/ControlledTextField' | ||
import {AnnouncementItem, createAnnouncement, updateAnnouncement} from './apiAnnouncement' | ||
|
||
const formId = 'announcements-form' | ||
|
||
type EditAnnouncementItem = { | ||
[id: string]: any, | ||
text: string | null, | ||
enabled: boolean | ||
} | ||
|
||
type EditAnnouncementFormProps = { | ||
data: EditAnnouncementItem | ||
} | ||
|
||
export default function AnnouncementsForm({data}: EditAnnouncementFormProps) { | ||
export default function AnnouncementsForm({data}: { data: AnnouncementItem|null }) { | ||
const {token} = useSession() | ||
const {handleSubmit, register, control, setValue, formState: {errors}} = useForm<EditAnnouncementItem>({ | ||
const {showErrorMessage} = useSnackbar() | ||
const {handleSubmit, register, control, reset, formState} = useForm<AnnouncementItem>({ | ||
defaultValues: { | ||
...data | ||
...data | ||
}, | ||
mode: 'onChange' | ||
}) | ||
|
||
function onSubmit(dataToSubmit: EditAnnouncementItem) { | ||
saveAnnouncement(dataToSubmit).then( | ||
(resp) => { | ||
if (resp && [200, 201].includes(resp.status) && resp?.object) { | ||
const newObj = resp.object[0] | ||
setValue('enabled', newObj.enabled) | ||
setValue('id', newObj.id) | ||
setValue('text', newObj.text) | ||
} else { | ||
console.log('Error saving data:', resp) | ||
} | ||
} | ||
) | ||
} | ||
// track form state | ||
const {isValid, isDirty} = formState | ||
|
||
async function saveAnnouncement(item: EditAnnouncementItem) { | ||
try { | ||
let method | ||
let url | ||
if (item.id == '') { | ||
delete item.id | ||
method = 'POST' | ||
url = '/api/v1/global_announcement' | ||
} else { | ||
method = 'PATCH' | ||
url = `/api/v1/global_announcement?id=eq.${item.id}` | ||
} | ||
const resp = await fetch(url, { | ||
method: method, | ||
headers: { | ||
...createJsonHeaders(token), | ||
Prefer: 'return=representation', | ||
}, | ||
body: JSON.stringify(item) | ||
}) | ||
if ([200, 201].includes(resp.status)) { | ||
return { | ||
status: 201, | ||
object: await resp.json() | ||
} | ||
} | ||
// return extractReturnMessage(resp, item.text ?? '') | ||
} catch (e: any) { | ||
logger(`saveAnnouncement: ${e?.message}`, 'error') | ||
return { | ||
status: 500, | ||
message: e?.message | ||
async function onSubmit(item: AnnouncementItem) { | ||
let resp | ||
if (item.id) { | ||
// update | ||
resp = await updateAnnouncement({ | ||
id: item.id, | ||
enabled: item.enabled, | ||
text: item.text | ||
}, token) | ||
} else { | ||
// create | ||
resp = await createAnnouncement({ | ||
enabled: item.enabled, | ||
text: item.text | ||
}, token) | ||
} | ||
// debugger | ||
if (resp.status === 200) { | ||
// use values returned from api | ||
const update = { | ||
id: resp.message?.id ?? null, | ||
enabled: resp.message.enabled ?? false, | ||
text: resp.message.text ?? null | ||
} | ||
// will reset form state | ||
reset(update) | ||
} else { | ||
showErrorMessage(`Failed to save announcement. ${resp.message}`) | ||
} | ||
} | ||
|
||
function isSaveDisabled() { | ||
if (isValid === false) return true | ||
if (isDirty === false) return true | ||
return false | ||
} | ||
|
||
return ( | ||
<form | ||
id={formId} | ||
onSubmit={handleSubmit(onSubmit)} | ||
className="w-full md:w-[42rem]" | ||
> | ||
<Input type="hidden" {...register('id')}/> | ||
<ControlledTextField | ||
control={control} | ||
options={{ | ||
name: 'text', | ||
label: 'Announcement' | ||
}} | ||
rules={{ | ||
maxLength: { | ||
value: 300, | ||
message: 'Maximum length is 300.' | ||
} | ||
}} | ||
/> | ||
<ControlledSwitch | ||
label='Visible' | ||
name='enabled' | ||
control={control} | ||
/> | ||
<SubmitButtonWithListener | ||
disabled={false} | ||
formId={formId} | ||
/> | ||
className="flex-1" | ||
> | ||
{/* id */} | ||
<input type="hidden" {...register('id')} /> | ||
{/* active/visible */} | ||
<ControlledSwitch | ||
label='Visible' | ||
name='enabled' | ||
control={control} | ||
/> | ||
<div className="flex justify-between items-center gap-8 py-4"> | ||
<ControlledTextField | ||
control={control} | ||
options={{ | ||
name: 'text', | ||
label: 'Announcement', | ||
multiline: true | ||
}} | ||
rules={{ | ||
required: 'Announcement text is required', | ||
minLength: { | ||
value: 3, | ||
message: 'Minimum length is 3.' | ||
}, | ||
maxLength: { | ||
value: 300, | ||
message: 'Maximum length is 300.' | ||
} | ||
}} | ||
/> | ||
<SubmitButtonWithListener | ||
disabled={isSaveDisabled()} | ||
formId={formId} | ||
/> | ||
</div> | ||
</form> | ||
) | ||
} |
Oops, something went wrong.