Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Mmc room groups DO NOT MERGE - JUST FOR DISCUSSIONS #240

Draft
wants to merge 7 commits into
base: main
Choose a base branch
from
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 3 additions & 2 deletions .github/workflows/docker-build-push.yml
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ on:
push:
branches:
- 'main'
- 'mmc-room-groups'
tags:
- 'v[0-9]+.[0-9]+.[0-9]+'

Expand Down Expand Up @@ -41,7 +42,7 @@ jobs:
REGISTRY_USER: ${{ github.actor }}
REGISTRY_PASS: ${{ secrets.GITHUB_TOKEN }}

- name: Docker build and push 'latest' image
- name: Docker build and push 'latest-mmc' image
if: startsWith(github.ref, 'refs/heads/')
run: >
TAG_ARGS=$(echo -n "$IMAGE_TAGS" | sed -r "s_([^ :/]+)_ --tag $REGISTRY/$IMAGE_NAME:\1 _g") &&
Expand All @@ -56,7 +57,7 @@ jobs:
env:
REGISTRY: 'ghcr.io'
IMAGE_NAME: ${{ github.repository }}
IMAGE_TAGS: latest
IMAGE_TAGS: latest-mmc
FULL_REPO_URL: "https://github.com/${{ github.repository }}"
COMMIT_HASH: ${{ github.sha }}

Expand Down
10 changes: 10 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -78,3 +78,13 @@ scp public.tgz [email protected]:projects/
ssh [email protected] -t "bash -l -c 'scripts/update-app.sh'"
rm -f public.tgz
```

### other commands

```
# run the typescript compiler to check for type errors
npm run tsc

