diff --git a/src/routes/sessions/new/index.tsx b/src/routes/sessions/new/index.tsx index 62ecf8c..06c6462 100644 --- a/src/routes/sessions/new/index.tsx +++ b/src/routes/sessions/new/index.tsx @@ -1,6 +1,12 @@ // YOLO: on -import { FunctionalComponent, h } from 'preact'; +import { FunctionalComponent, h, JSX } from 'preact'; +import { useEffect, useState } from 'preact/hooks'; + +import { fetchFullSet } from '../../../services/set'; +import { createSession } from '../../../services/session'; +import Set from '../../../components/sets/Set'; +import type { NewSessionStateType, FullSetType } from '../../../types/types'; import style from './style.css'; @@ -8,16 +14,110 @@ type NewSessionPropsType = { setId?: string, }; -const NewSession: FunctionalComponent = () => { +const NewSession: FunctionalComponent = (props) => { + const _now = new Date(); + const [set, setSet] = useState(null); + const [session, setSession] = useState({ + setId: props.setId || '', + name: { value: '', valid: false, message: 'Name can not be empty' }, + alias: { value: '', valid: false, message: 'Alias can not be empty' }, + date: { value: _now, internal: _now.toISOString(), valid: true, message: '' }, + }); + + useEffect(() => { + if (!session.setId) { + setSet(null); + return; + } + + fetchFullSet(session.setId).then((puzzleSet) => { + setSet(puzzleSet); + }, (error) => { + alert(JSON.stringify(error)); + }) + }, [session.setId]); + + function onNameChange(event: JSX.TargetedEvent): void { + const { value } = event.currentTarget; + + if (!value) { + return setSession({ ...session, name: { value, valid: false, message: 'Name can not be empty' } }); + } + + return setSession({ ...session, name: { value, valid: true, message: '' } }); + } + + function onAliasChange(event: JSX.TargetedEvent): void { + const { value } = event.currentTarget; + + if (!value) { + return setSession({ ...session, alias: { value, valid: false, message: 'Alias can not be empty' } }); + } + + if (!/^[A-Za-z0-9_-]+$/.test(value)) { + return setSession({ ...session, alias: { value, valid: false, message: 'Alias can contain only letters, numbers, dash and underscore' } }); + } + + return setSession({ ...session, alias: { value, valid: true, message: '' } }); + } + + function onDateChange(event: JSX.TargetedEvent): void { + const { validity, valueAsDate } = event.currentTarget; + + if (!validity.valid || !valueAsDate) { + return setSession({ ...session, date: { value: new Date(), valid: false, message: 'Invalid date' } }); + } + + return setSession({ ...session, date: { value: valueAsDate, internal: valueAsDate.toISOString(), valid: true, message: '' } }); + } + + async function createSessionClick() { + try { + const alias = await createSession(session); + alert(`Session created! Session alias = ${alias}`); + } catch(error) { + alert(JSON.stringify(error)); + } + } + return ( <>
NEW SESSION
- +
+
+
Set id:
+ setSession({ ...session, setId: event.currentTarget.value })} class={style.setIdInput} /> +
+
+
Name:
+ + {session?.name?.message &&
{session?.name?.message}
} +
+
+
Alias:
+ + {session?.alias?.message &&
{session?.alias?.message}
} +
+
+
Date:
+ + {session?.date?.message &&
{session?.date?.message}
} +
+
+
+
Puzzle set:
+
); diff --git a/src/routes/sessions/new/style.css b/src/routes/sessions/new/style.css index ee6841c..6995768 100644 --- a/src/routes/sessions/new/style.css +++ b/src/routes/sessions/new/style.css @@ -10,5 +10,32 @@ } .inputs { - padding: .5em 0; + padding: .5em; +} +.inputValid { + border-color: #177317; +} +.inputInvalid { + border-color: #9b1d1d; + background: #fde4e4; +} +.errorMessage { + color: #9b1d1d; + font-size: 80%; +} +.propName { + font-weight: bold; +} +.setId { + display: flex; +} +.name { + display: flex; +} +.alias { + display: flex; +} + +.setContainerHeading { + font-size: 1.2rem; } diff --git a/src/services/session.ts b/src/services/session.ts new file mode 100644 index 0000000..e795c2a --- /dev/null +++ b/src/services/session.ts @@ -0,0 +1,26 @@ +import type { NewSessionStateType } from '../types/types'; + +// const API_BASE_URL = `${window.location.origin}/api/`; +const API_BASE_URL = 'http://localhost:3000/api/'; + +export async function createSession(newSession: NewSessionStateType): Promise { + const url = new URL('v1/createPuzzleSession', API_BASE_URL); + + const response = await fetch(url.toString(), { + method: 'POST', + body: JSON.stringify({ + name: newSession.name.value, + puzzleSetId: newSession.setId, + alias: newSession.alias.value, + date: newSession.date.internal, // ISO string + }), + headers: { 'Content-Type': 'application/json' }, + }); + + if (!response.ok) { + throw await response.json(); + } + + const { puzzleSession } = await response.json(); + return puzzleSession.alias; +}; diff --git a/src/types/types.ts b/src/types/types.ts index 13a5462..36e7a8e 100644 --- a/src/types/types.ts +++ b/src/types/types.ts @@ -1,7 +1,7 @@ export type GameType = 'CSS' | 'JS' | 'Lodash'; export type GameTypeType = 'cssqd' | 'jsqd' | '_qd'; -type NewPuzzleStatePropertyType = { +type ValidityStatePropertyType = { value: ValueT, internal?: InternalT | null, valid: boolean, @@ -9,14 +9,14 @@ type NewPuzzleStatePropertyType = { } export type NewPuzzleStateType = { - name: NewPuzzleStatePropertyType, - description: NewPuzzleStatePropertyType, - timeLimit: NewPuzzleStatePropertyType, + name: ValidityStatePropertyType, + description: ValidityStatePropertyType, + timeLimit: ValidityStatePropertyType, expected: string, - banned: NewPuzzleStatePropertyType, - input: NewPuzzleStatePropertyType, - solution: NewPuzzleStatePropertyType, - solutionLengthLimit: NewPuzzleStatePropertyType, + banned: ValidityStatePropertyType, + input: ValidityStatePropertyType, + solution: ValidityStatePropertyType, + solutionLengthLimit: ValidityStatePropertyType, }; export type NewSetStateType = { @@ -24,6 +24,14 @@ export type NewSetStateType = { order: FullPuzzleType[], }; +export type NewSessionStateType = { + setId: string, + name: ValidityStatePropertyType, + alias: ValidityStatePropertyType, + date: ValidityStatePropertyType, + participantLimit?: ValidityStatePropertyType, +}; + export type UserType = { provider: string, providerId: string,