diff --git a/tasks/train-a/admin/carriages.md b/tasks/train-a/admin/carriages.md new file mode 100644 index 000000000..57d3e940f --- /dev/null +++ b/tasks/train-a/admin/carriages.md @@ -0,0 +1,157 @@ +# Carriages (manager) + +**Browser path:** _/admin/carriages_ + +**Score**: 60 + +**Access**: only for manager (run by guards) + +## Description + +Each train can consist of carriages of different types. Manager can create several types of carriages and then compose trains from them for different routes. + +### Main designation of carriage elements + +Carriage is composed with parameters: + +- **code** - auto-generated unique code for item. User to update carriage. +- **name** - unique name of the carriage type. +- **rows** - number of rows (16 in example below) +- **leftSeats** - - the number of seats to the left of the aisle in a row (2 in example below) +- **rightSeats** - the number of seats to the right of the aisle in a row (3 in example below) + +![Designation of carriage elements](../designs/search-details/carriage%20hints.png) + +### Requirements + +#### Requirement 1: Page layout + +- The page must display a "Create" button at the top. +- Below the "Create" button, there must be a list of already created carriages displayed vertically. +- Each carriage in the list must include: + - The name of the carriage type. + - A rendered picture of the train car based on its configuration parameters: rows, leftSeats, and rightSeats. +- If the manager clicks the "Create" button or the "Update" button for any existing carriage, a form must appear at the top of the page. + +#### Requirement 2: Carriage configuration form + +- The form must include fields for configuring the carriage: + - rows: Number of rows in the carriage. + - leftSeats: Number of seats to the left of the aisle. + - rightSeats: Number of seats to the right of the aisle. +- The form must include a "Save" button. +- As the manager fills out the form, a dynamically rendered prototype of the carriage must be displayed to visualize the configuration. + +#### Requirement 3: Saving and updating carriages + +- When the manager clicks the "Save" button, the new or updated carriage must be inserted into the list of carriages. +- After saving, the form must disappear. + +### Acceptance Criteria (60) + +#### Acceptance Criteria 1: Page layout (10) + +- The page must display a "Create" button at the top. +- Below the "Create" button, a list of existing carriages must be displayed vertically, each with: + - The name of the carriage type. + - A rendered picture of the carriage based on its configuration parameters. + - "Update" button for each item. + +#### Acceptance Criteria 2: Displaying carriage configuration form (15) + +- When the manager clicks the "Create" button: + - A form must appear at the top with fields for rows, leftSeats, and rightSeats. + - A "Save" button must be present in the form. + - A dynamically rendered prototype of the carriage must be displayed based on the values entered in the form fields. +- When the manager clicks the "Update" button for an existing carriage: + - The form must appear at the top with the fields pre-filled with the current configuration of the selected carriage. + - A "Update" button must be present in the form. + - The dynamically rendered prototype must update according to changes made in the form. + +#### Acceptance Criteria 3: Dynamically rendering prototype (25) + +- As the manager fills out or updates the rows, leftSeats, and rightSeats fields in the form: + - The prototype of the carriage must dynamically update to reflect the changes. + +#### Acceptance Criteria 4: Saving and displaying new/updated carriages (10) + +- When the manager clicks the "Save" button: + - If creating a new carriage: + - The new carriage must be added to the top of the list of carriages. + - The form must disappear. + - If updating an existing carriage: + - The updated carriage must replace the old one in the list. + - The form must disappear. + +## API + +### Retrieve carriage list + +> `(GET) /api/carriage` + +- Response 200 (application/json) + - Attributes (array[object]) + - code: `type5` (string) - Carriage unique identifier + - name: `Only-women` (string) - Carriage name + - rows: `18` (number) - amount of rows in carriage + - leftSeats: `2` (number) - amount of seats to the left of the aisle + - rightSeats: `2` (number) - amount of seats to the right of the aisle + +### Create new carriage type + +> `(POST) /api/carriage` + +- Request (application/json) + + - Body (object) + - name: `Only-women` (string) - Carriage name + - rows: `18` (number) - amount of rows in carriage + - leftSeats: `2` (number) - amount of seats to the left of the aisle + - rightSeats: `2` (number) - amount of seats to the right of the aisle + +- Response 201 (application/json) + + - Attributes (object) + - code: `type18` (string) - Carriage unique identifier + +- Response 401 (application/json) + - Attributes (object) + - error: + - message: `Access is not granted` (string) - Error message + - reason: `invalidAccessToken` (string) - Wrong token idintifier + +### Update carriage type + +> `(PUT) /api/carriage/{code}` + +- Request (application/json) + + - Parameters: (object) + - code: `type18` (string) - Carriage unique identifierØ + - Body: (object) + - name: `Only-women` (string) - Carriage name + - rows: `16` (number) - amount of rows in carriage + - leftSeats: `2` (number) - amount of seats to the left of the aisle + - rightSeats: `3` (number) - amount of seats to the right of the aisle + +- Response 200 (application/json) + + - Attributes: (object) + - code: `type18` (string) - Carriage unique identifier + +- Response 401 (application/json) + + - Attributes: (object) + - error: + - message: `Access is not granted` (string) - Error message + - reason: `invalidAccessToken` (string) - Wrong token idintifier + +- Response 400 (application/json) + - Attributes: (object) + - error: + - message: `Carriage not found` (string) - Error message + - reason: `recordNotFound` (string) - Wrong token idintifier + +## Next section + +- [Routes](./routes.md) diff --git a/tasks/train-a/admin/readme.md b/tasks/train-a/admin/readme.md new file mode 100644 index 000000000..0fa2d20c2 --- /dev/null +++ b/tasks/train-a/admin/readme.md @@ -0,0 +1,27 @@ +# Manager/Administrator page + +## Description + +The page is designed for managing trips and train movement infrastructure, with the ability to create any type of carriage, place stations, and set up routes. + +> There is only 1 user with this role: +> _email:_ admin@admin.com +> _password:_ my-password + +### Pages + +#### [Stations](./stations.md) + +List of all stations in cities than can be connected to each other and makes single thread of road. + +> One city can have only 1 train station! + +#### [Carriages](./carriages.md) + +Manager can create few types of carriages to use them composing the routes. + +#### [Routes](./routes.md) + +On the routes page, manager can plan a trip between stations and create a composition of carriages to form a train. + +Additionally, for each route, a train schedule must be created, and the fare between stations for each type of carriage must be specified. diff --git a/tasks/train-a/admin/ride.md b/tasks/train-a/admin/ride.md new file mode 100644 index 000000000..07e781ab7 --- /dev/null +++ b/tasks/train-a/admin/ride.md @@ -0,0 +1,193 @@ +# Schedule + +**Browser path:** _/admin/routes/{id}_ + +**Score**: 100 + +**Access**: only for manager (run by guards) + +## Description + +After creating the general route, the manager must set up the train schedule and specify the fare. + +To allow users to travel any number of stations and pay only for that portion of the journey rather than the entire route, the manager needs to specify the price for each segment between stations and for each type of carriage present on the train. + +Travel time is indicated for each station as _arrival time_ (except for the first one) and _departure time_ (except for the last one). + +**Segment** is a part of the route between stations that has its own fare for each type of carriage. + +### Requirements + +#### Requirement 1: Navigation + +- The page must include a "Back" button that navigates the manager back to the route page. + +#### Requirement 2: Page title + +- The page title must include the route index. + +#### Requirement 3: List of rides + +- The page must display a list of ride cards, each with: + - Ride id in the title. + - A list of stations, arrival/departure time and prices. + +#### Requirement 4: Editing times + +- Each cell for arrival/departure times must have an "Edit" button. +- Clicking the "Edit" button must allow the manager to edit the time and save the changes. + +#### Requirement 5: Editing prices + +- Each cell for prices must have an "Edit" button. +- Clicking the "Edit" button must allow the manager to edit the prices and save the changes. + +#### Requirement 6: Creating new rides + +- The page must include a "Create" button. +- Clicking the "Create" button must create a new ride list with: + - Predefined stations vertically. + - Form fields for entering departure/arrival times. + - Form fields for entering prices. + +### Acceptance Criteria (100) + +#### Acceptance Criteria 1: Navigation (5) + +- When the "Back" button is clicked, the manager must be navigated back to the route page. + +#### Acceptance Criteria 2: Page title (5) + +- The page title must include the route id, displaying it clearly at the top. + +#### Acceptance Criteria 3: List of rides (30) + +- Each ride card in the list must display: + - The ride id in the title. + - A vertical list of stations as the first column. + - The arrival time (except for the first station) and departure time (except for the last station) for each station as the second column. + - Prices for all carriage types in the middle of the previous and next station rows. + +#### Acceptance Criteria 4: Editing times (20) + +- Each cell for arrival/departure time must include an "Edit" button. +- When the "Edit" button is clicked, the cell must become editable. +- The manager must be able to edit the time and save the changes. +- After saving, the updated time must be displayed in the cell. + +#### Acceptance Criteria 5: Editing prices (20) + +- Each cell for prices must include an "Edit" button. +- When the "Edit" button is clicked, the cell must become editable. +- The manager must be able to edit the prices for each carriage type and save the changes. +- After saving, the updated prices must be displayed in the cell. + +#### Acceptance Criteria 6: Creating new rides (10) + +- When the "Create" button is clicked, a new ride list must be created with: + - Predefined stations listed as the first column. + - Form fields for entering departure and arrival times as the second column. + - Form fields for entering prices as the third column. + +#### Acceptance Criteria 7: Deleting a ride (10) + +- Rides departing from the first station in the future contain a "Delete" button +- When the "Delete" button is clicked: + - A confirmation modal must appear. + - Upon confirming the deletion, the ride must be removed from the list. + +## API + +### Retrieve route information + +> `(GET) /api/route/{id}` + +- Request (application/json) +- Parameters: + + - id: `17` (number, required) - Identifier of the route + +- Response 200 (application/json) + - Attributes: (array[object]) + - id: `64` (number) - Route identifier + - path: `[33, 5, 62, 11, 48, 34]` (array[number]) - List of station identifiers + - carriages: `['carriage_type_2', 'carriage_type_2', 'carriage_type_2', 'carriage_type_2', 'carriage_type_7', 'carriage_type_7', 'carriage_type_7', 'carriage_type_7']` (array[string]) - List of carriage types for the train + - schedule: (array[object]) - List of potential rides + - rideId: `44` (number) - Identifier of certain schedule for the route + - segments: (array[object]) - List of road section between each station. Always 1 less than the number of stations on the route + - time: `['2024-08-08T22:19:57.708Z', '2024-08-12T03:29:57.708Z']` ([string, string]) - dates of the start of movement on the section and the end of movement on the section in string form - [departure_from_prev_station, arrival_at_next_station] + - price: (object) - set of prices for all carriage types on this route. It has vary size depending on the train configuration + - dynamic-carriage-type-1: `210` (number) - the price of 210 units for _dynamic-carriage-type-1_ carriage type on current section + +### Create new ride + +> `(POST) /api/route/{routeId}/ride` + +- Request (application/json) + + - Parameters: + - routeId: `17` (number, required) - Identifier of the route + - Body (object) + - segments: (array[object]) + - time: `['2024-08-08T22:19:57.708Z', '2024-08-12T03:29:57.708Z']` ([string, string]) - dates of the start of movement on the section and the end of movement on the section in string form - [departure_from_prev_station, arrival_at_next_station] + - price: (object) - set of prices for all carriage types on this route. It has vary size depending on the train configuration + - dynamic-carriage-type-1: `210` (number) - the price of 210 units for _dynamic-carriage-type-1_ carriage type on current section + +- Response 201 (application/json) + + - Attributes (object) + - id: `215` (number) - New ride identifier + +- Response 401 (application/json) + + - Attributes (object) + - error: + - message: `Access is not granted` (string) - Error message + - reason: `invalidAccessToken` (string) - Wrong token idintifier + +- Response 400 (application/json) + - Attributes (object) + - error: + - message: `Route not found` (string) - Error message if routeId is wrong + - reason: `recordNotFound` (string) - Wrong token idintifier + +### Update ride + +> `(PUT) /api/route/{routeId}/ride/{rideId}` + +- Request (application/json) + + - Parameters: + - routeId: `17` (number, required) - Identifier of the route + - rideId: `215` (number, required) - Identifier of the ride + - Body (object) + - segments: (array[object]) + - time: `['2024-08-08T22:19:57.708Z', '2024-08-12T03:29:57.708Z']` ([string, string]) - dates of the start of movement on the section and the end of movement on the section in string form - [departure_from_prev_station, arrival_at_next_station] + - price: (object) - set of prices for all carriage types on this route. It has vary size depending on the train configuration + - dynamic-carriage-type-1: `210` (number) - the price of 210 units for _dynamic-carriage-type-1_ carriage type on current section + +- Response 200 (application/json) + + - Attributes (object) + +- Response 401 (application/json) + + - Attributes (object) + - error: + - message: `Access is not granted` (string) - Error message + - reason: `invalidAccessToken` (string) - Wrong token idintifier + +- Response 400 (application/json) + - Attributes (object) + - error: + - message: `Ride not found` (string) - Error message if routeId or rideId is wrong + - reason: `recordNotFound` (string) - Wrong token idintifier + +## Design example + +> [!WARNING] +> All mockups should not be considered as final versions! We are providing an idea; you can come up with any design as long as it meets the functional requirements. + +### Ride list with edit field + +![Ride page](../designs/rides/update.png) diff --git a/tasks/train-a/admin/routes.md b/tasks/train-a/admin/routes.md new file mode 100644 index 000000000..ceab97bbd --- /dev/null +++ b/tasks/train-a/admin/routes.md @@ -0,0 +1,187 @@ +# Routes + +**Browser path:** _/admin/routes_ + +**Score**: 100 + +**Access**: only for manager (run by guards) + +## Description + +The page allows the manager to combine stations and types of carriages, creating a unique train route between cities with a set of carriages of different types, quantities, and sequences. + +A route is just a map of the train's movement; it does not include prices or travel times. + +Minimum 3 carriages, minimum 3 stations; + +### Requirements + +#### Requirement 1: Display of routes + +- The page must display a list of routes, each with the detail information. + +#### Requirement 2: Route management actions + +- Each route card must have the following action buttons: "Delete", "assign Ride", "Update". + +#### Requirement 3: Deleting a route + +- The "Delete" button must open a confirmation modal. +- Upon confirmation, the route must be deleted from the list. + +#### Requirement 4: Assigning a ride + +- The "Assign Ride" button must open a [page](./ride.md) where the manager can create a new schedule for the route. + +#### Requirement 5: Creating and updating routes + +- A "Create" button must be present at the top of the page. +- Clicking the "Create" or "Update" button must display a form with two sections: + - Left Section with stations + - Right Section with carriages +- The form must include a "Save" button to save the new or updated route. + +#### Requirement 6: Saving routes + +- Clicking the "Save" button must save the new route or update the existing route. +- The new route must appear in the list of routes, or the updated route must replace the existing route. + +### Acceptance Criteria (100) + +#### Acceptance Criteria 1: Display of routes (10) + +- The page must list all routes with the following information: + - Route name. + - Sequence of cities the route passes through. + - Sequence of carriage types assigned to the route. + +#### Acceptance Criteria 2: Route management actions (15) + +- Each route card must include: + - A "Delete" button that opens a confirmation modal. + - An "Assign Ride" button that navigates to a new page for schedule creation. + - An "Update" button that displays the form for updating the route. + +#### Acceptance Criteria 3: Deleting a route (10) + +- When the "Delete" button is clicked: + - A confirmation modal must appear. + - Upon confirming the deletion, the route must be removed from the list. + +#### Acceptance Criteria 4: Assigning a ride (10) + +- When the "Assign Ride" button is clicked: + - The manager must be navigated to a [page](./ride.md) where they can observe existing rides or create a new schedule for the route. + +#### Acceptance Criteria 5: Creating and updating routes (45) + +- When the "Create" button is clicked: + - A form must appear at the top of the page with two sections for adding stations and carriages. +- When the "Update" button on a route card is clicked: + - The same form must appear pre-filled with the existing route details. +- The form must allow: + - Adding any number of stations in sequence, with a new empty selector appearing when the last one is filled. + - **In the list of selectable stations, there should be only those stations that are explicitly connected to the previous one in the list, i.e., stations from the 'connectedTo' list.** + - Adding any number of carriage types in sequence, with a new empty selector appearing when the last one is filled. + +#### Acceptance Criteria 6: Saving routes (10) + +- When the "Save" button is clicked: + - The new route must be added to the list of routes if creating a new route. + - The updated route must replace the existing route in the list if updating a route. + +## API + +### To retrieve routes + +> `(GET) /api/route` + +- Request (application/json) + + - Attributes: (object) + +- Response 200 (application/json) + - Attributes: (array[object]) + - id: `64` (number) - Route identifier + - path: `[33, 5, 62, 11, 48, 34]` (array[number]) - List of station identifiers + - carriages: `['carriage_type_2', 'carriage_type_2', 'carriage_type_2', 'carriage_type_2', 'carriage_type_7', 'carriage_type_7', 'carriage_type_7', 'carriage_type_7']` (array[string]) - List of carriage types for the train + +### To create new route + +> `(POST) /api/route` + +- Request (application/json) + + - Body (object) + - path: `[33, 5, 62, 11, 48, 34]` (array[number]) - List of station identifiers + - carriages: `['carriage_type_2', 'carriage_type_2', 'carriage_type_2', 'carriage_type_2', 'carriage_type_7', 'carriage_type_7', 'carriage_type_7', 'carriage_type_7']` (array[string]) - List of carriage identifiers for the train + +- Response 201 (application/json) + + - Attributes (object) + - id: `17` (number) - New route identifier + +- Response 401 (application/json) + - Attributes (object) + - error: + - message: `Access is not granted` (string) - Error message + - reason: `invalidAccessToken` (string) - Wrong token idintifier + +### To update route + +> `(PUT) /api/route/{id}` + +- Request (application/json) + + - Parameters: + - id: `17` (number, required) - Identifier of the route + - Body (object) + - path: `[33, 5, 62, 11, 48, 34]` (array[number]) - List of station identifiers + - carriages: `['carriage_type_2', 'carriage_type_2', 'carriage_type_2', 'carriage_type_2', 'carriage_type_7', 'carriage_type_7', 'carriage_type_7', 'carriage_type_7']` (array[string]) - List of carriage identifiers for the train + +- Response 200 (application/json) + + - Attributes (object) + - id: `17` (number) - Route identifier + +- Response 401 (application/json) + - Attributes (object) + - error: + - message: `Access is not granted` (string) - Error message + - reason: `invalidAccessToken` (string) - Wrong token idintifier + +### Delete route + +> `(DELETE) /api/route/{id}` + +- Request (application/json) + + - Parameters: + - id: `17` (number, required) - Identifier of the route + +- Response 200 (application/json) + + - Attributes: (object) + +- Response 401 (application/json) + - Attributes (object) + - error: + - message: `Access is not granted` (string) - Error message + - reason: `invalidAccessToken` (string) - Wrong token idintifier + +## Design example + +> [!WARNING] +> All mockups should not be considered as final versions! We are providing an idea; you can come up with any design as long as it meets the functional requirements. + +### Route list + +![Route list](../designs/routes/list.png) + +### Create new route + +![Route form](../designs/routes/create.png) + +## Next section + +- [Assign ride](./ride.md) diff --git a/tasks/train-a/admin/stations.md b/tasks/train-a/admin/stations.md new file mode 100644 index 000000000..89b01d090 --- /dev/null +++ b/tasks/train-a/admin/stations.md @@ -0,0 +1,148 @@ +# Stations (manager) + +**Browser path:** _/admin/stations_ + +**Score**: 50 + +**Access**: only for manager (run by guards) + +## Description + +A train station is a place where trains stop for passengers to get on and off. They are connected by railway tracks, which allow trains to travel only between them. + +> [!IMPORTANT] +> Application constraints: maximum 1 station can be in one city. + +Each station must be marked with geographical coordinates. Technically, this can be done in various ways: by searching with autocomplete for cities, by searching for a point on a map, or even by combining both methods. + +It does not matter what the way is chosen, latitude/longitude have to be assigned to the station instance. + +### Requirements + +#### Requirement 1: Station coordinates input + +- The page must provide any methods for inputting station coordinates (latitude and longitude): + - Form fields where the manager can manually enter latitude and longitude values. + - An interactive map where the manager can drop a point to set the station's coordinates. + +#### Requirement 2: Connecting stations + +- The page must allow the manager to connect the new station to a list of existing stations, enabling the train to move between them. +- The approach for connecting stations can be implemented in one of the following ways: + - Using the map to visually connect stations. + - Using form fields where the manager can select stations from a list and establish connections. + +#### Requirement 3: Displaying existing stations + +- Below the station creation section, the page must display a list of existing stations. + +#### Requirement 4: Deleting Stations + +- The page must allow the manager to delete an existing station, but only if there are no active rides passing through that station. +- If an attempt is made to delete a station with active rides, an appropriate error message must be displayed. + +### Acceptance Criteria (50) + +#### Acceptance Criteria 1: Station coordinates input (20) + +- The page must provide form fields for latitude and longitude. +- When valid latitude and longitude values are entered, they must be accepted and saved. +- The page could also provide an interactive map. +- When a point is dropped on the map, the corresponding latitude and longitude values must be populated and synchronized with form field. + +#### Acceptance Criteria 2: Connecting stations (15) + +- The page must allow the manager to connect the new station to other stations. It is dynamic number of items, selecting one item the next form field appears. +- If using a map, the manager must be able to draw connections between stations. +- If using form fields, the manager must be able to select and connect stations from a list. +- Name, coordinates and connection list are required. The connections must be saved and displayed correctly. + +#### Acceptance Criteria 3: Displaying existing stations (10) + +- The page must display a list of existing stations below the station creation section. +- Each station entry in the list must display: + - The station name. + - The coordinates (latitude and longitude). + - A list of other stations it is connected with. + +#### Acceptance Criteria 4: Deleting stations (5) + +- The page must allow the deletion of a station only if there are no active rides passing through it. +- If an attempt is made to delete a station with active rides, an error message "Cannot delete station with active rides" must be displayed. +- If there are no active rides, the station must be successfully deleted from the list. + +## API + +### Retrieve station list + +> `(GET) /api/station` + +- Response 200 (application/json) + - Attributes (array[object]) + - id: `5` (number) - Station identifier + - city: `London` (string) - City name where station is located + - latitude: `51.5074` (number) - Latitude of the station + - longitude: `-0.1278` - Longitude of the station + - connectedTo: `[{id: 4, distance: 44},...]` (array[object]) - List of connected stations, where _id_ - identifier, _distance_ - distance between them in km + +### Create new station + +> `(POST) /api/station` + +- Request (application/json) + + - Body (object) + - city: `Birmingham` (string) - City name where station is located + - latitude: `52.4862` (number) - Latitude of the station + - longitude: `-1.8904` - Longitude of the station + - relations: `[4,7,9,14,21]` (array[number]) - List of related station identifiers + +- Response 201 (application/json) + + - Attributes (object) + - id: `17` (number) - New station identifier + +- Response 401 (application/json) + + - Attributes (object) + - error: + - message: `Access is not granted` (string) - Error message + - reason: `invalidAccessToken` (string) - Wrong token idintifier + +- Response 400 (application/json) + - Attributes (object) + - error: + - message: `Invalid station data` (string) - Error message + - reason: `invalidStationData` (string) - Wrong token idintifier + +### Delete station + +> `(DELETE) /api/station/{id}` + +- Request (application/json) + + - Parameters: + - id: `215` (number, required) - Identifier of the station + +- Response 200 (application/json) + + - Attributes: (object) + +- Response 401 (application/json) + - Attributes (object) + - error: + - message: `Access is not granted` (string) - Error message + - reason: `invalidAccessToken` (string) - Wrong token idintifier + +## Design example + +> [!WARNING] +> All mockups should not be considered as final versions! We are providing an idea; you can come up with any design as long as it meets the functional requirements. + +### Proposed page layout + +![Station page](../designs/stations/new_station.png) + +## Next section + +- [Carriages](./carriages.md) diff --git a/tasks/train-a/designs/header/guest.png b/tasks/train-a/designs/header/guest.png new file mode 100644 index 000000000..f7e225ffc Binary files /dev/null and b/tasks/train-a/designs/header/guest.png differ diff --git a/tasks/train-a/designs/header/manager.png b/tasks/train-a/designs/header/manager.png new file mode 100644 index 000000000..8b3f215be Binary files /dev/null and b/tasks/train-a/designs/header/manager.png differ diff --git a/tasks/train-a/designs/header/user.png b/tasks/train-a/designs/header/user.png new file mode 100644 index 000000000..3ecf0821f Binary files /dev/null and b/tasks/train-a/designs/header/user.png differ diff --git a/tasks/train-a/designs/profile/change-password.png b/tasks/train-a/designs/profile/change-password.png new file mode 100644 index 000000000..b08de11ec Binary files /dev/null and b/tasks/train-a/designs/profile/change-password.png differ diff --git a/tasks/train-a/designs/profile/manager.png b/tasks/train-a/designs/profile/manager.png new file mode 100644 index 000000000..51b050eef Binary files /dev/null and b/tasks/train-a/designs/profile/manager.png differ diff --git a/tasks/train-a/designs/profile/user.png b/tasks/train-a/designs/profile/user.png new file mode 100644 index 000000000..94c73d46f Binary files /dev/null and b/tasks/train-a/designs/profile/user.png differ diff --git a/tasks/train-a/designs/rides/static.png b/tasks/train-a/designs/rides/static.png new file mode 100644 index 000000000..72a9a6ca5 Binary files /dev/null and b/tasks/train-a/designs/rides/static.png differ diff --git a/tasks/train-a/designs/rides/update.png b/tasks/train-a/designs/rides/update.png new file mode 100644 index 000000000..7d64a460b Binary files /dev/null and b/tasks/train-a/designs/rides/update.png differ diff --git a/tasks/train-a/designs/routes/create.png b/tasks/train-a/designs/routes/create.png new file mode 100644 index 000000000..83240f9e2 Binary files /dev/null and b/tasks/train-a/designs/routes/create.png differ diff --git a/tasks/train-a/designs/routes/list.png b/tasks/train-a/designs/routes/list.png new file mode 100644 index 000000000..2ec21cd4f Binary files /dev/null and b/tasks/train-a/designs/routes/list.png differ diff --git a/tasks/train-a/designs/search-details/carriage 1.hints.png b/tasks/train-a/designs/search-details/carriage 1.hints.png new file mode 100644 index 000000000..714e8d419 Binary files /dev/null and b/tasks/train-a/designs/search-details/carriage 1.hints.png differ diff --git a/tasks/train-a/designs/search-details/carriage 1.png b/tasks/train-a/designs/search-details/carriage 1.png new file mode 100644 index 000000000..d8ff9ff36 Binary files /dev/null and b/tasks/train-a/designs/search-details/carriage 1.png differ diff --git a/tasks/train-a/designs/search-details/carriage 2.hints.png b/tasks/train-a/designs/search-details/carriage 2.hints.png new file mode 100644 index 000000000..cbab7cafd Binary files /dev/null and b/tasks/train-a/designs/search-details/carriage 2.hints.png differ diff --git a/tasks/train-a/designs/search-details/carriage 2.png b/tasks/train-a/designs/search-details/carriage 2.png new file mode 100644 index 000000000..3b0546d12 Binary files /dev/null and b/tasks/train-a/designs/search-details/carriage 2.png differ diff --git a/tasks/train-a/designs/search-details/carriage hints.png b/tasks/train-a/designs/search-details/carriage hints.png new file mode 100644 index 000000000..2ffe9b536 Binary files /dev/null and b/tasks/train-a/designs/search-details/carriage hints.png differ diff --git a/tasks/train-a/designs/search-details/modal bottom.png b/tasks/train-a/designs/search-details/modal bottom.png new file mode 100644 index 000000000..e164a136e Binary files /dev/null and b/tasks/train-a/designs/search-details/modal bottom.png differ diff --git a/tasks/train-a/designs/search-details/modal top.png b/tasks/train-a/designs/search-details/modal top.png new file mode 100644 index 000000000..f8dc30e8e Binary files /dev/null and b/tasks/train-a/designs/search-details/modal top.png differ diff --git a/tasks/train-a/designs/search/filter.desktop.png b/tasks/train-a/designs/search/filter.desktop.png new file mode 100644 index 000000000..22dd397f5 Binary files /dev/null and b/tasks/train-a/designs/search/filter.desktop.png differ diff --git a/tasks/train-a/designs/search/filter.mobile.png b/tasks/train-a/designs/search/filter.mobile.png new file mode 100644 index 000000000..5d247561b Binary files /dev/null and b/tasks/train-a/designs/search/filter.mobile.png differ diff --git a/tasks/train-a/designs/search/noresults.png b/tasks/train-a/designs/search/noresults.png new file mode 100644 index 000000000..88e3cf874 Binary files /dev/null and b/tasks/train-a/designs/search/noresults.png differ diff --git a/tasks/train-a/designs/search/result-list.desktop.png b/tasks/train-a/designs/search/result-list.desktop.png new file mode 100644 index 000000000..4225e14dc Binary files /dev/null and b/tasks/train-a/designs/search/result-list.desktop.png differ diff --git a/tasks/train-a/designs/search/result-list.mobile.png b/tasks/train-a/designs/search/result-list.mobile.png new file mode 100644 index 000000000..baaf3f744 Binary files /dev/null and b/tasks/train-a/designs/search/result-list.mobile.png differ diff --git a/tasks/train-a/designs/search/result-route.png b/tasks/train-a/designs/search/result-route.png new file mode 100644 index 000000000..515300141 Binary files /dev/null and b/tasks/train-a/designs/search/result-route.png differ diff --git a/tasks/train-a/designs/search/search.calendar.png b/tasks/train-a/designs/search/search.calendar.png new file mode 100644 index 000000000..bd48b4233 Binary files /dev/null and b/tasks/train-a/designs/search/search.calendar.png differ diff --git a/tasks/train-a/designs/search/search.desktop.png b/tasks/train-a/designs/search/search.desktop.png new file mode 100644 index 000000000..f349fa867 Binary files /dev/null and b/tasks/train-a/designs/search/search.desktop.png differ diff --git a/tasks/train-a/designs/search/search.mobile.png b/tasks/train-a/designs/search/search.mobile.png new file mode 100644 index 000000000..8b477743e Binary files /dev/null and b/tasks/train-a/designs/search/search.mobile.png differ diff --git a/tasks/train-a/designs/search/search.point.png b/tasks/train-a/designs/search/search.point.png new file mode 100644 index 000000000..e4100c4ec Binary files /dev/null and b/tasks/train-a/designs/search/search.point.png differ diff --git a/tasks/train-a/designs/search/search.tablet.png b/tasks/train-a/designs/search/search.tablet.png new file mode 100644 index 000000000..b5c9d641c Binary files /dev/null and b/tasks/train-a/designs/search/search.tablet.png differ diff --git a/tasks/train-a/designs/signin.png b/tasks/train-a/designs/signin.png new file mode 100644 index 000000000..ee7058f05 Binary files /dev/null and b/tasks/train-a/designs/signin.png differ diff --git a/tasks/train-a/designs/signup.png b/tasks/train-a/designs/signup.png new file mode 100644 index 000000000..86df52046 Binary files /dev/null and b/tasks/train-a/designs/signup.png differ diff --git a/tasks/train-a/designs/stations/new_station.png b/tasks/train-a/designs/stations/new_station.png new file mode 100644 index 000000000..7422ebf08 Binary files /dev/null and b/tasks/train-a/designs/stations/new_station.png differ diff --git a/tasks/train-a/header-fragment.md b/tasks/train-a/header-fragment.md new file mode 100644 index 000000000..8271efaec --- /dev/null +++ b/tasks/train-a/header-fragment.md @@ -0,0 +1,85 @@ +# Site header + +## Description + +The header serves as the primary navigation and information bar for users. It must be designed to be responsive and user-friendly, providing essential functions and access points to various parts of the application. + +### Requirements + +#### Requirement 1: Header states + +- The header must support three different states based on the user's authentication and authorization level: Guest, General User, and Manager/Administrator. + +#### Requirement 2: Guest state + +- The header must display the following links for guests: + - Sign In + - Sign Up + - Home (default) + +#### Requirement 3: General user state + +- The header must display the following links for authenticated general users: + - Home (default) + - Profile + - My Orders + +#### Requirement 4: Manager/Administrator state + +- The header must display the same links as for general users, plus an additional "Admin" link. +- The "Admin" link must be protected by a guard to ensure only users with the appropriate role can access it. + +#### Requirement 5: Additional information + +- Any logo can be added. +- Authorized user name can be added. + +### Acceptance Criteria + +#### Acceptance Criteria 1: Guest state + +- When a user is not authenticated: + - The header must display "Home", "Sign In", and "Sign Up" links. + - The "Home" link must redirect to the [home page](./search.md). + - The "Sign In" link must redirect to the [sign-in page](./sign-in.md). + - The "Sign Up" link must redirect to the [sign-up page](./sign-up.md). + +#### Acceptance Criteria 2: General user state + +- When a user is authenticated as a general user: + - The header must display "Home", "Profile", and "My Orders" links. + - The "Home" link must redirect to the [home page](./search.md). + - The "Profile" link must redirect to the user's [profile page](./profile.md). + - The "My Orders" link must redirect to the user's [orders page](./order.md). + +#### Acceptance Criteria 3: Manager/Administrator state + +- When a user is authenticated as a manager or administrator: + - The header must display "Home", "Profile", "My Orders", and "Admin" links. + - The "Home" link must redirect to the [home page](./search.md). + - The "Profile" link must redirect to the user's [profile page](./profile.md). + - The "My Orders" link must redirect to the user's [orders page](./order.md). + - The "Admin" link must redirect to the [admin page](./admin/readme.md). + - The "Admin" link must be accessible only to users with the manager or administrator role, enforced by a guard. + +#### Acceptance Criteria 4: Guard for admin page + +- The "Admin" link must be protected by a guard that checks the user's role. +- If a general user or guest tries to access the "Admin" page, they must be redirected to a "Not Authorized" page or receive an appropriate error message. + +## Design example + +> [!WARNING] +> All mockups should not be considered as final versions! We are providing an idea; you can come up with any design as long as it meets the functional requirements. + +### Guest version + +![Guest header](./designs/header/guest.png) + +### User version + +![User header](./designs/header/user.png) + +### Manager version + +![Manager header](./designs/header/manager.png) diff --git a/tasks/train-a/order.md b/tasks/train-a/order.md new file mode 100644 index 000000000..6ebc687e2 --- /dev/null +++ b/tasks/train-a/order.md @@ -0,0 +1,159 @@ +# My orders + +**Browser path**: _/orders_ + +**Score**: 40 + +**Access**: only for authenticated user (run by guards) + +## Description + +User is able to see their orders in the list. + +### Requirements + +#### Owner + +- The order list is displayed in the user's account, showing each train ticket order with detailed information. +- Each item in the list includes the start trip station, start trip time, end trip station, end trip time, trip duration, carriage type, seat number, car number and price. +- Active but not completed orders have a "Cancel" button. + +#### Manager + +- Manager can see all orders regardless of owner in the list. +- Order item should contain user name of the order owner. +- Manager can cancel any active order. + +### Acceptance Criteria (40) + +#### Acceptance Criteria 1: Order list display (4) + +- The order list is accessible from the site header. +- Each order in the list displays the following information: + - Start trip station + - Start trip time + - End trip station + - End trip time + - Trip duration + - Carriage type + - Car number + - Seat number + - Price +- Orders are sorted by the start trip time in ascending order by default. + +#### Acceptance Criteria 2: Start trip (4) + +- The start trip station name is displayed correctly for each order. +- The start trip station is the departure station for the journey. +- The start trip time is displayed in a clear and readable format "MMMM dd hh:mm". +- The start trip time reflects the exact departure time of the train from the booked station. + +#### Acceptance Criteria 3: End trip (4) + +- The end trip station name is displayed correctly for each order. +- The end trip station is the arrival station for the journey. +- The end trip time is displayed in a clear and readable format "MMMM dd hh:mm". +- The end trip time reflects the exact arrival time of the train. + +#### Acceptance Criteria 4: Trip duration (8) + +- The trip duration is calculated and displayed in hours and minutes (e.g., 3h 45m, 45h 12m). +- The trip duration represents the total travel time from the start trip time to the end trip time. + +#### Acceptance Criteria 5: Carriage type (4) + +- The carriage type is displayed (e.g., Economy, Business, First Class). +- The carriage type reflects the class of service selected by the user. + +#### Acceptance Criteria 6: Price (4) + +- The price of the ticket is displayed in the local currency (e.g., $50.00). +- The price reflects the total cost of the ticket for the trip (between selected stations as segments). + +#### Acceptance Criteria 7: Cancel button (4) + +- Active but not completed orders display a "Cancel" button. +- Clicking the "Cancel" button prompts the user to confirm the cancellation. +- If confirmed, the order is cancelled and appear in the list with new status, also a success message is displayed. +- If there is an error during cancellation, an error message is displayed. +- The "Cancel" button is hidden for completed or past orders. + +#### Acceptance Criteria 8: Manager (8) + +- The manager must be able to view a list of all orders, not limited to their own. +- The manager must see a "Cancel Order" button next to each active order in the list. +- The "Cancel Order" button must be visible only for orders with an "active" status. +- When the manager clicks the "Cancel Order" button, a confirmation dialog must appear, asking the manager to confirm the cancellation. +- The dialog must display the order ID and customer name to ensure the correct order is being cancelled. +- Upon confirming the cancellation, the order status must change from "active" to "cancelled". +- The order list must immediately reflect this status change. +- A success message must be displayed to the manager, confirming the order has been cancelled. +- Cancelled orders must remain in the order list with their updated status for record-keeping purposes. + +## API + +### To retrieve orders + +> `(GET) /api/order` + +- Request (application/json) + + - Attributes: (object) + - all: `true` (boolean, optional) - allows manager to retrieve all orders + +- Response 200 (application/json) + - Attributes: (array[object]) + - id: `64` (number) - Order identifier + - rideId: `45` (number) - Ride identifier + - routeId: `18` (number) - Route idenfitier + - seatId: `33` (number) - Seat index in the whole train + - userId: `3` (number) - User identifier + - status: `active|completed|rejected|canceled` (string) - Order status indicates current order state + - path: `[33, 5, 62, 11, 48, 34]` (array[number]) - List of station identifiers + - carriages: `['carriage_type_2', 'carriage_type_2', 'carriage_type_2', 'carriage_type_2', 'carriage_type_7', 'carriage_type_7', 'carriage_type_7', 'carriage_type_7']` (array[string]) - List of carriage types for the train + - schedule: (object) + - segments: (array[object]) - List of road section between each station. Always 1 less than the number of stations on the route + - time: `['2024-08-08T22:19:57.708Z', '2024-08-12T03:29:57.708Z']` (array[string]) - dates of the start of movement on the section and the end of movement on the section in string form - [departure_from_prev_station, arrival_at_next_station] + - price: (object) - set of prices for all carriage types on this route. It has vary size depending on the train configuration + - dynamic*carriage_type_1: `210` (number) - the price of 210 units for \_dynamic_carriage_type_1* carriage type on current section + +### To retrieve users (only for manager) + +> `(GET) /api/users` + +- Request (application/json) + + - Attributes: (object) + +- Response 200 (application/json) + - Attributes: (arra[object]) + - id: `3` (number) - User identifier + - email: `mail@mail.com` (string) - User email + - name: `Dohn` (string) - User name + - role: `user|manager` (string) - User role + +### To cancel active order + +> `(DELETE) /api/order/{orderId}` + +- Request (application/json) + + - Parameters: + - orderId: `215` (number, required) - Identifier of the order + +- Response 200 (application/json) + + - Attributes: (object) + +- Response 400 (application/json) + + - Attributes: (object) + - error: + - message: `Order is not found` (string) - Error message + - reason: `orderNotFound` (string) - Error type + +- Response 400 (application/json) + - Attributes: (object) + - error: + - message: `Order is not active` (string) - Error message + - reason: `orderNotActive` (string) - Error type diff --git a/tasks/train-a/profile.md b/tasks/train-a/profile.md new file mode 100644 index 000000000..97be5d4ee --- /dev/null +++ b/tasks/train-a/profile.md @@ -0,0 +1,179 @@ +# User profile + +**Browser path**: _/profile_ + +**Score**: 30 + +**Access**: only for authenticated user (run by guards) + +## Description + +Page allows authenticated users to view and update their personal information. Users can change details such as their _name_, _email_, and _password_. + +### Core elements + +- **Name record:** Name of the user. +- **Email record:** Registered user email. +- **Edit button:** Pencil button to allow user edit information. +- **Logout button:** Button terminate current session. +- **Change password button:** Button to open dedicated modal to change current account password. + +### Requirements + +#### Requirement 1: User information + +- The page displays the user’s name and email as text blocks. + +- Each text block has an icon button that, when clicked, changes to a form field allowing the user to enter a new value and changes the "Edit" button to a "Save" button. + +#### Requirement 2: Logout + +- The user profile page must include a "Logout" button to terminate the user session. + +- Upon successful logout, the user must be redirected to the main page. + +#### Requirement 3: Password edition + +- The user profile page must include a "Change Password" button. + +- Clicking the "Change Password" button must open a modal window with one form field to enter the new password and a "Save" button. + +- The modal window must close upon successful password change. + +### Acceptance Criteria (30) + +#### Acceptance Criteria 1: Editable user name (8) + +- The user's name must be displayed as a text block with an "Edit" button. + +- When the "Edit" button is clicked, the text block must turn into a form field to enter a new name, and the "Edit" button must change to a "Save" button. + +- When the "Save" button is clicked, the new name must be saved, the form field must revert to a text block, and the "Save" button must change back to an "Edit" button. + +#### Acceptance Criteria 2: Editable user email (8) + +- The user's email must be displayed as a text block with an "Edit" button. + +- When the "Edit" button is clicked, the text block must turn into a form field to enter a new email, and the "Edit" button must change to a "Save" button. + +- When the "Save" button is clicked, the new email must be saved, the form field must revert to a text block, and the "Save" button must change back to an "Edit" button. + +#### Acceptance Criteria 3: Logout functionality (4) + +- The user profile page must include a "Logout" button. +- When the "Logout" button is clicked, the user session must be terminated, and the user must be redirected to the main page. + +#### Acceptance Criteria 4: Change password functionality (10) + +- The user profile page must include a "Change Password" button. + +- When the "Change Password" button is clicked, a modal window must open with one form field to enter the new password and a "Save" button. + +- When the "Save" button in the modal window is clicked, the new password must be saved, and the modal window must close upon successful password change. + +## API + +### Get current user information + +> `(GET) /api/profile` + +- Response 200 (application/json) + + - Attributes (object) + - name: `John Doe` (string) - User name + - email: `user@example.com` (string) - User's email address + - role: `manager|user` (string) - Access level + +- Response 401 (application/json) + - Attributes (object) + - error: + - message: `Access is not granted` (string) - Error message + - reason: `invalidAccessToken` (string) - Wrong token idintifier + +### Update user information + +> `(PUT) /api/profile` + +- Request (application/json) + + - Body (object) + - email: `user@example.com` (string) - User's email address + - name: `Dohn Doe` (string) - User's name + +- Response 200 (application/json) + + - Attributes (object) + - name: `John Doe` (string) - User name + - email: `user@example.com` (string) - User's email address + - role: `manager|user` (string) - Access level + +- Response 401 (application/json) + + - Attributes (object) + - error: + - message: `Access is not granted` (string) - Error message + - reason: `invalidAccessToken` (string) - Wrong token idintifier + +- Response 400 (application/json) + - Attributes (object) + - error: + - message: `Email already exists` (string) - Error message + - reason: `invalidUniqueKey` (string) - Unique error type + +### Update password + +> `(PUT) /api/profile/password` + +- Request (application/json) + + - Body (object) + - password: `my-new-password` (string) - User's new password + +- Response 401 (application/json) + + - Attributes (object) + - error: + - message: `Access is not granted` (string) - Error message + - reason: `invalidAccessToken` (string) - Wrong token idintifier + +- Response 400 (application/json) + - Attributes (object) + - error: + - message: `Password is wrong` (string) - Error message + - reason: `invalidPassword` (string) - Unique error type + +### Terminate active session + +> `(DELETE) /api/logout` + +- Response 200 (application/json) + + - Attributes (object) + +- Response 401 (application/json) + - Attributes (object) + - error: + - message: `Access is not granted` (string) - Error message + - reason: `invalidAccessToken` (string) - Wrong token idintifier + +## Design examples + +> [!WARNING] +> All mockups should not be considered as final versions! We are providing an idea; you can come up with any design as long as it meets the functional requirements. + +### Profile page for user + +![Profile page for user](./designs/profile/user.png) + +### Profile page for manager + +![Profile page for manager](./designs/profile/manager.png) + +### Profile page change password modal + +![Profile page change password modal](./designs/profile/change-password.png) + +## Next section + +- [Home page](./home.md) +- [Manager page](./admin) diff --git a/tasks/train-a/readme.md b/tasks/train-a/readme.md new file mode 100644 index 000000000..b3de2410b --- /dev/null +++ b/tasks/train-a/readme.md @@ -0,0 +1,98 @@ +# Train-A + +## Welcome to the final project + +### Organization + +The project is a collaborative (team group) effort involving a maximum of five team members. The project timeline you can find in [RSApp Schedule](https://app.rs.school/course/schedule), and the final product must be a serverless application accessible to the public. + +### Project Purpose + +The primary goal of this project is to develop a platform for managing train trips. User roles include anonymous users, authorized users, and a resource manager with distinct responsibilities. + +#### Roles + +- an anonymous user can search for routes and find trips, but before booking, they will be required to authorize. +- an authorized user can book trips and view their past trips. +- the resource manager has the authority to create trips and manage routes and schedules. + +## Getting Started + +Before starting, please note the following technical details: + +### Technical Setup + +To simulate the backend functionality, install the latest `@planess/train-a-backend@latest` npm package into your project. Modify the `src/main.ts` file as instructed: + +#### src/main.ts + +```diff +import { bootstrapApplication } from '@angular/platform-browser'; ++ import { startServer } from '@planess/train-a-backend'; +import { appConfig } from './app/app.config'; +import { AppComponent } from './app/app.component'; + +- bootstrapApplication(AppComponent, appConfig) ++ startServer() ++ .then(() => bootstrapApplication(AppComponent, appConfig)) + .catch((err) => console.error(err)); +``` + +### Project Data + +A substantial amount of static data has been added to the database for testing purposes. This data does not accurately reflect real-world conditions but is sufficient for training. + +### Manager credentials + +> The manager and the root administrator can enter the system with +> _email:_ +> _password:_ my-password + +### Authorization token + +Since user passed authorization and `{token: "some-token-here"}` was replied to the browser, developer should include this data into additional header record: + +```yml +- headers + - Authorization: Bearer ${some-token-here} +``` + +### Currency + +Server saves only the number for the price. Frontend can be configured for whatever price you want. + +## Assessment + +Feel free to use all your knowledge and skills to implement the project, as the assessment will take place in two stages: + +- Peer review: Your project will be reviewed by members of other teams in **cross-check session**. +- **Presentation**: You will present your project to a jury that will evaluate not only the technical result but also teamwork, communication, and task distribution. You will need to demonstrate each team member's contribution and area of responsibility. + +## Clarifications + +The provided description and requirements may contain errors or omissions. Please use the Discord channel for any questions or concerns. + +### Page specifications + +Detailed information about the pages can be observed in corresponding files: + +#### User pages + +- [Registration page](./sign-up.md) +- [Login page](./sign-in.md) +- [User Profile page](./profile.md) +- [Search page](./search.md) includes [trip details](./search-detail.md) +- [Order page](./order.md) + +#### Manager pages + +- [Admin overview](./admin/readme.md) +- [Stations management](./admin/stations.md) +- [Carriages/Cars management](./admin/carriages.md) +- [Route management](./admin/routes.md) including [Ride management](./admin/ride.md) + +#### General requirements + +All pages must adhere to a consistent theme using component libraries, packages, and styles. A [shared header](./header-fragment.md) fragment is provided for this purpose. + +### Choose the appropriate strategy and good luck diff --git a/tasks/train-a/search-detail.md b/tasks/train-a/search-detail.md new file mode 100644 index 000000000..eb22341c8 --- /dev/null +++ b/tasks/train-a/search-detail.md @@ -0,0 +1,222 @@ +# Trip + +**Browser path**: _/trip/:rideId?from=stationId&to=stationId_ + +**Score**: 90 + +## Description + +**Ride** is certain instance of route and defines time slots with prices. Also, this page show cars grouped by carriage types. + +A **carriage** is a structure for transporting passengers with specific parameters. The type of carriage is configured by: + +- the number of rows of passenger seats +- the number of seats to the left of the aisle in a row +- the number of seats to the right of the aisle in a row + +![Carriage schema](./designs/search-details/carriage%20hints.png) + +> [!IMPORTANT] +> Each carriage type has different row amount, left and right side amount of seats. Developer should draw carriage/car visualization based on _rows_, _leftSeats_, _rightSeats_ parameters himself! + +### Requirements + +#### Requirement 1: Display start and end stations + +- The page must display the start and end stations at the top of the page. +- The display must include the departure and arrival times of the trip. +- The ride identifier must be displayed directly under the trip stations. +- A "Back" button navigates user to general search page. +- A "Route" button must be present, which opens a modal window with route path. + +#### Requirement 2: Carriage type tabs + +- Tabs must be provided for each carriage type available on the train. +- Clicking on a tab must update the list of displayed train cars. + +#### Requirement 3: Train Car List Items + +- Each list item must represent an individual train car for the selected carriage type. +- Seats within each car must be color-coded. +- User can select a seat + +#### Requirement 4: Booking a Seat + +- The page must include a "Book seat" button. +- Clicking the "Book seat" button must reserved seat for user. +- If user acts like a guest, the modal authorization window should appear(or any other option) before seat is booked to determine which user runs the process. + +### Acceptance criteria (90) + +#### Acceptance Criteria 1: Display start and end stations (10) + +- The top of the page must display the start and end stations, including departure and arrival times. +- The ride identifier must be visible below the station names. +- A "Back" button navigates user to general search page. +- When the "Route" button is clicked, a modal window must open displaying ([design](#route-stations-modal)): + - The start and end stations of the trip. + - All intermediate stations along with their respective arrival and departure times. + - Dwell time. + +#### Acceptance Criteria 2: Carriage type tabs (30) + +- Tabs for each carriage type must be visible and clearly labeled with: + - Carriage type name. + - Number of available seats. + - Price for the trip. +- Clicking on a tab must: + - Display the list of train cars corresponding to the selected carriage type. +- Update the seat availability information accordingly. + +#### Acceptance Criteria 3: Train car list items (30) + +- Each train car list item must: + - Display the car number and the number of available seats. + - Color-code seats as blue (available), grey (occupied), or yellow (currently selected). +- Selecting a seat must: + - Change the seat color to yellow. + - Display the seat number and price in a sticky panel on the right. + +#### Acceptance 4: Booking a seat (20) + +- The "Book seat" button must be present and clickable only when a seat is selected. +- Clicking the "Book seat" button must: + - Change the selected seat’s color from yellow to grey. + - Update the seat’s status in the system to occupied. + - Display a confirmation message indicating the seat has been booked. +- The user cannot book a seat on a train where a reservation already exists. They must cancel the previous reservation and create a new one. + +## API + +> [!NOTE] +> Endpoint provides general information. Developer should independently calculate required information on the page based on the http-response. + +### To get ride information + +> `(GET) /api/search/{rideId}` + +- Request (application/json) + + - Parameters: + - rideId: `745` (number, required) - Identifier of the ride, it is instance of the route + +- Response 200 (application/json) + + - Attributes: (object) + - rideId: `745` (number) - Identifier of certain schedule for the route + - path: `[33, 5, 62, 11, 48, 34]` (array[number]) - List of station identifiers + - carriages: `['carriage_type_2', 'carriage_type_2', 'carriage_type_2', 'carriage_type_2', 'carriage_type_7', 'carriage_type_7', 'carriage_type_7', 'carriage_type_7']` (array[string]) - List of carriage types for the train + - schedule: (object) - Information about the ride + - segments: (array[object]) - List of road section between each station. Always 1 less than the number of stations on the route + - time: `['2024-08-08T22:19:57.708Z', '2024-08-12T03:29:57.708Z']` (array[string]) - dates of the start of movement on the section and the end of movement on the section in string form - [departure_from_prev_station, arrival_at_next_station] + - price: (object) - set of prices for all carriage types on this route. It has vary size depending on the train configuration + - dynamic-carriage-type-1: `210` (number) - the price of 210 units for _dynamic-carriage-type-1_ carriage type on current section + - occupiedSeats: `[4,28,42,61]` (array[number]) - list of occupied seat numbers + +- Response 400 (application/json) + - Attributes (object) + - error: + - message: `Ride not found` (string) - Error message + - reason: `rideNotFound` (string) - Error type + +### To make an order + +> `(POST) /api/order` + +- Request (application/json) + + - Body (object) + - rideId: `745` (number, required) - Identifier of certain schedule for the route + - seat: `27` (number, required) - Selected seat number + - stationStart: `7` (number, required) - Station id which user boards the train + - stationEnd: `61` (number, required) - Station id which user leaves the train + +- Response 201 (application/json) + + - Attributes: (object) + - id: `209` (string) - identifier of new order + +- Response 400 (application/json) + + - Attributes: (object) + - error: + - message: `Ride not found` (string) - Error message + - reason: `rideNotFound` (string) - Error type + +- Response 400 (application/json) + + - Attributes: (object) + - error: + - message: `Invalid stations` (string) - Error message + - reason: `invalidStations` (string) - Error type + +- Response 400 (application/json) + - Attributes: (object) + - error: + - message: `Ride is already booked` (string) - Error message + - reason: `alreadyBooked` (string) - Error type + +### To cancel active order + +> `(DELETE) /api/order/{orderId}` + +- Request (application/json) + + - Parameters: + - orderId: `215` (number, required) - Identifier of the order + +- Response 200 (application/json) + + - Attributes: (object) + +- Response 400 (application/json) + + - Attributes: (object) + - error: + - message: `Order is not found` (string) - Error message + - reason: `orderNotFound` (string) - Error type + +- Response 400 (application/json) + - Attributes: (object) + - error: + - message: `Order is not active` (string) - Error message + - reason: `orderNotActive` (string) - Error type + +## Design example + +> [!WARNING] +> All mockups should not be considered as final versions! We are providing an idea; you can come up with any design as long as it meets the functional requirements. + +### Seat options + +#### One of carriage type tab is selected + +![Cars on grouped type](./designs/search-details/carriage%201.png) + +#### Another carriage type tab is selected + +![Cars on grouped type](./designs/search-details/carriage%202.png) + +#### Page with hints + +#### One of carriage type tab is selected + +![Cars on grouped type](./designs/search-details/carriage%201.hints.png) + +#### Another carriage type tab is selected + +![Cars on grouped type](./designs/search-details/carriage%202.hints.png) + +### Route stations modal + +#### Top side + +![Route top modal](./designs/search-details/modal%20top.png) + +#### Bottom side + +![Route bottom modal](./designs/search-details/modal%20bottom.png) + +## Next section + +- [My orders](./order.md) diff --git a/tasks/train-a/search.md b/tasks/train-a/search.md new file mode 100644 index 000000000..4d3df5d5d --- /dev/null +++ b/tasks/train-a/search.md @@ -0,0 +1,376 @@ +# Home Page + +**Browser path**: _/_ + +**Score**: 70 + +## Description + +User can book train tickets simply enter his departure and destination points, select travel date and time, and browse through a list of available trains. + +To get from one point to another, user need to find a route between stations as well as the date of a possible trip. + +A **station** is a place where a train arrives in a city, like a terminal, which is clearly connected to some other stations. A train can only move between stations that are connected to each other. _There can be only one station in a city._ + +A **carriage** is a structure for transporting passengers with specific parameters. The type of carriage is configured by: + +- the number of rows of passenger seats +- the number of seats to the left of the aisle in a row +- the number of seats to the right of the aisle in a row + +A **route** is a set of connected stations that a train travels through with a specific list of carriages. A train can consist of carriages of different types in any order and in any quantity. A route does not have a direct association with travel time. + +A **schedule** or **ride** is a precisely defined time for a train’s journey between first and last stations on the route, indicating the _price for each type of carriage for each segment between stations_, so that passengers can pay only for the trip between the desired stations. + +Apart from ride, the **trip** is a journey between selected stations by user. It is part of ride. + +### Core elements + +- **Search section**: + - **Form field _From_**: Where the trip starts + - **Form field _To_**: Where the trip ends + - **Form field _Date_**: From what date to search + - **Search**: Button to initiate the search process +- **Result list item**: + - **Arrival date/time**: When a train reaches the station + - **Departure date/time**: When a train departs from the station + - **Duration**: Travel time + - **Carriage(s)**: Carriage type(s) with free seats and price + +### Requirements: Search section [(design 1)](#search-block-1) + +#### Requirement 1: Start City Field + +- The form must include a field to define the city where the trip starts. +- This field must have an autocomplete function to assist users in selecting the city. + +#### Requirement 2: End City Field + +- The form must include a field to define the city where the trip ends. +- This field must have an autocomplete function to assist users in selecting the city. + +#### Requirement 3: Date/Time Field + +- The form must include a calendar field to specify the minimal date and time of the trip. + +#### Requirement 4: Submit Button + +- The form must include a submit button. +- The submit button must be disabled until the form is valid (all fields are correctly filled). + +### Acceptance Criteria: Search section [(design 1)](#search-block-1) (20) + +#### Acceptance Criteria 1: Start city field autocomplete (6) + +> [!WARNING] > **Technical quirk** +> Developer should obtain coordinates of the location through whatever external services they want while user fills city name in the form field. + +- The form must include a field labeled "From" for the start city. +- When the user begins typing in the "From" field, an autocomplete dropdown must appear with city suggestions. +- The user must be able to select a city from the autocomplete suggestions. + +#### Acceptance Criteria 2: End city field autocomplete (6) + +> [!WARNING] > **Technical quirk** +> Developer should obtain coordinates of the location through whatever external services they want while user fills city name in the form field. + +- The form must include a field labeled "To" for the end city. +- When the user begins typing in the "To" field, an autocomplete dropdown must appear with city suggestions. +- The user must be able to select a city from the autocomplete suggestions. + +#### Acceptance Criteria 3: Calendar field (6) + +- The form must include a field labeled "Date/Time" for specifying the date and optional time of the trip. +- The user must be able to select a date from a calendar that only allows future days. +- The time selection must be optional and only available if a date is selected. + +#### Acceptance Criteria 4: Submit button validation (2) + +- The form must include a submit button labeled "Search". +- The submit button must remain disabled until the "From", "To", and "Date/Time" fields are all filled and valid. +- Once all fields are valid, the submit button must become enabled. + +### Requirements: Filter [(design 2)](#filter-2) + +#### Requirement 1: Display of dates and days + +- The inline tabs must display each date in the format "MMMM dd" (e.g., "August 01"). +- Each tab must also display the corresponding day name (e.g., "Monday"). + +#### Requirement 2: Clickable tabs + +- Each tab must be clickable and must allow the user to select a specific date. +- Clicking on a tab must update the list of train rides to show the rides available for the selected date. + +#### Requirement 3: Highlighting selected tab + +- The currently selected tab must be visually highlighted to indicate the active selection. + +#### Requirement 4: Data binding + +- The tabs must dynamically reflect the search results, displaying only the dates for which there are available train rides. +- The list of train rides must update accordingly when a tab is selected. + +#### Requirement 5: Responsive design + +- The inline tabs must be responsive and must display correctly on various devices (mobile, tablet, desktop). + +### Acceptance Criteria: Filter [(design 2)](#filter-2) (15) + +#### Acceptance Criteria 1: Display dormat (2) + +- Each tab must display the date in the format "MMMM dd" (e.g., "August 01") and the corresponding day name (e.g., "Monday"). + +#### Acceptance Criteria 2: Clickable functionality (2) + +- When a user clicks on a tab, the list of train rides must update to show the rides available for the selected date. +- Each tab must be individually clickable. + +#### Acceptance Criteria 3: Visual highlighting of selected tab (2) + +- The selected tab must be visually highlighted to indicate it is the active selection. +- Only one tab can be highlighted at a time. + +#### Acceptance Criteria 4: Reflecting search results (7) + +- The tabs must dynamically display only the dates for which there are available train rides based on the search results. +- When a tab is selected, the corresponding train rides for that date must be displayed. + +#### Acceptance Criteria 5: Responsive Design (2) + +- The inline tabs must display correctly and be fully functional on mobile, tablet, and desktop devices. +- The layout must adjust to ensure usability across different screen sizes. + +### Requirements: Result list ([design 3](#result-list-3)) + +#### Requirement 1: Display structure + +- The result list must display either a grouped array of train movement or a message indicating that no rides are available. +- Each item in the list must have an "information section" with stations and a "price section" with a cost of trip for each carriage type. +- Each item is clickable for whole area and redirects user to the detail page, when user can make an order. + +#### Requirement 2: Information section + +- The information section must display the departure time, arrival time, and the corresponding dates of the trip. +- The start city and end city of the trip (that user selected) must be displayed. +- The trip duration must be shown. +- There must be a "Route" button that opens a modal window with all stations in the route. +- The start and end stations of the route must be displayed. + +#### Requirement 3: Price section + +- The price section must display a dynamic list of unique carriage types with certain data. +- For each carriage type, the number of free seats and the price for the trip (between the selected stations) must be displayed. + +#### Requirement 4: Message for no rides available + +- If no rides are available, a message indicating that no rides are available must be displayed. + +### Acceptance Criteria: Result list ([design 3](#result-list-3)) (25) + +#### Acceptance Criteria 1: Display of train rides (6) + +- The result list must display train rides grouped by day. +- Each item must have an "information section" and a "price section" with the specified content. +- The list of results must be grouped by day from the filter, showing available routes for each day. +- Each item is clickable for whole area and redirects user to the [detail page](./search-detail.md), when user can make an order. + +#### Acceptance Criteria 2: Information section (8) + +- The departure time, arrival time, and corresponding dates must be displayed. +- The start city and end city of the trip (that user selected) must be displayed. +- The trip duration must be shown between dates. +- A "Route" button must be present and, when clicked, must open a modal window showing all stations in the route. +- The start and end stations of the route must be displayed below. + +#### Acceptance Criteria 3: Price section (8) + +- The price section must display a list of unique carriage types as a area. +- Each carriage area must display the name of carriage, number of free seats, and the price for the ride between the selected stations (that user selected). + +#### Acceptance Criteria 4: Message for no rides available (3) + +- If there are no available rides, a specific message such as "No rides available" must be displayed. + +### Requirements: Trip detail ([design 4](#result-trip-details-4)) + +#### Requirement 1: Route button functionality + +- Each result item element must include a "Route" button. +- Clicking the "Route" button must trigger a modal window. + +#### Requirement 2: Modal window content + +- The modal window must display the trip start station, trip end station, and all intermediate stations of the trip. +- The list of intermediate stations must be accurate and correspond to the specific route. + +#### Requirement 3: User Interaction + +- The modal window must be dismissible by the user, either by clicking a close button or by clicking outside the modal window. + +### Acceptance Criteria: Trip detail ([design 4](#result-trip-details-4)) (10) + +#### Acceptance Criteria 1: Route button presence (1) + +- Each result item element must have a "Route" button that is clearly visible. + +#### Acceptance Criteria 2: Modal window trigger (2) + +- When the "Route" button is clicked, a modal window must appear. + +#### Acceptance Criteria 3: Modal window content (5) + +- Title of modal contains text "Route" along with route id number. + +- The modal window must display: + + - The start station of the trip at the top. + - The end station of the trip at the bottom. + - All intermediate stations between the start and end stations listed in order. + +- For each station: + - Display the departure time in the first column (except for the last station). + - Display the arrival time in the first column (except for the first station). + - City name in the secord column + - Dwell time at each station in the third column. + +#### Acceptance Criteria 4: User interaction (2) + +- The modal window must include a close button. +- The modal window must also close when the user clicks outside of it. +- The modal window must remain open and display correctly until the user dismisses it. +- Internal content could have vertical scrolling if necessary. + +## API + +### Search + +> [!NOTE] +> Endpoint provides general information. Developer should independently calculate required information on the page based on the http-response. + +> [!IMPORTANT] +> The coordinates of the points must be passed that which the search will be performed by! + +> `(GET) /api/search` + +- Request (application/json) + + - Attributes: (object) + - fromLatitude: `48.8575` (number, required) - Latitude of the start city + - fromLongitude: `2.3514` (number, required) - Longitude of the start city + - toLatitude: `40.4167` (number, required) - Latitude of the end city + - toLongitude: `3.7033` (number, required) - Longitude of the end city + - time: `1723669200000` (number, optional) - unix timestamp of the trip + +- Response 200 (application/json) + + - Attributes: (object) + - from: (object) - set of data about start point + - stationId: `5` (number) - Unique station identifier + - city: `Paris` (string) - City name + - geolocation: (object) Station coordinates + - latitude: `48.8575` (number) - Latitude of the start station + - longitude: `2.3514` (number) - Longitude of the start station + - to: (object) - set of data about end point + - stationId: `48` (number) - Unique station identifier + - city: `Madrid` (string) - City name + - geolocation: (object) Station coordinates + - latitude: `40.4167` (number) - Latitude of the end station + - longitude: `3.7033` (number) - Longitude of the end station + - routes: (array[object]) - List of available routes + - id: `64` (number) - Route identifier + - path: `[33, 5, 62, 11, 48, 34]` (array[number]) - List of station identifiers + - carriages: `['carriage_type_2', 'carriage_type_2', 'carriage_type_2', 'carriage_type_2', 'carriage_type_7', 'carriage_type_7', 'carriage_type_7', 'carriage_type_7']` (array[string]) - List of carriage types for the train + - schedule: (array[object]) - List of potential rides + - rideId: `44` (number) - Identifier of certain schedule for the route + - segments: (array[object]) - List of road section between each station. Always 1 less than the number of stations on the route + - time: `['2024-08-08T22:19:57.708Z', '2024-08-12T03:29:57.708Z']` (array[string]) - dates of the start of movement on the section and the end of movement on the section in string form - [departure_from_prev_station, arrival_at_next_station] + - price: (object) - set of prices for all carriage types on this route. It has vary size depending on the train configuration + - dynamic-carriage-type-1: `210` (number) - the price of 210 units for _dynamic-carriage-type-1_ carriage type on current section + - occupiedSeats: (array[number]) - list of occupied seat numbers + +- Response 400 (application/json) + + - Attributes (object) + - error: + - message: `Start station not found` (string) - Error message + - reason: `stationNotFound` (string) - Error type + +- Response 400 (application/json) + - Attributes (object) + - error: + - message: `End station not found` (string) - Error message + - reason: `stationNotFound` (string) - Error type + +## Design example + +> [!WARNING] +> All mockups should not be considered as final versions! We are providing an idea; you can come up with any design as long as it meets the functional requirements. + +### Search block (1) + +#### Search form + +##### Mobile + +![Search block for mobile](./designs/search/search.mobile.png) + +##### Tablet + +![Search block for tablet](./designs/search/search.tablet.png) + +##### Desktop + +![Search block for desktop](./designs/search/search.desktop.png) + +#### City autocomplete + +Using any external service to obtain the coordinates of the city. + +![Search autocomplete field](./designs/search/search.point.png) + +#### Calendar selection + +![Search calendar selection](./designs/search/search.calendar.png) + +### Filter (2) + +#### Date filter + +##### Mobile + +![Date filter mobile](./designs/search/filter.mobile.png) + +##### Desktop + +![Date filter desktop](./designs/search/filter.desktop.png) + +### Result list (3) + +#### None available routes are found (desktop, tablet, mobile) + +![Empty ride list](./designs/search/noresults.png) + +#### Available rides for selected day + +##### Mobile + +Carriage types on the bottom + +![Ride list for selected day on mobile](./designs/search/result-list.mobile.png) + +##### Desktop + +Carriage types on the right + +![Ride list for selected day on desktop](./designs/search/result-list.desktop.png) + +### Result trip details (4) + +#### Modal window with trip stations (desktop, tablet, mobile) + +![Route station information](./designs/search/result-route.png) + +## Next section + +- [Trip](./search-detail.md) diff --git a/tasks/train-a/sign-in.md b/tasks/train-a/sign-in.md new file mode 100644 index 000000000..89355510a --- /dev/null +++ b/tasks/train-a/sign-in.md @@ -0,0 +1,137 @@ +# User login + +**Browser path**: _/signin_ + +**Score**: 30 + +**Access**: only for guests (run by guards) + +## Description + +User can enter _email_ and _password_ into relative form fields and be granted to use personal functions. + +### Core elements + +- **Email field:** Where users input their login credentials. +- **Password field:** For secure password entry. +- **Sign In button:** To initiate the login process. +- **Sign Up link:** To navigate user to Sign Up page. + +> [!IMPORTANT] +> Password recovery is not supported. If a user forgets their password, they will lose access to the system. + +### Requirements + +#### Requirement 1: User Authentication + +- The system must allow users to sign in using their email and password. +- The system should securely authenticate users based on the credentials provided. +- The sign-in page should have fields for email and password input. + +#### Requirement 2: User Experience + +- The sign-in page should display appropriate error messages for incorrect email/password combinations. +- The sign-in page should be responsive and work on various devices (mobile, tablet, desktop). + +#### Requirement 3: Response & Token + +- User can click "Sign In" button only once during http-request execution. If authentication was successful - browser receives token, save it into `localStorage` and follow the [instructions](./readme.md#authorization-token). + +### Acceptance Criteria (30) + +#### Acceptance Criteria 1: Access control (5) + +- User should be authenticated and redirected to the homepage upon entering valid email and password. + +- Once signed in, the user should not be able to access the Sign In page again until they log out. + +- If an authenticated user tries to access the Sign In page directly via the browser URL, they should be redirected to the [Homepage](./home.md). + +- After successful authentication each following http-requests must comply with [Authorization record](./readme.md#authorization-token) + +#### Acceptance Criteria 2: Error message for incorrect password (5) + +- Display "_Incorrect email or password_" error message when user enters a valid email but incorrect password. + +- User can fill in any symbols in any languages. + +- Password has to be not less 8 symbols excluding space around. + +#### Acceptance Criteria 3: Error message for email (5) + +- Display "_Incorrect email or password_" error message when user enters a non-existent email. + +- Display "_Incorrect email_" error message under email field if content of email field does not match _^[\w\d\_]+@[\w\d\_]+\.\w{2,7}$_ regular expression. + +#### Acceptance Criteria 4: Error message for empty field (5) + +- Display "_Required_" error message under the empty form fields **after** Sign In button is clicked first time. Error does not appear until user click button regardless of content. + +#### Acceptance Criteria 5: Disabled Sign In button (5) + +- Button "Sign In" is disabled until user fill valid email/password in. User cannot click it. + +#### Acceptance Criteria 6: Responsive design (5) + +- The sign-up page must display correctly and be fully functional on mobile, tablet, and desktop devices. + + + +## API + +### Sign In User + +> `(POST) /api/signin` + +- Request (application/json) + + - Body (object) + - email: `user@example.com` (string, required) - User's email address + - password: `Password123` (string, required) - User's password + +- Response 201 (application/json) + + - Attributes (object) + - token: `eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9...` (string, required) - Authentication token + +- Response 400 (application/json) + + - Attributes (object) + - error: + - message: `Fields are empty` (string) - Error message + - reason: `invalidFields` (string) - Unique error type + +- Response 400 (application/json) + + - Attributes (object) + - error: + - message: `Email is wrong` (string) - Error message + - reason: `invalidEmail` (string) - Unique error type + +- Response 400 (application/json) + + - Attributes (object) + - error: + - message: `User is not found` (string) - Error message + - reason: `userNotFound` (string) - Unique error type + +- Response 400 (application/json) + - Attributes (object) + - error: + - message: `Authorization error` (string) - Error message + - reason: `alreadyLoggedIn` (string) - Unique error type + +## Design example + +> [!WARNING] +> All mockups should not be considered as final versions! We are providing an idea; you can come up with any design as long as it meets the functional requirements. + +### Sign In page example + +![Sign In page example](./desgins/signin.png) + +## Next Section + +- [Profile page](./profile.md) diff --git a/tasks/train-a/sign-up.md b/tasks/train-a/sign-up.md new file mode 100644 index 000000000..34ac5d12f --- /dev/null +++ b/tasks/train-a/sign-up.md @@ -0,0 +1,131 @@ +# Sign-Up Page + +**Browser path**: _/signup_ + +**Score**: 30 + +**Access**: only for guests (run by guards) + +## Description + +User is able to enter _email_ and _password_ into the relative fields and create new account to get personal service. + +### Core elements + +- **Email field:** Where users input their login credentials. +- **Password field:** For secure password entry. +- **Sign In button:** To initiate the register process. +- **Sign In link:** To navigate user to Sign Up page. + +> [!IMPORTANT] +> Password recovery is not supported. If a user forgets their password, they will lose access to the system. + +### Requirements + +#### Requirement 1: User Registration + +- The system must allow users to sign up using their email, password, and repeat password fields. + +- The Sign Up page should include a "Register" button and a "Sign In" link for users who already have an account. + +#### Requirement 2: Password Validation + +- The system must ensure that the password consists of at least 8 symbols. + +- The system must validate that the password and repeat password fields match. + +#### Requirement 3: Error Handling + +- The Sign Up page should display appropriate error messages if the password and repeat password fields do not match. + +- The Sign Up page should display an error message if an account with the provided email already exists. + +#### Requirement 4: User Experience + +- The Sign Up page should be responsive and work on various devices (mobile, tablet, desktop). + +- The Sign Up page should provide a clear and user-friendly interface. + +### Acceptance Criteria (30) + +#### Acceptance Criteria 1: Access control (5) + +- User should be able to register with a valid email, password, and matching repeat password, and should be redirected to a [Sign In page](./sign-in.md) page upon successful registration. + +#### Acceptance Criteria 2: Password validation error (5) + +- Display "_Passwords do not match_" error message when the password and repeat password fields do not match. + +- Display "_Password must be at least 8 characters long_" error message when the password is less than 8 characters. + +#### Acceptance Criteria 3: Email already exists error (5) + +- Display "_Account with this email already exists_" error message when the user tries to sign up with an email that is already registered. + +#### Acceptance Criteria 4: Error message for empty field (5) + +- Display "_Required_" error message under the empty form fields **after** Register button is clicked first time. Error does not appear until user click button regardless of content. + +#### Acceptance Criteria 5: Disabled Sign Up button (5) + +- Button "Register" is disabled until user fill valid email/password in. User cannot click it. + +#### Acceptance Criteria 6: Responsive design (5) + +- The Sign Up page must display correctly and be fully functional on mobile, tablet, and desktop devices. + +## API + +### Sign Up User + +> `(POST) /api/signup` + +- Request (application/json) + + - Body (object) + - email: `user@example.com` (string, required) - User's email address + - password: `Password123` (string, required) - User's password + +- Response 201 (application/json) + + - Attributes (object) + +- Response 400 (application/json) + + - Attributes (object) + - error: + - message: `Fields are empty` (string) - Error message + - reason: `invalidFields` (string) - Unique error type + +- Response 400 (application/json) + + - Attributes (object) + - error: + - message: `Email is wrong` (string) - Error message + - reason: `invalidEmail` (string) - Unique error type + +- Response 400 (application/json) + + - Attributes (object) + - error: + - message: `Password is wrong` (string) - Error message + - reason: `invalidPassword` (string) - Unique error type + +- Response 400 (application/json) + - Attributes (object) + - error: + - message: `User already exists` (string) - Error message + - reason: `invalidUniqueKey` (string) - Unique error type + +## Design example + +> [!WARNING] +> All mockups should not be considered as final versions! We are providing an idea; you can come up with any design as long as it meets the functional requirements. + +### Sign Up page example + +![Sign Up page example](./desgins/signup.png) + +## Next section + +- [Sign In page](./sign-in.md)