Skip to content
Snippets Groups Projects
Commit 38ff8537 authored by Jonas Braathen's avatar Jonas Braathen
Browse files

Merge branch 'GREG-52-poc-guest-registration' into 'master'

Greg 52 simple comms with the backend

See merge request !65
parents a25b3046 f1ff0759
No related branches found
No related tags found
1 merge request!65Greg 52 simple comms with the backend
Pipeline #94122 passed
Showing
with 200 additions and 65 deletions
......@@ -22,6 +22,7 @@
"plugins": ["react", "@typescript-eslint"],
"rules": {
"semi": "off",
"react/jsx-props-no-spreading": "off",
"@typescript-eslint/semi": ["error", "never"]
}
}
......@@ -10,34 +10,11 @@
content="Web site created using create-react-app"
/>
<link rel="apple-touch-icon" href="%PUBLIC_URL%/logo192.png" />
<!--
manifest.json provides metadata used when your web app is installed on a
user's mobile device or desktop. See https://developers.google.com/web/fundamentals/web-app-manifest/
-->
<link rel="manifest" href="%PUBLIC_URL%/manifest.json" />
<!--
Notice the use of %PUBLIC_URL% in the tags above.
It will be replaced with the URL of the `public` folder during the build.
Only files inside the `public` folder can be referenced from the HTML.
Unlike "/favicon.ico" or "favicon.ico", "%PUBLIC_URL%/favicon.ico" will
work correctly both with client-side routing and a non-root public URL.
Learn how to configure a non-root public URL by running `npm run build`.
-->
<title>React App</title>
<title>Guest Registration</title>
</head>
<body>
<noscript>You need to enable JavaScript to run this app.</noscript>
<div id="root"></div>
<!--
This HTML file is a template.
If you open it directly in the browser, you will see an empty page.
You can add webfonts, meta tags, or analytics to this file.
The build step will place the bundled scripts into the <body> tag.
To begin the development, run `npm start` or `yarn start`.
To create a production bundle, use `npm run build` or `yarn build`.
-->
</body>
</html>
......@@ -14,5 +14,8 @@
},
"loading": "Loading...",
"termsHeader": "Terms",
"staging": "Staging"
"staging": "Staging",
"firstName": "First name",
"lastName": "Last name",
"dateOfBirth": "Date of birth"
}
......@@ -7,12 +7,15 @@
"change": "Bytt språk til {{lang}}"
},
"fnr": "Fødselsnummer",
"header":{
"header": {
"applicationTitle": "Gjesteregistrering",
"applicationDescription": "Registreringstjeneste for gjester",
"selectLanguage": "Velg språk"
},
"loading": "Laster...",
"termsHeader": "Vilkår",
"staging": "Staging"
"staging": "Staging",
"firstName": "Fornavn",
"lastName": "Etternavn",
"dateOfBirth": "Fødselsdato"
}
......@@ -8,12 +8,15 @@
"change": "Bytt språk til {{lang}}"
},
"fnr": "National identity number",
"header":{
"header": {
"applicationTitle": "Gjesteregistrering",
"applicationDescription": "Registreringsteneste for gjester",
"selectLanguage": "Velg språk"
},
"loading": "Lastar...",
"termsHeader": "Vilkår",
"staging": "Staging"
"staging": "Staging",
"firstName": "Fornamn",
"lastName": "Etternamn",
"dateOfBirth": "Fødselsdato"
}
......@@ -2,6 +2,7 @@ import styled from 'styled-components/macro'
export const Button = styled.a`
display: inline-block;
cursor: pointer;
border-radius: 3px;
padding: 0.5rem 0;
margin: 0.5rem 1rem;
......
import { addDays } from 'date-fns'
import nb from 'date-fns/locale/nb'
import nn from 'date-fns/locale/nn'
import React, { useState } from 'react'
import DatePicker, { registerLocale } from 'react-datepicker'
import React from 'react'
import DatePicker, {
ReactDatePickerProps,
registerLocale,
} from 'react-datepicker'
import 'react-datepicker/dist/react-datepicker.css'
import { useTranslation } from 'react-i18next'
import nb from 'date-fns/locale/nb'
import nn from 'date-fns/locale/nn'
registerLocale('nb', nb)
registerLocale('nn', nn)
// CSS Modules, react-datepicker-cssmodules.css
// import 'react-datepicker/dist/react-datepicker-cssmodules.css';
const DateInput = () => {
const [startDate, setStartDate] = useState(new Date())
const DateInput = (props: ReactDatePickerProps) => {
const { i18n } = useTranslation()
return (
<DatePicker
dateFormat="yyyy-MM-dd"
locale={i18n.language}
selected={startDate}
minDate={startDate}
maxDate={addDays(new Date(), 365)}
onChange={(date: any) => setStartDate(date)}
/>
<DatePicker dateFormat="yyyy-MM-dd" locale={i18n.language} {...props} />
)
}
......
import React from 'react'
import validator from '@navikt/fnrvalidator'
import { UseFormReturn } from 'react-hook-form'
export function isValidIdnr(data: string) {
const validationResult = validator.idnr(data)
return validationResult.status === 'valid'
}
import { UseFormReturn } from 'react-hook-form'
import { isValidFnr } from 'utils'
interface FnrProps extends Partial<Pick<UseFormReturn, 'register'>> {
name: string
......@@ -26,7 +22,7 @@ function Fnr(props: FnrProps) {
// eslint-disable-next-line react/jsx-props-no-spreading
{...register(name, {
required: 'Fnr is required',
validate: isValidIdnr,
validate: isValidFnr,
})}
id="fnr"
/>
......
......@@ -22,6 +22,7 @@ export default function Form(props: FormProps) {
...{
// eslint-disable-next-line react/jsx-props-no-spreading
...child.props,
control: methods.control,
register: methods.register,
errors,
key: child.props.name,
......
import React from 'react'
import styled from 'styled-components/macro'
import { UseFormReturn } from 'react-hook-form'
interface InputProps extends Partial<Pick<UseFormReturn, 'register'>> {
interface InputProps
extends Partial<React.InputHTMLAttributes<HTMLInputElement>>,
Partial<UseFormReturn> {
name: string
errors?: any
type?: 'text' | 'email' | 'number'
}
export const StyledInput = styled.input`
width: 50%;
border: 1px solid;
border-width: 0.013rem;
border-radius: 0.3rem;
border-color: #ccc;
box-shadow: inset 0 0 0 0.013rem #4d4d4d;
padding: 0.625rem;
`
function Input(props: InputProps) {
// eslint-disable-next-line react/jsx-props-no-spreading
const { register, name, errors, type, ...rest } = props
......@@ -14,7 +27,7 @@ function Input(props: InputProps) {
return <></>
}
// eslint-disable-next-line react/jsx-props-no-spreading
return <input {...register(name)} {...rest} />
return <StyledInput {...register(name)} {...rest} />
}
Input.defaultProps = {
......
......@@ -6,6 +6,11 @@ import styled from 'styled-components/macro'
const StyledPage = styled.main`
display: block;
justify-content: space-between;
margin: 0 auto;
max-width: ${(props) => props.theme.appMaxWidth};
padding: ${(props) =>
`0.5rem ${props.theme.horizontalPadding} 1rem ${props.theme.horizontalPadding}`};
`
const StyledPageHeader = styled.h2`
......
import { createGlobalStyle } from 'styled-components/macro'
const GlobalStyle = createGlobalStyle`
html {
font-size: clamp(24px, calc(24px * 1vw), 36px);
}
body {
margin: 0;
min-height: 100vh;
......
import React from 'react'
import { useTranslation } from 'react-i18next'
import { StyledInput } from 'components/form/input'
import DateInput from 'components/dateinput'
import { Fnr, Form, Input, Select } from 'components/form'
import Button from 'components/button'
import Page from 'components/page'
import { useForm, Controller, SubmitHandler } from 'react-hook-form'
import format from 'date-fns/format'
import { postJsonOpts } from 'utils'
type RegisterFormData = {
first_name: string
last_name: string
date_of_birth: Date
}
export default function Register() {
const { t } = useTranslation(['common', 'footer'])
const { t } = useTranslation(['common'])
const submit: SubmitHandler<RegisterFormData> = (data) => {
const payload = {
first_name: data.first_name,
last_name: data.last_name,
date_of_birth: format(data.date_of_birth, 'yyyy-MM-dd'),
}
console.log('submitting', JSON.stringify(payload))
fetch('http://localhost:3000/api/ui/v1/register/', postJsonOpts(payload))
.then((res) => res.text())
.then((result) => {
console.log('result', result)
})
.catch((error) => {
console.log('error', error)
})
}
const {
register,
control,
handleSubmit,
// setValue,
// formState: { errors },
} = useForm<RegisterFormData>()
const onSubmit = handleSubmit(submit)
return (
<Page header="Register as a guest">
<Form onSubmit={() => {}}>
<Input name="firstName" />
<Input name="lastName" />
<DateInput />
<Select name="gender" options={['female', 'male', 'other']} />
<Fnr name={t('common:fnr')} />
<button type="submit">Submit</button>
</Form>
<br />
<form onSubmit={onSubmit}>
<StyledInput
{...register(`first_name`)}
placeholder={t('common:firstName')}
/>
<StyledInput
{...register(`last_name`)}
placeholder={t('common:lastName')}
/>
<Controller
name="date_of_birth"
control={control}
render={({ field }) => (
<DateInput
customInput={<StyledInput />}
placeholderText={t('common:dateOfBirth')}
onChange={(date) => field.onChange(date)}
selected={field.value}
/>
)}
/>
{/* <Select name="gender" options={['female', 'male', 'other']} />
<Fnr name={t('common:fnr')} /> */}
<Button as="button" type="submit">
Submit
</Button>
</form>
</Page>
)
}
import validator from '@navikt/fnrvalidator'
export function getCookie(name: string) {
if (!document.cookie) {
return null
}
const cookies = document.cookie
.split(';')
.map((c) => c.trim())
.filter((c) => c.startsWith(`${name}=`))
if (cookies.length === 0) {
return null
}
return decodeURIComponent(cookies[0].split('=')[1])
}
export function maybeCsrfToken() {
const csrfToken = getCookie('csrftoken')
if (!csrfToken) {
return null
}
return {
'X-CSRFToken': csrfToken,
}
}
export function postJsonOpts(data: object): RequestInit {
return {
method: 'POST',
headers: {
'Content-Type': 'application/json',
...maybeCsrfToken(),
},
body: JSON.stringify(data),
credentials: 'same-origin',
}
}
export function isValidFnr(data: string): boolean {
return validator.idnr(data).status === 'valid'
}
......@@ -64,7 +64,7 @@ MIDDLEWARE = [
REST_FRAMEWORK = {
"DEFAULT_VERSIONING_CLASS": "rest_framework.versioning.NamespaceVersioning",
"DEFAULT_VERSION": "v1",
"ALLOWED_VERSIONS": ("v1",),
"ALLOWED_VERSIONS": ("v1", "gregui-v1"),
"DEFAULT_AUTHENTICATION_CLASSES": (
"rest_framework.authentication.TokenAuthentication",
"rest_framework.authentication.SessionAuthentication",
......
......@@ -17,10 +17,12 @@ from django.contrib import admin
from django.urls import include, path
from greg import urls as greg_urls
from gregui import urls as gregui_urls
admin.autodiscover()
urlpatterns = [
path("admin/", admin.site.urls),
path("", include(greg_urls.urlpatterns)),
path("", include(gregui_urls.urlpatterns)),
]
from rest_framework import serializers
from greg.models import Person
class GuestRegisterSerializer(serializers.ModelSerializer):
def create(self, validated_data):
obj = super().create(validated_data)
return obj
class Meta:
model = Person
fields = ("id", "first_name", "last_name", "date_of_birth")
read_only_fields = ("id",)
extra_kwargs = {
"first_name": {"required": True},
"last_name": {"required": True},
"date_of_birth": {"required": True},
# 'email': {'required': True},
# 'phone_number': {'required': True},
}
from django.urls import re_path
from rest_framework.routers import DefaultRouter
from gregui.api.views.guest import GuestRegisterView
router = DefaultRouter(trailing_slash=False)
urlpatterns = router.urls
urlpatterns += [
re_path(r"register/$", GuestRegisterView.as_view(), name="guest-register"),
]
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment