Verified Commit f627008e authored by Paweł Cierzniakowski's avatar Paweł Cierzniakowski 🐘
Browse files

Recruitment task

parents
# Zadanie rekrutacyjne
Zadanie rekrutacyjne polega na napisaniu programu, który ma przedstawiać proces
zakupowy książek (pobieranie danych o książkach z API, dodawanie i usuwanie
produktów z koszyka, wypełnienie formularza danymi osobowymi potrzebnymi do
realizacji zamówienia).
Rozwiązanie zadania należy umieścić w folderze _front_.
### Aplikacja powinna składać się z 3 podstron:
#### 1. Strona główna
###### Lista funkcjonalności składająca się z:
* pobieranie danych z API po wejściu na stronę aplikacji (**GET** _/book_),
* wyświetlenie wcześniej pobranych danych w konkretnym formacie, tj. każdą
książkę powinien przedstawiać jeden blok, który ma w sobie zawierać okładkę
książki, jej tytuł, autora, liczbę stron oraz przycisk:
* `DODAJ DO KOSZYKA`, który będzie dodawał konkretną pozycję do koszyka.
* przycisk, który umożliwia przejście do koszyka.
#### 2. Strona prezentująca koszyk
###### Lista funkcjonalności składająca się z:
* wyświetlanie listy wybranych przez użytkownika książek,
* przycisk `DALEJ`, który będzie kierował do etapu podsumowania zamówienia.
##### 3. Strona zawierająca formularz potrzebny do złożenia zamówienia
###### Lista funkcjonalności składająca z:
* przygotowanie formularza z polami:
* imię,
* nazwisko,
* miejscowość,
* kod pocztowy.
* przycisk `ZAMAWIAM I PŁACĘ` po naciśnięciu którego zostaną wysłane dane pod
endpoint **POST** _/order_.
###### Funkcjonalności dodatkowe:
* walidacja formularza.
### Spis **wymaganych** technologii przy pracy z zadaniem
* react,
* dowolna biblioteka do zarządania stanem np. redux.
## Dokumentacja API
Dokumentacja w formacie OpenAPI 3 dostępna jest po uruchomieniu mock serwera
i przejściu na adres: [localhost:3001/docs](http://localhost:3001/docs).
## Uruchamianie środowiska deweloperskiego
Wymagane jest posiadanie Node.js (zaleca się dodatkowo Yarn). Projekt wymaga
instalacji i uruchomienia API mock serwera:
```bash
cd api
yarn
yarn start
```
FRONT_HOST=http://localhost:3000
# Dependency directories
/node_modules/
# Logs
/npm-debug.log*
/yarn-debug.log*
/yarn-error.log*
const express = require('express');
const logger = require('morgan');
const app = express();
const swaggerUi = require('swagger-ui-express');
const { NotFoundError, RouteNotFoundError, FormValidationError } = require('./errors');
app.use(logger('dev'));
app.disable('etag');
app.use(express.json());
app.use(express.urlencoded({extended: true}));
app.use((req, res, next) => {
res.header('Access-Control-Allow-Origin', process.env.FRONT_HOST);
res.header('Access-Control-Allow-Methods', 'GET, POST, OPTIONS');
res.header('Access-Control-Allow-Headers', 'Origin, X-Requested-With, Content-Type, Accept');
res.header('Access-Control-Allow-Credentials', 'true');
'OPTIONS' === req.method ? res.sendStatus(200) : next();
});
app.use('/api/book', require('./routes/book'));
app.use('/api/order', require('./routes/order'));
app.use('/docs', swaggerUi.serve, swaggerUi.setup(require('./files/swagger.json')));
app.use('/static/cover', express.static(__dirname + '/files/cover'));
app.use((req, res, next) => {
next(new RouteNotFoundError(req.method, req.url));
});
app.use((err, req, res, next) => {
let response = { error: { message: err.message } };
let status = 500;
if (err instanceof NotFoundError) {
status = 404;
}
if (err instanceof FormValidationError) {
status = 422;
response.error.violations = err.violations;
}
return res.status(status).json(response);
});
module.exports = app;
#!/usr/bin/env node
require('dotenv').load();
const app = require('../app');
const http = require('http');
const normalizePort = val => {
const port = parseInt(val, 10);
return isNaN(port) ? val : port >= 0 ? port : false;
};
const onError = error => {
if (error.syscall !== 'listen') {
throw error;
}
const bind = typeof port === 'string' ? 'Pipe ' + port : 'Port ' + port;
switch (error.code) {
case 'EACCES':
console.error(bind + ' requires elevated privileges');
process.exit(1);
break;
case 'EADDRINUSE':
console.error(bind + ' is already in use');
process.exit(1);
break;
default:
throw error;
}
};
const onListening = () => {
const addr = server.address();
const bind = typeof addr === 'string' ? 'pipe ' + addr : 'port ' + addr.port;
console.log(' >> API Mock server started on ' + bind);
};
const port = normalizePort(process.env.PORT || '3001');
app.set('port', port);
const server = http.createServer(app);
server.listen(port);
server.on('error', onError);
server.on('listening', onListening);
class DomainError extends Error {
constructor(message) {
super(message);
this.name = this.constructor.name;
Error.captureStackTrace(this, this.constructor);
}
}
class NotFoundError extends DomainError { }
class ResourceNotFoundError extends NotFoundError {
constructor(resource, id) {
super(`Resource ${resource}::${id} was not found.`);
}
}
class RouteNotFoundError extends NotFoundError {
constructor(method, route) {
super(`[${method}] ${route} was not found.`);
}
}
class FormValidationError extends DomainError {
constructor(errors) {
super('Validation failed.');
this.violations = errors;
}
}
module.exports = {
NotFoundError,
ResourceNotFoundError,
RouteNotFoundError,
FormValidationError,
};
[
{
"id": 457,
"title": "Matematyka 1. Podręcznik. Zakres podstawowy",
"author": "M. Karpiński, M. Dobrowolska, M. Braun, J. Lech",
"cover_url": "/static/cover/book/457.jpg",
"pages": 280,
"price": 3200,
"currency": "PLN"
},
{
"id": 458,
"title": "Matematyka 1. Podręcznik. Zakres rozszerzony",
"author": "M. Karpiński, M. Dobrowolska, M. Braun, J. Lech",
"cover_url": "/static/cover/book/458.jpg",
"pages": 300,
"price": 3300,
"currency": "PLN"
},
{
"id": 905,
"title": "Nowa Matematyka z plusem 5. Podręcznik",
"author": "M. Dobrowolska, M. Jucewicz, M. Karpiński, P. Zarzycki",
"cover_url": "/static/cover/book/905.jpg",
"pages": 256,
"price": 2990,
"currency": "PLN"
},
{
"id": 1227,
"title": "Język polski 6. Między nami. Podręcznik",
"author": "A. Łuczak, A. Murdzek",
"cover_url": "/static/cover/book/1227.jpg",
"pages": 344,
"price": 3230,
"currency": "PLN"
},
{
"id": 1228,
"title": "Nowa Matematyka z plusem 6. Podręcznik",
"author": "M. Dobrowolska, M. Jucewicz, M. Karpiński, P. Zarzycki",
"cover_url": "/static/cover/book/1228.jpg",
"pages": 268,
"price": 3190,
"currency": "PLN"
},
{
"id": 1246,
"title": "Matematyka z plusem 7. Podręcznik",
"author": "praca zbiorowa pod redakcją M. Dobrowolskiej",
"cover_url": "/static/cover/book/1246.jpg",
"pages": 336,
"price": 3420,
"currency": "PLN"
},
{
"id": 1250,
"title": "Matematyka z plusem 4. Podręcznik",
"author": "M. Dobrowolska, M. Jucewicz, M. Karpiński, P. Zarzycki",
"cover_url": "/static/cover/book/1250.jpg",
"pages": 256,
"price": 3190,
"currency": "PLN"
},
{
"id": 1269,
"title": "Język polski 4. Między nami. Podręcznik. Nowa szkoła podstawowa",
"author": "A. Łuczak, A. Murdzek, K. Krzemieniewska-Kleban",
"cover_url": "/static/cover/book/1269.jpg",
"pages": 352,
"price": 3230,
"currency": "PLN"
},
{
"id": 1270,
"title": "Język polski 7. Między nami. Podręcznik. Nowa szkoła podstawowa",
"author": "A. Łuczak, E. Prylińska, A. Suchowierska, R. Maszka",
"cover_url": "/static/cover/book/1270.jpg",
"pages": 376,
"price": 3430,
"currency": "PLN"
},
{
"id": 1271,
"title": "Historia 4. Podróże w czasie. Podręcznik. Nowa szkoła podstawowa",
"cover_url": "/static/cover/book/1271.jpg",
"author": "T. Małkowski",
"pages": 160,
"price": 3050,
"currency": "PLN"
},
{
"id": 1272,
"title": "Historia 7. Podróże w czasie. Podręcznik. Nowa szkoła podstawowa",
"cover_url": "/static/cover/book/1272.jpg",
"author": "T. Małkowski",
"pages": 256,
"price": 3410,
"currency": "PLN"
},
{
"id": 1365,
"title": "Matematyka z plusem 5. Podręcznik na rok 2018/2019",
"author": "M. Dobrowolska, M. Jucewicz, M. Karpiński, P. Zarzycki",
"cover_url": "/static/cover/book/1365.jpg",
"pages": 272,
"price": 3190,
"currency": "PLN"
},
{
"id": 1374,
"title": "Matematyka z plusem 8. Podręcznik",
"author": "praca zbiorowa pod redakcją M. Dobrowolskiej",
"cover_url": "/static/cover/book/1374.jpg",
"pages": 288,
"price": 3420,
"currency": "PLN"
},
{
"id": 1403,
"title": "Język polski 5. Między nami. Podręcznik. Nowa szkoła podstawowa",
"author": "A. Łuczak, A. Murdzek",
"cover_url": "/static/cover/book/1403.jpg",
"pages": 390,
"price": 3230,
"currency": "PLN"
},
{
"id": 1409,
"title": "Język polski 8. Między nami. Podręcznik. Nowa szkoła podstawowa",
"author": "A. Łuczak, E. Prylińska, K. Krzemieniewska-Kleban, A. Suchowierska",
"cover_url": "/static/cover/book/1409.jpg",
"pages": 387,
"price": 3430,
"currency": "PLN"
},
{
"id": 1415,
"title": "Historia 5. Podróże w czasie. Podręcznik. Nowa szkoła podstawowa",
"cover_url": "/static/cover/book/1415.jpg",
"author": "T. Małkowski",
"pages": 224,
"price": 3050,
"currency": "PLN"
},
{
"id": 1417,
"title": "Historia 8. Podróże w czasie. Podręcznik. Nowa szkoła podstawowa",
"cover_url": "/static/cover/book/1417.jpg",
"author": "T. Małkowski",
"pages": 264,
"price": 3410,
"currency": "PLN"
}
]
Markdown is supported
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment