Úkol

Tvoja úloha bude implementovať jednoduchou zákaznickou stránku na front-endu v knihovně React v TypeScriptu. React aplikace se bude napojovat na GraphQL server pro získání údajů a na REST API pro zpřístupnění URL adresy obrázku. Skeleton projektu se nachází na githubu. Použití externích knihoven je na vašich preferencích.

Design naleznete na této URL adrese: https://www.figma.com/design/uIKNOAap7gIqOgXCTQloBD/Untitled?node-id=0-1&t=OmKUSUzJo2mADLA5-1

Prototyp: https://www.figma.com/proto/uIKNOAap7gIqOgXCTQloBD/Untitled?node-id=1-2&t=OmKUSUzJo2mADLA5-1

V rámci designu prosím o implementaci dvou podstránek:

Řídíme se přístupem mobile-first, takže design je orientovaný převážně na mobily, ale výsledné stránky musí být responzivní. V případě větších rozlišení můžete zobrazit více informací o události.

Co není povinné dělat:

V rámci jednoduchosti neimplementujte funkcionalitu ani design přihlašování a odhlašování – místo toho dostanete token, přes který budete moci získávat data. Na stránce Moje rezervace je implementace funkcionality Trasa a položek ze submenu Přidat do kalendáře a přidat do kontaktů volitelná. Všechny neimplementované položky z designu jako v submenu (odhlásit se, přidat do kalendáře...) a tlačítka (trasa…) nadizajnujte bez nutnosti implementace funkcionality.

Vysvětlivky k funkcionalitám:

Vytvoření další rezervace přesměruje uživatele na novou URL stránku: https://{{alias}}.snippet-test.myfox.cz/form/show.

Obrázek získáte na této URL adrese: https://api.myfox.cz/test/photo/{{secret}}. Autorizační token není potřebný.

URL GraphQL serveru: https://cmyp37vc53jlbv5wa3nrnez6mi0dktla.lambda-url.eu-central-1.on.aws/. Pro přístup k datům využijte tento testovací token v hlavičkách vašich požadavků: "X-Api-Key": "da2-gcyvktbwpfhnznbpdaghdbyf7m"

ID zákazníka je „cm0b8kilabkyu0783rc2uuzax“.

Resources:

GraphQL dokumentace:

Popis dat:

Bude vás zajímat především čtyři modely:

Customer

Informace o zákazníkovi, obrázek je v picture.

type Customer {
	id: String!
	createdAt: AWSDateTime!
	name: String
	surname: String
	phone: String
	email: String
	address: String
	locale: Locale
	picture: File
}
                

Calendar

Samotné rezervace. Rezervace se stavem Open patří do sekce aktuální rezervace, zbytek (kromě Canceled) patří do „navštivte znovu“. Obrázek události je první záznam v carts -> item -> picture. Pokud obrázek neexistuje, použijte logo subjektu (microsite -> logo).

type Calendar {
	id: String!
	capacity: Int
	eventType: EventType!
	carts: [Cart!]
	createdAt: AWSDateTime!
	employees: [Employee!]
	from: AWSDateTime
	note: String
	shop: Shop
	subject: Subject
	state: CalendarState!
	to: AWSDateTime
	updatedAt: AWSDateTime!
	isOnlineReservation: Boolean
	canBeCancelUntil: AWSDateTime
}
                

Shop

Informace o provozovně (adresa události, telefonní číslo, název...).

type Shop {
	id: String!
	name: String!
	address: Address
	email: String
	phone: String
}
                

Subject

Informace o samotné firmě. Zde vás bude zajímat alias a logo (microsite -> logo).

type Subject {
	id: String!
	name: String
	marketingName: String
	alias: String!
	address: Address
	microsite: Microsite
	customersLastChange: AWSDateTime
	locale: Locale
	currency: Currency
	dic: String
	ico: String
	vatPayer: Boolean
}
                

Employee

type Employee {
	id: String!
	color: String
	showCstContact: Boolean!
	notifyPhone: String
	receiptName: String
	userMyFox: UserMyFox
}
                

UserMyFox

type UserMyFox {
	id: String!
	address: String
	email: String
	name: String
	phone: String
	surname: String
	locale: Locale
	userPicture: File
}
                

File

Důležité k vytvoření URL na obrázek

type File {
	id: String!
	secret: String!
}
                

Cart

type Cart {
	id: String!
	count: Int
	item: Item
	name: String!
	price: Float
	priceVat: Float!
	vat: Float!
}
                

Item

type Item {
	id: String!
	color: String!
	code: String
	stockEnabled: Boolean
	warranty: String
	manufacturer: String
	duration: Int
	pauseAfter: Int
	pauseBefore: Int
	name: String!
	note: String
	ord: Int
	price: Float
	priceVat: Float
	picture: File
}
                

