Booksys is a booking system that allows different companies to book offices in a given event.
Explore the docs »
View Demo
·
Report Bug
·
Request Feature
Table of Contents
Booksys is a project focused mainly on the frontend features; However the API, DB schema and migrations are all defined but not implemented; the project uses mock data to display the backend resources correctly.
Bellow is the URLs to check the different application states:
URL | State |
---|---|
https://rxluz.github.io/booksys/#/f76a49f4-73c4-44ce-8fa1-7242bd3d3cc6 | Initial request booking form to Coca Cola users, only accepts emails with the following domains: - cocacola.com - cocacola.ie |
https://rxluz.github.io/booksys/#/f76a49f4-73c4-44ce-1fa1-7242bd3d3cc6 | Initial page to Pepsi Cola users, only accepts emails with the following domains: - pepsi.com - pepsi.ie |
https://rxluz.github.io/booksys/#/f76a49f4-73c4-44ce-1fa1-7242bd3d3ac6 | Page displayed when the event already happened |
https://rxluz.github.io/booksys/#/invalidCompanyId | Page displayed when the company doesn't exist |
https://rxluz.github.io/booksys#/f76a49f4-73c4-44ce-8fa1-7242bd3d3cc6/booking/a76a49f4-73c4-44ce-8fa1-7242bd3d3cc6 | Success page that is displayed when the user confirmed the booking using the link sent by their email. |
https://rxluz.github.io/booksys#/f76a49f4-73c4-44ce-8fa1-7242bd3d3cc6/booking/a7aa49f4-73c4-44ce-8fa1-7242bd3d3cc6 | Cancel booking page that is displayed when the user cancel the booking. |
https://rxluz.github.io/booksys#/f76a49f4-73c4-44ce-8fa1-7242bd3d3cc6/booking/a74t49f4-73c4-44ce-8fa1-7242bd3d3cc6 | Change booking page that is displayed when the requested time and seats preferences isn't available anymore. |
Two companies, COKE and PEPSI, are sharing an office building but as they are competitors, they don’t trust each other. Tomorrow is COLA day (for one day), that the two companies are celebrating. They are gathering a number of business partners in the building. In order to optimize space utilization, they have decided to set-up a joint booking system where any user can book one of the 20 meeting rooms available, 10 from each company (C01, C02, ..., C10 and P01, P02, ...., P10).
The booking system has the following functionalities:
● Users can see meeting rooms availability
● Users can book meeting rooms by the hour (first come first served)
● Users can cancel their own reservations
- Install Poedit to edit/add translations
- Install latest NodeJS and NPM versions
- Install Git
npm install --global yarn
git clone https://github.com/rxluz/booksys
cd booksys
yarn install
cp .env.example .env
(Mac/Linux) orcopy .env.example .env
(Windows)yarn client:start
The application will run in localhost:3000/booksys#/f76a49f4-73c4-44ce-8fa1-7242bd3d3cc6
Command | Description |
---|---|
yarn client:start |
Start the frontend application in watch mode |
yarn client:build |
Generate the frontend assets in build folder |
yarn client:deploy |
Deploy the application to GitHub Pages |
yarn client:test |
Run the tests created after the last commit and watch for new tests |
yarn client:cypress:open |
Run the E2E tests |
yarn client:test:coverage |
Run the tests and generate a coverage report in coverage folder |
yarn client:lighthouse |
Generate a lighthouse report in build/lighhouse folder |
yarn client:build-storybook |
Generate the storybook assets in build/storybook folder |
yarn client:bundle-analysis |
Generate the bundle size report in build folder |
yarn client:eject |
Allows to customize the configuration under Create-React-App, please note that this is a one-way operation and can't be undone |
yarn client:translation:extract |
Extract from code all the translatable strings |
yarn client:translation:import |
Import from .po files the translations |
yarn client:storybook |
Run the storybook in watch mode |
Title | Description |
---|---|
Lighthouse report | Lighthouse is an open-source, automated tool for improving the quality of web pages. You can run it against any web page, public or requiring authentication. It has audits for performance, a11y, progressive web apps, SEO and more. |
Storybook components | Storybook is an open-source tool for building UI components and pages in isolation. It streamlines UI development, testing, and documentation. |
Tests coverage report | Show all the tests done and how much they cover the code. |
Devices screenshots | Application screenshots |
Application diagrams | All flows and rules available in the app |
DB Schema | Show the database tables, foreign keys and fields |
Wireframes | The initial draft to build booksys |
Mockups | Low fidelity layout |
Figma | High fidelity layout |
API Documentation | Show all available endpoints, fields and methods |
The initial idea was to find a way to connect Booksys with the Metamask wallet; the purpose was to allow the users to log in with Metamask to see their bookings.
Once this is a fabulous solution from a technical perspective, it wasn't so good from a business perspective; the main problems found were: Users cannot log in from Mobile devices using Metamask wallets Users with Metamask wallet could be users not authorised to do this action (non-Pepsi/coca-cola employees)
So, to ensure that the application will run on mobile devices and only authorised users will use this, the solution required a booking confirmation from the corporate user emails; in this way, we avoid any attempts to fake bookings.
Another problem found was about the booking offices privacy; the initial solution was to display a calendar to the user with the available times and seats, however when the user access this application for the first time, we can't guarantee that this user is an authorised user.
The solution was to display the office availability only after the users confirmed their identity using a unique link sent by their emails.
Finally, to ensure that Pepsi users don't see the initial Coca-cola office availability and vice-versa, the Booksys URL to each company is different. This solution doesn't guarantee 100% privacy about each company office availability, but it solves most privacy issues cases.
First wireframe iteration | Second wireframe iteration |
Third wireframe iteration |
Users can:
- See the event name, address and date
- Get directions to the event
- Book a new office with the user preferences about seats and time
- Confirm a booking via email
- See other preferences options in their confirmation email
- See their confirmed bookings
- Cancel a booking
- See their cancelled bookings
- Find other seats and times options
Booksys uses GitHub Pages to store all the frontend assets. To make it faster, Create React App with Webpack splits the Javascript and CSS assets in chunks to ensure that a Booksys page only will call the resources required to load that specific page.
Also, we use Google Fonts to load the main font (Maven Pro) and the library animate.css stored in CloudFlare to create some animations like fade-in in change booking page pagination.
Lastly, the application uses Polyfill.io to ensure compatibility with different browsers, Google Analytics to track the user behaviour and Sentry to track applications errors.
├── CHANGELOG.md
├── CNAME
├── LICENSE
├── Procfile
├── README.md
├── build
├── coverage
│ ├── clover.xml
│ ├── coverage-final.json
│ ├── lcov-report
│ └── lcov.info
├── cypress
│ └── integration
│ └── home_spec.js
├── docs
├── eslint-config.json
├── jsconfig.json
├── migrations
│ └── tables.sql
├── package-lock.json
├── package.json
├── public
├── security_report.json
├── src
│ ├── common
│ │ ├── components
│ │ │ ├── button
│ │ │ │ ├── Button.js
│ │ │ │ ├── Button.scss
│ │ │ │ ├── Button.spec.js
│ │ │ │ ├── Button.stories.js
│ │ │ │ └── index.js
│ │ │ ├── companyCard
│ │ │ │ ├── CompanyCard.js
│ │ │ │ ├── CompanyCard.scss
│ │ │ │ ├── CompanyCard.spec.js
│ │ │ │ ├── CompanyCard.stories.js
│ │ │ │ ├── companyCardBackground.png
│ │ │ │ └── index.js
│ │ │ ├── input
│ │ │ │ ├── Input.js
│ │ │ │ ├── Input.scss
│ │ │ │ ├── Input.spec.js
│ │ │ │ ├── Input.stories.js
│ │ │ │ └── index.js
│ │ │ ├── loading
│ │ │ │ ├── Loading.js
│ │ │ │ ├── Loading.spec.js
│ │ │ │ └── index.js
│ │ │ ├── officeCard
│ │ │ │ ├── OfficeCard.js
│ │ │ │ ├── OfficeCard.scss
│ │ │ │ ├── OfficeCard.spec.js
│ │ │ │ ├── OfficeCard.stories.js
│ │ │ │ └── index.js
│ │ │ ├── pagination
│ │ │ │ ├── Pagination.js
│ │ │ │ ├── Pagination.scss
│ │ │ │ ├── Pagination.spec.js
│ │ │ │ ├── Pagination.stories.js
│ │ │ │ └── index.js
│ │ │ └── warning
│ │ │ ├── Warning.js
│ │ │ ├── Warning.scss
│ │ │ ├── Warning.spec.js
│ │ │ ├── Warning.stories.js
│ │ │ ├── email.svg
│ │ │ ├── index.js
│ │ │ └── something-wrong.svg
│ │ ├── locales
│ │ │ ├── en.mo
│ │ │ ├── en.po
│ │ │ ├── pt_BR.mo
│ │ │ ├── pt_BR.po
│ │ │ ├── template.pot
│ │ │ └── translations.js
│ │ ├── routes
│ │ │ ├── RoutesList.js
│ │ │ └── TestRoute.js
│ │ └── utils
│ │ ├── browser.utils.js
│ │ ├── colours.constants.js
│ │ ├── colours.scss
│ │ ├── general.constants.js
│ │ ├── general.utils.js
│ │ ├── os.utils.js
│ │ ├── state.utils.js
│ │ ├── storybookMode.js
│ │ ├── storybookMode.scss
│ │ └── test.utils.js
│ ├── enhancers
│ │ └── monitorReducer.js
│ ├── index.css
│ ├── index.js
│ ├── middleware
│ │ └── logger.js
│ ├── modules
│ │ ├── booking
│ │ │ ├── Booking.actions.js
│ │ │ ├── Booking.constants.js
│ │ │ ├── Booking.js
│ │ │ ├── Booking.services.js
│ │ │ ├── change
│ │ │ │ ├── Change.page.js
│ │ │ │ ├── Change.scss
│ │ │ │ └── Change.spec.js
│ │ │ ├── create
│ │ │ │ ├── Create.constants.js
│ │ │ │ ├── Create.js
│ │ │ │ ├── Create.page.js
│ │ │ │ ├── Create.scss
│ │ │ │ ├── Create.services.js
│ │ │ │ └── Create.spec.js
│ │ │ └── status
│ │ │ ├── Status.page.js
│ │ │ ├── Status.scss
│ │ │ └── Status.spec.js
│ │ ├── company
│ │ │ ├── Company.actions.js
│ │ │ ├── Company.constants.js
│ │ │ ├── Company.js
│ │ │ ├── Company.page.js
│ │ │ └── Company.services.js
│ │ └── main
│ │ ├── BodyClasses.js
│ │ ├── Main.js
│ │ ├── Main.scss
│ │ ├── NotFound.js
│ │ ├── NotFound.svg
│ │ ├── background.png
│ │ ├── loading.svg
│ │ └── logo.svg
│ ├── reducers
│ │ ├── booking.js
│ │ ├── company.js
│ │ ├── configureStore.js
│ │ ├── index.js
│ │ └── settings.js
│ ├── serviceWorker.js
│ └── setupTests.js
├── yarn-error.log
└── yarn.lock
The design adopted in the DB Schema allows to manage more than two companies and more than one event; the idea is allowing Booksys to control future events without the need to change tables.
This version doesn't manage the users directly; instead, it stores the user information (name and email) on the booking page.
Once the DB is multi-event ready, the API only allows one event per client; this is a limitation that could be easly removed in future versions due to how DB is structured.
Download database schemaView database migrations
The Booksys API is organized around REST. Our API has predictable resource-oriented URLs, allows form-encoded request bodies, returns JSON-encoded responses, and uses conventional HTTP response codes, authentication, and verbs.
You can utilize the Booksys API in test mode, which does not alter your live data. Use the links available in the example request section to mock the endpoints.
Note that the mock API will not change according to the post parameters.
Booksys uses conventional HTTP response codes to indicate the success or failure of an API request. In general: Codes in the 2xx
range indicate success. Codes in the 4xx
range indicate an error that failed given the information provided (e.g., a required parameter was omitted, a charge failed, etc.). Codes in the 5xx
range indicate an error with Booksys's servers.
Some 4xx
errors that could be handled programmatically and include an error array in the response body section that briefly explains the error reported.
Booksys uses Postman to mock data and store the URL in general.constants.js
file in src/common/utils/
folder.
The current mock system has some limitations, so it isn't possible to return a different simulated output when sending additional post content.
Security is an extensive topic and cannot be ensured only with code measures; it also requires an Architecture that avoids leak data to non-authorized users and many other actions.
The main thing that Booksys does to ensure security only allows users using email from authorized domains to confirm their bookings, but Booksys also does several code actions that we will detail in the topics below:
Booksys uses HTTPS protocol to transport all the data, including the mock API; the index.html page also has a script to redirect the page from HTTP to HTTPS automatically.
Booksys didn't create the backend, but the proposed API uses CloudFlare to avoid any DDOS attack.
One of the ways that many attackers use to steal data from the application is using malicious code that sends to their servers sensitive information like email, passwords or credit card numbers.
There are several ways to avoid this, but one of the most powerful ways is to define a CSP in the index.html file and the API; the CSP defined in the index list the allowed domains authorized to receive information from this application.
The Booksys will follow these headers to design the backend API and ensure the security:
Header | Value | Explanation |
---|---|---|
X-XSS-Protection | 1;mode=block | XSS filter enabled and prevented rendering the page if attack detected |
X-Frame-Options | DENY | Prevent any domain to embed your content using frame/iframe. |
X-Content-Type-Options | nosniff | Consider files types as defined and disallow content sniffing. |
Referrer Policy | no-referrer | The Referer header will be omitted entirely. |
HTTP Strict Transport Security | max-age=31536000; includeSubDomains | HTTP Strict Transport Security (HSTS) is a web security policy mechanism which helps to protect websites against protocol downgrade attacks and cookie hijacking. It allows web servers to declare that web browsers (or other complying user agents) should only interact with it using secure HTTPS connections, and never via the insecure HTTP protocol. |
Cookies | Secure=True; HttpOnly=True, SameSite=strict | "Secure" Cookie A secure cookie is only sent to the server with an encrypted request over the HTTPS protocol. Even with Secure, sensitive information should never be stored in cookies, as they are inherently insecure and this flag can't offer real protection. Starting with Chrome 52 and Firefox 52, insecure sites (http:) can't set cookies with the Secure directive. "HttpOnly" Cookie To prevent cross-site scripting (XSS) attacks, HttpOnly cookies are inaccessible to JavaScript's Document.cookie API; they are only sent to the server. For example, cookies that persist server-side sessions don't need to be available to JavaScript, and the HttpOnly flag should be set. |
Content-Security-Policy | default-src 'self' 'unsafe-inline' 'unsafe-eval' data: https://o1034630.ingest.sentry.io/ https://www.google-analytics.com https://www.googletagmanager.com https://fonts.gstatic.com https://fonts.googleapis.com http://cdn.polyfill.io https://cdnjs.cloudflare.com https://29b0050b-7001-4162-994f-acfec6195274.mock.pstmn.io; report-uri https://29b0050b-7001-4162-994f-acfec6195274.mock.pstmn.io/security/report | The Content Security Policy prevent XSS , clickjacking , code injection attacks by implementing the Content Security Policy (CSP) header in your web page HTTP response. |
NPM Audit is a helpful tool to detect security problems in external dependencies. To ensure that all new packages are safe, Booksys uses a hook automatically called after any new package installation to integrate it with the developer flow.
Booksys stores all the stateless components in the src/common/components
folder; also, to separate state from visual, all the pages have two files, one to manage states, routes, API services communication and the other to control the presentational components, responsiveness and local state.
Storybook stores all the stateless components list.
Booksys uses Storybook with the following addons:
- Parameters control: Allow to change all the parameters from a stateless component without creating several stories
Devices | Change booking page | Create booking page | Booking status page |
---|---|---|---|
4k Display | Screenshot | Screenshot | Screenshot |
5k Display | Screenshot | Screenshot | Screenshot |
Galaxy Fold | Screenshot | Screenshot | Screenshot |
Moto G4 | Screenshot | Screenshot | Screenshot |
Pixel 2 | Screenshot | Screenshot | Screenshot |
Surface Duo | Screenshot | Screenshot | Screenshot |
iPhone 5-SE | Screenshot | Screenshot | Screenshot |
iPhone X | Screenshot | Screenshot | Screenshot |
iPad | Screenshot | Screenshot | Screenshot |
iPad Pro | Screenshot | Screenshot | Screenshot |
Generic Laptop | Screenshot | Screenshot | Screenshot |
Laptop with MDPI screen | Screenshot | Screenshot | Screenshot |
MacBook Pro 13 | Screenshot | Screenshot | Screenshot |
MacBook Pro 16 | Screenshot | Screenshot | Screenshot |
Devices | Change booking page | Create booking page | Booking status page |
---|---|---|---|
4k Display | Screenshot | Screenshot | Screenshot |
5k Display | Screenshot | Screenshot | Screenshot |
Galaxy Fold | Screenshot | Screenshot | Screenshot |
Moto G4 | Screenshot | Screenshot | Screenshot |
Pixel 2 | Screenshot | Screenshot | Screenshot |
Surface Duo | Screenshot | Screenshot | Screenshot |
iPhone 5-SE | Screenshot | Screenshot | Screenshot |
iPhone X | Screenshot | Screenshot | Screenshot |
iPad | Screenshot | Screenshot | Screenshot |
iPad Pro | Screenshot | Screenshot | Screenshot |
Generic Laptop | Screenshot | Screenshot | Screenshot |
Laptop with MDPI screen | Screenshot | Screenshot | Screenshot |
MacBook Pro 13 | Screenshot | Screenshot | Screenshot |
MacBook Pro 16 | Screenshot | Screenshot | Screenshot |
Booksys do several actions to ensure that this application is a universal application and all the users can use it; the project was designed in a way that allows users with disabilities but also users living in places with low internet quality or slow/old devices.
- Remove any zoom limitation from the pages: Ideally, to mock app contacts, we usually disable the zoom option, making the mobile experience remember the native experience; however, some users with visual impairment need the zoom to see all the details.
- Added
role=presentation aria-hidden=true
to all elements that don't bring any valuable information to screen readers - Added
role=alert
to warning messages - Added
aria-live="polite" aria-busy="true"
to loading sections - Added
aria-label and role="button"
to all clickable icons - The dates format used the long-form inside
aria-label
tags - Improved pagination navigation using
aria-valuenow
- Added better fields description to seats and time filters
- Added form tag in creating a booking page
- Added role="status" to booking status field
View full report
Booksys uses GitHub Pages to store all the frontend assets. To make it faster, Create React App with Webpack splits the Javascript and CSS assets in chunks to ensure that a Booksys page only will call the resources required to load that specific page.
There are several performance improvements opportunities that Booksys didn't implement, but it would be great for future versions:
- Use NextJS to generate pre-rendered pages and static pages
- Use nextgen images format
- Use Brotli as CSS/JS compression method.
- Remove moment and object_hash packages
- Remove blocking scripts
View full report
View full report
I’ve tested some different ways to translate web applications, and I noticed that most of the developers work with strings inside a JSON file like this:
{
"ACTION_ACTIVE":"Aktiv",
"ACTION_CANCEL":"Cancel",
"ACTION_ADD":"Hinzufügen",
"ACTION_ADD_CUSTOMER":"Neuen Kunden hinzufügen",
"ACTION_BACK_TO_MENU":"Zurück zum Menü",
"ACTION_BACK_TO_LIST":"Zurück zur Liste",
"ACTION_CHECK":"Klick zum prüfen",
"ACTION_CHECK_NO_CV":"Keine CVV für diese Karte generieren",
"ACTION_CHECKOUT":"Check-out",
}
Unfortunately, there are a lot of problems with this approach, JSON strings aren’t friendly to translators professionals. It is a typical pattern to developers but a nightmare for people who don’t know rules like putting quotation marks, commas, and brackets.
When you have a number or a variable in the middle of the text, some situations will demand two strings. However, these strings together probably won’t make sense in other languages because different languages have different structures.
Booksys uses Redux i18n to manage translations; with this plugin, you can use POT files, and this file is readable by Poedit, which translation professionals primarily use.
Another advantage is we don’t need to create JSON files. Instead, just code and the plugin will extract your code the strings; looks cute, right?
This plugin has an option to import data from PO files. Thus the person responsible for translations doesn’t need to change internal files in your application, beyond easy is more secure.
View translation folder
There are two ways to use a different language:
- Auto-detected: Booksys detects your browser preferred language, and in case the application has this language package available, it displays the website in this language. Otherwise, the application displays English as default.
- Using the
lang
parameter: The application allows the user to set the language manually, sending in the URL the prefered language; for instance, you can see Booksys in Portuguese using the following URL:
https://rxluz.github.io/booksys/?lang=pt#/f76a49f4-73c4-44ce-8fa1-7242bd3d3cc6/booking/booking123 - Invalid booking page https://rxluz.github.io/booksys/?lang=pt#/f76a49f4-73c4-44ce-8fa1-7242bd3d3cc6 - New booking form
- Run
yarn client:translation:extract
- Download Poedit
- Using Poedit open the file
template.pot
insrc/common/locales/
folder - Create
.mo
and.po
file translation in Poedit and save this translation in the folder above - Run
yarn client:translation:import
- This command will update the file
translations.js
- Case needed import new languages packages to moment and add it inside the
general.utils.js
file insrc/common/utils
folder. (eg.:import 'moment/locale/es'
You can change the translations using Poedit and reimport the file or change the translation.js
in the src/common/locales/
folder.
Some situations require changing this file straight due to a bug to export plurals; you need to edit this file directly when you need plurals.
The application is compatible with the following browsers:
- Microsoft Edge
- Opera
- Firefox
- Safari
- Chrome
This application uses Polyfill.io to ensure that the application will run in an old browser. Also, all code is compiled to ES5 to avoid breaking the application due to some modern JS syntax.
However, due to modern CSS units, it not guaranteed that everything will work properly in an old browser like Internet Explorer.
This application isn't intended to be a social application or an application that needs to be well-positioned in Google Search. The SEO actions were mainly focused on ensuring that the shared images would appear correctly.
To do that was added inside index.html
several metatags to allow the crawlers to display the Booksys website thumbnail correctly.
Also, the application is connected with Google Analytics to collect valuable insights about the performance and general improvements.
Whatsapp card | Facebook card |
Slack card |
Booksys utilises testing-library and Jest to do unit tests and Cypress to do E2E tests; the following commands are available to run tests:
Command | Description |
---|---|
yarn client:test |
Run the tests created after the last commit and watch for new tests |
yarn client:cypress:open |
Run the E2E tests |
yarn client:test:coverage |
Run the tests and generate a coverage report in coverage folder |
The coverage tests report is initially generated in the /coverage/lcov-report/
folder, but these files are copied to the build/
folder during the deployment.
To add new tests to the application, do the following procedure according to the test type:
- Unit tests: The unit tests should be done inside the component folder using the exact name of the component, adding
.spec.js
at the end - E2E tests: All E2E tests should be located inside the
cypress/integration
folder, and they need to finish the file name with_spec.js
- Javascript
- Babel
- VSCode
- Prettier
- ESLint
- React
- Create React App
- Webpack
- Sass
- BEM
- Testing Library
- Jest
- Github
- Redux
- React Router
- NPM
- NodeJS
- Improve tests coverage
- Add e2e tests
- Use better a11y tests tools
- Remove moment
- Improve support to ie11
- Remove object hash
- Add NextJS
- Create the backend
- Implement CI/CD
Directory /src
Total: 87 files, 3241 codes, 49 comments, 613 blanks, all 3903 lines
Languages
language | files | code | comment | blank | total |
---|---|---|---|---|---|
JavaScript | 69 | 2,189 | 48 | 433 | 2,670 |
SCSS | 12 | 858 | 1 | 174 | 1,033 |
XML | 5 | 188 | 0 | 5 | 193 |
CSS | 1 | 6 | 0 | 1 | 7 |
Directories
path | files | code | comment | blank | total |
---|---|---|---|---|---|
. | 87 | 3,241 | 49 | 613 | 3,903 |
common | 48 | 1,748 | 1 | 317 | 2,066 |
common/components | 35 | 1,368 | 0 | 221 | 1,589 |
common/components/button | 5 | 136 | 0 | 27 | 163 |
common/components/companyCard | 5 | 259 | 0 | 45 | 304 |
common/components/input | 5 | 343 | 0 | 45 | 388 |
common/components/loading | 3 | 21 | 0 | 8 | 29 |
common/components/officeCard | 5 | 165 | 0 | 38 | 203 |
common/components/pagination | 5 | 129 | 0 | 26 | 155 |
common/components/warning | 7 | 315 | 0 | 32 | 347 |
common/locales | 1 | 68 | 0 | 1 | 69 |
common/routes | 2 | 23 | 0 | 8 | 31 |
common/utils | 10 | 289 | 1 | 87 | 377 |
enhancers | 1 | 15 | 0 | 6 | 21 |
middleware | 1 | 4 | 6 | 3 | 13 |
modules | 28 | 1,234 | 7 | 237 | 1,478 |
modules/booking | 16 | 915 | 7 | 171 | 1,093 |
modules/booking/change | 3 | 208 | 0 | 40 | 248 |
modules/booking/create | 6 | 326 | 0 | 55 | 381 |
modules/booking/status | 3 | 241 | 0 | 48 | 289 |
modules/company | 5 | 119 | 0 | 30 | 149 |
modules/main | 7 | 200 | 0 | 36 | 236 |
reducers | 5 | 89 | 0 | 26 | 115 |
- Extract translations isn't generating the plurals