# run the linter to check for warnings and errors
npm run lint
```
4 changes: 2 additions & 2 deletions src/apis/common.ts
Original file line number Diff line number Diff line change
Expand Up @@ -11,15 +11,15 @@ export class LoginRequiredError extends Error {
}
}

export interface ErrorDto<ErrorMessage extends string> {
export interface ErrorDto<ErrorMessageEnum extends string> {
// The time at which the error occurred.
readonly timestamp: string // 2006-01-02T15:04:05+07:00

// An internal trace id assigned to the error. Used to find logs associated with errors across our services. Display to the user as something to communicate to us with inquiries about the error.
readonly requestid: string // a8b7c6d5

// A keyed description of the error. We do not write human-readable text here because the user interface will be multi-language.
readonly message: ErrorMessage // attendee.owned.notfound or similar
readonly message: ErrorMessageEnum // attendee.owned.notfound or similar

// Optional additional details about the error. If available, will usually contain English language technobabble.
readonly details: Readonly<Record<string, readonly string[]>>
Expand Down
468 changes: 468 additions & 0 deletions src/apis/roomsrv.ts

Large diffs are not rendered by default.

13 changes: 13 additions & 0 deletions src/components/funnels/funnels/register/steps/roomshare.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
import { ReadonlyRouteComponentProps } from '~/util/readonly-types'
import { Router } from '@reach/router'
import { withPrefix } from 'gatsby'
import * as ROUTES from '~/navigation/routes'
import RoomShareJoin from '~/components/funnels/funnels/roomshare/join'
import RoomShareHome from '~/components/funnels/funnels/roomshare/home'
import RoomShareCreate from '~/components/funnels/funnels/roomshare/create'

const RoomShare = (_: ReadonlyRouteComponentProps) =>
<Router basepath={withPrefix('/register/room-share')}>
</Router>

export default RoomShare
42 changes: 38 additions & 4 deletions src/components/funnels/funnels/register/steps/summary.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -12,17 +12,24 @@
import { useFunnelForm } from '~/hooks/funnels/form'
import { Checkbox, ErrorMessage, Form } from '@eurofurence/reg-component-library'
import config from '~/config'
import { getRoomGroup } from '~/state/selectors/room-sharing'
import { GroupDto } from '~/apis/roomsrv'
import { is } from 'ramda'
import room from '~/components/funnels/funnels/hotel-booking/steps/room'

interface PropertyDefinition {
readonly id: string
readonly value: string
readonly wide?: boolean
readonly subvalue?: string
}

interface SectionProps {
readonly id: string
readonly editLink: string
readonly properties: readonly PropertyDefinition[]
readonly editText?: string
readonly showEditLink?: boolean
}

const SectionContainer = styled.section<{ readonly status: RegistrationStatus }>`
Expand Down Expand Up @@ -100,7 +107,6 @@
&:not(:first-child) {
margin-top: 2em;
}
}
`

const TermsForm = styled(Form)`
Expand All @@ -111,21 +117,40 @@
color: ${({ status }) => status === 'cancelled' ? 'var(--color-semantic-error)' : 'unset'};
`

const Section = ({ id: sectionId, editLink, properties }: SectionProps) => {
const Section = ({ id: sectionId, editLink, properties, editText, showEditLink }: SectionProps) => {
const status = useAppSelector(getStatus())!
const editTextStr = editText ?? 'Edit information'

return <SectionContainer status={status}>
<Localized id={`register-summary-section-${sectionId}-title`}><SectionTitle>{sectionId}</SectionTitle></Localized>
{status === 'cancelled' ? undefined : <Localized id="register-summary-edit"><Link css={editButtonStyle} to={editLink}>Edit information</Link></Localized>}
{status === 'cancelled' || showEditLink === false ? undefined : <Localized id="register-summary-edit"><Link css={editButtonStyle} to={editLink}>{editTextStr}</Link></Localized>}
<PropertyList>
{properties.map(({ id, value, wide = false }) => <Property key={id} wide={wide}>
{properties.map(({ id, value, subvalue, wide = false }) => <Property key={id} wide={wide}>
<Localized id={`register-summary-section-${sectionId}-property-${id}-name`}><PropertyName>{id}</PropertyName></Localized>
<PropertyDescription>{value}</PropertyDescription>
<PropertyName>{subvalue}</PropertyName>
</Property>)}
</PropertyList>
</SectionContainer>
}

const getRoomShareSectionProps = (isAttending: boolean, roomShare: GroupDto | null) => {
if (isAttending && roomShare) {
return [
{ id: 'room-share-group-name', value: roomShare.name },
{ id: 'room-share-members', value: roomShare.members.map(member => member.nickname).join('\n') },
]
}

if (isAttending && !roomShare) {
return [{ id: '', value: 'No group', subvalue: 'You can create or join one on the room sharing page' }]
}

if (!isAttending && !roomShare) {
return [{ id: '', value: 'No group', subvalue: 'Your registration needs to be approved by us first' }]
}
}

// eslint-disable-next-line max-statements
const Summary = (_: ReadonlyRouteComponentProps) => {
const registrationId = useAppSelector(getRegistrationId())!
Expand All @@ -134,6 +159,10 @@
const optionalInfo = useAppSelector(getOptionalInfo())!
const isEdit = useAppSelector(isEditMode())
const status = useAppSelector(getStatus())!
const isAttendingStatus = ['approved', 'partially-paid', 'paid', 'checked-in'].includes(status)

const roomShare = useAppSelector(getRoomGroup())
const roomShareSectionProps = getRoomShareSectionProps(isAttendingStatus, roomShare)
const locale = useCurrentLocale()
const { l10n } = useLocalization()
const { handleSubmit, register, formState: { errors } } = useFunnelForm('register-summary')
Expand All @@ -155,6 +184,11 @@
<RegistrationId>Badge number: {registrationId}</RegistrationId>
</Localized> : undefined }

{/*TODO: Localize editText*/}
{config.enableRoomshare
? <Section id="room sharing" showEditLink={isAttendingStatus} editLink="/room-share" editText="Set up" properties={roomShareSectionProps}/>

Check failure on line 189 in src/components/funnels/funnels/register/steps/summary.tsx

View workflow job for this annotation

GitHub Actions / build-and-test

Type '{ id: string; value: string; }[] | { id: string; value: string; subvalue: string; }[] | undefined' is not assignable to type 'readonly PropertyDefinition[]'.
: undefined}

<Section id="personal" editLink="/register/personal-info" properties={[
{ id: 'nickname', value: personalInfo.nickname },
{ id: 'full-name', value: `${personalInfo.firstName} ${personalInfo.lastName}` },
Expand Down
11 changes: 11 additions & 0 deletions src/components/funnels/funnels/roomshare/create.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
import type { ReadonlyRouteComponentProps } from '~/util/readonly-types'

const RoomShareCreate = (_: ReadonlyRouteComponentProps) => {
return (
<div>
{/*<RoomShareCreateForm />*/}
</div>
)
}

export default RoomShareCreate
11 changes: 11 additions & 0 deletions src/components/funnels/funnels/roomshare/home.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
import type { ReadonlyRouteComponentProps } from '~/util/readonly-types'

const RoomShareHome = (_: ReadonlyRouteComponentProps) => {
return (
<div>
<h3>Roomsharing</h3>
</div>
)
}

export default RoomShareHome
7 changes: 7 additions & 0 deletions src/components/funnels/funnels/roomshare/join.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
import type { ReadonlyRouteComponentProps } from '~/util/readonly-types'

const RoomShareJoin = (_: ReadonlyRouteComponentProps) => {
return <div>RoomShareJoin</div>
}

export default RoomShareJoin
5 changes: 5 additions & 0 deletions src/config.ts
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@ const config = checkConfig({
dayTicketEndDate: DateTime.fromISO('2024-09-21', { zone: 'Europe/Berlin' }),
earliestBirthDate: DateTime.fromISO('1901-01-01'),
minimumAge: 18,
enableRoomshare: true, // TODO: For development
allowedCountries: ['AF', 'AX', 'AL', 'DZ', 'AS', 'AD', 'AO', 'AI', 'AQ', 'AG', 'AR', 'AM', 'AW', 'AC', 'AU', 'AT', 'AZ', 'BS', 'BH', 'BD', 'BB', 'BY', 'BE', 'BZ', 'BJ', 'BM', 'BT', 'BO', 'BQ', 'BA', 'BW', 'BV', 'BR', 'IO', 'BN', 'BG', 'BF', 'BI', 'CV', 'KH', 'CM', 'CA', 'KY', 'CF', 'EA', 'TD', 'CL', 'CN', 'CX', 'CP', 'CC', 'CO', 'KM', 'CG', 'CD', 'CK', 'CR', 'HR', 'CU', 'CW', 'CY', 'CZ', 'CI', 'DK', 'DG', 'DJ', 'DM', 'DO', 'EC', 'EG', 'SV', 'GQ', 'ER', 'EE', 'SZ', 'ET', 'FK', 'FO', 'FJ', 'FI', 'FR', 'GF', 'PF', 'TF', 'GA', 'GM', 'GE', 'DE', 'GH', 'GI', 'GR', 'GL', 'GD', 'GP', 'GU', 'GT', 'GG', 'GN', 'GW', 'GY', 'HT', 'HM', 'VA', 'HN', 'HK', 'HU', 'IS', 'IN', 'ID', 'IR', 'IQ', 'IE', 'IM', 'IL', 'IT', 'JM', 'JP', 'JE', 'JO', 'IC', 'KZ', 'KE', 'KI', 'KP', 'KR', 'KW', 'KG', 'LA', 'LV', 'LB', 'LS', 'LR', 'LY', 'LI', 'LT', 'LU', 'MO', 'MG', 'MW', 'MY', 'MV', 'ML', 'MT', 'MH', 'MQ', 'MR', 'MU', 'YT', 'MX', 'FM', 'MD', 'MC', 'MN', 'ME', 'MS', 'MA', 'MZ', 'MM', 'NA', 'NR', 'NP', 'NL', 'NC', 'NZ', 'NI', 'NE', 'NG', 'NU', 'NF', 'MK', 'MP', 'NO', 'OM', 'PK', 'PW', 'PS', 'PA', 'PG', 'PY', 'PE', 'PH', 'PN', 'PL', 'PT', 'PR', 'QA', 'RE', 'RO', 'RU', 'RW', 'BL', 'SH', 'KN', 'LC', 'MF', 'PM', 'VC', 'WS', 'SM', 'ST', 'SA', 'SN', 'RS', 'SC', 'SL', 'SG', 'SX', 'SK', 'SI', 'SB', 'SO', 'ZA', 'GS', 'SS', 'ES', 'LK', 'SD', 'SR', 'SJ', 'SE', 'CH', 'SY', 'TW', 'TJ', 'TZ', 'TH', 'TL', 'TG', 'TK', 'TO', 'TT', 'TA', 'TN', 'TR', 'TM', 'TC', 'TV', 'UG', 'UA', 'AE', 'GB', 'UM', 'US', 'UY', 'UZ', 'VU', 'VE', 'VN', 'VG', 'VI', 'WF', 'EH', 'YE', 'ZM', 'ZW'],
ticketLevels: {
'standard': {
Expand Down Expand Up @@ -253,6 +254,10 @@ const config = checkConfig({
paysrv: {
url: apiPath('/paysrv/api/rest/v1'),
},
roomsrv: {
url: apiPath('/roomsrv/api/rest/v1'),
enable: true,
},
},
websiteLinks: {
// these two links need to be in the footer bar on each page
Expand Down
12 changes: 12 additions & 0 deletions src/navigation/router.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,10 @@ import * as ROUTES from './routes'
import { withPrefix } from 'gatsby'
import { useAppSelector } from '~/hooks/redux'
import { isEditMode } from '~/state/selectors/register'
import RoomShareHome from '~/components/funnels/funnels/roomshare/home'
import RoomShareCreate from '~/components/funnels/funnels/roomshare/create'
import RoomShareJoin from '~/components/funnels/funnels/roomshare/join'
import { REGISTER_ROOM_SHARE } from './routes'

export const EFRouter = () =>
<IndexPage />
Expand All @@ -39,3 +43,11 @@ export const HotelBookingRouter = () =>
<AdditionalInfo path={ROUTES.HOTEL_BOOKING_ADDITIONAL_INFO} />
<Email path={ROUTES.HOTEL_BOOKING_EMAIL} />
</Router>

export const RoomShareRouter = () => {
return <Router basepath={withPrefix(`/${REGISTER_ROOM_SHARE}`)}>
<RoomShareHome default path={ROUTES.REGISTER_ROOM_SHARE_HOME} />
<RoomShareCreate path={ROUTES.REGISTER_ROOM_SHARE_CREATE} />
<RoomShareJoin path={ROUTES.REGISTER_ROOM_SHARE_JOIN} />
</Router>
}
4 changes: 4 additions & 0 deletions src/navigation/routes.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,10 @@ export const REGISTER_TICKET_LEVEL = 'level'
export const REGISTER_PERSONAL = 'personal-info'
export const REGISTER_CONTACT = 'contact-info'
export const REGISTER_OPTIONAL = 'optional-info'
export const REGISTER_ROOM_SHARE = 'room-share'
export const REGISTER_ROOM_SHARE_HOME = 'home'
export const REGISTER_ROOM_SHARE_JOIN = 'join'
export const REGISTER_ROOM_SHARE_CREATE = 'create'
export const REGISTER_SUMMARY = 'summary'
export const REGISTER_THANK_YOU = 'thank-you'

Expand Down
14 changes: 14 additions & 0 deletions src/pages/room-share.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
import { RoomShareRouter } from '~/navigation/router'

import SEO from '~/components/seo'
import { ReadonlyRouteComponentProps } from '~/util/readonly-types'
import Layout from '~/components/layout'
import config from '~/config'

export const Head = () => <SEO title="Room Sharing" />

const RoomSharingPage = (_: ReadonlyRouteComponentProps) => <Layout deadline={config.hotelBookingLaunch}>
{RoomShareRouter()}
</Layout>

export default RoomSharingPage
3 changes: 3 additions & 0 deletions src/state/actions/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ import { ErrorAction } from './errors'
import { FormAction } from './forms'
import { RegisterAction } from './register'
import { NavigationAction } from './navigation'
import { RoomSharingAction } from './room-sharing'

export type { GetAction } from './create-action'

Expand All @@ -14,3 +15,5 @@ export type AnyAppAction =
| FormAction
| RegisterAction
| NavigationAction
| RoomSharingAction
// Add new action types here
7 changes: 7 additions & 0 deletions src/state/actions/room-sharing.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
import { createAction } from './create-action'
import { GroupDto } from '~/apis/roomsrv'

export const LoadRoomShareState = createAction<GroupDto | null, '[RoomSharing] Load room share state'>('[RoomSharing] Load room share state')

export type RoomSharingAction
= typeof LoadRoomShareState
2 changes: 2 additions & 0 deletions src/state/epics/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,11 +5,13 @@ import autosave from './autosave'
import register from './register'
import hotelBooking from './hotel-booking'
import navigation from './navigation'
import roomSharing from './room-sharing'

export default combineEpics(
auth,
autosave,
register,
hotelBooking,
navigation,
roomSharing,
)
27 changes: 27 additions & 0 deletions src/state/epics/room-sharing.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
import { findMyGroup } from '~/apis/roomsrv'
import { concatMap, of } from 'rxjs'
import { LoadRoomShareState } from '~/state/actions/room-sharing'
import { catchAppError } from '~/state/epics/operators/catch-app-error'
import { combineEpics } from 'redux-observable'
import { AnyAppAction, GetAction } from '~/state/actions'
import { AppState } from '~/state'
import { catchError } from 'rxjs/operators'

const loadNoGroup = () => of(LoadRoomShareState.create(null))

const loadMyGroup = () => findMyGroup().pipe(
concatMap(resp => {
return of(LoadRoomShareState.create(resp.response))
}),
catchError(error => {
// eslint-disable-next-line no-console
console.error('Failed to load room share state', error)

return loadNoGroup()
}),
catchAppError('room-share-load'),
)

export default combineEpics<GetAction<AnyAppAction>, GetAction<AnyAppAction>, AppState>(
loadMyGroup,
)
5 changes: 3 additions & 2 deletions src/state/models/errors.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,17 +9,18 @@ export type AppErrorOperation =
| 'registration-initiate-payment'
| 'registration-set-locale'
| 'user-info-lookup'
| 'room-share-load'
| 'unknown'

export interface ErrorReport {
readonly operation: AppErrorOperation
readonly error: unknown
}

export class AppError<ErrorCode extends string | number = string | number> extends Error {
export class AppError<ErrorCodeEnum extends string | number = string | number> extends Error {
constructor(
public category: string,
public code: ErrorCode,
public code: ErrorCodeEnum,
public detailedMessage: string,
) {
super(`${code} - ${detailedMessage}`)
Expand Down
3 changes: 3 additions & 0 deletions src/state/reducers/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,17 +5,20 @@ import auth from './auth'
import errors from './errors'
import register from './register'
import hotelBooking from './hotel-booking'
import roomSharing from './room-sharing'

export default combineReducers<{
readonly autosave: typeof autosave
readonly auth: typeof auth
readonly errors: typeof errors
readonly register: typeof register
readonly hotelBooking: typeof hotelBooking
readonly roomSharing: typeof roomSharing
}>({
autosave,
auth,
errors,
register,
hotelBooking,
roomSharing,
})
20 changes: 20 additions & 0 deletions src/state/reducers/room-sharing.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
import { GroupDto } from '~/apis/roomsrv'
import { AnyAppAction, GetAction } from '~/state/actions'
import { LoadRoomShareState } from '~/state/actions/room-sharing'

export interface RoomSharingState {
readonly roomShare: GroupDto | null
}

const defaultState: RoomSharingState = {
roomShare: null,
}

export default (state: RoomSharingState = defaultState, action: GetAction<AnyAppAction>): RoomSharingState => {
switch (action.type) {
case LoadRoomShareState.type:
return { ...state, roomShare: action.payload }
default:
return state
}
}
Loading
Loading