CalendarState

enum CalendarState {
	Open
	Paid
	Canceled
	Storno
	Test
}
                

EventType

enum EventType {
	Individual
	Group
	Unavailability
}
                

Queries:

Použijte na získání údajů customer a calendars queries resolvery:

getCustomer(
	where: CustomerWhereUniqueInput!
): Customer

type CustomerWhereUniqueInput {
 	id: String
}

listCalendars(where: CalendarWhereInput, orderBy: CalendarOrderByInput): [Calendar]!

type CalendarWhereInput {
	id: StringFilter
	capacity: IntNullableFilter
	customers: CustomerFilter
	employees: EmployeeFilter
	state: CalendarStateEnumFilter
	carts: CartFilter
	from: AWSDateTimeNullableFilter
	to: AWSDateTimeNullableFilter
	eventType: EventTypeEnumFilter
}

type CustomerFilter {
	some: CustomerWhereInputWithoutNullables
	every: CustomerWhereInputWithoutNullables
	none: CustomerWhereInputWithoutNullables
}

type CustomerWhereInputWithoutNullables {
	id: StringFilter
	createdAt: AWSDateTimeFilter
	star: BooleanNullableFilter
	updatedAt: AWSDateTimeFilter
	warning: BooleanNullableFilter
	name: StringNullableFilter
	surname: StringNullableFilter
	phone: StringNullableFilter
	email: StringNullableFilter
}

type EmployeeFilter {
	some: EmployeeWhereInputWithoutNullables
	every: EmployeeWhereInputWithoutNullables
	none: EmployeeWhereInputWithoutNullables
}

type EmployeeWhereInputWithoutNullables {
	id: StringFilter
	color: StringNullableFilter
	createdAt: AWSDateTimeFilter
	bookEnabled: BooleanFilter
	showCstContact: BooleanFilter
	notifyPhone: StringNullableFilter
	ord: IntNullableFilter
	receiptName: StringNullableFilter
}

type CartFilter {
	some: CartWhereInputWithoutNullables
	every: CartWhereInputWithoutNullables
	none: CartWhereInputWithoutNullables
}

type CartWhereInputWithoutNullables {
	id: StringFilter
	count: IntNullableFilter
	createdAt: AWSDateTimeFilter
	name: StringFilter
	priceVat: FloatFilter
	updatedAt: AWSDateTimeFilter
	vat: FloatFilter
	price: FloatNullableFilter
}

type StringFilter {
	contains: String
	endsWith: String
	equals: String
	in: [String!]
	not: StringFilter
	notIn: [String!]
	startsWith: String
	mode: String
}

type AWSDateTimeFilter {
	equals: AWSDateTime
	gt: AWSDateTime
	gte: AWSDateTime
	in: [AWSDateTime!]
	lt: AWSDateTime
	lte: AWSDateTime
	not: AWSDateTimeFilter
	notIn: [AWSDateTime!]
}

type BooleanFilter {
	equals: Boolean
	not: BooleanFilter
}

type IntNullableFilter {
	equals: Int
	gt: Int
	gte: Int
	in: [Int!]
	lt: Int
	lte: Int
	not: IntFilter
	notIn: [Int!]
	isNull: Boolean
}

type FloatFilter {
	equals: Float
	gt: Float
	gte: Float
	in: [Float!]
	lt: Float
	lte: Float
	not: FloatFilter
	notIn: [Float!]
	isNull: Boolean
}

type CalendarStateEnumFilter {
	equals: CalendarState
	in: [CalendarState!]
	not: CalendarStateEnumFilter
	notIn: [CalendarState!]
}

type EventTypeEnumFilter {
	equals: EventType
	in: [EventType!]
	not: EventTypeEnumFilter
	notIn: [EventType!]
}
                

CalendarOrderByInput

type CalendarOrderByInput {
	id: OrderByArg
	billNumber: OrderByArg
	capacity: OrderByArg
	createdAt: OrderByArg
	from: OrderByArg
	note: OrderByArg
	paidAt: OrderByArg
	payType: OrderByArg
	eventType: OrderByArg
	state: OrderByArg
	to: OrderByArg
	updatedAt: OrderByArg
}

enum OrderByArg {
	ASC
	DESC
}
                

Mutation:

Budete potřebovat pouze updateCustomer:

updateCustomer(
	data: CustomerUpdateInput!
	where: CustomerWhereUniqueInput!
): Customer

type CustomerUpdateInput {
	name: String
	surname: String
	phone: String
	email: String
	address: String
	locale: Locale
}

type CustomerWhereUniqueInput {
	id: String
}
                

V případě technických dotazů k úkolu mě kontaktujte na této emailové adrese: lukco@myfox.cz