Skip to content

Commit

Permalink
wip
Browse files Browse the repository at this point in the history
  • Loading branch information
dcousens committed Aug 20, 2024
1 parent 08b4ce2 commit 96049f6
Show file tree
Hide file tree
Showing 32 changed files with 707 additions and 1,237 deletions.
5 changes: 5 additions & 0 deletions .changeset/remove-data-getter.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
"@keystone-6/core": major
---

Removes `makeDataGetter` from `@keystone-6/core/admin-ui/utils`
52 changes: 16 additions & 36 deletions packages/auth/src/pages/InitPage.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -158,23 +158,20 @@ function InitPage ({
enableWelcome: boolean
}) {
const { adminMeta } = useKeystone()
const lists = adminMeta?.lists ?? {}
const list = lists[listKey]

const fields = useMemo(() => {
const fields: Record<string, FieldMeta> = {}
fieldPaths.forEach(fieldPath => {
fields[fieldPath] = adminMeta.lists[listKey].fields[fieldPath]
})
for (const fieldPath of fieldPaths) {
fields[fieldPath] = list?.fields[fieldPath]
}
return fields
}, [fieldPaths, adminMeta.lists, listKey])
}, [list, fieldPaths])

const [value, setValue] = useState(() => {
let state: Record<string, any> = {}
Object.keys(fields).forEach(fieldPath => {
state[fieldPath] = { kind: 'value', value: fields[fieldPath].controller.defaultValue }
})
return state
})
const [itemValue, setItemState] = useState(() => getDefaultItemValue(fields))

const invalidFields = useInvalidFields(fields, value)
const invalidFields = useInvalidFields(fields, itemValue)
const [forceValidation, setForceValidation] = useState(false)
const [mode, setMode] = useState<'init' | 'welcome'>('init')

