= Имплементација на кориснички сценарија == Општи информации Нашата апликација, како што е наведено и во претходните фази, се темели на клиент-сервер архитектура. Во овој случај клиентската страна е „претставена“ преку React апликација, додека пак серверската страна е Spring Boot апликација. Во основа на оваа архитектура е комуникација меѓу клиентот и серверот преку HTTP барања. За визуелизација и давање можност за интеракција на крајниот корисник, кој управува директно со апликацијата на клиентска страна, беше потребно да воспоставуваме конекција и соодветно да ги обработуваме простите податоци кои пристигнуваат од серверската страна. Ова го постигнавме преку Axios HTTP клиентот кој е инсталиран на клиенстката апликација и неговата инстанца во продолжение е онаа преку која ги испраќаме сите барања до серверот. {{{#!javascript import axios from "axios"; const instance = axios.create({ baseURL: "http://localhost:8080/", withCredentials: true, maxRedirects: 1, }) export default instance }}} На овој начин обезбедуваме при секое барање основниот URL да биде основниот URL на серверската апликација, додека пак со поставување на параметарот withCredentials овозможуваме препраќање на колачињата и XMLHttpRequest, што овозможува прибавување на нови податоци без целосно одновно вчитување на страницата. === Custom React Hook-и За поедноставно справување со HTTP GET и POST барањата, кои како што претходно споменавме се главната врска меѓу клиентската и серверската страна и практично не е можно да постои компонента каде не се употребуваат, креиравме наши React Hook-и кои се реискористливи и можат да се употребуваат во различни сценарија со едноставна промена на URL-то ==== useGet {{{#!javascript import axios from "../../axios"; import {useState, useEffect, useContext} from 'react'; const useGet = (url) => { const [data, setData] = useState(null); const [isLoading, setIsLoading] = useState(true); const [changed, setChanged] = useState(0) const getData = async (uurl) => { await axios.get(uurl}).then((res) => { setData(res.data); }).catch((error) => { console.log(error) window.location.href = '/error' }) .finally(() => { setIsLoading(false); }); }; useEffect(() => { setIsLoading(true); getData(url); }, [dep, url, changed]); return { data, setData, isLoading, getData, setChanged }; }; export default useGet; }}} Како што и самото име кажува, овој Hook го користиме за GET барања кон серверската страна. При инстанцирање се наведува URL кон кое се праќаат барањата. За чување на податоците кои се добиваат како одговор се користи концептот на useState од React што овозможува ререндерирање на сите компоненти при промена на состојбата на објектот. На сличен начин се постапува и со знаменцето за вчитување на податоци, кое при испраќање на барањето се поставува на true, додека пак кога ќе заврши обработката се враќа на false. Истото служи како guard clause за компонентите кои ги користат податоците кои се чекаат од GET барањето, како би се избегнале исклучоци во случај кога рендерирањето на компонентата ќе заврши пред податоците да бидат вчитани. Асинхроната функција која ги прибавува податоците од серверот е ставена во useEffect callback, што овозможува нејзино реизвршување при промена на некој од елементите во низата зависности. Овој концепт го искористивме за да овозможиме на едноставен начин ререднерирање на компонентите во кои додаваме нови записи, преку вметнување на дополнителна вредност/состојба changed во низата зависноти која пак, при праќање на барање кое очекуваме да предизвика промена на податоците достапни на страницата, се менува и со тоа автоматски се испраќа ново GET барање и промените се одразуваат на компонените кои се зависни од нив. GET барањето испратено преку Axios HTTP клиентот враќа Promise со кој соодветно се справуваме на горенаведениот начин: - во then сегментот состојбата на data се поставува на податоците кои се вратени од серверот (ова значи дека барањето поминало со статус 200); - catch сегментот се извршува кога одговорот на барањето е со статус 4ХХ, односно грешка, па во овој случај корисникот се пренасочува до страница со грешка - finally сегментот се извршува во секој случај, односно означува дека комуникацијата е завршена, вратен е одговор од серверот, па тука се поставува знаменцето isLoading назад на false ==== usePost {{{#!javascript import axios from "../../axios"; const useCreate = () => { const createEntity = async (url, entity, getData) => { console.log(entity) await axios .post(url, null, { params: entity, }) .then((res) => { getData(prev => ++prev) }) .catch((err) => { console.log(err); }) .finally(() => { }); } return { createEntity }; } export default useCreate; }}} Овој Hook го користиме за POST барања кон серверската страна. Од него постојат неколку модифицирани верзии во зависоност од тоа дали парамтерите се испраќаат во телото на барањето, како URL параметри или како променливи во патеката, но во основа се работи за иста имплементација. За разлика од useGet, тука, URL-то се испраќа како аргумент на функцијата, заедно со податоците кои треба да се испратат како параметри и состојбата на зависната променлива за која зборувавме во претходната компонент. Откако ќе се испрати POST, se враќа Promise со кој соодветно се справуваме на горенаведениот начин, со единствна разлика што во then сегментот, кога статусот означува успех, ја менуваме состојбата на зависната променлива за да се ререндерираат зависните компоненти. ==== useFormData {{{#!javascript import { useState } from 'react'; const useFormData = (editData) => { const [formData, setData] = useState({ ...editData }); const onFormChange = (e) => { setData({ ...formData, [e.target.name]: e.target.value, }); }; const onCheckBoxChange = (e) => { setData((prevData) => ({ ...prevData, [e.target.name]: !prevData[e.target.name], })); } const setFormData = (newData) => { setData({...newData}); } return { formData, onFormChange, onCheckBoxChange, setFormData }; }; export default useFormData; }}} Во секој формулар каде што имаме кориснички влез потребно е да се справиме со него и да го проследиме до серверот во утврдената форма. Ова може да се постигне на стариот добар начин, со користење на концептот useState од React и посебна состојба за секое од input полињата. За да го поедноставиме ова, успешно имплементиравме општа функција за справување со корисничкиот влез која работи на следниот начин. Наместо посебни состојби за секое поле, чуваме состојба на еден објект, каде клучот е соодветно name атрибутот на полето, а вредноста е неговиот value. За процесирање на текстуалните полиња, при промена на вредноста одговорна е функцијата onFormChange, каде се менува состојбата на записот од објектот кој е засегнат според name атрибутот. Оваа функција се извршува на секој onChange настан испален од било која input компонента. onCheckBoxChange е ништо повеќе од специјална имплементација на onFormChange функцијата за checkbox input. Како и во претходно наведените Hook-и, и тука, референци до функциите и состојбите кои треба да бидат пристапени од другите компоненти се враќаат од функцијата. ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// == '''Имплементација''' == Се најавува на системот [[Image(login.png)]] == Се регистрира на системот [[Image(registartion.png)]] == Корисник со основни привилегии === Листа сместувања [[Image(SearchHotel.png)]] {{{#!java public List getRoomsAvailibilityByDateAndLocation(String hotelLocation, Date dateFrom, Date dateTo, int numberOfBeds, Boolean flexible) { if(flexible) { Calendar calendar = Calendar.getInstance(); Calendar calendar1 = Calendar.getInstance(); calendar.setTime(dateTo); calendar1.setTime(dateFrom); return em.createQuery("SELECT hr FROM HotelRoomAvailable hr WHERE " + "((hr.dateFrom <= :dateTo AND hr.dateTo >= :dateFrom) OR " + "(hr.dateFrom <= :dateToMinus1 AND hr.dateTo >= :dateFromMinus1) OR " + "(hr.dateFrom <= :dateToMinus2 AND hr.dateTo >= :dateFromMinus2) OR " + "(hr.dateFrom <= :dateToMinus3 AND hr.dateTo >= :dateFromMinus3) OR " + "(hr.dateFrom <= :dateToPlus1 AND hr.dateTo >= :dateFromPlus1) OR " + "(hr.dateFrom <= :dateToPlus2 AND hr.dateTo >= :dateFromPlus2) OR " + "(hr.dateFrom <= :dateToPlus3 AND hr.dateTo >= :dateFromPlus3)) " + "AND hr.hotelRoom.hotel.hotelLocation LIKE :hotelLocation " + "AND hr.hotelRoom.numOfBeds >= :numBeds") .setParameter("hotelLocation", hotelLocation) .setParameter("dateFrom", dateFrom) .setParameter("dateTo", dateTo) .setParameter("dateToMinus1", subtractDays(calendar, 1)) .setParameter("dateToMinus2", subtractDays(calendar, 2)) .setParameter("dateToMinus3", subtractDays(calendar, 3)) .setParameter("dateToPlus1", addDays(calendar, 1)) .setParameter("dateToPlus2", addDays(calendar, 2)) .setParameter("dateToPlus3", addDays(calendar, 3)) .setParameter("dateFromMinus1", subtractDays(calendar1, 1)) .setParameter("dateFromMinus2", subtractDays(calendar1, 2)) .setParameter("dateFromMinus3", subtractDays(calendar1, 3)) .setParameter("dateFromPlus1", addDays(calendar1, 1)) .setParameter("dateFromPlus2", addDays(calendar1, 2)) .setParameter("dateFromPlus3", addDays(calendar1, 3)) .setParameter("numBeds", numberOfBeds) .getResultList(); } else { return em.createQuery("SELECT hr FROM HotelRoomAvailable hr WHERE " + "((hr.dateFrom <= :dateTo AND hr.dateTo >= :dateFrom)) " + "AND hr.hotelRoom.hotel.hotelLocation LIKE :hotelLocation " + "AND hr.hotelRoom.numOfBeds >= :numBeds") .setParameter("hotelLocation", hotelLocation) .setParameter("dateFrom", dateFrom) .setParameter("dateTo", dateTo) .setParameter("numBeds", numberOfBeds) .getResultList(); } } }}} Ова е кверито од кое се зимаат хотелите по криетриум, каде што првиот дел е додека флексибилноста од 3 дена има вредност true, па тогаш се листаат +/- (1,2,3) дена од бараните критериуми, а доколку има вредност false тогаш се листаат според точните дати кои се внесени. {{{#!java public List getRoomsAvailibilityByDateAndLocation(String hotelLocation, Date dateFrom, Date dateTo, int numberOfBeds, Boolean flexible) { long numberOfNights = Duration.between(dateFrom.toInstant(), dateTo.toInstant()).toDays(); List roomsAvailible = hotelDao.getRoomsAvailibilityByDateAndLocation(hotelLocation, dateFrom, dateTo, numberOfBeds, flexible); Map> roomsByHotels = roomsAvailible.stream().collect(Collectors.groupingBy(x -> x.getHotelRoom().getHotel())); List hotelsList = roomsByHotels.keySet().stream() .map(x -> new HotelDto( x.getHotelId(), x.getHotelName(), x.getHotelDescripiton(), x.getHotelLocation(), x.getHotelEDBS(), x.getParking(), x.getPetFriendly(), x.getInternetAvailable(), roomsByHotels.get(x).stream().mapToDouble(y -> y.getHotelRoom().getPrice()).min().getAsDouble() * numberOfNights, roomsByHotels.get(x), getReviewsForHotel(x.getHotelId()), getReviewsForHotel(x.getHotelId()).stream().mapToDouble(Reviews::getNumStar).average().orElse(0), getHotelImages(x.getHotelId()) )).toList(); return hotelsList; } }}} Во сервисот за да не ги користиме сите податоци од вистинската класа користиме класа HotelDto односно data transfer object. Со ова овозможуваме да за пренос на податоци меѓу backend и frontend. Во функцијата ги листаме сите слободни соби од сите хотели со наведените криетриуми. {{{#!java @GetMapping(path = "/hotel/search") public List searchAvailibleRooms(@RequestParam(name = "hotelLocation") String hotelLocation, @RequestParam(name = "dateFrom") @DateTimeFormat(pattern = "yyyy-MM-dd") Date dateFrom, @RequestParam(name = "dateTo") @DateTimeFormat(pattern = "yyyy-MM-dd") Date dateTo, @RequestParam(name = "numBeds") int numBeds, @RequestParam(name = "flexible") Boolean flexible) { return hotelManager.getRoomsAvailibilityByDateAndLocation(hotelLocation, dateFrom, dateTo, numBeds, flexible); } }}} Контролерот ги прима потребните податоци и ја повикува функцијата од сервисот. === Листа ресторани [[Image(RestaurantSearch.png)]] {{{#!java public List getTablesByDateAndLocation(String restaurantLocation, Date hourFrom, Date hourTo, int noSeats){ return em.createQuery("select hr from RestaurantsAvailible hr where hr.hourFrom <= :hourFrom and hr.hourTo >= :hourTo " + "and hr.restaurantTable.restaurant.restaurantLocation LIKE :restaurantLocation and hr.restaurantTable.noSeats >= :noSeats") .setParameter("restaurantLocation", restaurantLocation) .setParameter("hourFrom", hourFrom) .setParameter("hourTo", hourTo) .setParameter("noSeats", noSeats) .getResultList(); } }}} Ова е кверито кое ги листа сите маси во одредена локација, со одредено време на доаѓање, заминување и бројка колку луѓе ќе дојдат. {{{#!java public List getTablesByDateAndLocation(String restaurantLocation, Date date, String hourFrom, String hourTo, int noSeats) { Date dateFrom = date; Date dateTo = Date.from(date.toInstant()); String[] splittedFrom = hourFrom.split(":"); String[] splittedTo = hourTo.split(":"); dateFrom.setHours(Integer.parseInt(splittedFrom[0])); dateFrom.setMinutes(Integer.parseInt(splittedFrom[1])); dateTo.setHours(Integer.parseInt(splittedTo[0])); dateTo.setMinutes(Integer.parseInt(splittedTo[1])); List restaurantsAvailibles = restaurantDao.getTablesByDateAndLocation(restaurantLocation, hourFrom, hourTo, noSeats); Map> tablesByRestaurants = restaurantsAvailibles.stream().collect(Collectors.groupingBy(x -> x.getRestaurantTable().getRestaurant())); List restaurantsList = tablesByRestaurants.keySet().stream() .map(x -> new RestaurantDto( x.getRestaurantID(), x.getRestaurantName(), x.getRestaurantLocation(), x.getCousineType(), x.getRestaurantDescription(), x.getRestaurantEdbs(), x.getMenus(), tablesByRestaurants.get(x), getReviewsForRestaurant(x.getRestaurantID()), getReviewsForRestaurant(x.getRestaurantID()).stream().mapToDouble(Reviews::getNumStar).average().orElse(0), getRestaurantImages(x.getRestaurantID()), getMenuImagesByRestaurant(x.getRestaurantID()) )).toList(); return restaurantsList; } }}} Сервисот кој исто како и во хотели користи Dto притоа ги листа сите ресторани кои можат да го опслужат барањето од корисниците. Со тоа што времето од-до се интерпретира како текст, па тие ги средуваме во сервисот. {{{#!java @GetMapping(path = "/restaurant/search") public List searchAvailableRestaurant(@RequestParam(name = "restaurantLocation") String restaurantLocation, @RequestParam(name = "date") @DateTimeFormat(pattern = "yyyy-MM-dd") Date date, @RequestParam(name = "hourFrom") String hourFrom, @RequestParam(name = "hourTo") String hourTo, @RequestParam(name = "numPeople") int noSeats) { return restaurantManager.getTablesByDateAndLocation(restaurantLocation, date, dateFrom, dateTo, noSeats); } }}} Во контролерот се примаат податоци за локација, датум, време од до во форма на текст и број на седишта === Листа превоз [[Image(TransportSearch.png)]] {{{#!java public List getTransportsAvailableByFilters(String fromL, String toL, Date date, int numPassengers) { return em.createQuery("select h from TransportRoute h where h.from = :froml and h.to = :tol and h.freeSpace >= :nump") .setParameter("froml", fromL) .setParameter("tol", toL) .setParameter("nump", numPassengers) .getResultList(); } }}} Ова е кверито со кое се наоѓаат сите слобдни транспорти со одредена дата од-до одредено место и број на патници {{{#!java public List getTransportsAvailableByFilters(String from, String to, Date date, int numPassengers) { List transportAvailable = transportDao.getTransportsAvailableByFilters(from, to, date, numPassengers); Map> transportsByTransporter = transportAvailable.stream().collect(Collectors.groupingBy(x -> x.getParentRoute())); List transportList = transportsByTransporter.keySet().stream().toList().stream() .map(x -> new TransportListingDto( x.getTransportAvailibleId(), x.getFrom(), x.getTo(), x.getDate(), x.getFreeSpace(), x.getTime(), transportsByTransporter.get(x).stream().mapToDouble(y -> y.getPrice()).min().getAsDouble(), x.getRoutes(), x.getTransport(), getReviewsForTransport(x.getTransport().getTransportID()), getReviewsForTransport(x.getTransport().getTransportID()).stream().mapToDouble(Reviews::getNumStar).average().orElse(0) )).collect(Collectors.toList()); return transportList; } }}} Исто како и останатите имаме Dto и ги листаме сите слобдони транспорти со наведените критериуми. Притоа што доколку се внесе некој град кој е попатен во некоја рута се листа и таа со тоа што се зима главната рута на тој град. {{{#!comment Не знам дали вака види }}} {{{#!java @GetMapping(path = "/transport/search") public List searchAvailableTransport(@RequestParam(name = "from") String from, @RequestParam(name = "to") String to, @RequestParam(name = "date") @DateTimeFormat(pattern = "yyyy-MM-dd") Date date, @RequestParam(name = "numPassengers") int numPassengers){ return transportManager.getTransportsAvailableByFilters(from, to, date, numPassengers); } }}} Контролерот кој според влезните критериуми ги листаме сите слобдони транспорти за тие дестинации. == Најавен давател на услуги === Регистра бизнис [[Image(registerBusiness.png)]] ---- [[Image(RegisterBusinessForm.png)]] {{{#!java public void createBusiness(Business business) { em.persist(business); } }}} Се креира нов бизнис во база {{{#!java public void createBusiness(Business business, long userId) { User u = usersManager.findUserByID(userId); business.setUser(u); businessDao.createBusiness(business); } }}} Креирање бизнис во сервисот, каде што се наоѓа корисникот кој сака да го креира {{{#!java @PostMapping(path = "/business/add") public void addBusiness(@RequestBody Business business, @RequestParam(name = "userId") long userId) { businessManager.createBusiness(business, userId); } }}} Контролерот каде се зима цел објект бизнис од Frontend, па се повикува функцијата од сервисот === Додава хотел ---- [[Image(MyResources1.png)]] ---- [[Image(MyResourcesHotel.png)]] ---- [[Image(myResourcesHotel1.png)]] {{{#!java public void createHotel(Hotels hotel) { em.persist(hotel); } }}} Се креира нов хотел во база {{{#!java public void createHotel(Hotels hotels, long userId) { User u = usersManager.findUserByID(userId); hotels.setOwner(u); hotelDao.createHotel(hotels); } }}} Во сервисот се наоѓа корисникот кој сака да отвори хотел, и истиот се сетира за поседител на хотелот {{{#!java @PostMapping(path = "/hotel/add") public void add(@RequestBody Hotels hotel, @RequestParam(name = "userId") long userId) { hotelManager.createHotel(hotel, userId); } }}} Хотелот се зима како цел објект преку frontend и се зима id на user и се повикува функцијата од сервисот ==== Додава соба во хотел [[Image(MyResourcesHotelRooms.png)]] ---- [[Image(HotelRoomAdd.png)]] ---- [[Image(HotelRoom.png)]] {{{#!java public void createRoom(HotelRoom hotelRoom) { em.persist(hotelRoom); } }}} Се крериа нова соба за одреден хотел {{{#!java public void createRoom(Long hotel, String hotelRoomDescription, String hotelRoomName, Boolean kitchenAvailable, Boolean airConditioning, Boolean balcony, double price, int numOfBeds) { Hotel hotel = findHotelByID(hotel); HotelRoom hotelRoom = new HotelRoom(hotel, hotelRoomDescription, hotelRoomName, kitchenAvailable, airConditioning, balcony, price, numOfBeds); hotelDao.createRoom(hotelRoom); } }}} Се креира нова соба во сервисот притоа што се зима id на хотелот на кој ќе се креира новата соба {{{#!java @PostMapping(path = "/hotel/rooms/add") public void addRoom(@RequestBody HotelRoom room, @RequestParam(name = "hotelId") long hotelId) { hotelManager.createRoom(hotelId, room.getHotelRoomDescription(), room.getHotelRoomName(), room.getKitchenAvailable(), room.getAirConditioning(), room.getBalcony(), room.getPrice(), room.getNumOfBeds()); } }}} Во контролерот се зима цел објект HotelRoom и се зима id на хотелот и се повикува функцијата за креирање соба од сервисот ---- [[Image(HotelRoomFreeDates.png)]] {{{#!java public void createRoomAvailible(HotelRoomAvailable hra) { em.persist(hra); } }}} Се креира слободна соба за хотелот во база {{{#!java public void createRoomAvailible(Long id, Date dateFrom, Date dateTo, int numberOfBeds) { HotelRoom hotelRoom = findRoomById(id); HotelRoomAvailable hra = new HotelRoomAvailable(hotelRoom, dateFrom, dateTo, numberOfBeds); hotelDao.createRoomAvailible(hra); } }}} Во сервисот се зима претходно креирана соба и се креира нов објект од тип HotelRoomAVailable кој чува за која соба од кога до кога е слободна {{{#!java @PostMapping(path = "/hotel/rooms/available/{id}/add") public void addRoomAvailible(@RequestBody HotelRoomAvailable hotelRoomAvailable, @PathVariable long id) { hotelManager.createRoomAvailible(id, hotelRoomAvailable.getDateFrom(), hotelRoomAvailable.getDateTo(), hotelRoomAvailable.getNumberOfBeds()); } }}} Во контролерот се повикува функцијата од сервисот за креирање на соба, а прима цел објект од типот преку frontend и id за која соба станува збор ---- [[Image(HotelRoomEdit.png)]] {{{#!java public void editRoomAvailible(long hotelRoomAvailableId, HotelRoom hotelRoom, Date dateFrom, Date dateTo, int numberOfBeds) { HotelRoomAvailable hr = findAvailibleRoomById(hotelRoomAvailableId); hr.setHotelRoom(hotelRoom); hr.setDateFrom(dateFrom); hr.setDateTo(dateTo); hr.setNumberOfBeds(numberOfBeds); hotelDao.createRoomAvailible(hr); } }}} Креирањето и менувањето во база е исто затоа не се наведува, а во сервисот се менуваат сите ново наведени детали за самата соба {{{#!java @PostMapping(path = "/hotel/rooms/available/edit") public void editRoomAvailible(@RequestBody HotelRoomAvailable hotelRoomAvailable) { hotelManager.editRoomAvailible(hotelRoomAvailable.getHotelRoomAvailableId(), hotelRoomAvailable.getHotelRoom(), hotelRoomAvailable.getDateFrom(), hotelRoomAvailable.getDateTo(), hotelRoomAvailable.getNumberOfBeds()); } }}} Во Котнтролерот се зима цел објект за слободната соба и таа се повикува функцијата од сервисот ---- [[Image(HotelReservation.png)]] {{{#!java public List findReservationByHotel(Hotels hotel) { List hotelRooms = getRoomsOfHotel(hotel); return em.createQuery("select hr from HotelRoomReservations hr where hr.hotelRoom.hotel = :hotel").setParameter("hotel", hotel).getResultList(); } }}} Се зимаат сите резервации за одреден хотел од база {{{#!java public List findVaidReseravtionsByHotel(Long hotelId) { Hotels hotel = findHotelByID(hotelId); List reservations = hotelDao.findReservationByHotel(hotel); return reservations.stream() .map(x -> new HotelReservationDto( x.getUser(), x.getHotelRoom(), x.getDateFrom(), x.getDateTo(), x.getNumberOfBeds() )).toList(); } }}} Исто како и претходно се користи Dto со кој се па се враќаат сите резервации за одреден хотел {{{#!java @GetMapping(path = "/hotel/{id}/reservations/active") public List getActiveReservationsForHotel(@PathVariable Long id) { return hotelManager.findVaidReseravtionsByHotel(id); } }}} Се праќа само id на хотелот и во сервисот се прави логиката. === Менува хотел ---- [[Image(MyResourcesHotelEdit.png)]] {{{#!java public void editHotel(long hotelId, String hotelName, String hotelDescripiton, String hotelLocation, String hotelEDBS, Boolean parking, Boolean petFriendly, Boolean internetAvailable) { Hotels hotel = hotelDao.findHotelByID(hotelId); hotel.setHotelName(hotelName); hotel.setHotelDescripiton(hotelDescripiton); hotel.setHotelLocation(hotelLocation); hotel.setHotelEDBS(hotelEDBS); hotel.setParking(parking); hotel.setPetFriendly(petFriendly); hotel.setInternetAvailable(internetAvailable); hotelDao.editHotel(hotel); } }}} Зачувување во база е исто како и креирање притоа што не го покажуваме, а менувањето на хотел во сервисот ги менува сите потребни детали за истиот {{{#!java @PostMapping(path = "/hotel/edit") public void edit(@RequestBody Hotels hotel) { hotelManager.editHotel(hotel.getHotelId(), hotel.getHotelName(), hotel.getHotelDescripiton(), hotel.getHotelLocation(), hotel.getHotelEDBS(), hotel.getParking(), hotel.getPetFriendly(), hotel.getInternetAvailable()); } }}} Во контролерот се зима цел објект преку frontend и истиот се препраќа во функцијата од сервисот === Додава ресторан ---- [[Image(MyResourcesRestaurant.png)]] ---- [[Image(MyResourcesRestaurantAdd.png)]] {{{#!java public void createRestaurant(Restaurant restaurant) { em.persist(restaurant); } }}} Се креира ресторан во базата {{{#!java public void createRestaurant(Restaurant restaurant, long userId) { User u = usersManager.findUserByID(userId); Restaurant r = new Restaurant(restaurant.getRestaurantName(), restaurant.getRestaurantLocation(), restaurant.getCousineType(), restaurant.getRestaurantDescription(), restaurant.getRestaurantEdbs(), u); restaurantDao.createRestaurant(r); } }}} Во сервисот се наоѓа корисникот кој го креира и се става ресторанот под негово име {{{#!java @PostMapping(path = "/restaurant/add") public void add(@RequestBody Restaurant restaurant, @RequestParam(name = "userId") long userId) { restaurantManager.createRestaurant(restaurant, userId); } }}} Во контролерот се зима ресторанот како цел објект и id на корисникот кој го креира, па се повикува функцијата за креирање ресторан од сервисот === Менува ресторан [[Image(RestaurantEdit.png)]] {{{#!java public void editRestaurant(long restaurantID, String restaurantName, String restaurantLocation, String cousineType, String restaurantDescription, String restaurantEdbs, User restaurantOwner) { Restaurant res = findRestaurantByID(restaurantID); res.setRestaurantName(restaurantName); res.setRestaurantLocation(restaurantLocation); res.setRestaurantEdbs(restaurantEdbs); res.setRestaurantDescription(restaurantDescription); res.setRestaurantOwner(restaurantOwner); res.setCousineType(cousineType); restaurantDao.createRestaurant(res); } }}} Исто е креирање и менување на ресторан во база, затоа не го покажуваме. Во сервисот се зима ресторанот и се менуваат сите негови детали што поседителот ги изменил {{{#!java @PostMapping(path = "/restaurant/edit") public List edit(@RequestBody Restaurant restaurant) { restaurantManager.editRestaurant(restaurant.getRestaurantID(), restaurant.getRestaurantName(), restaurant.getRestaurantLocation(), restaurant.getCousineType(), restaurant.getRestaurantDescription(), restaurant.getRestaurantEdbs(), restaurant.getRestaurantOwner()); return restaurantManager.getRestaurants(); } }}} Во контролерот се зима целиот ресторан како објект од frontend и тој се препраќа во сервисот ==== Додава мени ---- [[Image(RestaurantMenuAdd.png)]] ---- [[Image(RestaurantMenu.png)]] {{{#!java public void addMenuToRestaurant(Menu menu) { em.persist(menu); } }}} Менито се зачувува во база {{{#!java public void addMenuToRestaurant(long restaurantId, Menu menu) { Restaurant r = findRestaurantByID(restaurantId); menu.setRestaurant(r); restaurantDao.addMenuToRestaurant(menu); } }}} Во сервисот се ноѓа ресторанот на кого ќе се додава нова ставка од менито и истото се додава {{{#!java @PostMapping(path = "/restaurant/{id}/menu/add") public void addMenu(@PathVariable(name = "id") long restaurantId, @RequestBody Menu menu) { restaurantManager.addMenuToRestaurant(restaurantId, menu); } }}} Во котрнолерот се зима id на ресторан и цел објект од менито, и истото се препраќа на сервисот да ја прави логиката ---- [[Image(RestaurantMenuEdit.png)]] {{{#!java public void editMenu(long menuId, String name, String ingredients, double price) { Menu menu = findMenuById(menuId); menu.setName(name); menu.setIngredients(ingredients); menu.setPrice(price); menuDao.editMenu(menu); } }}} Повторно дека менување и креирање е исто не се покажува тој дел. Во сервисот се зима менито според id и истото се менува според новите детали кои се внесени {{{#!java @PostMapping(path = "/menu/{id}/edit") public void editMenu(@PathVariable Long id, @RequestParam String name, @RequestParam String ingredients, @RequestParam double price) { menuManager.editMenu(id, name, ingredients, price); } }}} Контролерот прима id од менито и негови нови детали кои ги предава на сервисот да ја доврши логиката ==== Додава маси [[Image(RestaurantTablesAdd.png)]] ---- [[Image(RestaurantTables.png)]] {{{#!java public void saveTable(RestaurantsTable resTable) { em.persist(resTable); } }}} Додавање на нова маса во базата {{{#!java public void createTable(Long restaurantId, int noSeats) { Restaurant r = restaurantDao.findRestaurantByID(restaurantId); RestaurantsTable restaurantsTable = new RestaurantsTable(r, noSeats); restaurantDao.saveTable(restaurantsTable); } }}} Во сервисот се зима ресторанот на кој ќе се додава новата маса, за неа се внесуваат број на седишта {{{#!java @PostMapping(path = "/restaurant/table/{id}/add") public void addTable(@PathVariable Long id, @RequestParam Integer noSeats) { restaurantManager.createTable(id, noSeats); } }}} Се зима id на ресторанот и бројот за колку луѓе е наменета масата, па се повикува функцијата од сервисот за понатамошна логика ---- [[Image(RestaurantTableAvailable.png)]] {{{#!java public void saveTableAvailable(RestaurantsAvailible ra){ em.persist(ra); } }}} Се зачувува слободната маса во база {{{#!java public void createTableAvailable(Long rt, Date hourFrom, Date hourTo, int numTables) { RestaurantsTable rtabl = findTableById(rt); RestaurantsAvailible ra = new RestaurantsAvailible(rtabl, hourFrom, hourTo, numTables); restaurantDao.saveTableAvailable(ra); } }}} Во сервисот се зима масата преку id и се прави нов објект од расположливост на маси и се сетира од кога до кога и колку такви маси има на располагање {{{#!java @PostMapping(path = "/restaurant/table/{id}/available/add") public void addTableAvailable(@RequestBody RestaurantsAvailible restaurantsAvailible, @PathVariable Long id) { restaurantManager.createTableAvailable(id, restaurantsAvailible.getHourFrom(), restaurantsAvailible.getHourTo(), restaurantsAvailible.getNumTables()); } }}} За додавање на расположливост во контролерот се зима објект од RestaurantsAvailable и се зима id од која маса ---- [[Image(RestaurantTableEdit.png)]] {{{#!java public void editTable(Restaurant restaurant, long tableId, int noSeats) { RestaurantsTable resTable = findTableById(tableId); resTable.setRestaurant(restaurant); resTable.setNoSeats(noSeats); restaurantDao.saveTable(resTable); } }}} Се наоѓа масата преку id и на истата се додава се менуваат потребните детали. Зачувувањето во база е исто па не се покажува {{{#!java @PostMapping(path = "/restaurant/table/edit") public void editTable(@RequestBody RestaurantsTable restaurantsTable) { restaurantManager.editTable(restaurantsTable.getRestaurant(), restaurantsTable.getTableId(), restaurantsTable.getNoSeats()); } }}} Се зима цел објект од масата и за истата се пропушта на сервисот за логиката ---- [[Image(ReservationTable.png)]] {{{#!java public List findReservationByRestaurant(Restaurant restaurant) { return em.createQuery("select rr from RestaurantReservations rr where rr.table.restaurant = :rest").setParameter("rest", restaurant).getResultList(); } }}} Се листаат сите резервации кои се резервирани на ресторанот {{{#!java public List findReservationByRestaurant(Long restaurant) { Restaurant r = findRestaurantByID(restaurant); List res = restaurantDao.findReservationByRestaurant(r); return res.stream().map(x -> new RestaurantReservationDto( x.getUser(), x.getTable(), x.getTimeFrom(), x.getTimeTo() )).collect(Collectors.toList()); } }}} Исто како и горе се прави Dto и се ноѓаат сите резервации од конкретниот ресторан и истите се враќаат како резултатна листа {{{#!java @GetMapping(path = "/restaurant/{id}/reservations/active") public List getActiveReservationsForRestaurant(@PathVariable Long id) { return restaurantManager.findReservationByRestaurant(id); } }}} Преку контролерот се зимаат податоци за id на на ресторанот и потоа се повикува функцијата од сервисот за резервација. === Додава транспорт '''При додавање на транспорт, има форма во која може да се внесат детали за транпосртот.''' ---- [[Image(MyResourcesTransport.png)]] ---- [[Image(MyResourcesTranposrtAdd.png)]] ---- [[Image(MyResourcesTransport1.png)]] ---- '''Истиот тој транспорт може да се изменува по потреба''' ---- [[Image(transportEdit.png)]] ==== Додава рути '''Секој транпосрт може да нуди различни рути, од кој до кој град патува патува и попатните градови низ кој поминува. За истите се додава и цена на чинење за секоја релација''' ---- [[Image(RouteAdd.png)]] ---- [[Image(RoutesAddExtended.png)]] ---- [[Image(route.png)]] ---- '''Покрај додавањето има опција и да се прегледуваат резервациите кои ги има одреден транспорт''' ---- [[Image(RouteReservation.png)]] == Најавен администратор '''Администраторот има улога да ги управува профилите, односно да ги одборува, а истото важи и за фирмите.''' === Управување со профили [[Image(profiles4.png)]] === Управува со фирми [[Image(profiles5.png)]] == Најавен корисник на услуги '''Откога корисникот ќе се реши за некоја понуда, тој може да ги види деталите за истата. При резервација на услугата се добива порака за успешна резервација. ''' === Резеревација ресторан [[Image(RestaurantDetails1-min.png)]] ---- [[Image(RestaurantDetails2.png)]] ---- [[Image(RestaurantReservation.png)]] === Резервира транспорт [[Image(TransportDetails.png)]] ---- [[Image(TransportDetails2.png)]] ---- [[Image(TransportReservation.png)]] === Резервира хотел [[Image(HotelDetails1-min.png)]] ---- [[Image(HotelReservationUser-min.png)]] === Откажува услуга '''Корисникот кој ги закажува резервациите има можност да ги прегледува истите. Има резервации кои се активни, односно што следат, но има и претходни резервации за кои може да остави некакво мислење''' ---- [[Image(ReservationsHotel.png)]]