diff --git a/frontend/public/locales/en/common.json b/frontend/public/locales/en/common.json index 1f686858f434b85fa241fe5c273b04c01f1ab457..50e97ac8e33aef22a29100f193eda99457341969 100644 --- a/frontend/public/locales/en/common.json +++ b/frontend/public/locales/en/common.json @@ -82,7 +82,7 @@ "name": "Name", "role": "Guest role", "period": "Period", - "host": "Host", + "host": "Host", "ou": "Organisation", "department": "Department", "choice": "Choices", @@ -103,6 +103,7 @@ "expiring_other": "Expiring in {{count}} days", "expiring_one": "Expiring in {{count}} day", "waitingForGuest": "Waiting for guest", + "invalidEmail": "Invalid e-mail address", "waitingForSponsor": "Needs confirmation", "invitationExpired": "Invitation expired" }, diff --git a/frontend/public/locales/nb/common.json b/frontend/public/locales/nb/common.json index d145058f3648e43023f30631cefa0f1662b5f878..a6849c3e66d272b502822488e32476761ff926fe 100644 --- a/frontend/public/locales/nb/common.json +++ b/frontend/public/locales/nb/common.json @@ -82,7 +82,7 @@ "name": "Navn", "role": "Gjesterolle", "period": "Periode", - "host": "Vert", + "host": "Vert", "ou": "Organisasjon", "department": "Avdeling", "choice": "Valg", @@ -103,6 +103,7 @@ "expiring_other": "Utløper om {{count}} dager", "expiring_one": "Utløper om {{count}} dag", "waitingForGuest": "Venter på gjest", + "invalidEmail": "Ugyldig e-postadresse", "waitingForSponsor": "Trenger godkjenning", "invitationExpired": "Invitasjon utløpt" }, diff --git a/frontend/public/locales/nn/common.json b/frontend/public/locales/nn/common.json index e39c0bd019a1b016fa392d9cc537f771cf913500..aa5b64c7794bf4302d990d0596e79edb664bb39b 100644 --- a/frontend/public/locales/nn/common.json +++ b/frontend/public/locales/nn/common.json @@ -82,7 +82,7 @@ "name": "Namn", "role": "Gjesterolle", "period": "Periode", - "host": "Vert", + "host": "Vert", "ou": "Organisasjonsenhet", "department": "Enhet", "choice": "Val", @@ -103,6 +103,7 @@ "expiring_other": "Utløper om {{count}} dagar", "expiring_one": "Utløper om {{count}} dag", "waitingForGuest": "Venter på gjest", + "invalidEmail": "Ugyldig e-postadresse", "waitingForSponsor": "Trenger godkjenning", "invitationExpired": "Invitasjon utløpt" }, diff --git a/frontend/src/routes/sponsor/frontpage/index.tsx b/frontend/src/routes/sponsor/frontpage/index.tsx index d489fc4915e792cb5c5172caebb3e90bf9e2146d..c992c5a8c7c927591a5343ecbbcfd1069dcccb4e 100644 --- a/frontend/src/routes/sponsor/frontpage/index.tsx +++ b/frontend/src/routes/sponsor/frontpage/index.tsx @@ -82,16 +82,17 @@ const StyledTableHead = styled(TableHead)(({ theme }) => ({ borderRadius: '0', })) -const Status = ({ person, role }: StatusProps) => { +const calculateStatus = (person: Guest, role: Role): [string, number] => { const today = new Date() today.setHours(0, 0, 0, 0) - const { t } = useTranslation('common') let status = '' const days = differenceInDays(role.end_date, today) if (!person.registered) { status = 'waitingForGuest' - if (person.invitation_status !== 'active') { + if (person.invitation_status === 'invalidEmail') { + status = 'invalidEmail' + } else if (person.invitation_status !== 'active') { status = 'invitationExpired' } } else if (person.registered && !person.verified) { @@ -105,6 +106,12 @@ const Status = ({ person, role }: StatusProps) => { status = 'active' } } + return [status, days] +} + +const Status = ({ person, role }: StatusProps) => { + const { t } = useTranslation('common') + const [status, days] = calculateStatus(person, role) switch (status) { case 'active': @@ -143,6 +150,12 @@ const Status = ({ person, role }: StatusProps) => { <Trans t={t} i18nKey="statusText.expiring" count={days} /> </TableCell> ) + case 'invalidEmail': + return ( + <TableCell sx={{ color: 'error.main' }} align="left"> + <Trans t={t} i18nKey="statusText.invalidEmail" count={days} /> + </TableCell> + ) default: return ( <TableCell sx={{ color: 'error.main' }} align="left"> diff --git a/frontend/src/routes/sponsor/guest/guestInfo/index.tsx b/frontend/src/routes/sponsor/guest/guestInfo/index.tsx index 43f663e108804052d016205021efd1799ae63d0b..1a6fb58d3db0a4853b6ec6093af37f05a581da25 100644 --- a/frontend/src/routes/sponsor/guest/guestInfo/index.tsx +++ b/frontend/src/routes/sponsor/guest/guestInfo/index.tsx @@ -172,6 +172,9 @@ export default function GuestInfo({ }) } }) + // Reload guests to update status on sponsor frontpage + // in case the email was invalid before update + reloadGuests() } const onSubmit = handleSubmit(submit) diff --git a/greg/migrations/0023_identity_invalid.py b/greg/migrations/0023_identity_invalid.py new file mode 100644 index 0000000000000000000000000000000000000000..0ffd9e9c7610c8f148d41db9285ec6e2eec8e364 --- /dev/null +++ b/greg/migrations/0023_identity_invalid.py @@ -0,0 +1,18 @@ +# Generated by Django 4.0.2 on 2022-05-24 08:16 + +from django.db import migrations, models + + +class Migration(migrations.Migration): + + dependencies = [ + ('greg', '0022_alter_identity_type'), + ] + + operations = [ + migrations.AddField( + model_name='identity', + name='invalid', + field=models.BooleanField(null=True), + ), + ] diff --git a/greg/models.py b/greg/models.py index 85f208a0dc2dd570a61d37264ad189009bd0546c..4055837fbd9ba9259c5c1c8e83aa0af111dd4b9b 100644 --- a/greg/models.py +++ b/greg/models.py @@ -264,6 +264,21 @@ class Notification(BaseModel): class Identity(BaseModel): + """ + Model used for storing identifiers of a person + + A person must have at least one verified PASSPORT_NUMBER or + NORWEGIAN_NATIONAL_ID_NUMBER to be active. + + If for some reason we find that a value is invalid, even if it has + been verified, it can be marked as invalid by setting the invalid + field to True. + + MIGRATION_ID is used to identify persons imported from another + source system. + PRIVATE_EMAIL is used when sending invitation emails. + """ + class IdentityType(models.TextChoices): FEIDE_ID = "feide_id" FEIDE_EMAIL = "feide_email" @@ -294,6 +309,7 @@ class Identity(BaseModel): blank=True, ) verified_at = models.DateTimeField(null=True, blank=True) + invalid = models.BooleanField(null=True) def __str__(self) -> str: return "{}(id={!r}, type={!r}, value={!r})".format( diff --git a/gregui/api/serializers/guest.py b/gregui/api/serializers/guest.py index 4161b2ff4e7d3d2bfa499e41999d0150dc25771a..b7474eade11c78ef2552d2398c3e061eb9d1cb8b 100644 --- a/gregui/api/serializers/guest.py +++ b/gregui/api/serializers/guest.py @@ -40,6 +40,7 @@ def create_identity_or_update( ) else: existing_identity.value = value + existing_identity.invalid = None existing_identity.save() @@ -257,6 +258,9 @@ class GuestSerializer(serializers.ModelSerializer): return obj.feide_id and obj.feide_id.value def get_invitation_status(self, obj): + if obj.private_email and obj.private_email.invalid: + return "invalidEmail" + invitation_links = InvitationLink.objects.filter( invitation__role__person__id=obj.id )