Expand All @@ -188,46 +185,27 @@ function InitPage ({
}
}
}`)
const reinitContext = useReinitContext()
const router = useRouter()
const redirect = useRedirect()

const onSubmit = async (event: React.FormEvent) => {
event.preventDefault()
// Check if there are any invalidFields

const newForceValidation = invalidFields.size !== 0
setForceValidation(newForceValidation)

// if yes, don't submit the form
if (newForceValidation) return

// If not we serialize the data
const data: Record<string, any> = {}
const allSerializedValues = serializeValueToObjByFieldKey(fields, value)

for (const fieldPath of Object.keys(allSerializedValues)) {
const { controller } = fields[fieldPath]
const serialized = allSerializedValues[fieldPath]
// we check the serialized values against the default values on the controller
if (!isDeepEqual(serialized, controller.serialize(controller.defaultValue))) {
// if they're different add them to the data object.
Object.assign(data, serialized)
}
}

try {
await createFirstItem({
variables: {
data,
data: itemValueToGraphQL(fields, itemValue)
},
})
} catch (e) {
console.error(e)
return
}

await reinitContext()

if (enableWelcome) return setMode('welcome')
router.push(redirect)
}
Expand All @@ -249,8 +227,10 @@ function InitPage ({
fields={fields}
forceValidation={forceValidation}
invalidFields={invalidFields}
onChange={setValue}
value={value}
onChange={setItemState}
itemValue={itemValue}
position="form"
mode="create"
/>
<Button
isLoading={
Expand All @@ -268,7 +248,7 @@ function InitPage ({
</SigninContainer>
) : (
<SigninContainer>
<Welcome value={value} onContinue={onComplete} />
<Welcome value={itemValue} onContinue={onComplete} />
</SigninContainer>
)
}
Expand Down
155 changes: 47 additions & 108 deletions packages/auth/src/pages/SigninPage.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -15,28 +15,23 @@ import { TextInput } from '@keystone-ui/fields'
import { Notice } from '@keystone-ui/notice'

import { useMutation, gql } from '@keystone-6/core/admin-ui/apollo'
import { useRawKeystone, useReinitContext } from '@keystone-6/core/admin-ui/context'
import { useRouter } from '@keystone-6/core/admin-ui/router'
import { SigninContainer } from '../components/SigninContainer'
import { useRedirect } from '../lib/useFromRedirect'

type SigninPageProps = {
identityField: string
secretField: string
mutationName: string
successTypename: string
failureTypename: string
}

export const getSigninPage = (props: SigninPageProps) => () => <SigninPage {...props} />
export const getSigninPage = (props: Parameters<typeof SigninPage>[0]) => () => <SigninPage {...props} />

export function SigninPage ({
identityField,
secretField,
mutationName,
successTypename,
failureTypename,
}: SigninPageProps) {
}: {
identityField: string
secretField: string
mutationName: string
successTypename: string
failureTypename: string
}) {
const mutation = gql`
mutation($identity: String!, $secret: String!) {
authenticate: ${mutationName}(${identityField}: $identity, ${secretField}: $secret) {
Expand All @@ -52,136 +47,80 @@ export function SigninPage ({
}
`

const [mode, setMode] = useState<'signin' | 'forgot password'>('signin')
const [state, setState] = useState({ identity: '', secret: '' })
const [submitted, setSubmitted] = useState(false)

const identityFieldRef = useRef<HTMLInputElement>(null)
useEffect(() => {
identityFieldRef.current?.focus()
}, [mode])

const [mutate, { error, loading, data }] = useMutation(mutation)
const reinitContext = useReinitContext()
const router = useRouter()
const rawKeystone = useRawKeystone()
const redirect = useRedirect()

// if we are signed in, redirect immediately
useEffect(() => {
if (submitted) return
if (rawKeystone.authenticatedItem.state === 'authenticated') {
router.push(redirect)
}
}, [rawKeystone.authenticatedItem, router, redirect, submitted])

useEffect(() => {
if (!submitted) return

// TODO: this is horrible, we need to resolve this mess
// @ts-expect-error
if (rawKeystone.adminMeta?.error?.message === 'Access denied') {
router.push('/no-access')
return
}

router.push(redirect)
}, [rawKeystone.adminMeta, router, redirect, submitted])
}, [])

const [tryAuthenticate, { error, loading, data }] = useMutation(mutation)
const onSubmit = async (event: FormEvent<HTMLFormElement>) => {
event.preventDefault()

if (mode !== 'signin') return

try {
const { data } = await mutate({
await tryAuthenticate({
variables: {
identity: state.identity,
secret: state.secret,
},
})
if (data.authenticate?.__typename !== successTypename) return
} catch (e) {
console.error(e)
return
}

await reinitContext()
setSubmitted(true)
}

return (
<SigninContainer title="Keystone - Sign in">
<Stack gap="xlarge" as="form" onSubmit={onSubmit}>
<H1>Sign In</H1>
{error && (
<Notice title="Error" tone="negative">
{error.message}
</Notice>
)}
<SigninContainer title='Keystone - Sign in'>
<Stack gap='xlarge' as='form' onSubmit={onSubmit}>
<H1>Sign in</H1>
{error ? <Notice title='Error' tone='negative'>{error.message}</Notice> : null}
{data?.authenticate?.__typename === failureTypename && (
<Notice title="Error" tone="negative">
<Notice title='Error' tone='negative'>
{data?.authenticate.message}
</Notice>
)}
<Stack gap="medium">
<VisuallyHidden as="label" htmlFor="identity">
<Stack gap='medium'>
<VisuallyHidden as='label' htmlFor='identity'>
{identityField}
</VisuallyHidden>
<TextInput
id="identity"
name="identity"
id='identity'
name='identity'
value={state.identity}
onChange={e => setState({ ...state, identity: e.target.value })}
placeholder={identityField}
ref={identityFieldRef}
/>
{mode === 'signin' && (
<Fragment>
<VisuallyHidden as="label" htmlFor="password">
{secretField}
</VisuallyHidden>
<TextInput
id="password"
name="password"
value={state.secret}
onChange={e => setState({ ...state, secret: e.target.value })}
placeholder={secretField}
type="password"
/>
</Fragment>
)}
<Fragment>
<VisuallyHidden as='label' htmlFor='password'>
{secretField}
</VisuallyHidden>
<TextInput
id='password'
name='password'
value={state.secret}
onChange={e => setState({ ...state, secret: e.target.value })}
placeholder={secretField}
type='password'
/>
</Fragment>
</Stack>

{mode === 'forgot password' ? (
<Stack gap="medium" across>
<Button type="submit" weight="bold" tone="active">
Log reset link
</Button>
<Button weight="none" tone="active" onClick={() => setMode('signin')}>
Go back
</Button>
</Stack>
) : (
<Stack gap="medium" across>
<Button
weight="bold"
tone="active"
isLoading={
loading ||
// this is for while the page is loading but the mutation has finished successfully
data?.authenticate?.__typename === successTypename
}
type="submit"
>
Sign in
</Button>
{/* Disabled until we come up with a complete password reset workflow */}
{/* <Button weight="none" tone="active" onClick={() => setMode('forgot password')}>
Forgot your password?
</Button> */}
</Stack>
)}
<Stack gap='medium' across>
<Button
weight='bold'
tone='active'
isLoading={
loading ||
// this is for while the page is loading but the mutation has finished successfully
data?.authenticate?.__typename === successTypename
}
type='submit'
>
Sign in
</Button>
</Stack>
</Stack>
</SigninContainer>
)
Expand Down
2 changes: 1 addition & 1 deletion packages/cloudinary/src/views/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -57,7 +57,7 @@ type CloudinaryImageValue =

type CloudinaryImageController = FieldController<CloudinaryImageValue>

export const controller = (config: FieldControllerConfig): CloudinaryImageController => {
export function controller (config: FieldControllerConfig): CloudinaryImageController {
return {
path: config.path,
label: config.label,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -32,8 +32,8 @@ export function controller (
description: config.description,
graphqlSelection: config.path,
defaultValue: undefined,
deserialize: () => {},
serialize: () => ({}),
deserialize: data => data[config.path],
serialize: value => ({ [config.path]: value }),
filter: {
Filter (props) {
const { autoFocus, context, onChange, type, typeLabel, value, ...otherProps } = props
Expand Down
Original file line number Diff line number Diff line change
@@ -1,16 +1,13 @@
import React from 'react'
import { Core } from '@keystone-ui/core'
import { type AppProps } from 'next/app'
import { type DocumentNode } from 'graphql'
import { type AdminConfig, type FieldViews } from '../../../../types'
import { ErrorBoundary } from '../../../../admin-ui/components'
import { KeystoneProvider } from '../../../../admin-ui/context'

type AppConfig = {
adminConfig: AdminConfig
adminMetaHash: string
fieldViews: FieldViews
lazyMetadataQuery: DocumentNode
apiPath: string
}

Expand Down
Loading

0 comments on commit 96049f6

Please sign in to comment.