diff --git a/backend/sportsmatch/src/main/java/com/sportsmatch/SportsmatchApplication.java b/backend/sportsmatch/src/main/java/com/sportsmatch/SportsmatchApplication.java index 34a1bacf..488ad31b 100644 --- a/backend/sportsmatch/src/main/java/com/sportsmatch/SportsmatchApplication.java +++ b/backend/sportsmatch/src/main/java/com/sportsmatch/SportsmatchApplication.java @@ -29,6 +29,7 @@ public class SportsmatchApplication implements CommandLineRunner { private final EventRepository eventRepository; private final PasswordEncoder passwordEncoder; private final InitProperties initProperties; + private final RatingRepository ratingRepository; private final UserEventRatingRepository userEventRatingRepository; diff --git a/backend/sportsmatch/src/main/java/com/sportsmatch/controllers/EventsController.java b/backend/sportsmatch/src/main/java/com/sportsmatch/controllers/EventsController.java index c0652882..2bc8aeca 100644 --- a/backend/sportsmatch/src/main/java/com/sportsmatch/controllers/EventsController.java +++ b/backend/sportsmatch/src/main/java/com/sportsmatch/controllers/EventsController.java @@ -2,6 +2,7 @@ import com.sportsmatch.dtos.EventDTO; import com.sportsmatch.dtos.EventHistoryDTO; +import com.sportsmatch.dtos.HostEventDTO; import com.sportsmatch.dtos.RequestEventDTO; import com.sportsmatch.models.Event; import com.sportsmatch.services.EventService; @@ -31,8 +32,8 @@ public ResponseEntity getEvent(@PathVariable("id") Long id) { } @PostMapping("") - public ResponseEntity addEvent(@RequestBody @Valid EventDTO eventDTO) { - Event newEvent = eventService.createEvent(eventDTO); + public ResponseEntity addEvent(@RequestBody @Valid HostEventDTO hostEventDTO) { + Event newEvent = eventService.createEvent(hostEventDTO); return ResponseEntity.status(HttpStatus.CREATED) .body(eventService.getEventDTObyEventId(newEvent.getId())); } diff --git a/backend/sportsmatch/src/main/java/com/sportsmatch/dtos/EventDTO.java b/backend/sportsmatch/src/main/java/com/sportsmatch/dtos/EventDTO.java index 7309ddb0..97d73b32 100644 --- a/backend/sportsmatch/src/main/java/com/sportsmatch/dtos/EventDTO.java +++ b/backend/sportsmatch/src/main/java/com/sportsmatch/dtos/EventDTO.java @@ -33,5 +33,6 @@ public class EventDTO { private String player1Name; private String player2Name; + @NotNull private PlaceDTO placeDTO; } diff --git a/backend/sportsmatch/src/main/java/com/sportsmatch/dtos/HostEventDTO.java b/backend/sportsmatch/src/main/java/com/sportsmatch/dtos/HostEventDTO.java new file mode 100644 index 00000000..05aad09a --- /dev/null +++ b/backend/sportsmatch/src/main/java/com/sportsmatch/dtos/HostEventDTO.java @@ -0,0 +1,30 @@ +package com.sportsmatch.dtos; + +import jakarta.validation.constraints.NotBlank; +import jakarta.validation.constraints.NotNull; +import lombok.*; + +import java.time.LocalDateTime; + +@Getter +@Setter +@AllArgsConstructor +@NoArgsConstructor +@Builder +public class HostEventDTO { + + @NotNull + private LocalDateTime dateStart; + @NotNull + private LocalDateTime dateEnd; + @NotNull + private Integer minElo; + @NotNull + private Integer maxElo; + @NotBlank + private String title; + @NotBlank + private String sport; + @NotNull + private Long locationId; +} diff --git a/backend/sportsmatch/src/main/java/com/sportsmatch/dtos/PlaceDTO.java b/backend/sportsmatch/src/main/java/com/sportsmatch/dtos/PlaceDTO.java index f2573727..7f3669ec 100644 --- a/backend/sportsmatch/src/main/java/com/sportsmatch/dtos/PlaceDTO.java +++ b/backend/sportsmatch/src/main/java/com/sportsmatch/dtos/PlaceDTO.java @@ -13,6 +13,8 @@ @AllArgsConstructor public class PlaceDTO { + private Long id; + @NotBlank private String name; diff --git a/backend/sportsmatch/src/main/java/com/sportsmatch/mappers/EventMapper.java b/backend/sportsmatch/src/main/java/com/sportsmatch/mappers/EventMapper.java index fe4dfa27..9caefae6 100644 --- a/backend/sportsmatch/src/main/java/com/sportsmatch/mappers/EventMapper.java +++ b/backend/sportsmatch/src/main/java/com/sportsmatch/mappers/EventMapper.java @@ -2,6 +2,7 @@ import com.sportsmatch.dtos.EventDTO; import com.sportsmatch.dtos.EventHistoryDTO; +import com.sportsmatch.dtos.HostEventDTO; import com.sportsmatch.models.Event; import com.sportsmatch.models.EventPlayer; import com.sportsmatch.models.EventStatusOptions; @@ -44,9 +45,9 @@ public EventDTO convertEventToEventDTO(Event event) { return eventDTO; } - public Event convertEventDTOtoEvent(EventDTO eventDTO) { + public Event convertHostEventDTOtoEvent(HostEventDTO hostEventDTO) { modelMapper.typeMap(EventDTO.class, Event.class).addMappings(e -> e.skip(Event::setId)); - return modelMapper.map(eventDTO, Event.class); + return modelMapper.map(hostEventDTO, Event.class); } /** diff --git a/backend/sportsmatch/src/main/java/com/sportsmatch/mappers/PlaceMapper.java b/backend/sportsmatch/src/main/java/com/sportsmatch/mappers/PlaceMapper.java index 94fe4696..2eb97f64 100644 --- a/backend/sportsmatch/src/main/java/com/sportsmatch/mappers/PlaceMapper.java +++ b/backend/sportsmatch/src/main/java/com/sportsmatch/mappers/PlaceMapper.java @@ -15,6 +15,7 @@ public class PlaceMapper { */ public PlaceDTO toDTO(Place entity) { return PlaceDTO.builder() + .id(entity.getId()) .name(entity.getName()) .address(entity.getAddress()) .latitude(entity.getLatitude()) diff --git a/backend/sportsmatch/src/main/java/com/sportsmatch/services/EventService.java b/backend/sportsmatch/src/main/java/com/sportsmatch/services/EventService.java index a92072be..5b73e7b6 100644 --- a/backend/sportsmatch/src/main/java/com/sportsmatch/services/EventService.java +++ b/backend/sportsmatch/src/main/java/com/sportsmatch/services/EventService.java @@ -2,14 +2,17 @@ import com.sportsmatch.dtos.EventDTO; import com.sportsmatch.dtos.EventHistoryDTO; +import com.sportsmatch.dtos.HostEventDTO; import com.sportsmatch.dtos.RequestEventDTO; import com.sportsmatch.mappers.EventMapper; import com.sportsmatch.models.Event; import com.sportsmatch.models.EventPlayer; import com.sportsmatch.models.EventStatusOptions; +import com.sportsmatch.models.Place; import com.sportsmatch.models.User; import com.sportsmatch.repositories.EventPlayerRepository; import com.sportsmatch.repositories.EventRepository; +import com.sportsmatch.repositories.PlaceRepository; import com.sportsmatch.repositories.SportRepository; import com.sportsmatch.repositories.UserRepository; import lombok.AllArgsConstructor; @@ -31,7 +34,7 @@ public class EventService { private final UserRepository userRepository; private final SportRepository sportRepository; private final EventPlayerRepository eventPlayerRepository; - + private final PlaceRepository placeRepository; public Event getEventById(Long id) { return eventRepository @@ -79,23 +82,18 @@ public EventPlayer addPlayerToEvent(Long playerId, Long eventId) { return eventPlayerRepository.save(eventPlayer); } - public Event createEvent(EventDTO eventDTO) { - Event newEvent = eventMapper.convertEventDTOtoEvent(eventDTO); + public Event createEvent(HostEventDTO hostEventDTO) { + User user = userService.getUserFromContext(); + Event newEvent = eventMapper.convertHostEventDTOtoEvent(hostEventDTO); + Place place = placeRepository.findById(hostEventDTO.getLocationId()).orElseThrow(); + newEvent.setPlace(place); newEvent.setSport( sportRepository - .findSportByName(eventDTO.getSport()) + .findSportByName(hostEventDTO.getSport()) .orElseThrow(() -> new ResponseStatusException(HttpStatus.BAD_REQUEST))); newEvent = eventRepository.save(newEvent); - - Set players = new HashSet<>(); - if (eventDTO.getPlayer1Id() != null) { - players.add(addPlayerToEvent(eventDTO.getPlayer1Id(), newEvent.getId())); - } - if (eventDTO.getPlayer2Id() != null) { - players.add(addPlayerToEvent(eventDTO.getPlayer2Id(), newEvent.getId())); - } - - newEvent.setPlayers(players); + EventPlayer userPlayer = new EventPlayer(null, null, user, newEvent); + eventPlayerRepository.save(userPlayer); return newEvent; } @@ -107,7 +105,8 @@ public void deleteEventFromDatabase(Event eventById) { * Retrieves the event history of the logged-in user. * * @param pageable contains the pagination information (page, size) - * @return a list of EventHistoryDTOs representing the logged-in user's event history + * @return a list of EventHistoryDTOs representing the logged-in user's event history$ ../gradlew + * clean checkStyleMain -info */ public List getEventsHistory(final Pageable pageable) { String loggedUserName = userService.getUserFromContext().getName(); @@ -122,12 +121,10 @@ public List getEventsHistory(final Pageable pageable) { * Returns the checked status of the match (check the score is matching or missing). * * @param players who entered the event (2 playerEvent) - * @return the status of the match - * There is 4 option: - * - Invalid Player -> if one of the player don't present. - * - Waiting for ratings -> if one of the players doesn't response with the score information. - * - Match -> when both player submitted their result and it is match. - * - Mismatch -> when both players have submitted their result, and it isn't a match. + * @return the status of the match There is 4 option: - Invalid Player -> if one of the player + * don't present. - Waiting for ratings -> if one of the players doesn't response with the + * score information. - Match -> when both player submitted their result and it is match. - + * Mismatch -> when both players have submitted their result, and it isn't a match. */ public EventStatusOptions checkScoreMatch(Set players) { @@ -187,18 +184,26 @@ public void joinEvent(Long id) throws Exception { } /** - * Finds events near the provided location and filters them based on optional sport names filters, returning a page of event data transfer objects (DTOs). + * Finds events near the provided location and filters them based on optional sport names filters, + * returning a page of event data transfer objects (DTOs). * * @param requestEventDTO containing the request parameters for filtering events. - * @param pageable containing page and size - * @return a list of EventDTO representing Events entity and filtered by sport names is given, and coordinate. + * @param pageable containing page and size + * @return a list of EventDTO representing Events entity and filtered by sport names is given, and + * coordinate. */ public List getNearbyEvents(RequestEventDTO requestEventDTO, final Pageable pageable) { // Convert the given sportNames to lowercase because of the native custom query - List sportNamesWithLowerCase = requestEventDTO.getSportsName().stream().map(String::toLowerCase).toList(); - - List events = eventRepository.findNearbyEvents(requestEventDTO.getLongitude(), requestEventDTO.getLatitude(), sportNamesWithLowerCase, pageable); + List sportNamesWithLowerCase = + requestEventDTO.getSportsName().stream().map(String::toLowerCase).toList(); + + List events = + eventRepository.findNearbyEvents( + requestEventDTO.getLongitude(), + requestEventDTO.getLatitude(), + sportNamesWithLowerCase, + pageable); return events.stream().map(eventMapper::convertEventToEventDTO).collect(Collectors.toList()); } diff --git a/frontend/sportsmatch-app/package-lock.json b/frontend/sportsmatch-app/package-lock.json index 3aaf5a18..bb4f14de 100644 --- a/frontend/sportsmatch-app/package-lock.json +++ b/frontend/sportsmatch-app/package-lock.json @@ -11,6 +11,7 @@ "bootstrap": "^5.3.2", "react": "^18.2.0", "react-bootstrap": "^2.10.0", + "react-datepicker": "^6.6.0", "react-dom": "^18.2.0", "react-icons": "^5.0.1", "react-router-dom": "^6.21.3", @@ -21,6 +22,7 @@ "@testing-library/jest-dom": "^6.3.0", "@testing-library/react": "^14.1.2", "@types/react": "^18.2.43", + "@types/react-datepicker": "^6.2.0", "@types/react-dom": "^18.2.17", "@types/react-router-dom": "^5.3.3", "@typescript-eslint/eslint-plugin": "^6.19.1", @@ -1063,6 +1065,54 @@ "node": "^12.22.0 || ^14.17.0 || >=16.0.0" } }, + "node_modules/@floating-ui/core": { + "version": "1.6.0", + "resolved": "https://registry.npmjs.org/@floating-ui/core/-/core-1.6.0.tgz", + "integrity": "sha512-PcF++MykgmTj3CIyOQbKA/hDzOAiqI3mhuoN44WRCopIs1sgoDoU4oty4Jtqaj/y3oDU6fnVSm4QG0a3t5i0+g==", + "dependencies": { + "@floating-ui/utils": "^0.2.1" + } + }, + "node_modules/@floating-ui/dom": { + "version": "1.6.3", + "resolved": "https://registry.npmjs.org/@floating-ui/dom/-/dom-1.6.3.tgz", + "integrity": "sha512-RnDthu3mzPlQ31Ss/BTwQ1zjzIhr3lk1gZB1OC56h/1vEtaXkESrOqL5fQVMfXpwGtRwX+YsZBdyHtJMQnkArw==", + "dependencies": { + "@floating-ui/core": "^1.0.0", + "@floating-ui/utils": "^0.2.0" + } + }, + "node_modules/@floating-ui/react": { + "version": "0.26.11", + "resolved": "https://registry.npmjs.org/@floating-ui/react/-/react-0.26.11.tgz", + "integrity": "sha512-fo01Cu+jzLDVG/AYAV2OtV6flhXvxP5rDaR1Fk8WWhtsFqwk478Dr2HGtB8s0HqQCsFWVbdHYpPjMiQiR/A9VA==", + "dependencies": { + "@floating-ui/react-dom": "^2.0.0", + "@floating-ui/utils": "^0.2.0", + "tabbable": "^6.0.0" + }, + "peerDependencies": { + "react": ">=16.8.0", + "react-dom": ">=16.8.0" + } + }, + "node_modules/@floating-ui/react-dom": { + "version": "2.0.8", + "resolved": "https://registry.npmjs.org/@floating-ui/react-dom/-/react-dom-2.0.8.tgz", + "integrity": "sha512-HOdqOt3R3OGeTKidaLvJKcgg75S6tibQ3Tif4eyd91QnIJWr0NLvoXFpJA/j8HqkFSL68GDca9AuyWEHlhyClw==", + "dependencies": { + "@floating-ui/dom": "^1.6.1" + }, + "peerDependencies": { + "react": ">=16.8.0", + "react-dom": ">=16.8.0" + } + }, + "node_modules/@floating-ui/utils": { + "version": "0.2.1", + "resolved": "https://registry.npmjs.org/@floating-ui/utils/-/utils-0.2.1.tgz", + "integrity": "sha512-9TANp6GPoMtYzQdt54kfAyMmz1+osLlXdg2ENroU7zzrtflTLrrC/lgrIfaSe+Wu0b89GKccT7vxXA0MoAIO+Q==" + }, "node_modules/@humanwhocodes/config-array": { "version": "0.11.14", "resolved": "https://registry.npmjs.org/@humanwhocodes/config-array/-/config-array-0.11.14.tgz", @@ -1698,6 +1748,17 @@ "csstype": "^3.0.2" } }, + "node_modules/@types/react-datepicker": { + "version": "6.2.0", + "resolved": "https://registry.npmjs.org/@types/react-datepicker/-/react-datepicker-6.2.0.tgz", + "integrity": "sha512-+JtO4Fm97WLkJTH8j8/v3Ldh7JCNRwjMYjRaKh4KHH0M3jJoXtwiD3JBCsdlg3tsFIw9eQSqyAPeVDN2H2oM9Q==", + "dev": true, + "dependencies": { + "@floating-ui/react": "^0.26.2", + "@types/react": "*", + "date-fns": "^3.3.1" + } + }, "node_modules/@types/react-dom": { "version": "18.2.18", "resolved": "https://registry.npmjs.org/@types/react-dom/-/react-dom-18.2.18.tgz", @@ -2625,6 +2686,14 @@ "url": "https://github.com/sponsors/sindresorhus" } }, + "node_modules/clsx": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/clsx/-/clsx-2.1.0.tgz", + "integrity": "sha512-m3iNNWpd9rl3jvvcBnu70ylMdrXt8Vlq4HYadnU5fwcOtvkSQWPmj7amUcDT2qYI7risszBjI5AUIUox9D16pg==", + "engines": { + "node": ">=6" + } + }, "node_modules/color-convert": { "version": "2.0.1", "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", @@ -2732,6 +2801,15 @@ "node": ">=18" } }, + "node_modules/date-fns": { + "version": "3.6.0", + "resolved": "https://registry.npmjs.org/date-fns/-/date-fns-3.6.0.tgz", + "integrity": "sha512-fRHTG8g/Gif+kSh50gaGEdToemgfj74aRX3swtiouboip5JDLAyDE9F11nHMIcvOaXeOC6D7SpNhi7uFyB7Uww==", + "funding": { + "type": "github", + "url": "https://github.com/sponsors/kossnocorp" + } + }, "node_modules/debug": { "version": "4.3.4", "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.4.tgz", @@ -5953,6 +6031,22 @@ } } }, + "node_modules/react-datepicker": { + "version": "6.6.0", + "resolved": "https://registry.npmjs.org/react-datepicker/-/react-datepicker-6.6.0.tgz", + "integrity": "sha512-ERC0/Q4pPC9bNIcGUpdCbHc+oCxhkU3WI3UOGHkyJ3A9fqALCYpEmLc5S5xvAd7DuCDdbsyW97oRPM6pWWwjww==", + "dependencies": { + "@floating-ui/react": "^0.26.2", + "clsx": "^2.1.0", + "date-fns": "^3.3.1", + "prop-types": "^15.7.2", + "react-onclickoutside": "^6.13.0" + }, + "peerDependencies": { + "react": "^16.9.0 || ^17 || ^18", + "react-dom": "^16.9.0 || ^17 || ^18" + } + }, "node_modules/react-dom": { "version": "18.2.0", "resolved": "https://registry.npmjs.org/react-dom/-/react-dom-18.2.0.tgz", @@ -5984,6 +6078,19 @@ "resolved": "https://registry.npmjs.org/react-lifecycles-compat/-/react-lifecycles-compat-3.0.4.tgz", "integrity": "sha512-fBASbA6LnOU9dOU2eW7aQ8xmYBSXUIWr+UmF9b1efZBazGNO+rcXT/icdKnYm2pTwcRylVUYwW7H1PHfLekVzA==" }, + "node_modules/react-onclickoutside": { + "version": "6.13.0", + "resolved": "https://registry.npmjs.org/react-onclickoutside/-/react-onclickoutside-6.13.0.tgz", + "integrity": "sha512-ty8So6tcUpIb+ZE+1HAhbLROvAIJYyJe/1vRrrcmW+jLsaM+/powDRqxzo6hSh9CuRZGSL1Q8mvcF5WRD93a0A==", + "funding": { + "type": "individual", + "url": "https://github.com/Pomax/react-onclickoutside/blob/master/FUNDING.md" + }, + "peerDependencies": { + "react": "^15.5.x || ^16.x || ^17.x || ^18.x", + "react-dom": "^15.5.x || ^16.x || ^17.x || ^18.x" + } + }, "node_modules/react-refresh": { "version": "0.14.0", "resolved": "https://registry.npmjs.org/react-refresh/-/react-refresh-0.14.0.tgz", @@ -6781,6 +6888,11 @@ "url": "https://opencollective.com/unts" } }, + "node_modules/tabbable": { + "version": "6.2.0", + "resolved": "https://registry.npmjs.org/tabbable/-/tabbable-6.2.0.tgz", + "integrity": "sha512-Cat63mxsVJlzYvN51JmVXIgNoUokrIaT2zLclCXjRd8boZ0004U4KCs/sToJ75C6sdlByWxpYnb5Boif1VSFew==" + }, "node_modules/tapable": { "version": "2.2.1", "resolved": "https://registry.npmjs.org/tapable/-/tapable-2.2.1.tgz", diff --git a/frontend/sportsmatch-app/package.json b/frontend/sportsmatch-app/package.json index 08b5b92e..f85cdcd9 100644 --- a/frontend/sportsmatch-app/package.json +++ b/frontend/sportsmatch-app/package.json @@ -18,6 +18,7 @@ "bootstrap": "^5.3.2", "react": "^18.2.0", "react-bootstrap": "^2.10.0", + "react-datepicker": "^6.6.0", "react-dom": "^18.2.0", "react-icons": "^5.0.1", "react-router-dom": "^6.21.3", @@ -28,6 +29,7 @@ "@testing-library/jest-dom": "^6.3.0", "@testing-library/react": "^14.1.2", "@types/react": "^18.2.43", + "@types/react-datepicker": "^6.2.0", "@types/react-dom": "^18.2.17", "@types/react-router-dom": "^5.3.3", "@typescript-eslint/eslint-plugin": "^6.19.1", diff --git a/frontend/sportsmatch-app/src/App.tsx b/frontend/sportsmatch-app/src/App.tsx index f9090e3b..153a1ab8 100644 --- a/frontend/sportsmatch-app/src/App.tsx +++ b/frontend/sportsmatch-app/src/App.tsx @@ -11,6 +11,7 @@ import NotFound from './pages/NotFound' import { OpenAPI } from './generated/api' import UserInfo from './pages/UserInfo' import UserRating from './pages/UserRating' +import HostEvent from './pages/HostEvent' function App() { OpenAPI.TOKEN = localStorage.getItem('token')! @@ -41,6 +42,11 @@ function App() { path="/user/:id/ratings" element={} />} /> + } />} /> + } />} + /> diff --git a/frontend/sportsmatch-app/src/components/HostEventComponent.tsx b/frontend/sportsmatch-app/src/components/HostEventComponent.tsx new file mode 100644 index 00000000..13622bee --- /dev/null +++ b/frontend/sportsmatch-app/src/components/HostEventComponent.tsx @@ -0,0 +1,211 @@ +import { FormEvent, useEffect, useState } from 'react' +import '../styles/HostEventComponent.css' +import { + PlaceControllerService, + SportControllerService, + SportDTO, + PlaceDTO, + EventsControllerService, + HostEventDTO, +} from '../generated/api' +import DatePicker from 'react-datepicker' +import 'react-datepicker/dist/react-datepicker.css' +import { format } from 'date-fns' +import { useNavigate } from 'react-router-dom' + +function HostEventComponent() { + const [matchTitle, setMatchTitle] = useState('') + const [selectSport, setSelectedSport] = useState('') + const [sportsOptions, setSportsOptions] = useState([]) + const [selectRank, setSelectedRank] = useState('') + const rankOptions = Array.from({ length: 20 }, (_, index) => ({ + value: index * 500 + 1, + label: `${index * 500 + 1} - ${(index + 1) * 500}`, + })) + const [selectOppGender, setSelectedOppGender] = useState('') + const genderOptions = ['Male', 'Female'] + const [selectLocation, setSelectedLocation] = useState() + const [locationsOptions, setLocationOptions] = useState([]) + const [selectStartDateAndTime, setStartDateAndTime] = useState( + null, + ) + const [selectEndDateAndTime, setEndDateAndTime] = useState(null) + const navigate = useNavigate() + + useEffect(() => { + SportControllerService.getSports().then((response) => { + setSportsOptions(response) + }) + PlaceControllerService.searchPlaces('').then((response) => + setLocationOptions(response), + ) + }) + + const handleHostEvent = async (e: FormEvent) => { + e.preventDefault() + console.log(selectLocation) + + const [minElo, maxElo] = selectRank + .split(' - ') + .map((str) => parseInt(str.trim(), 10)) + + const formattedStartDate = selectStartDateAndTime + ? format(selectStartDateAndTime, "yyyy-MM-dd'T'HH:mm:ss") + : '' + const formattedEndDate = selectEndDateAndTime + ? format(selectEndDateAndTime, "yyyy-MM-dd'T'HH:mm:ss") + : '' + const event: HostEventDTO = { + dateStart: formattedStartDate, + dateEnd: formattedEndDate, + minElo: minElo, + maxElo: maxElo, + title: matchTitle, + sport: selectSport, + locationId: selectLocation!, + } + EventsControllerService.addEvent(event).then((response) => { + navigate('/app') + }) + } + + const handleSportSelection = ( + event: React.ChangeEvent, + ) => { + setSelectedSport(event.target.value) + } + const handleRankSelection = (event: React.ChangeEvent) => { + setSelectedRank(event.target.value) + } + const handleOppGenderSelection = ( + event: React.ChangeEvent, + ) => { + setSelectedOppGender(event.target.value) + } + const handleLocationSelection = ( + event: React.ChangeEvent, + ) => { + setSelectedLocation(parseInt(event.target.value)) + } + const handleStartDateSelection = (date: Date | null) => { + setStartDateAndTime(date) + } + const handleEndDateSelection = (date: Date | null) => { + setEndDateAndTime(date) + } + + return ( +
+
+
+
+ + setMatchTitle(e.target.value)} + required + /> +
+
+ + +
+
+ + +
+
+ + +
+
+ + +
+
+
+ + +
+
+
+
+ + +
+
+
+ +
+
+
+
+ ) +} + +export default HostEventComponent diff --git a/frontend/sportsmatch-app/src/generated/api/index.ts b/frontend/sportsmatch-app/src/generated/api/index.ts index 6e0320cf..c18c452c 100644 --- a/frontend/sportsmatch-app/src/generated/api/index.ts +++ b/frontend/sportsmatch-app/src/generated/api/index.ts @@ -10,6 +10,7 @@ export type { OpenAPIConfig } from './core/OpenAPI'; export type { AuthRequestDTO } from './models/AuthRequestDTO'; export type { EventDTO } from './models/EventDTO'; export { EventHistoryDTO } from './models/EventHistoryDTO'; +export type { HostEventDTO } from './models/HostEventDTO'; export type { PlaceDTO } from './models/PlaceDTO'; export type { RatingDTO } from './models/RatingDTO'; export type { SportDTO } from './models/SportDTO'; diff --git a/frontend/sportsmatch-app/src/generated/api/models/EventDTO.ts b/frontend/sportsmatch-app/src/generated/api/models/EventDTO.ts index 9b43b821..af6754eb 100644 --- a/frontend/sportsmatch-app/src/generated/api/models/EventDTO.ts +++ b/frontend/sportsmatch-app/src/generated/api/models/EventDTO.ts @@ -15,6 +15,6 @@ export type EventDTO = { sport: string; player1Name?: string; player2Name?: string; - placeDTO?: PlaceDTO; + placeDTO: PlaceDTO; }; diff --git a/frontend/sportsmatch-app/src/generated/api/models/HostEventDTO.ts b/frontend/sportsmatch-app/src/generated/api/models/HostEventDTO.ts new file mode 100644 index 00000000..60ae8f46 --- /dev/null +++ b/frontend/sportsmatch-app/src/generated/api/models/HostEventDTO.ts @@ -0,0 +1,14 @@ +/* generated using openapi-typescript-codegen -- do no edit */ +/* istanbul ignore file */ +/* tslint:disable */ +/* eslint-disable */ +export type HostEventDTO = { + dateStart: string; + dateEnd: string; + minElo: number; + maxElo: number; + title: string; + sport: string; + locationId: number; +}; + diff --git a/frontend/sportsmatch-app/src/generated/api/models/PlaceDTO.ts b/frontend/sportsmatch-app/src/generated/api/models/PlaceDTO.ts index b9020d3b..1a97df7d 100644 --- a/frontend/sportsmatch-app/src/generated/api/models/PlaceDTO.ts +++ b/frontend/sportsmatch-app/src/generated/api/models/PlaceDTO.ts @@ -3,6 +3,7 @@ /* tslint:disable */ /* eslint-disable */ export type PlaceDTO = { + id?: number; name: string; address: string; latitude: number; diff --git a/frontend/sportsmatch-app/src/generated/api/services/EventsControllerService.ts b/frontend/sportsmatch-app/src/generated/api/services/EventsControllerService.ts index 4aa72b60..f2f27009 100644 --- a/frontend/sportsmatch-app/src/generated/api/services/EventsControllerService.ts +++ b/frontend/sportsmatch-app/src/generated/api/services/EventsControllerService.ts @@ -4,6 +4,7 @@ /* eslint-disable */ import type { EventDTO } from '../models/EventDTO'; import type { EventHistoryDTO } from '../models/EventHistoryDTO'; +import type { HostEventDTO } from '../models/HostEventDTO'; import type { CancelablePromise } from '../core/CancelablePromise'; import { OpenAPI } from '../core/OpenAPI'; import { request as __request } from '../core/request'; @@ -14,7 +15,7 @@ export class EventsControllerService { * @throws ApiError */ public static addEvent( - requestBody: EventDTO, + requestBody: HostEventDTO, ): CancelablePromise> { return __request(OpenAPI, { method: 'POST', diff --git a/frontend/sportsmatch-app/src/pages/AllSportsList.tsx b/frontend/sportsmatch-app/src/pages/AllSportsList.tsx index 297e253b..ae494206 100644 --- a/frontend/sportsmatch-app/src/pages/AllSportsList.tsx +++ b/frontend/sportsmatch-app/src/pages/AllSportsList.tsx @@ -126,3 +126,31 @@ export function AllSportsList() { ) } + +export const sampleSports: SportDTO[] = [ + { + name: 'Badminton', + emoji: '🏸', + backgroundUImageURL: './assets/sport-component-badminton.png', + }, + { + name: 'Tennis', + emoji: '🎾', + backgroundUImageURL: './assets/sport-component-tennis.png', + }, + { + name: 'Boxing', + emoji: '🥊', + backgroundUImageURL: './assets/sport-component-boxing.png', + }, + { + name: 'Table Tennis', + emoji: '🏓', + backgroundUImageURL: './assets/sport-component-table-tennis.png', + }, + { + name: 'Squash', + emoji: '🥎', + backgroundUImageURL: './assets/sport-component-squash.png', + }, +] diff --git a/frontend/sportsmatch-app/src/pages/HostEvent.tsx b/frontend/sportsmatch-app/src/pages/HostEvent.tsx new file mode 100644 index 00000000..3d4d26c6 --- /dev/null +++ b/frontend/sportsmatch-app/src/pages/HostEvent.tsx @@ -0,0 +1,9 @@ +import HostEventComponent from '../components/HostEventComponent' + +export default function HostEvent() { + return ( + <> + + + ) +} diff --git a/frontend/sportsmatch-app/src/pages/UserPage.tsx b/frontend/sportsmatch-app/src/pages/UserPage.tsx deleted file mode 100644 index a2c4db2d..00000000 --- a/frontend/sportsmatch-app/src/pages/UserPage.tsx +++ /dev/null @@ -1,104 +0,0 @@ -import '../styles/UserPage.css' -import Match from '../components/Match' -import EventHistoryItem from '../components/EventHistoryItem' -import SportEvent from '../components/SportEvent' -import { Link } from 'react-router-dom' - -export default function UserPage() { - const sampleUserWithEvent = { - id: 1, - username: `johndoe87`, - event: { - id: 1, - maxElo: 2000, - minElo: 1200, - dateEnd: '2024-05-02', - dateStart: '2024-05-01', - location: 'Prague, Stadium A', - title: 'Badminton match', - sport: 'Badminton', - playerOne: 'johndoe87', - playerTwo: 'jess_ward', - }, - } - - const sampleAllEventsDTO = [ - { - id: 1, - name: 'Event 1', - date: '2024-02-10', - location: 'Location 1', - userScore: 1, - opponentScore: 2, - }, - { - id: 2, - name: 'Event 1', - date: '2024-02-10', - location: 'Location 1', - userScore: 1, - opponentScore: 2, - }, - { - id: 3, - name: 'Event 1', - date: '2024-02-10', - location: 'Location 1', - userScore: 1, - opponentScore: 2, - }, - ] - - const { event } = sampleUserWithEvent - - return ( - <> -
-
- {event === null ?
: <>} -
- {event === null ? ( -
-
-

No upcoming match

- Find or Host a Match -
-
- ) : ( - - )} -
-
- {event === null ?
: <>} -
-
-

History

-
-
- View all -
-
-
-
-
-
- {sampleAllEventsDTO === null ? ( -

No match history

- ) : ( - sampleAllEventsDTO.map((e) => ( - - )) - )} -
-
-
-
-

Nearby

-
- - -
-
- - ) -} diff --git a/frontend/sportsmatch-app/src/styles/HostEventComponent.css b/frontend/sportsmatch-app/src/styles/HostEventComponent.css new file mode 100644 index 00000000..eb75edc5 --- /dev/null +++ b/frontend/sportsmatch-app/src/styles/HostEventComponent.css @@ -0,0 +1,62 @@ +@import url('https://fonts.googleapis.com/css2?family=Inter:wght@400;900&display=swap'); + +* { + margin: 0; + padding: 0; + box-sizing: border-box; + } + +.wrapper-host-event { + width: 430px; + background: #fff; + border: 2px solid rgba(65, 58, 58, 0.2); + box-shadow: 0 0 10px rgba(0, 0, 0, 0.2); + backdrop-filter: blur(30px); + color: #ccc; + border-radius: 10px; + padding: 30px 20px; +} + +.host-event-input-box { + position: relative; + width: 100%; + height: 40px; + margin: 5px 0; +} + +.host-event-input-box input, +.host-event-input-box select { + width: 100%; + height: 100%; + border-radius: 40px; + font-size: 13px; + padding: 10px 20px; +} + +.wrapper-host-event button { + width: 100%; + height: 45px; + background: #e85f29; + border: none; + outline: none; + border-radius: 40px; + box-shadow: 0 0 10px rgba(0, 0, 0, 0.1); + cursor: pointer; + font-size: 16px; + color: #fff; + font-weight: 600; +} + +.wrapper-host-event button:hover { + transform: scale(1.05); +} + +.date-picker button { + background: none; + height: 32px; + width: 32px; +} + +.react-datepicker-wrapper { + width: 100%; +} \ No newline at end of file