diff --git a/.vscode/settings.json b/.vscode/settings.json index 3030643..ec4b2f3 100644 --- a/.vscode/settings.json +++ b/.vscode/settings.json @@ -1,5 +1,6 @@ { "cSpell.words": [ "bottomnavbar" - ] + ], + "dart.enableCompletionCommitCharacters": true } \ No newline at end of file diff --git a/Backend/go.mod b/Backend/go.mod index a50abaf..050b34a 100644 --- a/Backend/go.mod +++ b/Backend/go.mod @@ -15,6 +15,7 @@ require ( github.com/gobwas/glob v0.2.3 // indirect github.com/gohugoio/hugo v0.124.1 // indirect github.com/gorilla/mux v1.8.1 // indirect + github.com/gorilla/websocket v1.5.1 // indirect github.com/mattn/go-colorable v0.1.13 // indirect github.com/mattn/go-isatty v0.0.20 // indirect github.com/mitchellh/hashstructure v1.1.0 // indirect @@ -23,7 +24,8 @@ require ( github.com/spf13/afero v1.11.0 // indirect github.com/spf13/cast v1.6.0 // indirect github.com/tdewolff/parse/v2 v2.7.12 // indirect - golang.org/x/sys v0.18.0 // indirect + golang.org/x/net v0.24.0 // indirect + golang.org/x/sys v0.19.0 // indirect golang.org/x/text v0.14.0 // indirect google.golang.org/protobuf v1.33.0 // indirect ) diff --git a/Backend/go.sum b/Backend/go.sum index a3410cf..8eb2714 100644 --- a/Backend/go.sum +++ b/Backend/go.sum @@ -50,6 +50,8 @@ github.com/google/go-cmp v0.5.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/ github.com/google/go-cmp v0.5.7/go.mod h1:n+brtR0CgQNWTVd5ZUFpTBC8YFBDLK/h/bpaJ8/DtOE= github.com/gorilla/mux v1.8.1 h1:TuBL49tXwgrFYWhqrNgrUNEY92u81SPhu7sTdzQEiWY= github.com/gorilla/mux v1.8.1/go.mod h1:AKf9I4AEqPTmMytcMc0KkNouC66V3BtZ4qD5fmWSiMQ= +github.com/gorilla/websocket v1.5.1 h1:gmztn0JnHVt9JZquRuzLw3g4wouNVzKL15iLr/zn/QY= +github.com/gorilla/websocket v1.5.1/go.mod h1:x3kM2JMyaluk02fnUJpQuwD2dCS5NDG2ZHL0uE0tcaY= github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo= github.com/kr/pretty v0.3.0/go.mod h1:640gp4NfQd8pI5XOwp5fnNeVWj67G7CFk/SaSQn7NBk= github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ= @@ -93,6 +95,8 @@ golang.org/x/net v0.0.0-20180724234803-3673e40ba225/go.mod h1:mL1N/T3taQHkDXs73r golang.org/x/net v0.0.0-20180826012351-8a410e7b638d/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20190213061140-3a22650c66bd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20190311183353-d8887717615a/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= +golang.org/x/net v0.24.0 h1:1PcaxkF854Fu3+lvBIx5SYn9wRlBzzcnHZSiaFFAb0w= +golang.org/x/net v0.24.0/go.mod h1:2Q7sJY5mzlzWjKtYUEXSlBWCdyaioyXzRB2RtU8KVE8= golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U= golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20181108010431-42b317875d0f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= @@ -103,6 +107,8 @@ golang.org/x/sys v0.0.0-20220811171246-fbc7d0a398ab/go.mod h1:oPkhp1MJrh7nUepCBc golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.18.0 h1:DBdB3niSjOA/O0blCZBqDefyWNYveAYMNF1Wum0DYQ4= golang.org/x/sys v0.18.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= +golang.org/x/sys v0.19.0 h1:q5f1RH2jigJ1MoAWp2KTp3gm5zAGFUTarQZ5U386+4o= +golang.org/x/sys v0.19.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= golang.org/x/text v0.14.0 h1:ScX5w1eTa3QqT8oi6+ziP7dTV1S2+ALU0bI+0zXKWiQ= golang.org/x/text v0.14.0/go.mod h1:18ZOQIKpY8NJVqYksKHtTdi31H5itFRjB5/qKTNYzSU= diff --git a/Backend/handlers/bookinghandler.go b/Backend/handlers/bookinghandler.go new file mode 100644 index 0000000..351538f --- /dev/null +++ b/Backend/handlers/bookinghandler.go @@ -0,0 +1,29 @@ +package handlers + +import ( + "encoding/json" + "net/http" + + "github.com/piyushkumar/hotelsystem/models" +) + +// GetBookings returns the list of bookings +func GetBookings(w http.ResponseWriter, r *http.Request) { + w.Header().Set("Content-Type", "application/json") + json.NewEncoder(w).Encode(models.Bookings) +} + + + +func CreateBooking(w http.ResponseWriter, r *http.Request) { + var newBooking models.Booking + err := json.NewDecoder(r.Body).Decode(&newBooking) + if err != nil { + http.Error(w, err.Error(), http.StatusBadRequest) + return + } + + models.Bookings = append(models.Bookings, newBooking) + w.WriteHeader(http.StatusCreated) + json.NewEncoder(w).Encode(newBooking) +} \ No newline at end of file diff --git a/Backend/handlers/cityHotelHandler.go b/Backend/handlers/cityHotelHandler.go new file mode 100644 index 0000000..95afc6e --- /dev/null +++ b/Backend/handlers/cityHotelHandler.go @@ -0,0 +1,23 @@ +package handlers + +import ( + "encoding/json" + "net/http" + + "github.com/gorilla/mux" + "github.com/piyushkumar/hotelsystem/models" +) + +func GetHotelsByCity(w http.ResponseWriter, r *http.Request) { + vars := mux.Vars(r) + city := vars["city"] + + hotels, ok := models.Cities[city] + if !ok { + http.Error(w, "City not found", http.StatusNotFound) + return + } + + w.Header().Set("Content-Type", "application/json") + json.NewEncoder(w).Encode(hotels) +} diff --git a/Backend/handlers/getprofileHandler.go b/Backend/handlers/getprofileHandler.go new file mode 100644 index 0000000..77e4327 --- /dev/null +++ b/Backend/handlers/getprofileHandler.go @@ -0,0 +1,17 @@ +package handlers + +import ( + "encoding/json" + "net/http" + + "github.com/piyushkumar/hotelsystem/models" +) + +// For simplicity, we're using a package-level variable. In a real application, this could be a database. +var userProfile = models.Profile{} + +// GetProfile sends the user's profile as a JSON response. +func GetProfile(w http.ResponseWriter, r *http.Request) { + w.Header().Set("Content-Type", "application/json") + json.NewEncoder(w).Encode(userProfile) +} diff --git a/Backend/handlers/listhotelHandler.go b/Backend/handlers/listhotelHandler.go index 8acf73f..20dee30 100644 --- a/Backend/handlers/listhotelHandler.go +++ b/Backend/handlers/listhotelHandler.go @@ -2,6 +2,7 @@ package handlers import ( "encoding/json" + "log" "net/http" "github.com/piyushkumar/hotelsystem/models" @@ -11,10 +12,75 @@ import ( var hotels = []models.Hotel{ {ID: "1", Name: "Hotel Sunshine", Location: "California", Rating: 5}, {ID: "2", Name: "Grand Plaza", Location: "New York", Rating: 4}, + {ID: "3", Name: "Hyat Plaza", Location: "Australia", Rating: 3}, + {ID: "4", Name: "Grinding Plaza", Location: "Hamburg", Rating: 2}, +} + + +var recommendHotels = []models.ReccHotel{ + + {ID: "1", Name: "Hotel Ramuplaz", Location: "California", Distance: 5, Rating: 5}, + {ID: "2", Name: "Plaza", Location: "New York", Distance: 4, Rating: 4}, + {ID: "3", Name: "Huluk Plaza", Location: "Australia", Distance: 3, Rating: 3}, + {ID: "4", Name: "Indus Plaza", Location: "Hamburg", Distance: 2, Rating: 2}, + + +} + +var popullarHotels = []models.PopHotel{ + + {Name: "Hotel Oceana", Location: "California", Distance: 5, Price: 5000, Rating: 5}, + {Name: "Placo", Location: "New York", Distance: 4, Price: 4000, Rating: 4}, + {Name: "Pizza Put Plaza", Location: "Australia", Distance: 3, Price: 3000, Rating: 3}, + {Name: "PakLand Plaza", Location: "Hamburg", Distance: 2, Price: 2000, Rating: 2}, + + } // ListHotels sends a list of hotels as a JSON response. -func ListHotels(w http.ResponseWriter, r *http.Request) { +func WishListHotels(w http.ResponseWriter, r *http.Request) { w.Header().Set("Content-Type", "application/json") json.NewEncoder(w).Encode(hotels) } + +func RecommendHotels(w http.ResponseWriter, r *http.Request) { + log.Println("RecommendHotels handler called") + w.Header().Set("Content-Type", "application/json") + json.NewEncoder(w).Encode(recommendHotels) +} + +func PopHotels(w http.ResponseWriter, r *http.Request) { + log.Println("PopHotels handler called") + w.Header().Set("Content-Type", "application/json") + json.NewEncoder(w).Encode(popullarHotels) +} + +//! handler for searching hotels by name + +func SearchHotels(w http.ResponseWriter, r *http.Request) { + log.Println("SearchHotels handler called") + w.Header().Set("Content-Type", "application/json") + var searchResults []models.Hotel + query := r.URL.Query().Get("name") + for _, hotel := range hotels { + if hotel.Name == query { + searchResults = append(searchResults, hotel) + } + } + json.NewEncoder(w).Encode(searchResults) +} + +//! handler for searching hotels by location + +func SearchHotelsByLocation(w http.ResponseWriter, r *http.Request) { + log.Println("SearchHotelsByLocation handler called") + w.Header().Set("Content-Type", "application/json") + var searchResults []models.Hotel + query := r.URL.Query().Get("location") + for _, hotel := range hotels { + if hotel.Location == query { + searchResults = append(searchResults, hotel) + } + } + json.NewEncoder(w).Encode(searchResults) +} \ No newline at end of file diff --git a/Backend/handlers/liveChatHandler.go b/Backend/handlers/liveChatHandler.go new file mode 100644 index 0000000..6092eea --- /dev/null +++ b/Backend/handlers/liveChatHandler.go @@ -0,0 +1,26 @@ +package handlers + +import ( + "github.com/piyushkumar/hotelsystem/websocket" + "net/http" +) + +type ChatHandler struct { + hub *websocket.Hub +} + +func NewChatHandler() *ChatHandler { + hub := websocket.NewHub() + go hub.Run() + return &ChatHandler{hub: hub} +} + +func (c *ChatHandler) HandleConnections(w http.ResponseWriter, r *http.Request) { + websocket.ServeWs(c.hub, w, r) +} + +func (c *ChatHandler) HandleMessages() { + // Here you would handle incoming messages. This example doesn't implement it. + + +} diff --git a/Backend/handlers/popHandler.go b/Backend/handlers/popHandler.go new file mode 100644 index 0000000..98e6dff --- /dev/null +++ b/Backend/handlers/popHandler.go @@ -0,0 +1,19 @@ +package handlers + +import ( + "encoding/json" + "net/http" + + "github.com/piyushkumar/hotelsystem/models" +) + +// GetPopularHotels handles the request for retrieving popular hotels +func GetPopularHotels(w http.ResponseWriter, r *http.Request) { + w.Header().Set("Content-Type", "application/json") + popularHotels := []models.PopHotels{ + {"Hotel California", "Los Angeles", 5.0}, + {"The Great Northern", "Twin Peaks", 15.0}, + {"The Overlook Hotel", "Colorado Rockies", 20.0}, + } + json.NewEncoder(w).Encode(popularHotels) +} diff --git a/Backend/handlers/searchHandlers.go b/Backend/handlers/searchHandlers.go new file mode 100644 index 0000000..0257064 --- /dev/null +++ b/Backend/handlers/searchHandlers.go @@ -0,0 +1,32 @@ +package handlers + +// import ( +// "encoding/json" +// "net/http" + +// "github.com/gorilla/mux" +// "github.com/piyushkumar/hotelsystem/models" + +// ) + +// func findHotelHandler(w http.ResponseWriter, r *http.Request) { + + + +// vars := mux.Vars(r) + +// city := vars["city"] +// bedroom := vars["bedroom"] +// bathroom := vars["bathroom"] +// services := vars["services"] +// payment := vars["payment"] + +// for _, hotel := range models.SearchHotel { +// if hotel. == city && hotel.Bedrooms == bedroom && hotel.Bathrooms == bathroom && hotel.Service == services && hotel.Payment == payment { +// json.NewEncoder(w).Encode(hotel) +// return +// } +// } +// } + +// SearchHotels handler function diff --git a/Backend/handlers/setprofileHandler.go b/Backend/handlers/setprofileHandler.go new file mode 100644 index 0000000..a4b2be4 --- /dev/null +++ b/Backend/handlers/setprofileHandler.go @@ -0,0 +1,23 @@ +package handlers + +import ( + "encoding/json" + "net/http" + + "github.com/piyushkumar/hotelsystem/models" +) + +// SetProfile updates the user's profile. +func SetProfile(w http.ResponseWriter, r *http.Request) { + var profile models.Profile + if err := json.NewDecoder(r.Body).Decode(&profile); err != nil { + http.Error(w, err.Error(), http.StatusBadRequest) + return + } + + // Update the global userProfile variable with the new data + userProfile = profile + + w.WriteHeader(http.StatusOK) + json.NewEncoder(w).Encode(profile) +} diff --git a/Backend/handlers/tripHandlers.go b/Backend/handlers/tripHandlers.go new file mode 100644 index 0000000..f89cb0b --- /dev/null +++ b/Backend/handlers/tripHandlers.go @@ -0,0 +1,19 @@ +package handlers + +import ( + "encoding/json" + "net/http" + + "github.com/piyushkumar/hotelsystem/models" +) + +// GetPlaces displays all the places +func GetPlaces(w http.ResponseWriter, r *http.Request) { + w.Header().Set("Content-Type", "application/json") + places := []models.Place{ + {"Paris", 3000.0, 4.5, 200.0}, + {"Bali", 12000.0, 4.8, 150.0}, + {"Tokyo", 8000.0, 4.6, 300.0}, + } + json.NewEncoder(w).Encode(places) +} diff --git a/Backend/main.go b/Backend/main.go index 6aa6bd8..b395a56 100644 --- a/Backend/main.go +++ b/Backend/main.go @@ -41,23 +41,54 @@ func main() { //! Register the OTP checking handler r.HandleFunc("/checkotp", handlers.CheckOTP).Methods("POST") - - //! Forgot password route r.HandleFunc("/forgot-password", handlers.ForgotPassword).Methods("POST") log.Println("Server starting on port 9080...") - //! hotel apis + //! hotel apis + + r.HandleFunc("/hotels", handlers.WishListHotels).Methods("GET") + r.HandleFunc("/rechotels", handlers.RecommendHotels).Methods("GET") + r.HandleFunc("/pophotels", handlers.PopHotels).Methods("GET") - r.HandleFunc("/hotels", handlers.ListHotels).Methods("GET") r.HandleFunc("/hotel", handlers.AddHotel).Methods("POST") r.HandleFunc("/hotel/{id}", handlers.DeleteHotel).Methods("DELETE") + // Start the server + //! trip routes + + r.HandleFunc("/places", handlers.GetPlaces).Methods("GET") + r.HandleFunc("/popularhotels", handlers.GetPopularHotels).Methods("GET") + + //! hotel by city + r.HandleFunc("/city/{city}", handlers.GetHotelsByCity).Methods("GET") + + //! profile routes + + r.HandleFunc("/profile", handlers.GetProfile).Methods("GET") + r.HandleFunc("/profile", handlers.SetProfile).Methods("POST") + + //! search hotels by name + r.HandleFunc("/search", handlers.SearchHotels).Methods("GET") + + + + + + //! booking apis + + r.HandleFunc("/bookings", handlers.GetBookings).Methods("GET") + r.HandleFunc("/booking", handlers.CreateBooking).Methods("POST") + + //! live chat api + r.HandleFunc("/chat", handlers.NewChatHandler().HandleConnections).Methods("GET") + log.Fatal(http.ListenAndServe(":9080", r)) //log.Fatal(http.ListenAndServe(":8080", r)) } +//! frontend port is 9080 // package main diff --git a/Backend/models/bookingModel.go b/Backend/models/bookingModel.go new file mode 100644 index 0000000..c5bf22e --- /dev/null +++ b/Backend/models/bookingModel.go @@ -0,0 +1,15 @@ +package models + +type Booking struct { + RoomType string `json:"roomType"` + Status string `json:"status"` // Pending, Confirmed, Completed + Price float64 `json:"price"` +} + +// Mocked data for demonstration +var Bookings = []Booking{ + {"Single", "Confirmed", 100.0}, + {"Double", "Pending", 150.0}, + {"Suite", "Completed", 250.0}, + {"Dinning Hall", "Pending", 500.0}, +} diff --git a/Backend/models/cityhotModel.go b/Backend/models/cityhotModel.go new file mode 100644 index 0000000..34ca48f --- /dev/null +++ b/Backend/models/cityhotModel.go @@ -0,0 +1,17 @@ +package models + +type CityHotel struct { + Name string `json:"name"` + Distance float64 `json:"distance"` // Distance from city center in km +} + +var Cities = map[string][]CityHotel{ + "Dhaka": {{"Dhaka Delight", 1.5}, {"Capital Stay", 2.0}, {"Urban Retreat", 0.5}, {"City Lights", 1.0}, {"Skyline View", 2.2}}, + "Chittagong": {{"Chittagong Charm", 1.2}, {"Harbor Haven", 2.1}, {"Seafront Suite", 3.5}, {"Bay Breeze", 1.8}, {"Maritime Luxury", 4.0}}, + "Rajshahi": {{"Rajshahi Respite", 0.6}, {"Vineyard Vista", 1.5}, {"Silk City Sleep", 1.1}, {"Mango Manor", 2.3}, {"Padma Place", 3.0}}, + "Dinajpur": {{"Dinajpur Den", 1.4}, {"Ricefield Retreat", 2.6}, {"Historic Hideaway", 1.9}, {"Northern Nook", 2.8}, {"Tea Terrace", 3.1}}, + "Khulna": {{"Khulna Comfort", 0.8}, {"Sundarban Stay", 3.2}, {"Riverfront Residence", 1.7}, {"Mangrove Hotel", 2.9}, {"Tiger Trail Inn", 4.5}}, + "Mymensingh": {{"Mymensingh Manor", 0.5}, {"Brahmaputra B&B", 1.6}, {"Garo Hills Getaway", 2.4}, {"Central Cottage", 1.2}, {"Tea Town Terrace", 3.3}}, + "Barisal": {{"Barisal Bay Inn", 0.7}, {"Delta Delight", 1.3}, {"Floating Hotel", 2.0}, {"Southern Star", 2.5}, {"Venice of the East", 3.6}}, + "Sylhet": {{"Sylhet Springs", 1.0}, {"Cloud-Covered Lodge", 2.2}, {"Tea Garden Guesthouse", 3.8}, {"Monsoon Magic", 1.7}, {"Hillside Hotel", 2.8}}, +} diff --git a/Backend/models/livechatModel.go b/Backend/models/livechatModel.go new file mode 100644 index 0000000..962d117 --- /dev/null +++ b/Backend/models/livechatModel.go @@ -0,0 +1,16 @@ +package models + + +type LiveChat struct { + + message string `json:"message"` + chatProfile ChatProfile `json:"chatProfile"` + +} + + +type ChatProfile struct { + + userid string `json:"userid"` + username string `json:"username"` +} \ No newline at end of file diff --git a/Backend/models/popTrip.go b/Backend/models/popTrip.go new file mode 100644 index 0000000..3e36c51 --- /dev/null +++ b/Backend/models/popTrip.go @@ -0,0 +1,8 @@ +package models + +// Hotel represents the structure of our hotel resource +type PopHotels struct { + Name string `json:"name"` + Place string `json:"place"` + Distance float64 `json:"distance"` // in kilometers, from a central point of interest +} diff --git a/Backend/models/pophotelModel.go b/Backend/models/pophotelModel.go new file mode 100644 index 0000000..f6fc56a --- /dev/null +++ b/Backend/models/pophotelModel.go @@ -0,0 +1,10 @@ +package models + +// Hotel represents the hotel model. +type PopHotel struct { + Name string `json:"name"` + Location string `json:"location"` + Distance int `json:"distance"` + Price int `json:"price"` + Rating int `json:"rating"` +} diff --git a/Backend/models/profileModel.go b/Backend/models/profileModel.go new file mode 100644 index 0000000..e3f9b98 --- /dev/null +++ b/Backend/models/profileModel.go @@ -0,0 +1,8 @@ +package models + +// Profile represents a user's profile with Name, Email, and Gender. +type Profile struct { + Name string `json:"name"` + Email string `json:"email"` + Gender string `json:"gender"` +} diff --git a/Backend/models/recomhotelModel.go b/Backend/models/recomhotelModel.go new file mode 100644 index 0000000..0b2b6b0 --- /dev/null +++ b/Backend/models/recomhotelModel.go @@ -0,0 +1,10 @@ +package models + +// Hotel represents the hotel model. +type ReccHotel struct { + ID string `json:"id"` + Name string `json:"name"` + Location string `json:"location"` + Distance int `json:"distance"` + Rating int `json:"rating"` +} diff --git a/Backend/models/searchModel.go b/Backend/models/searchModel.go new file mode 100644 index 0000000..d2d6c3e --- /dev/null +++ b/Backend/models/searchModel.go @@ -0,0 +1,50 @@ +package models + + +type SearchModel struct { + + Bedrooms int `json:"bedrooms"` + Bathrooms int `json:"bathrooms"` + payment paymentModel `json:"payment"` + service serviceModel `json:"service"` + city string `json:"city"` + +} + +//! store dummy data +var SearchHotel = []SearchModel{ + + SearchModel{ Bedrooms: 2, Bathrooms: 2, payment: paymentModel{payathotel: true, payonline: true, advancepayment: true}, service: serviceModel{petsallowed: true, resturant: true, swimmingpool: true, carparking: true, fitnesscentre: true}, city: "Delhi"}, + SearchModel{ Bedrooms: 3, Bathrooms: 3, payment: paymentModel{payathotel: true, payonline: true, advancepayment: true}, service: serviceModel{petsallowed: true, resturant: true, swimmingpool: true, carparking: true, fitnesscentre: true}, city: "Mumbai"}, + SearchModel{ Bedrooms: 4, Bathrooms: 4, payment: paymentModel{payathotel: true, payonline: true, advancepayment: true}, service: serviceModel{petsallowed: true, resturant: true, swimmingpool: true, carparking: true, fitnesscentre: true}, city: "Bangalore"}, + SearchModel{ Bedrooms: 5, Bathrooms: 5, payment: paymentModel{payathotel: true, payonline: true, advancepayment: true}, service: serviceModel{petsallowed: true, resturant: true, swimmingpool: true, carparking: true, fitnesscentre: true}, city: "Chennai"}, + SearchModel{ Bedrooms: 6, Bathrooms: 6, payment: paymentModel{payathotel: true, payonline: true, advancepayment: true}, service: serviceModel{petsallowed: true, resturant: true, swimmingpool: true, carparking: true, fitnesscentre: true}, city: "Kolkata"}, + SearchModel{ Bedrooms: 7, Bathrooms: 7, payment: paymentModel{payathotel: true, payonline: true, advancepayment: true}, service: serviceModel{petsallowed: true, resturant: true, swimmingpool: true, carparking: true, fitnesscentre: true}, city: "Hyderabad"}, + SearchModel{ Bedrooms: 8, Bathrooms: 8, payment: paymentModel{payathotel: true, payonline: true, advancepayment: true}, service: serviceModel{petsallowed: true, resturant: true, swimmingpool: true, carparking: true, fitnesscentre: true}, city: "Pune"}, + SearchModel{ Bedrooms: 9, Bathrooms: 9, payment: paymentModel{payathotel: true, payonline: true, advancepayment: true}, service: serviceModel{petsallowed: true, resturant: true, swimmingpool: true, carparking: true, fitnesscentre: true}, city: "Jaipur"}, + SearchModel{ Bedrooms: 10, Bathrooms: 10, payment: paymentModel{payathotel: true, payonline: true, advancepayment: true}, service: serviceModel{petsallowed: true, resturant: true, swimmingpool: true, carparking: true, fitnesscentre: true}, city: "Ahmedabad"}, + SearchModel{ Bedrooms: 11, Bathrooms: 11, payment: paymentModel{payathotel: true, payonline: true, advancepayment: true}, service: serviceModel{petsallowed: true, resturant: true, swimmingpool: true, carparking: true, fitnesscentre: true}, city: "Lucknow"}, +} + + +type serviceModel struct { + + petsallowed bool `json:"petsallowed"` + resturant bool `json:"resturant"` + swimmingpool bool `json:"swimmingpool"` + carparking bool `json:"carparking"` + fitnesscentre bool `json:"fitnesscentre"` + + +} + + +type paymentModel struct { + + payathotel bool `json:"payathotel"` + payonline bool `json:"payonline"` + advancepayment bool `json:"advancepayment"` + +} + + diff --git a/Backend/models/tripModel.go b/Backend/models/tripModel.go new file mode 100644 index 0000000..defab39 --- /dev/null +++ b/Backend/models/tripModel.go @@ -0,0 +1,9 @@ +package models + +// Place represents the structure of our resource +type Place struct { + Name string `json:"name"` + Distance float64 `json:"distance"` // in kilometers + Rating float64 `json:"rating"` // 1 to 5 + Charges float64 `json:"charges"` // in USD +} diff --git a/Backend/tmp/build-errors.log b/Backend/tmp/build-errors.log index 8cfae36..7ef9e2b 100644 --- a/Backend/tmp/build-errors.log +++ b/Backend/tmp/build-errors.log @@ -1 +1 @@ -exit status 1exit status 1exit status 1exit status 1exit status 1exit status 1exit status 1exit status 1exit status 1exit status 1exit status 1exit status 1exit status 1exit status 1exit status 1exit status 1exit status 1exit status 1exit status 1exit status 1exit status 1exit status 1exit status 1exit status 1exit status 1exit status 1exit status 1exit status 1exit status 1exit status 1exit status 1exit status 1exit status 1exit status 1exit status 1exit status 1exit status 1exit status 1exit status 1exit status 1exit status 1exit status 1exit status 1exit status 1exit status 1exit status 1exit status 1exit status 1exit status 1exit status 1exit status 1exit status 1exit status 1exit status 1exit status 1exit status 1exit status 1exit status 1exit status 1exit status 1exit status 1exit status 1exit status 1exit status 1exit status 1exit status 1exit status 1exit status 1exit status 1exit status 1exit status 1exit status 1exit status 1exit status 1exit status 1exit status 1exit status 1exit status 1exit status 1exit status 1exit status 1exit status 1exit status 1exit status 1exit status 1exit status 1exit status 1exit status 1exit status 1exit status 1exit status 1exit status 1exit status 1exit status 1exit status 1exit status 1exit status 1 \ No newline at end of file +exit status 1exit status 1exit status 1exit status 1exit status 1exit status 1exit status 1exit status 1exit status 1exit status 1exit status 1exit status 1exit status 1exit status 1exit status 1exit status 1exit status 1exit status 1exit status 1exit status 1exit status 1exit status 1exit status 1exit status 1exit status 1exit status 1exit status 1exit status 1exit status 1exit status 1exit status 1exit status 1exit status 1exit status 1exit status 1exit status 1exit status 1exit status 1exit status 1exit status 1exit status 1exit status 1exit status 1exit status 1exit status 1exit status 1exit status 1exit status 1exit status 1exit status 1exit status 1exit status 1exit status 1exit status 1exit status 1exit status 1exit status 1exit status 1exit status 1exit status 1exit status 1exit status 1exit status 1exit status 1exit status 1exit status 1exit status 1exit status 1exit status 1exit status 1exit status 1exit status 1exit status 1exit status 1exit status 1exit status 1exit status 1exit status 1exit status 1exit status 1exit status 1exit status 1exit status 1exit status 1exit status 1exit status 1exit status 1exit status 1exit status 1exit status 1exit status 1exit status 1exit status 1exit status 1exit status 1exit status 1exit status 1exit status 1exit status 1exit status 1exit status 1exit status 1exit status 1exit status 1exit status 1exit status 1exit status 1exit status 1exit status 1exit status 1exit status 1exit status 1exit status 1exit status 1exit status 1exit status 1exit status 1exit status 1exit status 1exit status 1exit status 1exit status 1exit status 1exit status 1exit status 1exit status 1exit status 1exit status 1exit status 1exit status 1exit status 1exit status 1exit status 1exit status 1exit status 1exit status 1exit status 1exit status 1exit status 1exit status 1exit status 1exit status 1exit status 1exit status 1exit status 1exit status 1exit status 1exit status 1exit status 1exit status 1exit status 1exit status 1exit status 1exit status 1exit status 1exit status 1exit status 1exit status 1exit status 1exit status 1exit status 1exit status 1exit status 1exit status 1exit status 1exit status 1exit status 1exit status 1exit status 1exit status 1exit status 1exit status 1exit status 1exit status 1exit status 1exit status 1exit status 1exit status 1exit status 1exit status 1exit status 1exit status 1exit status 1exit status 1exit status 1exit status 1exit status 1exit status 1exit status 1exit status 1exit status 1exit status 1exit status 1exit status 1exit status 1exit status 1exit status 1exit status 1exit status 1exit status 1exit status 1exit status 1exit status 1exit status 1exit status 1exit status 1exit status 1exit status 1exit status 1exit status 1exit status 1exit status 1exit status 1exit status 1exit status 1exit status 1exit status 1exit status 1exit status 1exit status 1exit status 1exit status 1exit status 1exit status 1exit status 1exit status 1exit status 1exit status 1exit status 1exit status 1exit status 1exit status 1exit status 1exit status 1exit status 1exit status 1exit status 1exit status 1exit status 1exit status 1exit status 1exit status 1exit status 1exit status 1exit status 1exit status 1exit status 1exit status 1exit status 1exit status 1exit status 1exit status 1exit status 1exit status 1exit status 1exit status 1exit status 1exit status 1exit status 1exit status 1exit status 1exit status 1exit status 1exit status 1exit status 1exit status 1exit status 1exit status 1exit status 1exit status 1exit status 1exit status 1exit status 1exit status 1exit status 1exit status 1exit status 1exit status 1exit status 1exit status 1exit status 1exit status 1exit status 1exit status 1exit status 1exit status 1exit status 1exit status 1exit status 1exit status 1exit status 1exit status 1exit status 1exit status 1exit status 1exit status 1exit status 1exit status 1exit status 1exit status 1exit status 1exit status 1exit status 1exit status 1exit status 1exit status 1exit status 1exit status 1exit status 1exit status 1exit status 1exit status 1exit status 1exit status 1exit status 1exit status 1exit status 1exit status 1exit status 1exit status 1exit status 1exit status 1exit status 1exit status 1exit status 1exit status 1exit status 1exit status 1exit status 1exit status 1exit status 1exit status 1exit status 1exit status 1exit status 1exit status 1 \ No newline at end of file diff --git a/Backend/tmp/main.exe b/Backend/tmp/main.exe index 714e68a..88d7639 100644 Binary files a/Backend/tmp/main.exe and b/Backend/tmp/main.exe differ diff --git a/Backend/websocket/client.go b/Backend/websocket/client.go new file mode 100644 index 0000000..c1e878f --- /dev/null +++ b/Backend/websocket/client.go @@ -0,0 +1,81 @@ +package websocket + +import ( + "log" + "net/http" + + "github.com/gorilla/websocket" +) + +type Client struct { + Hub *Hub + Conn *websocket.Conn + Send chan []byte +} + +// NewClient creates a new client +func NewClient(hub *Hub, conn *websocket.Conn) *Client { + return &Client{Hub: hub, Conn: conn, Send: make(chan []byte, 256)} +} + +// writePump pumps messages from the hub to the websocket connection. +func (c *Client) writePump() { + defer func() { + c.Conn.Close() + }() + for { + select { + case message, ok := <-c.Send: + if !ok { + // The hub closed the channel. + c.Conn.WriteMessage(websocket.CloseMessage, []byte{}) + return + } + + if err := c.Conn.WriteMessage(websocket.TextMessage, message); err != nil { + return + } + } + } +} + +// readPump pumps messages from the websocket connection to the hub. +func (c *Client) readPump() { + defer func() { + c.Hub.Unregister <- c + c.Conn.Close() + }() + for { + _, message, err := c.Conn.ReadMessage() + if err != nil { + if websocket.IsUnexpectedCloseError(err, websocket.CloseGoingAway, websocket.CloseAbnormalClosure) { + // log the error + } + break + } + c.Hub.Broadcast <- message + } +} + +// ServeWs handles websocket requests from the peer. +func ServeWs(hub *Hub, w http.ResponseWriter, r *http.Request) { + conn, err := upgrader.Upgrade(w, r, nil) // Use the upgrader to upgrade the connection to a WebSocket. + if err != nil { + log.Printf("error upgrading HTTP to WebSocket: %v", err) + return + } + client := NewClient(hub, conn) + hub.Register <- client + + // Start the read and write pumps. + go client.writePump() + go client.readPump() +} + + +var upgrader = websocket.Upgrader{ + ReadBufferSize: 1024, + WriteBufferSize: 1024, + + CheckOrigin: func(r *http.Request) bool { return true }, +} diff --git a/Backend/websocket/hub.go b/Backend/websocket/hub.go new file mode 100644 index 0000000..f97ee5c --- /dev/null +++ b/Backend/websocket/hub.go @@ -0,0 +1,49 @@ +package websocket + +// Hub maintains the set of active clients and broadcasts messages to the clients. +type Hub struct { + // Registered clients. + Clients map[*Client]bool + + // Inbound messages from the clients. + Broadcast chan []byte + + // Register requests from the clients. + Register chan *Client + + // Unregister requests from clients. + Unregister chan *Client +} + +func NewHub() *Hub { + return &Hub{ + Broadcast: make(chan []byte), + Register: make(chan *Client), + Unregister: make(chan *Client), + Clients: make(map[*Client]bool), + } +} + +// Run starts the hub to accept new clients and broadcast messages. +func (h *Hub) Run() { + for { + select { + case client := <-h.Register: + h.Clients[client] = true + case client := <-h.Unregister: + if _, ok := h.Clients[client]; ok { + delete(h.Clients, client) + close(client.Send) + } + case message := <-h.Broadcast: + for client := range h.Clients { + select { + case client.Send <- message: + default: + close(client.Send) + delete(h.Clients, client) + } + } + } + } +} diff --git a/Frontend/android/app/src/main/AndroidManifest.xml b/Frontend/android/app/src/main/AndroidManifest.xml index 404d3b9..650bb1b 100644 --- a/Frontend/android/app/src/main/AndroidManifest.xml +++ b/Frontend/android/app/src/main/AndroidManifest.xml @@ -1,6 +1,39 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/Frontend/lib/GlobalComponents/data_provider.dart b/Frontend/lib/GlobalComponents/data_provider.dart index d058e99..77fa855 100644 --- a/Frontend/lib/GlobalComponents/data_provider.dart +++ b/Frontend/lib/GlobalComponents/data_provider.dart @@ -8,9 +8,23 @@ List maanGetChatList() { list.add(LMSModel(title: 'Cristin', subTitle: 'About what Course?', image: "https://assets.iqonic.design/old-themeforest-images/prokit/datingApp/Image.1.jpg")); list.add(LMSModel(title: 'Chris Hameshorth', subTitle: 'hello', image: "https://assets.iqonic.design/old-themeforest-images/prokit/datingApp/Image.3.jpg")); list.add(LMSModel(title: 'Eliyahou Amoyelle', subTitle: 'How ypu doing?', image: "https://assets.iqonic.design/old-themeforest-images/prokit/datingApp/Image.4.jpg")); + list.add(LMSModel(title: 'Eliyahou Amoyelle', subTitle: 'How ypu doing?', image: "https://assets.iqonic.design/old-themeforest-images/prokit/datingApp/Image.4.jpg")); + list.add(LMSModel(title: 'Eliyahou Amoyelle', subTitle: 'How ypu doing?', image: "https://assets.iqonic.design/old-themeforest-images/prokit/datingApp/Image.4.jpg")); + list.add(LMSModel(title: 'Izzy Sruly', subTitle: 'About what Course?', image: "https://assets.iqonic.design/old-themeforest-images/prokit/datingApp/Image.6.jpg")); + list.add(LMSModel(title: 'Izzy Sruly', subTitle: 'About what Course?', image: "https://assets.iqonic.design/old-themeforest-images/prokit/datingApp/Image.6.jpg")); + list.add(LMSModel(title: 'Izzy Sruly', subTitle: 'About what Course?', image: "https://assets.iqonic.design/old-themeforest-images/prokit/datingApp/Image.6.jpg")); list.add(LMSModel(title: 'Izzy Sruly', subTitle: 'About what Course?', image: "https://assets.iqonic.design/old-themeforest-images/prokit/datingApp/Image.6.jpg")); list.add(LMSModel(title: 'Tom Holland', subTitle: 'hello', image: "https://assets.iqonic.design/old-themeforest-images/prokit/datingApp/Image.5.jpg")); list.add(LMSModel(title: 'Salma Hayek', subTitle: 'How ypu doing?', image: "https://assets.iqonic.design/old-themeforest-images/prokit/datingApp/Image.7.jpg")); + list.add(LMSModel(title: 'Salma Hayek', subTitle: 'How ypu doing?', image: "https://assets.iqonic.design/old-themeforest-images/prokit/datingApp/Image.7.jpg")); + list.add(LMSModel(title: 'Salma Hayek', subTitle: 'How ypu doing?', image: "https://assets.iqonic.design/old-themeforest-images/prokit/datingApp/Image.7.jpg")); + list.add(LMSModel(title: 'Salma Hayek', subTitle: 'How ypu doing?', image: "https://assets.iqonic.design/old-themeforest-images/prokit/datingApp/Image.7.jpg")); + list.add(LMSModel(title: 'Salma Hayek', subTitle: 'How ypu doing?', image: "https://assets.iqonic.design/old-themeforest-images/prokit/datingApp/Image.7.jpg")); + list.add(LMSModel(title: 'Nora fatehi', subTitle: 'About what Course?', image: "https://assets.iqonic.design/old-themeforest-images/prokit/datingApp/Image.8.jpg")); + list.add(LMSModel(title: 'Nora fatehi', subTitle: 'About what Course?', image: "https://assets.iqonic.design/old-themeforest-images/prokit/datingApp/Image.8.jpg")); + list.add(LMSModel(title: 'Nora fatehi', subTitle: 'About what Course?', image: "https://assets.iqonic.design/old-themeforest-images/prokit/datingApp/Image.8.jpg")); + list.add(LMSModel(title: 'Nora fatehi', subTitle: 'About what Course?', image: "https://assets.iqonic.design/old-themeforest-images/prokit/datingApp/Image.8.jpg")); + list.add(LMSModel(title: 'Nora fatehi', subTitle: 'About what Course?', image: "https://assets.iqonic.design/old-themeforest-images/prokit/datingApp/Image.8.jpg")); list.add(LMSModel(title: 'Nora fatehi', subTitle: 'About what Course?', image: "https://assets.iqonic.design/old-themeforest-images/prokit/datingApp/Image.8.jpg")); return list; } diff --git a/Frontend/lib/Livechat/helper/constants.dart b/Frontend/lib/Livechat/helper/constants.dart new file mode 100644 index 0000000..8bd2495 --- /dev/null +++ b/Frontend/lib/Livechat/helper/constants.dart @@ -0,0 +1,5 @@ + +class Constants{ + + static String myName = ""; +} \ No newline at end of file diff --git a/Frontend/lib/Livechat/helper/helperfunction.dart b/Frontend/lib/Livechat/helper/helperfunction.dart new file mode 100644 index 0000000..a613bed --- /dev/null +++ b/Frontend/lib/Livechat/helper/helperfunction.dart @@ -0,0 +1,43 @@ +import 'package:shared_preferences/shared_preferences.dart'; + +class HelperFunctions{ + + static String sharedPreferenceUserLoggedInKey = "ISLOGGEDIN"; + static String sharedPreferenceUserNameKey = "USERNAMEKEY"; + static String sharedPreferenceUserEmailKey = "USEREMAILKEY"; + + /// saving data to sharedpreference + static Future saveUserLoggedInSharedPreference(bool isUserLoggedIn) async{ + + SharedPreferences preferences = await SharedPreferences.getInstance(); + return await preferences.setBool(sharedPreferenceUserLoggedInKey, isUserLoggedIn); + } + + static Future saveUserNameSharedPreference(String userName) async{ + SharedPreferences preferences = await SharedPreferences.getInstance(); + return await preferences.setString(sharedPreferenceUserNameKey, userName); + } + + static Future saveUserEmailSharedPreference(String userEmail) async{ + SharedPreferences preferences = await SharedPreferences.getInstance(); + return await preferences.setString(sharedPreferenceUserEmailKey, userEmail); + } + + /// fetching data from sharedpreference + + static Future getUserLoggedInSharedPreference() async{ + SharedPreferences preferences = await SharedPreferences.getInstance(); + return await preferences.getBool(sharedPreferenceUserLoggedInKey); + } + + static Future getUserNameSharedPreference() async{ + SharedPreferences preferences = await SharedPreferences.getInstance(); + return await preferences.getString(sharedPreferenceUserNameKey); + } + + static Future getUserEmailSharedPreference() async{ + SharedPreferences preferences = await SharedPreferences.getInstance(); + return await preferences.getString(sharedPreferenceUserEmailKey); + } + +} \ No newline at end of file diff --git a/Frontend/lib/Livechat/helper/theme.dart b/Frontend/lib/Livechat/helper/theme.dart new file mode 100644 index 0000000..b413958 --- /dev/null +++ b/Frontend/lib/Livechat/helper/theme.dart @@ -0,0 +1,6 @@ +import 'package:flutter/material.dart'; + +class CustomTheme { + static Color colorAccent = Color(0xff007EF4); + static Color textColor = Color(0xff071930); +} \ No newline at end of file diff --git a/Frontend/lib/Livechat/model/user.dart b/Frontend/lib/Livechat/model/user.dart new file mode 100644 index 0000000..b3b9dc4 --- /dev/null +++ b/Frontend/lib/Livechat/model/user.dart @@ -0,0 +1,5 @@ +class User { + final String uid; + + User({required this.uid}); +} \ No newline at end of file diff --git a/Frontend/lib/Livechat/services/database.dart b/Frontend/lib/Livechat/services/database.dart new file mode 100644 index 0000000..c25a5cb --- /dev/null +++ b/Frontend/lib/Livechat/services/database.dart @@ -0,0 +1,64 @@ +import 'package:cloud_firestore/cloud_firestore.dart'; + +class DatabaseMethods { + final FirebaseFirestore _firestore = FirebaseFirestore.instance; + + Future addUserInfo(Map userData) async { + _firestore.collection("users").add(userData).catchError((e) { + print(e.toString()); + }); + } + + Future getUserInfo(String email) async { + return _firestore + .collection("users") + .where("userEmail", isEqualTo: email) + .get() + .catchError((e) { + print(e.toString()); + }); + } + + Future searchByName(String searchField) { + return _firestore + .collection("users") + .where('userName', isEqualTo: searchField) + .get(); + } + + Future addChatRoom(Map chatRoom, String chatRoomId) async { + _firestore + .collection("chatRoom") + .doc(chatRoomId) + .set(chatRoom) + .catchError((e) { + print(e.toString()); + }); + } + + Stream getChats(String chatRoomId) { + return _firestore + .collection("chatRoom") + .doc(chatRoomId) + .collection("chats") + .orderBy('time') + .snapshots(); + } + + Future addMessage(String chatRoomId, Map chatMessageData) async { + _firestore.collection("chatRoom") + .doc(chatRoomId) + .collection("chats") + .add(chatMessageData).catchError((e) { + print(e.toString()); + }); + } + + Stream getUserChats(String itIsMyName) { + return _firestore + .collection("chatRoom") + .where('users', arrayContains: itIsMyName) + .snapshots(); +} + +} diff --git a/Frontend/lib/Livechat/view/charroom.dart b/Frontend/lib/Livechat/view/charroom.dart new file mode 100644 index 0000000..af57330 --- /dev/null +++ b/Frontend/lib/Livechat/view/charroom.dart @@ -0,0 +1,142 @@ + +import 'package:cloud_firestore/cloud_firestore.dart'; +import 'package:flutter/material.dart'; +import 'package:hotel_booking/Livechat/helper/constants.dart'; +import 'package:hotel_booking/Livechat/helper/helperfunction.dart'; +import 'package:hotel_booking/Livechat/helper/theme.dart'; +import 'package:hotel_booking/Livechat/services/database.dart'; +import 'package:hotel_booking/Livechat/view/chat.dart'; + +class ChatRoom extends StatefulWidget { + @override + _ChatRoomState createState() => _ChatRoomState(); +} + +class _ChatRoomState extends State { + //late Stream chatRooms; + Stream? chatRooms; + Widget chatRoomsList() { + return StreamBuilder( + stream: chatRooms, + builder: (context, AsyncSnapshot snapshot) { + if (snapshot.hasData) { + return ListView.builder( + itemCount: snapshot.data!.docs.length, + shrinkWrap: true, + itemBuilder: (context, index) { + DocumentSnapshot ds = snapshot.data!.docs[index]; + String chatRoomId = ds["chatRoomId"]; + String userName = chatRoomId + .replaceAll("_", "") + .replaceAll(Constants.myName!, ""); // Make sure Constants.myName is not null + return ChatRoomsTile( + userName: userName, + chatRoomId: chatRoomId, + ); + }); + } + return Container(); + }, + ); + } + + @override + void initState() { + getUserInfogetChats(); + super.initState(); + } +getUserInfogetChats() async { + Constants.myName = (await HelperFunctions.getUserNameSharedPreference())!; + chatRooms = DatabaseMethods().getUserChats(Constants.myName!); // Ensure Constants.myName is non-null here + setState(() {}); + } + + + @override + Widget build(BuildContext context) { + return Scaffold( + appBar: AppBar( + title: Image.asset( + "assets/images/logo.png", + height: 40, + ), + elevation: 0.0, + centerTitle: false, + actions: [ + // GestureDetector( + // onTap: () { + // AuthService().signOut(); + // Navigator.pushReplacement(context, + // MaterialPageRoute(builder: (context) => Authenticate())); + // }, + // child: Container( + // padding: EdgeInsets.symmetric(horizontal: 16), + // child: Icon(Icons.exit_to_app)), + // ) + ], + ), + body: Container( + child: chatRoomsList(), + ), + floatingActionButton: FloatingActionButton( + child: Icon(Icons.search), + onPressed: () { + // Navigator.push( + // context, MaterialPageRoute(builder: (context) => Search())); + }, + ), + ); + } +} + +class ChatRoomsTile extends StatelessWidget { + final String userName; + final String chatRoomId; + + ChatRoomsTile({required this.userName,required this.chatRoomId}); + + @override + Widget build(BuildContext context) { + return GestureDetector( + onTap: (){ + Navigator.push(context, MaterialPageRoute( + builder: (context) => Chat( + chatRoomId: chatRoomId, + ) + )); + }, + child: Container( + color: Colors.black26, + padding: EdgeInsets.symmetric(horizontal: 24, vertical: 20), + child: Row( + children: [ + Container( + height: 30, + width: 30, + decoration: BoxDecoration( + color: CustomTheme.colorAccent, + borderRadius: BorderRadius.circular(30)), + child: Text(userName.substring(0, 1), + textAlign: TextAlign.center, + style: TextStyle( + color: Colors.white, + fontSize: 16, + fontFamily: 'OverpassRegular', + fontWeight: FontWeight.w300)), + ), + SizedBox( + width: 12, + ), + Text(userName, + textAlign: TextAlign.start, + style: TextStyle( + color: Colors.white, + fontSize: 16, + fontFamily: 'OverpassRegular', + fontWeight: FontWeight.w300)) + ], + ), + ), + ); + } +} \ No newline at end of file diff --git a/Frontend/lib/Livechat/view/chat.dart b/Frontend/lib/Livechat/view/chat.dart new file mode 100644 index 0000000..8f525da --- /dev/null +++ b/Frontend/lib/Livechat/view/chat.dart @@ -0,0 +1,188 @@ +import 'dart:io'; + +import 'package:cloud_firestore/cloud_firestore.dart'; +import 'package:flutter/material.dart'; +import 'package:hotel_booking/Livechat/helper/constants.dart'; +import 'package:hotel_booking/Livechat/services/database.dart'; +import 'package:hotel_booking/Livechat/widgets/widgets.dart'; + +class Chat extends StatefulWidget { + final String chatRoomId; + + Chat({ required this.chatRoomId}); + + @override + _ChatState createState() => _ChatState(); +} + +class _ChatState extends State { + + late Stream chats; + TextEditingController messageEditingController = new TextEditingController(); + +Widget chatMessages() { + return StreamBuilder( + stream: chats, + builder: (context, AsyncSnapshot snapshot) { + if (snapshot.hasData) { + return ListView.builder( + itemCount: snapshot.data!.docs.length, + itemBuilder: (context, index) { + var doc = snapshot.data!.docs[index]; + return MessageTile( + message: doc["message"], + sendByMe: Constants.myName == doc["sendBy"], + ); + }); + } + return Container(); + }, + ); + } + + addMessage() { + if (messageEditingController.text.isNotEmpty) { + Map chatMessageMap = { + "sendBy": Constants.myName, + "message": messageEditingController.text, + 'time': DateTime + .now() + .millisecondsSinceEpoch, + }; + + DatabaseMethods().addMessage(widget.chatRoomId, chatMessageMap); + + setState(() { + messageEditingController.text = ""; + }); + } + } + + @override + void initState() { + chats = DatabaseMethods().getChats(widget.chatRoomId); + super.initState(); + } + + @override + Widget build(BuildContext context) { + return Scaffold( + appBar: AppBar( + title: Text("Chat"), + ), + body: Container( + child: Stack( + children: [ + chatMessages(), + Container(alignment: Alignment.bottomCenter, + width: MediaQuery + .of(context) + .size + .width, + child: Container( + padding: EdgeInsets.symmetric(horizontal: 24, vertical: 24), + color: Color(0x54FFFFFF), + child: Row( + children: [ + Expanded( + child: TextField( + controller: messageEditingController, + style: simpleTextStyle(), + decoration: InputDecoration( + hintText: "Message ...", + hintStyle: TextStyle( + color: Colors.white, + fontSize: 16, + ), + border: InputBorder.none + ), + )), + SizedBox(width: 16,), + GestureDetector( + onTap: () { + addMessage(); + }, + child: Container( + height: 40, + width: 40, + decoration: BoxDecoration( + gradient: LinearGradient( + colors: [ + const Color(0x36FFFFFF), + const Color(0x0FFFFFFF) + ], + begin: FractionalOffset.topLeft, + end: FractionalOffset.bottomRight + ), + borderRadius: BorderRadius.circular(40) + ), + padding: EdgeInsets.all(12), + child: Image.asset("assets/images/send.png", + height: 25, width: 25,)), + ), + ], + ), + ), + ), + ], + ), + ), + ); + } + +} + +class MessageTile extends StatelessWidget { + final String message; + final bool sendByMe; + + MessageTile({required this.message, required this.sendByMe}); + + + @override + Widget build(BuildContext context) { + return Container( + padding: EdgeInsets.only( + top: 8, + bottom: 8, + left: sendByMe ? 0 : 24, + right: sendByMe ? 24 : 0), + alignment: sendByMe ? Alignment.centerRight : Alignment.centerLeft, + child: Container( + margin: sendByMe + ? EdgeInsets.only(left: 30) + : EdgeInsets.only(right: 30), + padding: EdgeInsets.only( + top: 17, bottom: 17, left: 20, right: 20), + decoration: BoxDecoration( + borderRadius: sendByMe ? BorderRadius.only( + topLeft: Radius.circular(23), + topRight: Radius.circular(23), + bottomLeft: Radius.circular(23) + ) : + BorderRadius.only( + topLeft: Radius.circular(23), + topRight: Radius.circular(23), + bottomRight: Radius.circular(23)), + gradient: LinearGradient( + colors: sendByMe ? [ + const Color(0xff007EF4), + const Color(0xff2A75BC) + ] + : [ + const Color(0x1AFFFFFF), + const Color(0x1AFFFFFF) + ], + ) + ), + child: Text(message, + textAlign: TextAlign.start, + style: TextStyle( + color: Colors.white, + fontSize: 16, + fontFamily: 'OverpassRegular', + fontWeight: FontWeight.w300)), + ), + ); + } +} diff --git a/Frontend/lib/Livechat/widgets/widgets.dart b/Frontend/lib/Livechat/widgets/widgets.dart new file mode 100644 index 0000000..bf574f4 --- /dev/null +++ b/Frontend/lib/Livechat/widgets/widgets.dart @@ -0,0 +1,30 @@ +import 'package:flutter/material.dart'; + +Widget appBarMain(BuildContext context) { + return AppBar( + title: Image.asset( + "assets/images/logo.png", + height: 40, + ), + elevation: 0.0, + centerTitle: false, + ); +} + +InputDecoration textFieldInputDecoration(String hintText) { + return InputDecoration( + hintText: hintText, + hintStyle: TextStyle(color: Colors.white54), + focusedBorder: + UnderlineInputBorder(borderSide: BorderSide(color: Colors.white)), + enabledBorder: + UnderlineInputBorder(borderSide: BorderSide(color: Colors.white))); +} + +TextStyle simpleTextStyle() { + return TextStyle(color: Colors.white, fontSize: 16); +} + +TextStyle biggerTextStyle() { + return TextStyle(color: Colors.white, fontSize: 17); +} \ No newline at end of file diff --git a/Frontend/lib/Notification/controller/messagescr.dart b/Frontend/lib/Notification/controller/messagescr.dart new file mode 100644 index 0000000..a65bece --- /dev/null +++ b/Frontend/lib/Notification/controller/messagescr.dart @@ -0,0 +1,22 @@ + + +import 'package:flutter/material.dart'; + +class MessageScreen extends StatefulWidget { + final String id ; + const MessageScreen({Key? key , required this.id}) : super(key: key); + + @override + State createState() => _MessageScreenState(); +} + +class _MessageScreenState extends State { + @override + Widget build(BuildContext context) { + return Scaffold( + appBar: AppBar( + title: Text('Message Screen' +widget.id) , + ), + ); + } +} \ No newline at end of file diff --git a/Frontend/lib/Notification/controller/notificationservice.dart b/Frontend/lib/Notification/controller/notificationservice.dart new file mode 100644 index 0000000..dd5811c --- /dev/null +++ b/Frontend/lib/Notification/controller/notificationservice.dart @@ -0,0 +1,224 @@ + + +//! class for managing notificatin services + + + +import 'dart:io'; +import 'dart:math'; + + +import 'package:firebase_messaging/firebase_messaging.dart'; +import 'package:http/http.dart' as http; +import 'package:flutter/foundation.dart'; +import 'package:flutter/material.dart'; +import 'package:flutter_local_notifications/flutter_local_notifications.dart'; +import 'package:path_provider/path_provider.dart'; + +import 'messagescr.dart'; + + +class NotificationServices { + + //initialising firebase message plugin + FirebaseMessaging messaging = FirebaseMessaging.instance ; + + //initialising firebase message plugin + final FlutterLocalNotificationsPlugin _flutterLocalNotificationsPlugin = FlutterLocalNotificationsPlugin(); + + + + //function to initialise flutter local notification plugin to show notifications for android when app is active + void initLocalNotifications(BuildContext context, RemoteMessage message)async{ + var androidInitializationSettings = const AndroidInitializationSettings('@mipmap/ic_launcher'); + var iosInitializationSettings = const DarwinInitializationSettings(); + + var initializationSetting = InitializationSettings( + android: androidInitializationSettings , + iOS: iosInitializationSettings + ); + + await _flutterLocalNotificationsPlugin.initialize( + initializationSetting, + onDidReceiveNotificationResponse: (payload){ + // handle interaction when app is active for android + handleMessage(context, message); + } + ); + } + + void firebaseInit(BuildContext context){ + + + + + FirebaseMessaging.onMessage.listen((message) { + + RemoteNotification? notification = message.notification ; + AndroidNotification? android = message.notification!.android ; + + if (kDebugMode) { + print("notifications title:${notification!.title}"); + print("notifications body:${notification.body}"); + print('count:${android!.count}'); + print('data:${message.data.toString()}'); + } + + if(Platform.isIOS){ + forgroundMessage(); + } + + if(Platform.isAndroid){ + initLocalNotifications(context, message); + showNotification(message); + } + }); + } + + + void requestNotificationPermission() async { + NotificationSettings settings = await messaging.requestPermission( + alert: true, + announcement: true, + badge: true, + carPlay: true, + criticalAlert: true, + provisional: true, + sound: true , + ); + + if (settings.authorizationStatus == AuthorizationStatus.authorized) { + if (kDebugMode) { + print('user granted permission'); + } + } else if (settings.authorizationStatus == + AuthorizationStatus.provisional) { + if (kDebugMode) { + print('user granted provisional permission'); + } + } else { + //appsetting.AppSettings.openNotificationSettings(); + if (kDebugMode) { + print('user denied permission'); + } + } + } + + // function to show visible notification when app is active + Future showNotification(RemoteMessage message)async{ + + AndroidNotificationChannel channel = AndroidNotificationChannel( + message.notification!.android!.channelId.toString(), + message.notification!.android!.channelId.toString() , + importance: Importance.max , + showBadge: true , + playSound: true, + sound: const RawResourceAndroidNotificationSound('jetsons_doorbell') + ); + + AndroidNotificationDetails androidNotificationDetails = AndroidNotificationDetails( + channel.id.toString(), + channel.name.toString() , + channelDescription: 'your channel description', + importance: Importance.high, + priority: Priority.high , + playSound: true, + ticker: 'ticker' , + sound: channel.sound + // sound: RawResourceAndroidNotificationSound('jetsons_doorbell') + // icon: largeIconPath + ); + + const DarwinNotificationDetails darwinNotificationDetails = DarwinNotificationDetails( + presentAlert: true , + presentBadge: true , + presentSound: true + ) ; + + NotificationDetails notificationDetails = NotificationDetails( + android: androidNotificationDetails, + iOS: darwinNotificationDetails + ); + + Future.delayed(Duration.zero , (){ + _flutterLocalNotificationsPlugin.show( + 0, + message.notification!.title.toString(), + message.notification!.body.toString(), + notificationDetails , + ); + }); + + } + + //function to get device token on which we will send the notifications + Future getDeviceToken() async { + String? token = await messaging.getToken(); + return token!; + } + + void isTokenRefresh()async{ + messaging.onTokenRefresh.listen((event) { + event.toString(); + if (kDebugMode) { + print('refresh'); + } + }); + } + + //handle tap on notification when app is in background or terminated + Future setupInteractMessage(BuildContext context)async{ + + // when app is terminated + RemoteMessage? initialMessage = await FirebaseMessaging.instance.getInitialMessage(); + + if(initialMessage != null){ + handleMessage(context, initialMessage); + } + + + //when app ins background + FirebaseMessaging.onMessageOpenedApp.listen((event) { + handleMessage(context, event); + }); + + } + + void handleMessage(BuildContext context, RemoteMessage message) { + + if(message.data['type'] =='msj'){ + Navigator.push(context, + MaterialPageRoute(builder: (context) => MessageScreen( + id: message.data['id'] , + ))); + } + } + + + Future forgroundMessage() async { + await FirebaseMessaging.instance.setForegroundNotificationPresentationOptions( + alert: true, + badge: true, + sound: true, + ); + } + + //! Fetching notification coming from firebase + Future getNotification() async { + await messaging.getInitialMessage(); + + //! print all the notification + FirebaseMessaging.onMessage.listen((RemoteMessage message) { + if (kDebugMode) { + print('Got a message whilst in the foreground!'); + print('Message data: ${message.data}'); + if (message.notification != null) { + print('Message also contained a notification: ${message.notification}'); + } + } + }); + } + + +} + diff --git a/Frontend/lib/Notification/notification.dart b/Frontend/lib/Notification/notification.dart index efb1113..880116f 100644 --- a/Frontend/lib/Notification/notification.dart +++ b/Frontend/lib/Notification/notification.dart @@ -1,9 +1,15 @@ +import 'dart:async'; + +import 'package:flutter/foundation.dart'; import 'package:flutter/material.dart'; +import 'package:get/get_connect/http/src/utils/utils.dart'; import 'package:google_fonts/google_fonts.dart'; import 'package:nb_utils/nb_utils.dart'; import '../../../constant.dart'; import '../GlobalComponents/data_provider.dart'; import '../GlobalComponents/lms_model.dart'; +import 'controller/notificationservice.dart'; +import 'package:async/async.dart'; class Notificationlist extends StatefulWidget { const Notificationlist({Key? key}) : super(key: key); @@ -15,6 +21,63 @@ class Notificationlist extends StatefulWidget { class _NotificationlistState extends State { List listData = maanGetChatList(); //! here we have notification display list bool isChecked = true; + final ScrollController _scrollController = ScrollController(); + double _scrollPosition = 0; + + + NotificationServices notificationServices = NotificationServices(); + + //! here we have notification display list + + //! Fetching notification coming from firebase + + Future getNotification() async { + await notificationServices.getNotification(); + } + + @override + void initState() { + // TODO: implement initState + super.initState(); + notificationServices.requestNotificationPermission(); + notificationServices.forgroundMessage(); + notificationServices.firebaseInit(context); + notificationServices.setupInteractMessage(context); + notificationServices.isTokenRefresh(); + + notificationServices.getDeviceToken().then((value) { + if (kDebugMode) { + print('device token'); + print(value); + } + }); + + _scrollController.addListener(() { + setState(() { + _scrollPosition = _scrollController.offset; + }); + }); + + Timer(const Duration(seconds: 4), () { + printScrollPosition(); + }); + getNotification(); + } + + //! printing scroll position + + void printScrollPosition() { + if (kDebugMode) { + print('scroll position: $_scrollPosition'); + }} + + + + @override + void dispose() { + _scrollController.dispose(); + super.dispose(); + } @override Widget build(BuildContext context) { @@ -29,6 +92,7 @@ class _NotificationlistState extends State { ), ), SingleChildScrollView( + controller: _scrollController, child: Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ @@ -52,7 +116,7 @@ class _NotificationlistState extends State { fontSize: 18.0, fontWeight: FontWeight.bold), ), - const Spacer(), + Spacer(), PopupMenuButton( padding: EdgeInsets.zero, itemBuilder: (BuildContext bc) => [ @@ -120,11 +184,32 @@ class _NotificationlistState extends State { borderRadius: BorderRadius.circular(5.0), ), ), - onTap: () {}, + onTap: () { + printScrollPosition(); + }, ); }, ).toList(), ), + // ListView.builder( + + // itemCount: listData.length, + // itemBuilder: (BuildContext context, int index) { + // return ListTile( + // title: Text(listData[index].title!), + // subtitle: Text(listData[index].subTitle!), + // leading: Image.network(listData[index].image!), + // trailing: Checkbox( + // value: isChecked, + // onChanged: (bool? value) { + // setState(() { + // isChecked = value!; + // }); + // }, + // ), + // ); + // }, + // ), ], ), ], diff --git a/Frontend/lib/Profile/edit_profile.dart b/Frontend/lib/Profile/edit_profile.dart index 0975a62..82c0d3d 100644 --- a/Frontend/lib/Profile/edit_profile.dart +++ b/Frontend/lib/Profile/edit_profile.dart @@ -1,8 +1,11 @@ +import 'dart:convert'; + import 'package:flutter/material.dart'; import 'package:flutter_feather_icons/flutter_feather_icons.dart'; import 'package:hotel_booking/Screen/Home/home.dart'; import 'package:nb_utils/nb_utils.dart'; import '../../../constant.dart'; +import 'package:http/http.dart' as http; class EditProfile extends StatefulWidget { const EditProfile({Key? key}) : super(key: key); @@ -41,6 +44,37 @@ class _EditProfileState extends State { final dateController = TextEditingController(); + TextEditingController nameController = TextEditingController(); + TextEditingController emailController = TextEditingController(); + TextEditingController genderController = TextEditingController(); + + //! post api for update profile + //! http://192.168.85.111:9080/profile + + Future profileUpdatepostApi(String name, String email, gender) async { + var url = Uri.parse('http://192.168.85.111:9080/profile'); + var response = await http.post( + url, + headers: { + 'Content-Type': 'application/json; charset=UTF-8', + }, + body: json.encode( + {"name": name, "email": email, "gender": gender}), + ); + if (response.statusCode == 200) { + print('Response status: ${response.statusCode}'); + print('Response body: ${response.body}'); + Navigator.push( + context, + MaterialPageRoute(builder: (context) => Home()), + ); + } else { + print('Response status: ${response.statusCode}'); + print('Response body: ${response.body}'); + toast("wrong data"); + } + } + @override Widget build(BuildContext context) { return Scaffold( @@ -66,27 +100,33 @@ class _EditProfileState extends State { ), child: Padding( padding: const EdgeInsets.all(20.0), - child: Column( - mainAxisSize: MainAxisSize.min, - children: [ - Container( - width: context.width(), - decoration: BoxDecoration( - borderRadius: BorderRadiusDirectional.circular(30.0), - color: kMainColor, - ), - child: Padding( - padding: const EdgeInsets.all(15.0), - child: Center( - child: Text( - 'Update', - style: kTextStyle.copyWith( - color: Colors.white, fontSize: 18.0), + child: InkWell( + onTap: () { + profileUpdatepostApi( + nameController.text, emailController.text, "Male"); + }, + child: Column( + mainAxisSize: MainAxisSize.min, + children: [ + Container( + width: context.width(), + decoration: BoxDecoration( + borderRadius: BorderRadiusDirectional.circular(30.0), + color: kMainColor, + ), + child: Padding( + padding: const EdgeInsets.all(15.0), + child: Center( + child: Text( + 'Update', + style: kTextStyle.copyWith( + color: Colors.white, fontSize: 18.0), + ), ), ), ), - ), - ], + ], + ), ), ), ), @@ -122,6 +162,7 @@ class _EditProfileState extends State { height: 70.0, ), AppTextField( + controller: nameController, textFieldType: TextFieldType.NAME, decoration: kInputDecoration.copyWith( labelText: 'Full Name', @@ -132,6 +173,7 @@ class _EditProfileState extends State { ), const SizedBox(height: 20.0), AppTextField( + controller: emailController, textFieldType: TextFieldType.EMAIL, decoration: kInputDecoration.copyWith( labelText: 'Email Address*', @@ -210,13 +252,15 @@ class _EditProfileState extends State { bottom: 3.0, right: 20.0, child: Container( - padding: const EdgeInsets.all(4.0), + padding: const EdgeInsets.all(4.0), decoration: BoxDecoration( - color: Colors.white, - borderRadius: BorderRadius.circular(10.0), - border: Border.all(color: kMainColor) - ), - child: const Icon(FeatherIcons.camera,size: 12.0,)), + color: Colors.white, + borderRadius: BorderRadius.circular(10.0), + border: Border.all(color: kMainColor)), + child: const Icon( + FeatherIcons.camera, + size: 12.0, + )), ), ], ), diff --git a/Frontend/lib/Profile/profile.dart b/Frontend/lib/Profile/profile.dart index 1640a6b..a777920 100644 --- a/Frontend/lib/Profile/profile.dart +++ b/Frontend/lib/Profile/profile.dart @@ -1,3 +1,5 @@ +import 'dart:convert'; + import 'package:flutter/material.dart'; import 'package:flutter_feather_icons/flutter_feather_icons.dart'; import 'package:hotel_booking/Chat/chat_list.dart'; @@ -10,6 +12,8 @@ import '../../constant.dart'; import '../Notification/notification.dart'; import '../Screen/Home/Payment/payment.dart'; +import 'package:http/http.dart' as http; + class Profile extends StatefulWidget { const Profile({Key? key}) : super(key: key); @@ -46,6 +50,41 @@ class _ProfileState extends State { // } // } + @override + void initState() { + super.initState(); + getWishListApi(); + } + + //! http://192.168.85.111:9080/profile + + //! post api for fetching profile data + + //! http://192.168.85.111:9080/profile + + Future> getWishListApi() async { + var url = Uri.parse('http://192.168.85.111:9080/profile'); + var response = await http.get(url); + if (response.statusCode == 200) { + Map profile = json.decode(response.body); + + print('Response status: ${response.statusCode}'); + print('Response body: ${response.body}'); + + // Update the state with the fetched data + setState(() { + name = profile['name']; + email = profile['email']; + }); + + return profile; + } else { + // Handle error or return empty list + print('Request failed with status: ${response.statusCode}.'); + return {}; + } + } + @override Widget build(BuildContext context) { return Scaffold( diff --git a/Frontend/lib/Screen/Authentication/add_info.dart b/Frontend/lib/Screen/Authentication/add_info.dart index d2c0511..ccd4fd8 100644 --- a/Frontend/lib/Screen/Authentication/add_info.dart +++ b/Frontend/lib/Screen/Authentication/add_info.dart @@ -1,9 +1,13 @@ +import 'dart:convert'; + import 'package:flutter/material.dart'; import 'package:hotel_booking/Screen/Authentication/sign_in.dart'; import 'package:nb_utils/nb_utils.dart'; import '../../GlobalComponents/button_global.dart'; import '../../constant.dart'; +import 'package:http/http.dart' as http; + class AddInfo extends StatefulWidget { const AddInfo({Key? key}) : super(key: key); @@ -68,6 +72,39 @@ class _AddInfoState extends State { } //final dateController = TextEditingController(); + //! http://192.168.85.111:9080/profile + + //! post api for adding user info + + Future addProfilePostApi(String fullname, String email, String gender, + BuildContext context) async { + var response = await http.post( + Uri.parse('http://192.168.85.111:9080/profile'), + headers: { + 'Content-Type': 'application/json; charset=UTF-8', + }, + body: json.encode({ + "name": fullname, + "email": email, + "gender": gender, + }), + ); + + var responseString = response.body; + var jsonData = jsonDecode(responseString); + if (response.statusCode == 200) { + print(jsonData); + print("Response status code is" + response.statusCode.toString()); + Navigator.push( + context, + MaterialPageRoute(builder: (context) => SignIn()), + ); + } else { + print("Response status code is" + response.statusCode.toString()); + toast("There is Error" + jsonData['message']); + print(jsonData['message']); + } + } @override Widget build(BuildContext context) { @@ -166,7 +203,10 @@ class _AddInfoState extends State { ButtonGlobal( buttontext: 'Save Info', onPressed: () { - const SignIn().launch(context); + // addProfilePostApi(emailController.text, context); + addProfilePostApi(nameController.text, emailController.text, + gender, context); + // const SignIn().launch(context); }, buttonDecoration: kButtonDecoration.copyWith(color: kMainColor), diff --git a/Frontend/lib/Screen/Authentication/welcome.dart b/Frontend/lib/Screen/Authentication/welcome.dart index 9c9e971..dcc2707 100644 --- a/Frontend/lib/Screen/Authentication/welcome.dart +++ b/Frontend/lib/Screen/Authentication/welcome.dart @@ -2,8 +2,10 @@ import 'package:flutter/material.dart'; import 'package:font_awesome_flutter/font_awesome_flutter.dart'; import 'package:hotel_booking/Screen/Authentication/sign_in.dart'; import 'package:hotel_booking/Screen/Authentication/sign_up.dart'; +import 'package:hotel_booking/Screen/Home/home.dart'; import 'package:hotel_booking/constant.dart'; import 'package:nb_utils/nb_utils.dart'; +import 'package:vibration/vibration.dart'; import '../../GlobalComponents/button_global.dart'; import 'controller/googlesignin.dart'; @@ -71,21 +73,37 @@ class _WelcomeState extends State { child: Row( mainAxisAlignment: MainAxisAlignment.center, children: [ - Padding( - padding: const EdgeInsets.all(10.0), - child: Card( - elevation: 0.0, - color: const Color(0xFF3B5998), - shape: RoundedRectangleBorder( - borderRadius: BorderRadius.circular(30.0), - ), - child: const Padding( - padding: EdgeInsets.only( - left: 30.0, right: 30.0, top: 10.0, bottom: 10.0), - child: Center( - child: Icon( - FontAwesomeIcons.facebookF, - color: Colors.white, + InkWell( + onTap: () async { + bool? canVibrate = await Vibration.hasVibrator(); + if (canVibrate == true) { + Vibration.vibrate(duration: 200); + + Navigator.push( + context, + MaterialPageRoute(builder: (context) => Home()), + ); + } + }, + child: Padding( + padding: const EdgeInsets.all(10.0), + child: Card( + elevation: 0.0, + color: const Color(0xFF3B5998), + shape: RoundedRectangleBorder( + borderRadius: BorderRadius.circular(30.0), + ), + child: const Padding( + padding: EdgeInsets.only( + left: 30.0, + right: 30.0, + top: 10.0, + bottom: 10.0), + child: Center( + child: Icon( + FontAwesomeIcons.facebookF, + color: Colors.white, + ), ), ), ), @@ -101,7 +119,8 @@ class _WelcomeState extends State { ), child: InkWell( onTap: (() async { - final user = await AuthService().signInWithGoogle(context); + final user = + await AuthService().signInWithGoogle(context); if (user != null) { print("SignIn Successful: ${user.displayName}"); // Navigate to your app's home screen or dashboard @@ -126,21 +145,37 @@ class _WelcomeState extends State { ), ), ), - Padding( - padding: const EdgeInsets.all(10.0), - child: Card( - elevation: 0.0, - color: Colors.white, - shape: RoundedRectangleBorder( - borderRadius: BorderRadius.circular(30.0), - ), - child: const Padding( - padding: EdgeInsets.only( - left: 30.0, right: 30.0, top: 10.0, bottom: 10.0), - child: Center( - child: Icon( - FontAwesomeIcons.apple, - color: Color(0xFF412F2D), + InkWell( + onTap: () async { + bool? canVibrate = await Vibration.hasVibrator(); + if (canVibrate == true) { + Vibration.vibrate(duration: 200); + + Navigator.push( + context, + MaterialPageRoute(builder: (context) => Home()), + ); + } + }, + child: Padding( + padding: const EdgeInsets.all(10.0), + child: Card( + elevation: 0.0, + color: Colors.white, + shape: RoundedRectangleBorder( + borderRadius: BorderRadius.circular(30.0), + ), + child: const Padding( + padding: EdgeInsets.only( + left: 30.0, + right: 30.0, + top: 10.0, + bottom: 10.0), + child: Center( + child: Icon( + FontAwesomeIcons.apple, + color: Color(0xFF412F2D), + ), ), ), ), diff --git a/Frontend/lib/Screen/Home/Map/dhaka.dart b/Frontend/lib/Screen/Home/Map/dhaka.dart index 5a424db..0ee9120 100644 --- a/Frontend/lib/Screen/Home/Map/dhaka.dart +++ b/Frontend/lib/Screen/Home/Map/dhaka.dart @@ -1,13 +1,17 @@ import 'dart:async'; +import 'dart:convert'; import 'package:flutter/material.dart'; import 'package:google_maps_flutter/google_maps_flutter.dart'; +import 'package:hotel_booking/Livechat/view/charroom.dart'; import 'package:hotel_booking/constant.dart'; import 'package:nb_utils/nb_utils.dart'; +import '../../loading/loading.dart'; import '../hotel.dart'; - +import 'package:http/http.dart' as http; class Dhaka extends StatefulWidget { - const Dhaka({Key? key}) : super(key: key); + final String cityname; + const Dhaka({Key? key, this.cityname = "Dhaka"}) : super(key: key); @override _DhakaState createState() => _DhakaState(); @@ -17,6 +21,10 @@ class _DhakaState extends State { @override void initState() { super.initState(); + // getWishListApi(widget.cityname).then((value) { + // print(value); + // }); + getWishListApi(widget.cityname); } // ignore: prefer_final_fields @@ -25,21 +33,44 @@ class _DhakaState extends State { static const LatLng _center = LatLng(40.397419, -74.382103); final Set _markers = {}; - void _onCameraMove(CameraPosition position) {} void _onMapCreated(GoogleMapController controller) { _controller.complete(controller); } + //! http://192.168.85.111:9080//city/{city} + + Future> getWishListApi(String nameCity) async { + var url = Uri.parse('http://192.168.85.111:9080//city/$nameCity'); + var response = await http.get(url); + if (response.statusCode == 200) { + List hotels = json.decode(response.body); + + print('Response status: ${response.statusCode}'); + print('Response body: ${response.body}'); + return hotels; + } else { + // Handle error or return empty list + print('Request failed with status: ${response.statusCode}.'); + return []; + } + } @override Widget build(BuildContext context) { return Scaffold( + floatingActionButton: FloatingActionButton( + onPressed: () { + ChatRoom().launch(context); + }, + child: const Icon(Icons.add), + backgroundColor: kMainColor, + ), backgroundColor: Colors.white, appBar: AppBar( title: Text( - 'Dhaka', + widget.cityname, style: kTextStyle.copyWith(color: kTitleColor), ), iconTheme: const IconThemeData(color: kTitleColor), @@ -96,112 +127,247 @@ class _DhakaState extends State { //! draggablesheet DraggableScrollableSheet( initialChildSize: 0.30, - maxChildSize: 1.0, - minChildSize: 0.15, - builder: (BuildContext context, ScrollController scrollController){ - return Container( - height: 400.0, - decoration: const BoxDecoration( - color: kMainColor, - borderRadius: BorderRadius.only( - topRight: Radius.circular(30.0), - topLeft: Radius.circular(30.0), + maxChildSize: 1.0, + minChildSize: 0.15, + builder: (BuildContext context, ScrollController scrollController) { + return Container( + height: 400.0, + decoration: const BoxDecoration( + color: kMainColor, + borderRadius: BorderRadius.only( + topRight: Radius.circular(30.0), + topLeft: Radius.circular(30.0), + ), ), - ), - child: SingleChildScrollView( - controller: scrollController, - child: Column( - crossAxisAlignment: CrossAxisAlignment.center, - children: [ - const Icon( - Icons.remove, - color: Colors.white, - ), - Text( - '20 Places to stay', - style: kTextStyle.copyWith( - color: Colors.white, fontSize: 18.0), - ), - const SizedBox(height: 20.0), - Container( - decoration: const BoxDecoration( - color: Color(0xFFF7F7F7), - borderRadius: BorderRadius.only( - topRight: Radius.circular(30.0), - topLeft: Radius.circular(30.0), - ), + child: SingleChildScrollView( + controller: scrollController, + child: Column( + crossAxisAlignment: CrossAxisAlignment.center, + children: [ + const Icon( + Icons.remove, + color: Colors.white, + ), + Text( + '20 Places to stay', + style: kTextStyle.copyWith( + color: Colors.white, fontSize: 18.0), ), - child: ListView.builder( - itemCount: 10, - shrinkWrap: true, - physics: const NeverScrollableScrollPhysics(), - itemBuilder: (_, index) { - return Padding( - padding: const EdgeInsets.only(left: 20.0, right: 20.0,top: 10.0,), - child: Container( - padding: const EdgeInsets.all(10.0), - width: context.width() / 1.5, - decoration: BoxDecoration( - borderRadius: BorderRadius.circular(20.0), - color: Colors.white, - ), - child: Column( - crossAxisAlignment: CrossAxisAlignment.start, - mainAxisSize: MainAxisSize.min, - children: [ - Image.asset('images/banner6.png'), - const SizedBox(height: 10.0), - Text( - 'Sandy Hill Beach, West Sonadia', - style: kTextStyle.copyWith( - color: kTitleColor, - fontSize: 18.0, - fontWeight: FontWeight.bold), + const SizedBox(height: 20.0), + // Container( + // decoration: const BoxDecoration( + // color: Color(0xFFF7F7F7), + // borderRadius: BorderRadius.only( + // topRight: Radius.circular(30.0), + // topLeft: Radius.circular(30.0), + // ), + // ), + // child: ListView.builder( + // itemCount: 10, + // shrinkWrap: true, + // physics: const NeverScrollableScrollPhysics(), + // itemBuilder: (_, index) { + // return Padding( + // padding: const EdgeInsets.only( + // left: 20.0, + // right: 20.0, + // top: 10.0, + // ), + // child: Container( + // padding: const EdgeInsets.all(10.0), + // width: context.width() / 1.5, + // decoration: BoxDecoration( + // borderRadius: BorderRadius.circular(20.0), + // color: Colors.white, + // ), + // child: Column( + // crossAxisAlignment: CrossAxisAlignment.start, + // mainAxisSize: MainAxisSize.min, + // children: [ + // Image.asset('images/banner6.png'), + // const SizedBox(height: 10.0), + // Text( + // 'Sandy Hill Beach, West Sonadia', + // style: kTextStyle.copyWith( + // color: kTitleColor, + // fontSize: 18.0, + // fontWeight: FontWeight.bold), + // ), + // Row( + // children: [ + // const Icon( + // Icons.location_on, + // color: Color(0xFFFF8748), + // size: 18.0, + // ), + // Text( + // '2,984 kilometres away', + // style: kTextStyle.copyWith( + // color: kGreyTextColor, + // ), + // ), + // const Spacer(), + // Container( + // decoration: BoxDecoration( + // borderRadius: + // BorderRadius.circular(30.0), + // color: kMainColor, + // ), + // child: Padding( + // padding: const EdgeInsets.all(10.0), + // child: + // Image.asset('images/arrow.png'), + // ), + // ), + // ], + // ), + // ], + // ), + // ).onTap( + // () => const Hotel().launch(context), + // ), + // ); + // }, + // ), + // ), + + FutureBuilder>( + future: getWishListApi(widget.cityname), + builder: (BuildContext context, + AsyncSnapshot> snapshot) { + if (snapshot.connectionState == + ConnectionState.waiting) { + return ListView.separated( + physics: NeverScrollableScrollPhysics(), + itemCount: 4, + shrinkWrap: true, + separatorBuilder: + (BuildContext context, int index) { + return SizedBox( + height: 10, + ); + }, + itemBuilder: (BuildContext context, int index) { + return Padding( + padding: const EdgeInsets.all(15.0), + child: ShimmerLoadingContainer(), + ); + }, + ); + } else if (snapshot.hasError) { + return Text('Error: ${snapshot.error}'); + } else { + List hotels = snapshot.data ?? []; + return ListView.builder( + itemCount: hotels.length, + shrinkWrap: true, + physics: const NeverScrollableScrollPhysics(), + itemBuilder: (_, index) { + var hotel = hotels[index]; + return Container( + decoration: const BoxDecoration( + color: Color(0xFFF7F7F7), + borderRadius: BorderRadius.only( + topRight: Radius.circular(30.0), + topLeft: Radius.circular(30.0), + ), ), - Row( - children: [ - const Icon( - Icons.location_on, - color: Color(0xFFFF8748), - size: 18.0, - ), - Text( - '2,984 kilometres away', - style: kTextStyle.copyWith( - color: kGreyTextColor, - ), - ), - const Spacer(), - Container( - decoration: BoxDecoration( - borderRadius: BorderRadius.circular(30.0), - color: kMainColor, + child: ListView.builder( + itemCount: hotels.length, + shrinkWrap: true, + physics: + const NeverScrollableScrollPhysics(), + itemBuilder: (_, index) { + return Padding( + padding: const EdgeInsets.only( + left: 20.0, + right: 20.0, + top: 10.0, ), - child: Padding( + child: Container( padding: const EdgeInsets.all(10.0), - child: Image.asset('images/arrow.png'), + width: context.width() / 1.5, + decoration: BoxDecoration( + borderRadius: + BorderRadius.circular(20.0), + color: Colors.white, + ), + child: Column( + crossAxisAlignment: + CrossAxisAlignment.start, + mainAxisSize: MainAxisSize.min, + children: [ + Image.asset('images/banner6.png'), + const SizedBox(height: 10.0), + Text( + hotel['name'], + style: kTextStyle.copyWith( + color: kTitleColor, + fontSize: 18.0, + fontWeight: + FontWeight.bold), + ), + Row( + children: [ + const Icon( + Icons.location_on, + color: Color(0xFFFF8748), + size: 18.0, + ), + // Text( + // '2,984 kilometres away', + // style: kTextStyle.copyWith( + // color: kGreyTextColor, + // ), + // ), + Text( + hotel['distance'] + .toString() + + " KiloMeters away", + style: kTextStyle.copyWith( + color: kGreyTextColor, + ), + ), + const Spacer(), + Container( + decoration: BoxDecoration( + borderRadius: + BorderRadius.circular( + 30.0), + color: kMainColor, + ), + child: Padding( + padding: + const EdgeInsets.all( + 10.0), + child: Image.asset( + 'images/arrow.png'), + ), + ), + ], + ), + ], + ), + ).onTap( + () => const Hotel().launch(context), ), - ), - ], + ); + }, ), - ], - ), - ).onTap( - () => const Hotel().launch(context), - ), - ); + ); + }, + ); + } }, ), - ) - ], + ], + ), ), - ), - ); - },), + ); + }, + ), ], ), ); } } - - diff --git a/Frontend/lib/Screen/Home/Search/Filter/controllers/placeController.dart b/Frontend/lib/Screen/Home/Search/Filter/controllers/placeController.dart new file mode 100644 index 0000000..40247e7 --- /dev/null +++ b/Frontend/lib/Screen/Home/Search/Filter/controllers/placeController.dart @@ -0,0 +1,72 @@ + +import 'package:get/get.dart'; + + +class PlaceController extends GetxController{ + + RxBool open = false.obs; + + void openClose(){ + open.value = !open.value; + } + + +RxList placeList = [ + 'Dhaka', + 'Chittagong', + 'Sylhet', + 'Khulna', + 'Rajshahi', + 'Barishal', + 'Rangpur', + 'Mymensingh', + 'Coxs Bazar', + 'Sundarban', + 'Bandarban', + 'Kuakata', + 'Saint Martin', + 'Sajek', + 'Jaflong', + 'Srimangal', + 'Ratargul', + 'Bichanakandi', + 'Paharpur', + 'Mahasthangarh', + 'Sonargaon', + 'Mainamati', + 'Puthia', + 'Kantajew', + 'Paharpur', + 'Bagerhat', + 'Puthia', + 'Kantajew', + 'Paharpur', + 'Bagerhat', + 'Puthia', + 'Kantajew', + 'Paharpur', +].obs; + + + +RxList choosenCity = [].obs; + + +void addCity(String city){ + choosenCity.add(city); + +} + +void removeCity(String city){ + choosenCity.remove(city); +} + +//! clear all selected city + +void clearAll(){ + choosenCity.clear(); +} + + + +} \ No newline at end of file diff --git a/Frontend/lib/Screen/Home/Search/Filter/filter.dart b/Frontend/lib/Screen/Home/Search/Filter/filter.dart index f48cc59..158ed3e 100644 --- a/Frontend/lib/Screen/Home/Search/Filter/filter.dart +++ b/Frontend/lib/Screen/Home/Search/Filter/filter.dart @@ -1,8 +1,15 @@ +import 'dart:async'; +import 'dart:convert'; + import 'package:flutter/material.dart'; import 'package:flutter_feather_icons/flutter_feather_icons.dart'; +import 'package:get/get.dart'; +import 'package:hotel_booking/Screen/Home/Search/Filter/controllers/placeController.dart'; import 'package:hotel_booking/Screen/Home/Search/Filter/show_result.dart'; import 'package:hotel_booking/constant.dart'; import 'package:nb_utils/nb_utils.dart'; +import 'package:http/http.dart' as http; +import 'package:scroll_to_top/scroll_to_top.dart'; class Filter extends StatefulWidget { const Filter({Key? key}) : super(key: key); @@ -41,6 +48,52 @@ class _FilterState extends State { List selectedList = []; + ScrollController scrollController = ScrollController(); + + @override + void initState() { + super.initState(); + + scrollController.addListener(() { + if (scrollController.position.pixels > 100) { + scrollController.animateTo(0, duration: const Duration(milliseconds: 500), curve: Curves.easeInOut); + } + }); + + // Timer.periodic(Duration(seconds: 10), (timer) { + // controller.clearAll(); + // }); + serachApi(); + } + + @override + void dispose() { + scrollController.dispose(); + super.dispose(); + } + + //! serach api implementation + + //! http://192.168.85.111:9080/search + + Future> serachApi() async { + var url = Uri.parse('http://192.168.85.111:9080/search?name=Hotel Sunshine'); + var response = await http.get(url); + if (response.statusCode == 200) { + List hotels = json.decode(response.body); + + print('Response status: ${response.statusCode}'); + print('Response body: ${response.body}'); + return hotels; + } else { + // Handle error or return empty list + print('Request failed with status: ${response.statusCode}.'); + return []; + } + } + + PlaceController controller = Get.put(PlaceController()); + @override Widget build(BuildContext context) { return SafeArea( @@ -48,7 +101,7 @@ class _FilterState extends State { bottomNavigationBar: Padding( padding: const EdgeInsets.only(top: 10.0), child: Container( - width: context.width(), + width: MediaQuery.of(context).size.width, decoration: const BoxDecoration( color: Colors.white, borderRadius: BorderRadius.only( @@ -62,7 +115,7 @@ class _FilterState extends State { mainAxisSize: MainAxisSize.min, children: [ Container( - width: context.width(), + width: MediaQuery.of(context).size.width, decoration: BoxDecoration( borderRadius: BorderRadiusDirectional.circular(30.0), color: kMainColor, @@ -121,233 +174,330 @@ class _FilterState extends State { ), body: Padding( padding: const EdgeInsets.all(20.0), - child: SingleChildScrollView( - child: Column( - children: [ - ListView.builder( - shrinkWrap: true, - physics: const NeverScrollableScrollPhysics(), - itemCount: roomNumber, - itemBuilder: (_, index) { - return Column( - crossAxisAlignment: CrossAxisAlignment.start, - children: [ - Text( - 'Rooms & Beds', - style: kTextStyle.copyWith( - color: kTitleColor, - fontSize: 18.0, - fontWeight: FontWeight.bold), - ), - const SizedBox( - height: 10.0, - ), - Row( - children: [ - Text( - 'Bedrooms', - style: kTextStyle.copyWith(color: kTitleColor), - ), - const Spacer(), - const Icon(FeatherIcons.minusCircle).onTap(() { - setState(() { - bedrooms > 1 ? bedrooms-- : bedrooms = 1; - }); - }), - const SizedBox(width: 10.0), - Text(bedrooms.toString()), - const SizedBox(width: 10.0), - const Icon(FeatherIcons.plusCircle).onTap(() { - setState(() { - bedrooms++; - }); - }), - ], - ), - const SizedBox(height: 10.0), - Row( - children: [ - Text( - 'Bathrooms', - style: kTextStyle.copyWith(color: kTitleColor), - ), - const Spacer(), - const Icon(FeatherIcons.minusCircle).onTap(() { - setState(() { - bathrooms > 1 ? bathrooms-- : bathrooms = 1; - }); - }), - const SizedBox(width: 10.0), - Text(bathrooms.toString()), - const SizedBox(width: 10.0), - const Icon(FeatherIcons.plusCircle).onTap(() { - setState(() { - bathrooms++; - }); - }), - ], - ), - const SizedBox(height: 20.0), - Card( - child: ExpansionTile( - title: Text( - 'Service & Facilities', - style: kTextStyle.copyWith( - color: kTitleColor, fontSize: 18.0), - ), + child: ScrollToTop( + scrollController: scrollController, + child: SingleChildScrollView( + child: Column( + children: [ + ListView.builder( + shrinkWrap: true, + physics: const NeverScrollableScrollPhysics(), + itemCount: roomNumber, + itemBuilder: (_, index) { + return Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Text( + 'Rooms & Beds', + style: kTextStyle.copyWith( + color: kTitleColor, + fontSize: 18.0, + fontWeight: FontWeight.bold), + ), + const SizedBox( + height: 10.0, + ), + Row( children: [ - ListView.builder( - shrinkWrap: true, - physics: const NeverScrollableScrollPhysics(), - itemCount: filterList.length, - itemBuilder: (_, index) { - return Padding( - padding: const EdgeInsets.only(left: 20.0), - child: Row( - children: [ - Text( - filterList[index], - style: kTextStyle.copyWith( - color: kGreyTextColor), - ).onTap(() { - setState(() { - selectedList - .contains(filterList[index]) - ? selectedList - .remove(filterList[index]) - : selectedList - .add(filterList[index]); - }); - }), - const Spacer(), - Checkbox( - value: selectedList.contains( - filterList[index], - ), - onChanged: null, - shape: RoundedRectangleBorder( - borderRadius: BorderRadius.circular( - 30.0, - ), - ), - ), - ], - ), - ); - }, + Text( + 'Bedrooms', + style: kTextStyle.copyWith(color: kTitleColor), ), + const Spacer(), + const Icon(FeatherIcons.minusCircle).onTap(() { + setState(() { + bedrooms > 1 ? bedrooms-- : bedrooms = 1; + }); + }), + const SizedBox(width: 10.0), + Text(bedrooms.toString()), + const SizedBox(width: 10.0), + const Icon(FeatherIcons.plusCircle).onTap(() { + setState(() { + bedrooms++; + }); + }), ], ), - ), - const SizedBox(height: 10.0), - Card( - child: ExpansionTile( - title: Text( - 'Property Type', - style: kTextStyle.copyWith( - color: kTitleColor, fontSize: 18.0), - ), + const SizedBox(height: 10.0), + Row( children: [ - ListView.builder( - shrinkWrap: true, - physics: const NeverScrollableScrollPhysics(), - itemCount: propertyList.length, - itemBuilder: (_, index) { - return Padding( - padding: const EdgeInsets.only(left: 20.0), - child: Row( - children: [ - Text( - propertyList[index], - style: kTextStyle.copyWith( - color: kGreyTextColor), - ).onTap(() { - setState(() { - selectedList.contains( - propertyList[index]) - ? selectedList - .remove(propertyList[index]) - : selectedList - .add(propertyList[index]); - }); - }), - const Spacer(), - Checkbox( - value: selectedList.contains( - propertyList[index], - ), - onChanged: null, - shape: RoundedRectangleBorder( - borderRadius: BorderRadius.circular( - 30.0, - ), - ), - ), - ], - ), - ); - }, + Text( + 'Bathrooms', + style: kTextStyle.copyWith(color: kTitleColor), ), + const Spacer(), + const Icon(FeatherIcons.minusCircle).onTap(() { + setState(() { + bathrooms > 1 ? bathrooms-- : bathrooms = 1; + }); + }), + const SizedBox(width: 10.0), + Text(bathrooms.toString()), + const SizedBox(width: 10.0), + const Icon(FeatherIcons.plusCircle).onTap(() { + setState(() { + bathrooms++; + }); + }), ], ), - ), - const SizedBox(height: 10.0), - Card( - child: ExpansionTile( - title: Text( - 'Payment', - style: kTextStyle.copyWith( - color: kTitleColor, fontSize: 18.0), + const SizedBox(height: 20.0), + Card( + child: ExpansionTile( + title: Text( + 'Service & Facilities', + style: kTextStyle.copyWith( + color: kTitleColor, fontSize: 18.0), + ), + children: [ + ListView.builder( + shrinkWrap: true, + physics: const NeverScrollableScrollPhysics(), + itemCount: filterList.length, + itemBuilder: (_, index) { + return Padding( + padding: const EdgeInsets.only(left: 20.0), + child: Row( + children: [ + Text( + filterList[index], + style: kTextStyle.copyWith( + color: kGreyTextColor), + ).onTap(() { + setState(() { + selectedList + .contains(filterList[index]) + ? selectedList + .remove(filterList[index]) + : selectedList + .add(filterList[index]); + }); + }), + const Spacer(), + Checkbox( + value: selectedList.contains( + filterList[index], + ), + onChanged: null, + shape: RoundedRectangleBorder( + borderRadius: BorderRadius.circular( + 30.0, + ), + ), + ), + ], + ), + ); + }, + ), + ], ), - children: [ - ListView.builder( - shrinkWrap: true, - physics: const NeverScrollableScrollPhysics(), - itemCount: paymentList.length, - itemBuilder: (_, index) { - return Padding( - padding: const EdgeInsets.only(left: 20.0), - child: Row( - children: [ - Text( - paymentList[index], - style: kTextStyle.copyWith( - color: kGreyTextColor), - ).onTap(() { - setState(() { - selectedList.contains( - paymentList[index]) - ? selectedList - .remove(paymentList[index]) - : selectedList - .add(paymentList[index]); - }); - }), - const Spacer(), - Checkbox( - value: selectedList.contains( - paymentList[index], + ), + const SizedBox(height: 10.0), + Card( + child: ExpansionTile( + title: Text( + 'Property Type', + style: kTextStyle.copyWith( + color: kTitleColor, fontSize: 18.0), + ), + children: [ + ListView.builder( + shrinkWrap: true, + physics: const NeverScrollableScrollPhysics(), + itemCount: propertyList.length, + itemBuilder: (_, index) { + return Padding( + padding: const EdgeInsets.only(left: 20.0), + child: Row( + children: [ + Text( + propertyList[index], + style: kTextStyle.copyWith( + color: kGreyTextColor), + ).onTap(() { + setState(() { + selectedList.contains( + propertyList[index]) + ? selectedList + .remove(propertyList[index]) + : selectedList + .add(propertyList[index]); + }); + }), + const Spacer(), + Checkbox( + value: selectedList.contains( + propertyList[index], + ), + onChanged: null, + shape: RoundedRectangleBorder( + borderRadius: BorderRadius.circular( + 30.0, + ), + ), ), - onChanged: null, - shape: RoundedRectangleBorder( - borderRadius: BorderRadius.circular( - 30.0, + ], + ), + ); + }, + ), + ], + ), + ), + const SizedBox(height: 10.0), + Card( + child: ExpansionTile( + title: Text( + 'Payment', + style: kTextStyle.copyWith( + color: kTitleColor, fontSize: 18.0), + ), + children: [ + ListView.builder( + shrinkWrap: true, + physics: const NeverScrollableScrollPhysics(), + itemCount: paymentList.length, + itemBuilder: (_, index) { + return Padding( + padding: const EdgeInsets.only(left: 20.0), + child: Row( + children: [ + Text( + paymentList[index], + style: kTextStyle.copyWith( + color: kGreyTextColor), + ).onTap(() { + setState(() { + selectedList.contains( + paymentList[index]) + ? selectedList + .remove(paymentList[index]) + : selectedList + .add(paymentList[index]); + }); + }), + const Spacer(), + Checkbox( + value: selectedList.contains( + paymentList[index], + ), + onChanged: null, + shape: RoundedRectangleBorder( + borderRadius: BorderRadius.circular( + 30.0, + ), ), ), - ), - ], - ), + ], + ), + ); + }, + ), + ], + ), + ), + + const SizedBox(height: 10.0), + + + Obx(() => controller.open == false ? + + Container( + height: 50, + decoration: BoxDecoration( + borderRadius: BorderRadius.circular(10.0), + color: Colors.white, + + boxShadow: [ + BoxShadow( + color: Colors.grey.withOpacity(0.5), + spreadRadius: 5, + blurRadius: 7, + offset: const Offset(0, 3), + ), + ], + ), + + child: Padding( + padding: const EdgeInsets.all(8.0), + child: Row( + children: [ + Text("Choose City", style: kTextStyle.copyWith(color: kTitleColor, fontSize: 18.0),), + const Spacer(), + const Icon(FeatherIcons.chevronDown).onTap(() { + controller.openClose(); + }), + ], + ), + ), + ) + : AlertDialog( + + + title: Row( + children: [ + Text("Choose City", style: kTextStyle.copyWith(color: kTitleColor, fontSize: 18.0),), + + const Spacer(), + const Icon(FeatherIcons.x).onTap(() { + controller.openClose(); + }), + ], + ), + content: Container( + height: 200, + width: 300, + child: ListView.builder( + itemCount: controller.placeList.length, + itemBuilder: (context, index) { + return ListTile( + title: Text(controller.placeList[index]), + onTap: () { + // controller.openClose(); + + controller.choosenCity.contains(controller.placeList[index]) ? controller.choosenCity.remove(controller.placeList[index]) : controller.choosenCity.add(controller.placeList[index]); + }, ); }, ), - ], + ), + ),), + + Obx(() => + Wrap( + children: controller.choosenCity.map((e) => Padding( + padding: const EdgeInsets.all(8.0), + child: Container( + decoration: BoxDecoration( + borderRadius: BorderRadius.circular(10.0), + color: kMainColor, + ), + child: Padding( + padding: const EdgeInsets.all(8.0), + child: Row( + mainAxisSize: MainAxisSize.min, + children: [ + Text(e, style: kTextStyle.copyWith(color: Colors.white),), + const SizedBox(width: 10.0), + const Icon(FeatherIcons.x, color: Colors.white,).onTap(() { + controller.removeCity(e); + }), + ], + ), + ), + ), + )).toList(), ), - ), - ], - ); - }, - ), - ], + ), + + ], + ); + }, + ), + ], + ), ), ), ), diff --git a/Frontend/lib/Screen/Home/Trips/explore_trips.dart b/Frontend/lib/Screen/Home/Trips/explore_trips.dart index 738b94e..54d778d 100644 --- a/Frontend/lib/Screen/Home/Trips/explore_trips.dart +++ b/Frontend/lib/Screen/Home/Trips/explore_trips.dart @@ -1,8 +1,13 @@ +import 'dart:convert'; + import 'package:flutter/material.dart'; import 'package:font_awesome_flutter/font_awesome_flutter.dart'; import 'package:nb_utils/nb_utils.dart'; import '../../../constant.dart'; +import 'package:http/http.dart' as http; + +import '../../loading/loading.dart'; class ExploreTrips extends StatefulWidget { const ExploreTrips({Key? key}) : super(key: key); @@ -12,6 +17,46 @@ class ExploreTrips extends StatefulWidget { } class _ExploreTripsState extends State { + @override + void initState() { + super.initState(); + popHotelListapi(); + } + + Future> getWishListApi() async { + var url = Uri.parse('http://192.168.85.111:9080/places'); + var response = await http.get(url); + if (response.statusCode == 200) { + List hotels = json.decode(response.body); + + print('Response status: ${response.statusCode}'); + print('Response body: ${response.body}'); + return hotels; + } else { + // Handle error or return empty list + print('Request failed with status: ${response.statusCode}.'); + return []; + } + } + + //! http://192.168.85.111:9080/popularhotels + + Future> popHotelListapi() async { + var url = Uri.parse('http://192.168.85.111:9080/popularhotels'); + var response = await http.get(url); + if (response.statusCode == 200) { + List hotels = json.decode(response.body); + + print('Response status: ${response.statusCode}'); + print('Response body: ${response.body}'); + return hotels; + } else { + // Handle error or return empty list + print('Request failed with status: ${response.statusCode}.'); + return []; + } + } + @override Widget build(BuildContext context) { return Scaffold( @@ -36,208 +81,324 @@ class _ExploreTripsState extends State { style: kTextStyle.copyWith( color: kTitleColor, fontWeight: FontWeight.bold), ), - ListView.builder( - itemCount: 3, - shrinkWrap: true, - physics: const NeverScrollableScrollPhysics(), - itemBuilder: (_, index) { - return Padding( - padding: const EdgeInsets.only(top: 10.0), - child: Container( - padding: const EdgeInsets.all(10.0), - decoration: BoxDecoration( - borderRadius: BorderRadius.circular(10.0), - color: Colors.white, - ), - child: Row( - children: [ - Stack( - alignment: Alignment.topRight, - children: [ - Image.asset('images/sea.png'), - Padding( - padding: const EdgeInsets.all(4.0), - child: Container( - padding: const EdgeInsets.all(5.0), - decoration: BoxDecoration( - borderRadius: BorderRadius.circular(30.0), - border: Border.all(color: Colors.white.withOpacity(0.5),), - color: Colors.white.withOpacity(0.3), - ), - child: const Icon( - FontAwesomeIcons.heart, - size: 10.0, - color: Colors.white, - ), + const SizedBox(height: 10.0), + FutureBuilder>( + future: getWishListApi(), + builder: (BuildContext context, + AsyncSnapshot> snapshot) { + if (snapshot.connectionState == ConnectionState.waiting) { + return ListView.separated( + shrinkWrap: true, + itemCount: 4, + separatorBuilder: (BuildContext context, int index) { + return SizedBox(height: 10.0); + }, + itemBuilder: (BuildContext context, int index) { + return Padding( + padding: const EdgeInsets.all(8.0), + child: ShimmerLoadingContainer(), + ); + }, + ); + } else if (snapshot.hasError) { + return Text('Error: ${snapshot.error}'); + } else { + List hotels = snapshot.data ?? []; + return ListView.builder( + itemCount: hotels.length, + shrinkWrap: true, + physics: const NeverScrollableScrollPhysics(), + itemBuilder: (_, index) { + var hotel = hotels[index]; + return Padding( + padding: const EdgeInsets.only(top: 10.0), + child: Container( + padding: const EdgeInsets.all(10.0), + decoration: BoxDecoration( + borderRadius: BorderRadius.circular(10.0), + color: Colors.white, + ), + child: Row( + children: [ + Stack( + alignment: Alignment.topRight, + children: [ + Image.asset('images/sea.png'), + Padding( + padding: const EdgeInsets.all(4.0), + child: Container( + padding: const EdgeInsets.all(5.0), + decoration: BoxDecoration( + borderRadius: + BorderRadius.circular(30.0), + border: Border.all( + color: + Colors.white.withOpacity(0.5), + ), + color: Colors.white.withOpacity(0.3), + ), + child: const Icon( + FontAwesomeIcons.heart, + size: 10.0, + color: Colors.white, + ), + ), + ), + ], ), - ), - ], - ), - const SizedBox(width: 5.0), - Column( - crossAxisAlignment: CrossAxisAlignment.start, - children: [ - Text( - 'Cox\'s Bazar', - style: kTextStyle.copyWith( - color: kTitleColor, - fontSize: 18.0, - fontWeight: FontWeight.bold), - ), - const SizedBox(height: 5.0), - Row( - mainAxisAlignment: - MainAxisAlignment.spaceBetween, - children: [ - Image.asset('images/distance.png'), - const SizedBox(width: 5.0), - Text( - '16.3 km', - style: kTextStyle.copyWith( - color: kGreyTextColor), - ), - const SizedBox(width: 90), - RatingBarWidget( - onRatingChanged: null, - itemCount: 1, - inActiveColor: const Color(0xFFFFC60B), - size: 15.0, - ), - const SizedBox(width: 2.0), - Text( - '4.9', - style: - kTextStyle.copyWith(color: kTitleColor), - ) - ], - ), - const SizedBox(height: 5.0), - Text( - 'Start form \$60 per Night', - style: kTextStyle.copyWith(color: kTitleColor), - ), - ], + const SizedBox(width: 5.0), + Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Text( + hotel['name'], + style: kTextStyle.copyWith( + color: kTitleColor, + fontSize: 18.0, + fontWeight: FontWeight.bold), + ), + const SizedBox(height: 5.0), + Row( + mainAxisAlignment: + MainAxisAlignment.spaceBetween, + children: [ + // Image.asset('images/distance.png'), + //const SizedBox(width: 5.0), + Text( + hotel['distance'].toString() + ' km', + style: kTextStyle.copyWith( + color: kGreyTextColor), + ), + const SizedBox(width: 90), + RatingBarWidget( + onRatingChanged: null, + itemCount: 1, + inActiveColor: + const Color(0xFFFFC60B), + size: 15.0, + ), + const SizedBox(width: 2.0), + Text( + hotel['rating'].toString(), + style: kTextStyle.copyWith( + color: kTitleColor), + ) + ], + ), + const SizedBox(height: 5.0), + // Text( + // 'Start form \$60 per Night', + // style: kTextStyle.copyWith( + // color: kTitleColor), + // ), + + RichText( + text: TextSpan( + children: [ + TextSpan( + text: 'Start form ', + style: kTextStyle.copyWith( + color: kGreyTextColor), + ), + TextSpan( + text: '\$${hotel['charges']}', + style: kTextStyle.copyWith( + color: kMainColor), + ), + TextSpan( + text: ' per Night', + style: kTextStyle.copyWith( + color: kGreyTextColor), + ), + ], + )), + ], + ), + ], + ), ), - ], - ), - ), - ); + ); + }, + ); + } }, ), const SizedBox(height: 10.0), Text( 'Popular Place', - style: kTextStyle.copyWith(color: kTitleColor,fontWeight: FontWeight.bold), + style: kTextStyle.copyWith( + color: kTitleColor, fontWeight: FontWeight.bold), ), const SizedBox(height: 10.0), - Column( - children: [ - ListView.builder( - itemCount: 10, - shrinkWrap: true, - physics: const NeverScrollableScrollPhysics(), - itemBuilder: (_, index) { - return Padding( - padding: const EdgeInsets.only(bottom: 10.0), - child: Container( - padding: const EdgeInsets.all(10.0), - width: context.width() / 1.5, - decoration: BoxDecoration( - borderRadius: BorderRadius.circular(20.0), - color: Colors.white, - ), - child: Column( - crossAxisAlignment: CrossAxisAlignment.start, - mainAxisSize: MainAxisSize.min, - children: [ - Stack( - alignment: Alignment.topLeft, - children: [ - Image.asset('images/sea2.png', width: context.width(),fit: BoxFit.cover,), - Padding( - padding: const EdgeInsets.all(4.0), - child: Row( - mainAxisSize: MainAxisSize.min, + FutureBuilder>( + future: popHotelListapi(), + builder: (BuildContext context, + AsyncSnapshot> snapshot) { + if (snapshot.connectionState == ConnectionState.waiting) { + return ListView.separated( + shrinkWrap: true, + itemCount: 4, + separatorBuilder: (BuildContext context, int index) { + return SizedBox(height: 10.0); + }, + itemBuilder: (BuildContext context, int index) { + return Padding( + padding: const EdgeInsets.all(8.0), + child: ShimmerLoadingContainer(), + ); + }, + ); + } else if (snapshot.hasError) { + return Text('Error: ${snapshot.error}'); + } else { + List hotels = snapshot.data ?? []; + return Column( + children: [ + ListView.builder( + itemCount: hotels.length, + shrinkWrap: true, + physics: const NeverScrollableScrollPhysics(), + itemBuilder: (_, index) { + return Padding( + padding: const EdgeInsets.only(bottom: 10.0), + child: Container( + padding: const EdgeInsets.all(10.0), + width: context.width() / 1.5, + decoration: BoxDecoration( + borderRadius: BorderRadius.circular(20.0), + color: Colors.white, + ), + child: Column( + crossAxisAlignment: CrossAxisAlignment.start, + mainAxisSize: MainAxisSize.min, + children: [ + Stack( + alignment: Alignment.topLeft, children: [ - Container( - padding: const EdgeInsets.all(5.0), - decoration: BoxDecoration( - border: Border.all(color: Colors.white), - color: Colors.white.withOpacity(0.3), - borderRadius: const BorderRadius.only( - topLeft: Radius.circular(10.0), - ), + Image.asset( + 'images/sea2.png', + width: context.width(), + fit: BoxFit.cover, + ), + Padding( + padding: const EdgeInsets.all(4.0), + child: Row( + mainAxisSize: MainAxisSize.min, + children: [ + Container( + padding: + const EdgeInsets.all(5.0), + decoration: BoxDecoration( + border: Border.all( + color: Colors.white), + color: Colors.white + .withOpacity(0.3), + borderRadius: + const BorderRadius.only( + topLeft: + Radius.circular(10.0), + ), + ), + child: Text( + '\$99 per Night', + style: kTextStyle.copyWith( + color: Colors.white, + fontSize: 18.0), + ), + ), + const Spacer(), + Container( + alignment: Alignment.topRight, + decoration: BoxDecoration( + borderRadius: + BorderRadius.circular( + 30.0), + border: Border.all( + color: Colors.white + .withOpacity(0.5), + ), + color: Colors.white + .withOpacity(0.3), + ), + child: const Padding( + padding: EdgeInsets.all(5.0), + child: Icon( + FontAwesomeIcons.heart, + size: 18.0, + color: Colors.white, + ), + ), + ), + ], ), - child: Text( - '\$99 per Night', - style: kTextStyle.copyWith( - color: Colors.white, - fontSize: 18.0), + ), + ], + ), + const SizedBox(height: 10.0), + Row( + children: [ + Text( + hotels[index]['name'], + style: kTextStyle.copyWith( + color: kTitleColor, + fontSize: 18.0, + fontWeight: FontWeight.bold), + ), + Text( + " , ", + style: kTextStyle.copyWith( + color: kTitleColor, + fontSize: 18.0, + fontWeight: FontWeight.bold), + ), + Text( + hotels[index]['place'], + style: kTextStyle.copyWith( + color: kTitleColor, + fontSize: 18.0, + fontWeight: FontWeight.bold), + ), + ], + ), + Row( + children: [ + const Icon( + Icons.location_on, + color: Color(0xFFFF8748), + size: 18.0, + ), + Text( + hotels[index]['distance'].toString() + + ' km away', + style: kTextStyle.copyWith( + color: kGreyTextColor, ), ), const Spacer(), Container( - alignment: Alignment.topRight, decoration: BoxDecoration( - borderRadius: BorderRadius.circular(30.0), - border: Border.all(color: Colors.white.withOpacity(0.5),), - color: Colors.white.withOpacity(0.3), + borderRadius: + BorderRadius.circular(30.0), + color: kMainColor, ), - child: const Padding( - padding: EdgeInsets.all(5.0), - child: Icon( - FontAwesomeIcons.heart, - size: 18.0, - color: Colors.white, - ), + child: Padding( + padding: const EdgeInsets.all(10.0), + child: + Image.asset('images/arrow.png'), ), ), ], ), - ), - ], - ), - const SizedBox(height: 10.0), - Text( - 'Rio de Janeiro, Brazil', - style: kTextStyle.copyWith( - color: kTitleColor, - fontSize: 18.0, - fontWeight: FontWeight.bold), - ), - Row( - children: [ - const Icon( - Icons.location_on, - color: Color(0xFFFF8748), - size: 18.0, - ), - Text( - '10,984 kilometres away', - style: kTextStyle.copyWith( - color: kGreyTextColor, - ), - ), - const Spacer(), - Container( - decoration: BoxDecoration( - borderRadius: BorderRadius.circular(30.0), - color: kMainColor, - ), - child: Padding( - padding: const EdgeInsets.all(10.0), - child: Image.asset('images/arrow.png'), - ), - ), - ], + ], + ), ), - ], - ), + ); + }, ), - ); - }, - ), - ], + ], + ); + } + }, ), ], ), diff --git a/Frontend/lib/Screen/Home/Trips/trips.dart b/Frontend/lib/Screen/Home/Trips/trips.dart index d2ec60d..72c015d 100644 --- a/Frontend/lib/Screen/Home/Trips/trips.dart +++ b/Frontend/lib/Screen/Home/Trips/trips.dart @@ -1,7 +1,10 @@ +import 'dart:convert'; + import 'package:flutter/material.dart'; import 'package:hotel_booking/Screen/Home/Trips/explore_trips.dart'; import 'package:hotel_booking/constant.dart'; import 'package:nb_utils/nb_utils.dart'; +import 'package:http/http.dart' as http; class Trips extends StatefulWidget { const Trips({Key? key}) : super(key: key); @@ -11,6 +14,10 @@ class Trips extends StatefulWidget { } class _TripsState extends State { +//! http://192.168.85.111:9080/places + + + @override Widget build(BuildContext context) { return Scaffold( diff --git a/Frontend/lib/Screen/Home/booking_list.dart b/Frontend/lib/Screen/Home/booking_list.dart index 81c3953..fe61833 100644 --- a/Frontend/lib/Screen/Home/booking_list.dart +++ b/Frontend/lib/Screen/Home/booking_list.dart @@ -1,7 +1,12 @@ +import 'dart:convert'; + import 'package:flutter/material.dart'; import 'package:nb_utils/nb_utils.dart'; import '../../constant.dart'; +import 'package:http/http.dart' as http; + +import '../loading/loading.dart'; class BookingList extends StatefulWidget { const BookingList({Key? key}) : super(key: key); @@ -17,18 +22,16 @@ class _BookingListState extends State { ]; String selectedStatus = 'Last Month'; - List selectStatus =[ + List selectStatus = [ 'Completed', 'Confirmed', 'Pending', ]; - - List color =[ + List color = [ const Color(0xFF0E79F2), const Color(0xFF5BB26E), const Color(0xFFFF8748), - ]; DropdownButton getStatus() { @@ -51,6 +54,42 @@ class _BookingListState extends State { ); } + //! http://192.168.85.111:9080/bookings + + Future> getWishListApi() async { + var url = Uri.parse('http://192.168.85.111:9080/bookings'); + var response = await http.get(url); + if (response.statusCode == 200) { + List hotels = json.decode(response.body); + + print('Response status: ${response.statusCode}'); + print('Response body: ${response.body}'); + return hotels; + } else { + // Handle error or return empty list + print('Request failed with status: ${response.statusCode}.'); + return []; + } + } + + +Stream> futureGetApi() async* { + var url = Uri.parse('http://192.168.85.111:9080/bookings'); + var response = await http.get(url); + if (response.statusCode == 200) { + List hotels = json.decode(response.body); + + print('Response status: ${response.statusCode}'); + print('Response body: ${response.body}'); + yield hotels; + } else { + // Handle error or return empty list + print('Request failed with status: ${response.statusCode}.'); + yield []; + } + } + + @override Widget build(BuildContext context) { return Scaffold( @@ -71,7 +110,10 @@ class _BookingListState extends State { builder: (FormFieldState field) { return InputDecorator( decoration: InputDecoration( - contentPadding: const EdgeInsets.only(left: 10.0, right: 10.0,), + contentPadding: const EdgeInsets.only( + left: 10.0, + right: 10.0, + ), floatingLabelBehavior: FloatingLabelBehavior.never, labelStyle: kTextStyle, enabledBorder: outlineInputBorder().copyWith( @@ -92,123 +134,391 @@ class _BookingListState extends State { body: SingleChildScrollView( child: Column( children: [ - ListView.builder( - shrinkWrap: true, - physics: const NeverScrollableScrollPhysics(), - itemCount: 3, - itemBuilder: (_, index) { - return Padding( - padding: const EdgeInsets.only(top: 10.0), - child: Column( - children: [ - ListView.builder( - itemCount: 3, - shrinkWrap: true, //! - physics: const NeverScrollableScrollPhysics(), - itemBuilder: (_, index) { - return Padding( - padding: const EdgeInsets.all(10.0), - child: Container( - padding: const EdgeInsets.all(10.0), - decoration: BoxDecoration( - borderRadius: BorderRadius.circular(20.0), - color: Colors.white, - ), - child: Column( - crossAxisAlignment: CrossAxisAlignment.start, - mainAxisSize: MainAxisSize.min, - children: [ - Stack( - alignment: Alignment.topLeft, + // ListView.builder( + // shrinkWrap: true, + // physics: const NeverScrollableScrollPhysics(), + // itemCount: 3, + // itemBuilder: (_, index) { + // return Padding( + // padding: const EdgeInsets.only(top: 10.0), + // child: Column( + // children: [ + // ListView.builder( + // itemCount: 3, + // shrinkWrap: true, //! + // physics: const NeverScrollableScrollPhysics(), + // itemBuilder: (_, index) { + // return Padding( + // padding: const EdgeInsets.all(10.0), + // child: Container( + // padding: const EdgeInsets.all(10.0), + // decoration: BoxDecoration( + // borderRadius: BorderRadius.circular(20.0), + // color: Colors.white, + // ), + // child: Column( + // crossAxisAlignment: CrossAxisAlignment.start, + // mainAxisSize: MainAxisSize.min, + // children: [ + // Stack( + // alignment: Alignment.topLeft, + // children: [ + // Image.asset( + // 'images/indoor4.png', + // width: context.width(), + // fit: BoxFit.cover, + // ), + // Padding( + // padding: const EdgeInsets.all(4.0), + // child: Row( + // children: [ + // Container( + // padding: + // const EdgeInsets.all(5.0), + // decoration: BoxDecoration( + // border: Border.all( + // color: Colors.white), + // color: Colors.white, + // borderRadius: + // const BorderRadius.only( + // topLeft: + // Radius.circular(10.0), + // ), + // ), + // child: Text( + // 'Up to -30%', + // style: kTextStyle.copyWith( + // color: const Color(0xFFFF8748), + // ), + // ), + // ), + // const Spacer(), + // ], + // ), + // ), + // ], + // ), + // const SizedBox(height: 5.0), + // Text( + // 'Classic Flat Room', + // style: kTextStyle.copyWith( + // color: kTitleColor, + // fontSize: 18.0, + // fontWeight: FontWeight.bold), + // ), + // const SizedBox(height: 10.0), + // Row( + // children: [ + // Text( + // '\$399', + // style: kTextStyle.copyWith( + // color: const Color(0xFFFF8748), + // fontSize: 18.0, + // fontWeight: FontWeight.bold, + // ), + // ), + // Text( + // ' / 4 Night', + // style: kTextStyle.copyWith( + // color: kGreyTextColor), + // ), + // const Spacer(), + // Container( + // decoration: BoxDecoration( + // borderRadius: + // BorderRadius.circular(10.0), + // color: color[index].withOpacity(0.1), + // ), + // child: Padding( + // padding: const EdgeInsets.only(left: 20.0, right: 20.0, top: 10.0, bottom: 10.0), + // child: Text( + // selectStatus[index], + // style: kTextStyle.copyWith( + // color: color[index], + // fontSize: 18.0), + // ), + // ), + // ), + // ], + // ), + // ], + // ), + // ), + // ); + // }, + // ), + // ], + // ), + // ); + // }), + + FutureBuilder>( + future: getWishListApi(), + builder: (BuildContext context, + AsyncSnapshot> snapshot) { + if (snapshot.connectionState == ConnectionState.waiting) { + return ListView.separated( + shrinkWrap: true, + itemCount: 4, + separatorBuilder: (BuildContext context, int index) { + return SizedBox(height: 10.0); + }, + itemBuilder: (BuildContext context, int index) { + return Padding( + padding: const EdgeInsets.all(8.0), + child: ShimmerLoadingContainer(), + ); + }, + ); + } else if (snapshot.hasError) { + return Text('Error: ${snapshot.error}'); + } else { + List hotels = snapshot.data ?? []; + return ListView.builder( + itemCount: 3, + shrinkWrap: true, + physics: const NeverScrollableScrollPhysics(), + itemBuilder: (_, index) { + var hotel = hotels[index]; + return Padding( + padding: const EdgeInsets.all(10.0), + child: Container( + padding: const EdgeInsets.all(10.0), + decoration: BoxDecoration( + borderRadius: BorderRadius.circular(20.0), + color: Colors.white, + ), + child: Column( + crossAxisAlignment: CrossAxisAlignment.start, + mainAxisSize: MainAxisSize.min, + children: [ + Stack( + alignment: Alignment.topLeft, + children: [ + Image.asset( + 'images/indoor4.png', + width: context.width(), + fit: BoxFit.cover, + ), + Padding( + padding: const EdgeInsets.all(4.0), + child: Row( children: [ - Image.asset( - 'images/indoor4.png', - width: context.width(), - fit: BoxFit.cover, - ), - Padding( - padding: const EdgeInsets.all(4.0), - child: Row( - children: [ - Container( - padding: - const EdgeInsets.all(5.0), - decoration: BoxDecoration( - border: Border.all( - color: Colors.white), - color: Colors.white, - borderRadius: - const BorderRadius.only( - topLeft: - Radius.circular(10.0), - ), - ), - child: Text( - 'Up to -30%', - style: kTextStyle.copyWith( - color: const Color(0xFFFF8748), - ), - ), - ), - const Spacer(), - ], + Container( + padding: const EdgeInsets.all(5.0), + decoration: BoxDecoration( + border: + Border.all(color: Colors.white), + color: Colors.white, + borderRadius: + const BorderRadius.only( + topLeft: Radius.circular(10.0), + ), + ), + child: Text( + 'Up to -30%', + style: kTextStyle.copyWith( + color: const Color(0xFFFF8748), + ), ), ), + const Spacer(), ], ), - const SizedBox(height: 5.0), - Text( - 'Classic Flat Room', - style: kTextStyle.copyWith( - color: kTitleColor, - fontSize: 18.0, - fontWeight: FontWeight.bold), + ), + ], + ), + const SizedBox(height: 5.0), + Text( + hotel['roomType'], + style: kTextStyle.copyWith( + color: kTitleColor, + fontSize: 18.0, + fontWeight: FontWeight.bold), + ), + const SizedBox(height: 10.0), + Row( + children: [ + Text( + '\$${hotel['price']}', + style: kTextStyle.copyWith( + color: const Color(0xFFFF8748), + fontSize: 18.0, + fontWeight: FontWeight.bold, + ), + ), + Text( + ' / 4 Night', + style: kTextStyle.copyWith( + color: kGreyTextColor), + ), + const Spacer(), + Container( + decoration: BoxDecoration( + borderRadius: BorderRadius.circular(10.0), + color: color[index].withOpacity(0.1), + ), + child: Padding( + padding: const EdgeInsets.only( + left: 20.0, + right: 20.0, + top: 10.0, + bottom: 10.0), + child: Text( + hotel['status'], + style: kTextStyle.copyWith( + color: color[index], + fontSize: 18.0), + ), ), - const SizedBox(height: 10.0), - Row( + ), + ], + ), + ], + ), + ), + ); + }, + ); + } + }, + ), + + + //! want live changes using strema builder and making the same as in future builder + + StreamBuilder>( + stream: futureGetApi(), + builder: (BuildContext context, + AsyncSnapshot> snapshot) { + if (snapshot.connectionState == ConnectionState.waiting) { + return ListView.separated( + shrinkWrap: true, + itemCount: 4, + separatorBuilder: (BuildContext context, int index) { + return SizedBox(height: 10.0); + }, + itemBuilder: (BuildContext context, int index) { + return Padding( + padding: const EdgeInsets.all(8.0), + child: ShimmerLoadingContainer(), + ); + }, + ); + } else if (snapshot.hasError) { + return Text('Error: ${snapshot.error}'); + } else { + List hotels = snapshot.data ?? []; + return ListView.builder( + itemCount: 3, + shrinkWrap: true, + physics: const NeverScrollableScrollPhysics(), + itemBuilder: (_, index) { + var hotel = hotels[index]; + return Padding( + padding: const EdgeInsets.all(10.0), + child: Container( + padding: const EdgeInsets.all(10.0), + decoration: BoxDecoration( + borderRadius: BorderRadius.circular(20.0), + color: Colors.white, + ), + child: Column( + crossAxisAlignment: CrossAxisAlignment.start, + mainAxisSize: MainAxisSize.min, + children: [ + Stack( + alignment: Alignment.topLeft, + children: [ + Image.asset( + 'images/indoor4.png', + width: context.width(), + fit: BoxFit.cover, + ), + Padding( + padding: const EdgeInsets.all(4.0), + child: Row( children: [ - Text( - '\$399', - style: kTextStyle.copyWith( - color: const Color(0xFFFF8748), - fontSize: 18.0, - fontWeight: FontWeight.bold, - ), - ), - Text( - ' / 4 Night', - style: kTextStyle.copyWith( - color: kGreyTextColor), - ), - const Spacer(), Container( + padding: const EdgeInsets.all(5.0), decoration: BoxDecoration( + border: + Border.all(color: Colors.white), + color: Colors.white, borderRadius: - BorderRadius.circular(10.0), - color: color[index].withOpacity(0.1), + const BorderRadius.only( + topLeft: Radius.circular(10.0), + ), ), - child: Padding( - padding: const EdgeInsets.only(left: 20.0, right: 20.0, top: 10.0, bottom: 10.0), - child: Text( - selectStatus[index], - style: kTextStyle.copyWith( - color: color[index], - fontSize: 18.0), + child: Text( + 'Up to -30%', + style: kTextStyle.copyWith( + color: const Color(0xFFFF8748), ), ), ), + const Spacer(), ], ), - ], - ), + ), + ], + ), + const SizedBox(height: 5.0), + Text( + hotel['roomType'], + style: kTextStyle.copyWith( + color: kTitleColor, + fontSize: 18.0, + fontWeight: FontWeight.bold), ), - ); - }, + const SizedBox(height: 10.0), + Row( + children: [ + Text( + '\$${hotel['price']}', + style: kTextStyle.copyWith( + color: const Color(0xFFFF8748), + fontSize: 18.0, + fontWeight: FontWeight.bold, + ), + ), + Text( + ' / 4 Night', + style: kTextStyle.copyWith( + color: kGreyTextColor), + ), + const Spacer(), + Container( + decoration: BoxDecoration( + borderRadius: BorderRadius.circular(10.0), + color: color[index].withOpacity(0.1), + ), + child: Padding( + padding: const EdgeInsets.only( + left: 20.0, + right: 20.0, + top: 10.0, + bottom: 10.0), + child: Text( + hotel['status'], + style: kTextStyle.copyWith( + color: color[index], + fontSize: 18.0), + ), + ), + ), + ], + ), + ], + ), ), - ], - ), + ); + }, ); - }), + } + }, + ), + ], ), diff --git a/Frontend/lib/Screen/Home/home.dart b/Frontend/lib/Screen/Home/home.dart index a16d365..11c61cf 100644 --- a/Frontend/lib/Screen/Home/home.dart +++ b/Frontend/lib/Screen/Home/home.dart @@ -7,6 +7,7 @@ import 'package:hotel_booking/Screen/Home/booking_list.dart'; import 'package:hotel_booking/Screen/Home/wish_list.dart'; import '../../constant.dart'; import 'home_screen.dart'; +import 'package:vibration/vibration.dart'; class Home extends StatefulWidget { const Home({Key? key}) : super(key: key); diff --git a/Frontend/lib/Screen/Home/home_screen.dart b/Frontend/lib/Screen/Home/home_screen.dart index 52be5da..0b3d2f2 100644 --- a/Frontend/lib/Screen/Home/home_screen.dart +++ b/Frontend/lib/Screen/Home/home_screen.dart @@ -1,4 +1,5 @@ import 'dart:async'; +import 'dart:convert'; import 'package:carousel_slider/carousel_slider.dart'; import 'package:flutter/material.dart'; @@ -13,6 +14,10 @@ import 'package:hotel_booking/Screen/Home/Search/search.dart'; import 'package:hotel_booking/constant.dart'; import 'package:nb_utils/nb_utils.dart'; import 'Search/Filter/filter.dart'; +import 'package:scroll_to_top/scroll_to_top.dart'; + +import 'package:http/http.dart' as http; +import 'package:shimmer/shimmer.dart'; class HomeScreen extends StatefulWidget { const HomeScreen({Key? key}) : super(key: key); @@ -56,17 +61,71 @@ class _HomeScreenState extends State { // Add more images URLs to this list ]; + ScrollController _scrollController = ScrollController(); + @override void initState() { // TODO: implement initState - Timer.periodic(Duration(seconds: 3), (timer) { + Timer.periodic(Duration(seconds: 4), (timer) { controller.changeIndex(); }); + + _scrollController.addListener(() { + if (_scrollController.position.pixels == + _scrollController.position.maxScrollExtent) { + print('reached end'); + } + }); + super.initState(); } CurosalController controller = Get.put(CurosalController()); + + //! calling post api for showing hotels + + Future> getWishListApi() async { + + var url = Uri.parse('http://192.168.85.111:9080/rechotels'); + var response = await http.get(url); + if (response.statusCode == 200) { + List hotels = json.decode(response.body); + + print('Response status: ${response.statusCode}'); + print('Response body: ${response.body}'); + return hotels; + } else { + // Handle error or return empty list + print('Request failed with status: ${response.statusCode}.'); + return []; + } + } + + String name = 'User'; + + Future> profileDataApi() async { + + var url = Uri.parse('http://192.168.85.111:9080/profile'); + var response = await http.get(url); + if (response.statusCode == 200) { + List profile = json.decode(response.body); + + print('Response status: ${response.statusCode}'); + print('Response body: ${response.body}'); + + //! update name + setState(() { + name = profile[0]['name']; + }); + return profile; + } else { + // Handle error or return empty list + print('Request failed with status: ${response.statusCode}.'); + return []; + } + } + @override Widget build(BuildContext context) { return SafeArea( @@ -90,7 +149,7 @@ class _HomeScreenState extends State { crossAxisAlignment: CrossAxisAlignment.start, children: [ Text( - 'Hello, Korim', + 'Hello, Hemant', style: kTextStyle.copyWith(color: kGreyTextColor), ), Text( @@ -181,7 +240,9 @@ class _HomeScreenState extends State { ], ).onTap( () { - const Dhaka().launch( + Dhaka( + cityname: categoryList[i], + ).launch( context); //! here we have same launch for every screen }, ); @@ -213,7 +274,7 @@ class _HomeScreenState extends State { options: CarouselOptions( autoPlay: true, enlargeCenterPage: true, - // height: 200, // Fixed height + height: 150, // Fixed height aspectRatio: 16 / 9, // Aspect ratio for the width autoPlayInterval: Duration(seconds: 3), autoPlayAnimationDuration: Duration(milliseconds: 800), @@ -238,42 +299,42 @@ class _HomeScreenState extends State { ), ), ), - Obx(() => Positioned( - bottom: 70, - left: 100, - child: Row( - mainAxisAlignment: - MainAxisAlignment.spaceEvenly, - children: [ - Icon(Icons.circle, - color: controller.currentIndex == 0 - ? Colors.black - : Colors.white, - size: 10), - Icon(Icons.circle, - color: controller.currentIndex == 1 - ? Colors.black - : Colors.white, - size: 10), - Icon(Icons.circle, - color: controller.currentIndex == 2 - ? Colors.black - : Colors.white, - size: 10), - Icon(Icons.circle, - color: controller.currentIndex == 4 - ? Colors.black - : Colors.white, - size: 10), - ], - ), - )) + // Obx(() => Positioned( + // bottom: 70, + // left: 100, + // child: Row( + // mainAxisAlignment: + // MainAxisAlignment.spaceEvenly, + // children: [ + // Icon(Icons.circle, + // color: controller.currentIndex == 0 + // ? Colors.black + // : Colors.white, + // size: 10), + // Icon(Icons.circle, + // color: controller.currentIndex == 1 + // ? Colors.black + // : Colors.white, + // size: 10), + // Icon(Icons.circle, + // color: controller.currentIndex == 2 + // ? Colors.black + // : Colors.white, + // size: 10), + // Icon(Icons.circle, + // color: controller.currentIndex == 4 + // ? Colors.black + // : Colors.white, + // size: 10), + // ], + // ), + // )) ], ), ), ), - const SizedBox(height: 20.0), + // const SizedBox(height: 5.0), Text( 'Recomended for your next trip', style: kTextStyle.copyWith( @@ -281,109 +342,149 @@ class _HomeScreenState extends State { fontSize: 18.0, fontWeight: FontWeight.bold), ), - const SizedBox(height: 10.0), + const SizedBox(height: 15.0), //! here recommend for your next trip - HorizontalList( - padding: EdgeInsets.zero, - itemCount: 10, - itemBuilder: (_, i) { - return Container( - padding: const EdgeInsets.all(10.0), - width: MediaQuery.of(context).size.width / 1.5, - decoration: BoxDecoration( - borderRadius: BorderRadius.circular(20.0), - color: Colors.white, - ), - child: Column( - crossAxisAlignment: CrossAxisAlignment.start, - mainAxisSize: MainAxisSize.min, - children: [ - Stack( - alignment: Alignment.topLeft, - children: [ - Image.asset( - 'images/banner5.png'), //! background image - Padding( - padding: const EdgeInsets.all(4.0), - child: Row( - children: [ - Container( - padding: const EdgeInsets.all(5.0), - decoration: BoxDecoration( - border: Border.all(color: Colors.white), - color: Colors.white.withOpacity(0.3), - borderRadius: const BorderRadius.only( - topLeft: Radius.circular(10.0), - ), - ), - child: Text( - '\$35 per Night', - style: kTextStyle.copyWith( - color: Colors.white, - fontSize: 18.0), - ), - ), - const Spacer(), - Container( - decoration: BoxDecoration( - borderRadius: - BorderRadius.circular(30.0), - border: Border.all(color: Colors.white), - color: Colors.white.withOpacity(0.3), + + FutureBuilder>( + future: getWishListApi(), + builder: (BuildContext context, + AsyncSnapshot> snapshot) { + if (snapshot.connectionState == ConnectionState.waiting) { + return ListView.separated( + physics: NeverScrollableScrollPhysics(), + shrinkWrap: true, + itemCount: 2, + separatorBuilder: (BuildContext context, int index) { + return SizedBox(height: 10.0); + }, + itemBuilder: (BuildContext context, int index) { + return ShimmerLoadingContainer(); + }, + ); + } else if (snapshot.hasError) { + return Text('Error: ${snapshot.error}'); + } else { + List hotels = snapshot.data ?? []; + return ListView.builder( + //scrollDirection: Axis.horizontal, + itemCount: hotels.length, + shrinkWrap: true, + physics: const NeverScrollableScrollPhysics(), + itemBuilder: (_, index) { + var hotel = hotels[index]; + return Padding( + padding: const EdgeInsets.only(bottom: 10.0), + child: Container( + padding: const EdgeInsets.all(10.0), + width: MediaQuery.of(context).size.width, + decoration: BoxDecoration( + borderRadius: BorderRadius.circular(20.0), + color: Colors.white, + ), + child: Column( + crossAxisAlignment: CrossAxisAlignment.start, + mainAxisSize: MainAxisSize.min, + children: [ + Stack( + alignment: Alignment.topLeft, + children: [ + // Replace with your network image + Image.asset( + 'images/banner5.png', + width: + MediaQuery.of(context).size.width, + fit: BoxFit.cover, ), - child: const Padding( - padding: EdgeInsets.all(5.0), - child: Icon( - FontAwesomeIcons.heart, - size: 15.0, - color: Colors.white, + Padding( + padding: const EdgeInsets.all(8.0), + child: Row( + children: [ + Expanded( + child: Text( + '${hotel['name']} - \$99 per Night', + style: kTextStyle.copyWith( + color: Colors.white, + fontSize: 18.0), + overflow: TextOverflow.ellipsis, + ), + ), + Container( + decoration: BoxDecoration( + borderRadius: + BorderRadius.circular(30.0), + border: Border.all( + color: Colors.white), + color: Colors.white + .withOpacity(0.3), + ), + child: const Padding( + padding: EdgeInsets.all(5.0), + child: Icon( + FontAwesomeIcons.solidHeart, + size: 15.0, + color: Color(0xFFFF8748), + ), + ), + ), + ], ), ), - ), - ], - ), - ), - ], - ), - const SizedBox(height: 5.0), - Text( - 'Pan Pacific Sonargaon Dhaka', - style: kTextStyle.copyWith( - color: kTitleColor, - fontSize: 18.0, - fontWeight: FontWeight.bold), - ), - Row( - children: [ - const Icon( - Icons.location_on, - color: Color(0xFFFF8748), - size: 18.0, - ), - Text( - '2,984 kilometres away', - style: kTextStyle.copyWith( - color: kGreyTextColor, - ), - ), - const Spacer(), - Container( - decoration: BoxDecoration( - borderRadius: BorderRadius.circular(30.0), - color: kMainColor, - ), - child: Padding( - padding: const EdgeInsets.all(10.0), - child: Image.asset('images/arrow.png'), - ), + ], + ), + const SizedBox(height: 10.0), + Text( + hotel['name'], + style: kTextStyle.copyWith( + color: kTitleColor, + fontSize: 18.0, + fontWeight: FontWeight.bold), + ), + SizedBox(height: 4.0), + Row( + children: [ + Row( + children: [ + Icon( + Icons.location_on, + color: Color(0xFFFF8748), + size: 18.0, + ), + Text( + hotel['location'], + style: kTextStyle.copyWith( + color: kGreyTextColor), + ), + ], + ), + Spacer(), + Row( + children: [ + Icon( + Icons.star, + color: Color(0xFFFF8748), + size: 18.0, + ), + SizedBox( + width: 5, + ), + Text( + hotel['rating'].toString(), + style: kTextStyle.copyWith( + color: kGreyTextColor), + ), + ], + ) + ], + ), + ], ), - ], - ), - ], - ), - ).onTap( - () => const Hotel().launch(context), - ); + ).onTap( + () => const Hotel().launch(context), + ), + ); + }, + ); + } }, ), const SizedBox(height: 20.0), @@ -509,3 +610,38 @@ class _HomeScreenState extends State { ); } } + +class ShimmerLoadingContainer extends StatelessWidget { + final double width; + final double height; + + const ShimmerLoadingContainer({ + Key? key, + this.width = 500.0, + this.height = 200.0, + }) : super(key: key); + + @override + Widget build(BuildContext context) { + return Shimmer.fromColors( + baseColor: Colors.grey[300]!, + highlightColor: Colors.grey[100]!, + child: Container( + width: width, + height: height, + decoration: BoxDecoration( + color: Colors.grey[300], + borderRadius: BorderRadius.circular(30), + // boxShadow: [ + // BoxShadow( + // color: Colors.grey.withOpacity(0.5), + // spreadRadius: 5, + // blurRadius: 7, + // offset: Offset(0, 3), + // ), + // ], + ), + ), + ); + } +} diff --git a/Frontend/lib/Screen/Home/wish_list.dart b/Frontend/lib/Screen/Home/wish_list.dart index 3eb2284..da0485e 100644 --- a/Frontend/lib/Screen/Home/wish_list.dart +++ b/Frontend/lib/Screen/Home/wish_list.dart @@ -7,6 +7,8 @@ import 'package:hotel_booking/constant.dart'; import 'package:nb_utils/nb_utils.dart'; import 'package:http/http.dart' as http; +import 'package:shimmer/shimmer.dart'; +import 'package:vibration/vibration.dart'; class WishList extends StatefulWidget { const WishList({Key? key}) : super(key: key); @@ -63,109 +65,124 @@ class _WishListState extends State { shrinkWrap: true, physics: const NeverScrollableScrollPhysics(), itemBuilder: (_, index) { - return Padding( - padding: const EdgeInsets.only(bottom: 10.0), - child: Container( - padding: const EdgeInsets.all(10.0), - width: context.width() / 1.5, - decoration: BoxDecoration( - borderRadius: BorderRadius.circular(20.0), - color: Colors.white, - ), - child: Column( - crossAxisAlignment: CrossAxisAlignment.start, - mainAxisSize: MainAxisSize.min, - children: [ - Stack( - alignment: Alignment.topLeft, + return InkWell( + onTap: () async { + bool? canVibrate = await Vibration.hasVibrator(); + if (canVibrate == true) { + Vibration.vibrate(); + + Navigator.push( + context, + MaterialPageRoute(builder: (context) => Hotel()), + ); + } + }, + child: Padding( + padding: const EdgeInsets.only(bottom: 10.0), + child: Container( + padding: const EdgeInsets.all(10.0), + width: context.width() / 1.5, + decoration: BoxDecoration( + borderRadius: BorderRadius.circular(20.0), + color: Colors.white, + ), + child: Column( + crossAxisAlignment: CrossAxisAlignment.start, + mainAxisSize: MainAxisSize.min, children: [ - Image.asset( - 'images/banner6.png', - width: context.width(), - fit: BoxFit.cover, - ), - Padding( - padding: const EdgeInsets.all(4.0), - child: Row( - mainAxisSize: MainAxisSize.min, //! - children: [ - Container( - padding: const EdgeInsets.all(5.0), - decoration: BoxDecoration( - border: Border.all(color: Colors.white), - color: Colors.white.withOpacity(0.3), - borderRadius: const BorderRadius.only( - topLeft: Radius.circular(10.0), + Stack( + alignment: Alignment.topLeft, + children: [ + Image.asset( + 'images/banner6.png', + width: context.width(), + fit: BoxFit.cover, + ), + Padding( + padding: const EdgeInsets.all(4.0), + child: Row( + mainAxisSize: MainAxisSize.min, //! + children: [ + Container( + padding: const EdgeInsets.all(5.0), + decoration: BoxDecoration( + border: + Border.all(color: Colors.white), + color: + Colors.white.withOpacity(0.3), + borderRadius: + const BorderRadius.only( + topLeft: Radius.circular(10.0), + ), + ), + child: Text( + '\$99 per Night', + style: kTextStyle.copyWith( + color: Colors.white, + fontSize: 18.0), + ), ), - ), - child: Text( - '\$99 per Night', - style: kTextStyle.copyWith( - color: Colors.white, - fontSize: 18.0), - ), - ), - const Spacer(), //! - Container( - decoration: BoxDecoration( - borderRadius: - BorderRadius.circular(30.0), - border: Border.all(color: Colors.white), - color: Colors.white.withOpacity(0.3), - ), - child: const Padding( - padding: EdgeInsets.all(5.0), - child: Icon( - FontAwesomeIcons.solidHeart, - size: 15.0, - color: Color(0xFFFF8748), + const Spacer(), //! + Container( + decoration: BoxDecoration( + borderRadius: + BorderRadius.circular(30.0), + border: + Border.all(color: Colors.white), + color: + Colors.white.withOpacity(0.3), + ), + child: const Padding( + padding: EdgeInsets.all(5.0), + child: Icon( + FontAwesomeIcons.solidHeart, + size: 15.0, + color: Color(0xFFFF8748), + ), + ), ), - ), + ], ), - ], - ), - ), - ], - ), - const SizedBox(height: 10.0), - Text( - 'Sandy Hill Beach, West Sonadia', - style: kTextStyle.copyWith( - color: kTitleColor, - fontSize: 18.0, - fontWeight: FontWeight.bold), - ), - Row( - children: [ - const Icon( - Icons.location_on, - color: Color(0xFFFF8748), - size: 18.0, + ), + ], ), + const SizedBox(height: 10.0), Text( - '2,984 kilometres away', + 'Sandy Hill Beach, West Sonadia', style: kTextStyle.copyWith( - color: kGreyTextColor, - ), + color: kTitleColor, + fontSize: 18.0, + fontWeight: FontWeight.bold), ), - const Spacer(), - Container( - decoration: BoxDecoration( - borderRadius: BorderRadius.circular(30.0), - color: kMainColor, - ), - child: Padding( - padding: const EdgeInsets.all(10.0), - child: Image.asset('images/arrow.png'), - ), + Row( + children: [ + const Icon( + Icons.location_on, + color: Color(0xFFFF8748), + size: 18.0, + ), + Text( + '2,984 kilometres away', + style: kTextStyle.copyWith( + color: kGreyTextColor, + ), + ), + const Spacer(), + Container( + decoration: BoxDecoration( + borderRadius: BorderRadius.circular(30.0), + color: kMainColor, + ), + child: Padding( + padding: const EdgeInsets.all(10.0), + child: Image.asset('images/arrow.png'), + ), + ), + ], ), ], ), - ], - ), - ).onTap( - () => const Hotel().launch(context), - ), + )), ); }, ), @@ -178,7 +195,19 @@ class _WishListState extends State { builder: (BuildContext context, AsyncSnapshot> snapshot) { if (snapshot.connectionState == ConnectionState.waiting) { - return const Center(child: CircularProgressIndicator()); + return ListView.separated( + physics: NeverScrollableScrollPhysics(), + shrinkWrap: true, + itemCount: 4, + separatorBuilder: (BuildContext context, int index) { + return SizedBox( + height: 10, + ); + }, + itemBuilder: (BuildContext context, int index) { + return ShimmerLoadingContainer(); + }, + ); } else if (snapshot.hasError) { return Text('Error: ${snapshot.error}'); } else { @@ -310,3 +339,38 @@ class _WishListState extends State { ); } } + +class ShimmerLoadingContainer extends StatelessWidget { + final double width; + final double height; + + const ShimmerLoadingContainer({ + Key? key, + this.width = 500.0, + this.height = 200.0, + }) : super(key: key); + + @override + Widget build(BuildContext context) { + return Shimmer.fromColors( + baseColor: Colors.grey[300]!, + highlightColor: Colors.grey[100]!, + child: Container( + width: width, + height: height, + decoration: BoxDecoration( + color: Colors.grey[300], + borderRadius: BorderRadius.circular(30), + // boxShadow: [ + // BoxShadow( + // color: Colors.grey.withOpacity(0.5), + // spreadRadius: 5, + // blurRadius: 7, + // offset: Offset(0, 3), + // ), + // ], + ), + ), + ); + } +} diff --git a/Frontend/lib/Screen/loading/loading.dart b/Frontend/lib/Screen/loading/loading.dart new file mode 100644 index 0000000..5584738 --- /dev/null +++ b/Frontend/lib/Screen/loading/loading.dart @@ -0,0 +1,37 @@ +import 'package:flutter/material.dart'; +import 'package:shimmer/shimmer.dart'; + +class ShimmerLoadingContainer extends StatelessWidget { + final double width; + final double height; + + const ShimmerLoadingContainer({ + Key? key, + this.width = 500.0, + this.height = 200.0, + }) : super(key: key); + + @override + Widget build(BuildContext context) { + return Shimmer.fromColors( + baseColor: Colors.grey[300]!, + highlightColor: Colors.grey[100]!, + child: Container( + width: width, + height: height, + decoration: BoxDecoration( + color: Colors.grey[300], + borderRadius: BorderRadius.circular(30), + // boxShadow: [ + // BoxShadow( + // color: Colors.grey.withOpacity(0.5), + // spreadRadius: 5, + // blurRadius: 7, + // offset: Offset(0, 3), + // ), + // ], + ), + ), + ); + } +} diff --git a/Frontend/lib/main.dart b/Frontend/lib/main.dart index dd9156b..a001b6a 100644 --- a/Frontend/lib/main.dart +++ b/Frontend/lib/main.dart @@ -4,6 +4,8 @@ import 'package:hotel_booking/firebase_options.dart'; import 'Screen/SplashScreen/splash_screen.dart'; import 'package:get/get.dart'; + +@pragma('vm:entry-point') Future main() async { WidgetsFlutterBinding.ensureInitialized(); @@ -24,7 +26,7 @@ class MyApp extends StatelessWidget { debugShowCheckedModeBanner: false, title: 'Hotel Booking APP', theme: ThemeData(fontFamily: 'Display'), - home: const SplashScreen(), + home: SplashScreen(), ); } } diff --git a/Frontend/lib/razorpay/razorDashboard.dart b/Frontend/lib/razorpay/razorDashboard.dart new file mode 100644 index 0000000..20bc8f1 --- /dev/null +++ b/Frontend/lib/razorpay/razorDashboard.dart @@ -0,0 +1,173 @@ +import 'dart:convert'; +import 'dart:io'; +//import '' as razorCredentials; +import 'package:http/http.dart' as http; +import 'package:flutter/cupertino.dart'; +import 'package:flutter/material.dart'; +import 'package:razorpay_flutter/razorpay_flutter.dart'; +import 'package:hotel_booking/razorpay/razorcredential.dart' + as razorCredentials; + +class RazorpayDashboard extends StatefulWidget { + const RazorpayDashboard({Key? key}) : super(key: key); + + @override + State createState() => _RazorpayDashboardState(); +} + +class _RazorpayDashboardState extends State { + final _razorpay = Razorpay(); + + @override + void initState() { + WidgetsBinding.instance.addPostFrameCallback((timeStamp) { + _razorpay.on(Razorpay.EVENT_PAYMENT_SUCCESS, _handlePaymentSuccess); + _razorpay.on(Razorpay.EVENT_PAYMENT_ERROR, _handlePaymentError); + _razorpay.on(Razorpay.EVENT_EXTERNAL_WALLET, _handleExternalWallet); + }); + super.initState(); + } + + void _handlePaymentSuccess(PaymentSuccessResponse response) { + // Do something when payment succeeds + print(response); + verifySignature( + signature: response.signature, + paymentId: response.paymentId, + orderId: response.orderId, + ); + } + + void _handlePaymentError(PaymentFailureResponse response) { + print(response); + // Do something when payment fails + ScaffoldMessenger.of(context).showSnackBar( + SnackBar( + content: Text(response.message ?? ''), + ), + ); + } + + void _handleExternalWallet(ExternalWalletResponse response) { + print(response); + // Do something when an external wallet is selected + ScaffoldMessenger.of(context).showSnackBar( + SnackBar( + content: Text(response.walletName ?? ''), + ), + ); + } + +// create order + void createOrder() async { + String username = razorCredentials.keyId; + String password = razorCredentials.keySecret; + String basicAuth = + 'Basic ${base64Encode(utf8.encode('$username:$password'))}'; + + Map body = { + "amount": 100, + "currency": "INR", + "receipt": "rcptid_11" + }; + var res = await http.post( + Uri.https( + "api.razorpay.com", "v1/orders"), //https://api.razorpay.com/v1/orders + headers: { + "Content-Type": "application/json", + 'authorization': basicAuth, + }, + body: jsonEncode(body), + ); + + if (res.statusCode == 200) { + openGateway(jsonDecode(res.body)['id']); + } + print(res.body); + } + + openGateway(String orderId) { + var options = { + 'key': razorCredentials.keyId, + 'amount': 100, //in the smallest currency sub-unit. + 'name': 'Acme Corp.', + 'order_id': orderId, // Generate order_id using Orders API + 'description': 'Fine T-Shirt', + 'timeout': 60 * 5, // in seconds // 5 minutes + 'prefill': { + 'contact': '9123456789', + 'email': 'ary@example.com', + } + }; + _razorpay.open(options); + } + + verifySignature({ + String? signature, + String? paymentId, + String? orderId, + }) async { + Map body = { + 'razorpay_signature': signature, + 'razorpay_payment_id': paymentId, + 'razorpay_order_id': orderId, + }; + + var parts = []; + body.forEach((key, value) { + parts.add('${Uri.encodeQueryComponent(key)}=' + '${Uri.encodeQueryComponent(value)}'); + }); + var formData = parts.join('&'); + var res = await http.post( + Uri.https( + "10.0.2.2", // my ip address , localhost + "razorpay_signature_verify.php", + ), + headers: { + "Content-Type": "application/x-www-form-urlencoded", // urlencoded + }, + body: formData, + ); + + print(res.body); + if (res.statusCode == 200) { + ScaffoldMessenger.of(context).showSnackBar( + SnackBar( + content: Text(res.body), + ), + ); + } + } + + @override + void dispose() { + _razorpay.clear(); // Removes all listeners + super.dispose(); + } + + @override + Widget build(BuildContext context) { + return Scaffold( + appBar: AppBar( + title: const Text("Flutter Razorpay"), + ), + body: Center( + child: Column( + mainAxisAlignment: MainAxisAlignment.center, + children: [ + ElevatedButton( + onPressed: () { + createOrder(); + }, + child: const Text( + "Pay", + style: TextStyle(fontSize: 18, fontWeight: FontWeight.w600), + ), + ) + ], + ), + ), + ); + } +} diff --git a/Frontend/lib/razorpay/razorcredential.dart b/Frontend/lib/razorpay/razorcredential.dart new file mode 100644 index 0000000..b32f8db --- /dev/null +++ b/Frontend/lib/razorpay/razorcredential.dart @@ -0,0 +1,2 @@ +const String keyId = "rzp_test_VR6pKBTEFIcYW9"; +const String keySecret = "iJIcHFzLHX9smr5DhbK8SD9Z"; \ No newline at end of file diff --git a/Frontend/pubspec.lock b/Frontend/pubspec.lock index 4fb4124..80fd40d 100644 --- a/Frontend/pubspec.lock +++ b/Frontend/pubspec.lock @@ -65,6 +65,30 @@ packages: url: "https://pub.dev" source: hosted version: "1.1.1" + cloud_firestore: + dependency: "direct main" + description: + name: cloud_firestore + sha256: "31cfa4d65d6e9ea837234fffe121304034c30c9214c06207b4a35867e3757900" + url: "https://pub.dev" + source: hosted + version: "4.15.8" + cloud_firestore_platform_interface: + dependency: transitive + description: + name: cloud_firestore_platform_interface + sha256: a0097a26569b015faf8142e159e855241609ea9a1738b5fd1c40bfe8411b41a0 + url: "https://pub.dev" + source: hosted + version: "6.1.9" + cloud_firestore_web: + dependency: transitive + description: + name: cloud_firestore_web + sha256: ed680ece29a5750985119c09cdc276b460c3a2fa80e8c12f9b7241f6b4a7ca16 + url: "https://pub.dev" + source: hosted + version: "3.10.8" collection: dependency: transitive description: @@ -129,6 +153,30 @@ packages: url: "https://pub.dev" source: hosted version: "0.7.8" + device_info_plus: + dependency: transitive + description: + name: device_info_plus + sha256: "77f757b789ff68e4eaf9c56d1752309bd9f7ad557cb105b938a7f8eb89e59110" + url: "https://pub.dev" + source: hosted + version: "9.1.2" + device_info_plus_platform_interface: + dependency: transitive + description: + name: device_info_plus_platform_interface + sha256: d3b01d5868b50ae571cd1dc6e502fc94d956b665756180f7b16ead09e836fd64 + url: "https://pub.dev" + source: hosted + version: "7.0.0" + eventify: + dependency: transitive + description: + name: eventify + sha256: b829429f08586cc2001c628e7499e3e3c2493a1d895fd73b00ecb23351aa5a66 + url: "https://pub.dev" + source: hosted + version: "1.0.1" fake_async: dependency: transitive description: @@ -201,6 +249,30 @@ packages: url: "https://pub.dev" source: hosted version: "2.11.5" + firebase_messaging: + dependency: "direct main" + description: + name: firebase_messaging + sha256: e41586e0fd04fe9a40424f8b0053d0832e6d04f49e020cdaf9919209a28497e9 + url: "https://pub.dev" + source: hosted + version: "14.7.19" + firebase_messaging_platform_interface: + dependency: transitive + description: + name: firebase_messaging_platform_interface + sha256: f7a9d74ff7fc588a924f6b2eaeaa148b0db521b13a9db55f6ad45864fa98c06e + url: "https://pub.dev" + source: hosted + version: "4.5.27" + firebase_messaging_web: + dependency: transitive + description: + name: firebase_messaging_web + sha256: fc21e771166860c55b103701c5ac7cdb2eec28897b97c42e6e5703cbedf9e02e + url: "https://pub.dev" + source: hosted + version: "3.6.8" flutter: dependency: "direct main" description: flutter @@ -230,6 +302,30 @@ packages: url: "https://pub.dev" source: hosted version: "2.0.3" + flutter_local_notifications: + dependency: "direct main" + description: + name: flutter_local_notifications + sha256: f9a05409385b77b06c18f200a41c7c2711ebf7415669350bb0f8474c07bd40d1 + url: "https://pub.dev" + source: hosted + version: "17.0.0" + flutter_local_notifications_linux: + dependency: transitive + description: + name: flutter_local_notifications_linux + sha256: "33f741ef47b5f63cc7f78fe75eeeac7e19f171ff3c3df054d84c1e38bedb6a03" + url: "https://pub.dev" + source: hosted + version: "4.0.0+1" + flutter_local_notifications_platform_interface: + dependency: transitive + description: + name: flutter_local_notifications_platform_interface + sha256: "7cf643d6d5022f3baed0be777b0662cce5919c0a7b86e700299f22dc4ae660ef" + url: "https://pub.dev" + source: hosted + version: "7.0.0+1" flutter_plugin_android_lifecycle: dependency: transitive description: @@ -488,6 +584,22 @@ packages: url: "https://pub.dev" source: hosted version: "0.5.0" + package_info_plus: + dependency: transitive + description: + name: package_info_plus + sha256: "7e76fad405b3e4016cd39d08f455a4eb5199723cf594cd1b8916d47140d93017" + url: "https://pub.dev" + source: hosted + version: "4.2.0" + package_info_plus_platform_interface: + dependency: transitive + description: + name: package_info_plus_platform_interface + sha256: "9bc8ba46813a4cc42c66ab781470711781940780fd8beddd0c3da62506d3a6c6" + url: "https://pub.dev" + source: hosted + version: "2.0.1" path: dependency: transitive description: @@ -616,6 +728,14 @@ packages: url: "https://pub.dev" source: hosted version: "2.1.6" + razorpay_flutter: + dependency: "direct main" + description: + name: razorpay_flutter + sha256: eadd633997183286e924157ce5033f3b3d953530632f3e3c6057dc7b1f605fb7 + url: "https://pub.dev" + source: hosted + version: "1.3.6" readmore: dependency: "direct main" description: @@ -648,6 +768,14 @@ packages: url: "https://pub.dev" source: hosted version: "1.0.3+1" + scroll_to_top: + dependency: "direct main" + description: + name: scroll_to_top + sha256: f9bafa81665d5b6fd92ebd52f2201e4086eeb678216a10a1065533d07de572f4 + url: "https://pub.dev" + source: hosted + version: "0.0.5" searchfield: dependency: "direct main" description: @@ -712,6 +840,14 @@ packages: url: "https://pub.dev" source: hosted version: "2.3.2" + shimmer: + dependency: "direct main" + description: + name: shimmer + sha256: "5f88c883a22e9f9f299e5ba0e4f7e6054857224976a5d9f839d4ebdc94a14ac9" + url: "https://pub.dev" + source: hosted + version: "3.0.0" sky_engine: dependency: transitive description: flutter @@ -797,6 +933,14 @@ packages: url: "https://pub.dev" source: hosted version: "0.6.1" + timezone: + dependency: transitive + description: + name: timezone + sha256: "1cfd8ddc2d1cfd836bc93e67b9be88c3adaeca6f40a00ca999104c30693cdca0" + url: "https://pub.dev" + source: hosted + version: "0.9.2" typed_data: dependency: transitive description: @@ -813,6 +957,14 @@ packages: url: "https://pub.dev" source: hosted version: "2.1.4" + vibration: + dependency: "direct main" + description: + name: vibration + sha256: "778ace40e84852e6cf6017cdbaf6790a837d73ff3dd50b27da9ac232a19de8fc" + url: "https://pub.dev" + source: hosted + version: "1.8.4" web: dependency: transitive description: @@ -829,6 +981,14 @@ packages: url: "https://pub.dev" source: hosted version: "5.0.9" + win32_registry: + dependency: transitive + description: + name: win32_registry + sha256: "41fd8a189940d8696b1b810efb9abcf60827b6cbfab90b0c43e8439e3a39d85a" + url: "https://pub.dev" + source: hosted + version: "1.1.2" xdg_directories: dependency: transitive description: diff --git a/Frontend/pubspec.yaml b/Frontend/pubspec.yaml index f1fa81d..3a97030 100644 --- a/Frontend/pubspec.yaml +++ b/Frontend/pubspec.yaml @@ -57,6 +57,13 @@ dependencies: firebase_core: ^2.27.0 firebase_auth: ^4.17.8 google_sign_in: ^6.2.1 + vibration: ^1.8.4 + scroll_to_top: ^0.0.5 + shimmer: ^3.0.0 + razorpay_flutter: ^1.3.6 + firebase_messaging: ^14.7.19 + flutter_local_notifications: ^17.0.0 + cloud_firestore: ^4.15.8 diff --git a/README.md b/README.md new file mode 100644 index 0000000..20628b1 --- /dev/null +++ b/README.md @@ -0,0 +1,48 @@ + +# Project Title + +A Hotel booking App with Modern Fatures And Secure Backend + + +## Tech Stack + +**Client:** Flutter, Getx, Velocityx + +**Server:** Golang, Docker,Gorilla Mux, Web Sockets + + +## Installation + +Install my-project + + + +```bash + Frontend Running + + cd my_project Frontend + flutter pub get + flutter run +``` + +```bash + Backend Server Runnig + + cd my_project Backend + air + +``` + +## Features + +- Light/dark mode toggle +- Live previews +- Fullscreen mode +- Cross platform +- Live Notifications +- In App Purchase +- Razorpay Integration +- Cloud messaging +- Live Response +- Fully Responsive +