Skip to content

Commit 1c030a7

Browse files
author
Léo Guillaume
committed
Merge branch 'user_locations' into main
2 parents aa98d0b + 7f52313 commit 1c030a7

File tree

148 files changed

+6111
-5063
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

148 files changed

+6111
-5063
lines changed
Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
import BaseController from 'App/Controllers/Http/BaseController'
2+
import UserLocationService from 'App/Services/UserLocationService'
3+
import { HttpContextContract } from '@ioc:Adonis/Core/HttpContext'
4+
import UserLocationValidator from 'App/Validators/UserLocationValidator'
5+
6+
export default class UserLocationsController extends BaseController {
7+
public async getAllByUserId() {
8+
return await UserLocationService.getAllByUserId()
9+
}
10+
11+
public async update({ request }: HttpContextContract) {
12+
const { locations } = await request.validate(UserLocationValidator)
13+
return await UserLocationService.update(locations)
14+
}
15+
}

api/app/Models/Property.ts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@ export interface TravelTimes {
1111
driving: number
1212
walking: number
1313
transit: number
14+
bicycling: number
1415
}
1516

1617
export default class Property extends BaseModel {
@@ -114,6 +115,7 @@ export default class Property extends BaseModel {
114115
driving: travelTimes.driving || 0,
115116
walking: travelTimes.walking || 0,
116117
transit: travelTimes.transit || 0,
118+
bicycling: travelTimes.bicycling || 0,
117119
}
118120
}
119121

api/app/Models/Search.ts

Lines changed: 0 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,6 @@ import { DateTime } from 'luxon'
22
import { column, BaseModel, belongsTo, manyToMany } from '@ioc:Adonis/Lucid/Orm'
33
import type { BelongsTo, ManyToMany } from '@ioc:Adonis/Lucid/Orm'
44
import User from 'App/Models/User'
5-
import Location from 'App/Models/Location'
65

76
export default class Search extends BaseModel {
87
@column({ isPrimary: true })
@@ -11,17 +10,9 @@ export default class Search extends BaseModel {
1110
@column()
1211
public name: string
1312

14-
@column()
15-
public location_id: number
16-
1713
@column()
1814
public creator_id: number
1915

20-
@belongsTo(() => Location, {
21-
foreignKey: 'location_id',
22-
})
23-
public location: BelongsTo<typeof Location>
24-
2516
@belongsTo(() => User, {
2617
foreignKey: 'creator_id',
2718
serializeAs: null,

api/app/Models/UserLocation.ts

Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,35 @@
1+
import { DateTime } from 'luxon'
2+
import { BaseModel, belongsTo, column } from '@ioc:Adonis/Lucid/Orm'
3+
import type { BelongsTo } from '@ioc:Adonis/Lucid/Orm'
4+
import User from 'App/Models/User'
5+
import Location from 'App/Models/Location'
6+
7+
export default class UserLocation extends BaseModel {
8+
@column({ isPrimary: true })
9+
public id: number
10+
11+
@column()
12+
public name: string | null
13+
14+
@column()
15+
public user_id: number
16+
17+
@column()
18+
public location_id: number
19+
20+
@belongsTo(() => User, {
21+
foreignKey: 'user_id',
22+
})
23+
public user: BelongsTo<typeof User>
24+
25+
@belongsTo(() => Location, {
26+
foreignKey: 'location_id',
27+
})
28+
public location: BelongsTo<typeof Location>
29+
30+
@column.dateTime({ autoCreate: true })
31+
public createdAt: DateTime
32+
33+
@column.dateTime({ autoCreate: true, autoUpdate: true })
34+
public updatedAt: DateTime
35+
}

api/app/Services/GoogleMapsService.ts

Lines changed: 15 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,15 +1,27 @@
11
import { Client, TravelMode } from '@googlemaps/google-maps-services-js'
22
import Env from '@ioc:Adonis/Core/Env'
33

4+
type GoogleTransit = {
5+
driving?: number
6+
walking?: number
7+
transit?: number
8+
bicycling?: number
9+
}
10+
411
export class GoogleMapsService {
512
private static client = new Client({})
613

714
public static async getTravelTimes(
815
origin: { lat: number; lng: number },
916
destination: { lat: number; lng: number }
10-
): Promise<{ driving?: number; walking?: number; transit?: number }> {
11-
const result: { driving?: number; walking?: number; transit?: number } = {}
12-
const modes: TravelMode[] = [TravelMode.driving, TravelMode.walking, TravelMode.transit]
17+
): Promise<GoogleTransit> {
18+
const result: GoogleTransit = {}
19+
const modes: TravelMode[] = [
20+
TravelMode.driving,
21+
TravelMode.walking,
22+
TravelMode.transit,
23+
TravelMode.bicycling,
24+
]
1325
for (const mode of modes) {
1426
const response = await this.client.directions({
1527
params: {

api/app/Services/PropertyService.ts

Lines changed: 33 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -4,52 +4,59 @@ import Property, { TravelTimes } from 'App/Models/Property'
44
import PropertyValidator from 'App/Validators/PropertyValidator'
55
import SearchService from 'App/Services/SearchService'
66
import type { PropertyRatingWithUser } from 'App/Models/PropertyRating'
7+
import UserLocationService from 'App/Services/UserLocationService'
8+
import UserLocation from 'App/Models/UserLocation'
79

810
interface ServiceProperty extends Omit<Property, 'ratings'> {
9-
travelTimes: TravelTimes
1011
average_ratings?: number
1112
ratings: PropertyRatingWithUser[]
1213
}
1314

15+
type UserLocationWithTravelTime = {
16+
userLocation: UserLocation
17+
travelTimes: TravelTimes
18+
}
19+
20+
interface ServicePropertyWithLocations extends ServiceProperty {
21+
locations: Array<UserLocationWithTravelTime>
22+
}
23+
1424
export default class PropertyService extends BaseService {
15-
public static async getSearchProperties(searchId: number): Promise<ServiceProperty[] | null> {
25+
public static async getSearchProperties(searchId: number): Promise<Property[] | null> {
1626
const search = await SearchService.getById(searchId)
1727
if (!search) return null
1828

19-
const properties = await Property.query()
29+
return await Property.query()
2030
.where('search_id', search?.id)
2131
.where('is_deleted', false)
2232
.preload('location')
2333
.preload('ratings')
2434
.orderBy('created_at', 'desc')
2535
.exec()
26-
27-
return await Promise.all(
28-
properties.map(async (property) => {
29-
let travelTimes = { driving: 0, transit: 0, walking: 0 }
30-
try {
31-
travelTimes = await property.getTravelTimes({
32-
lat: search.location.lat,
33-
lng: search.location.lng,
34-
})
35-
} catch (e) {
36-
console.log(e)
37-
}
38-
return { ...property.toJSON(), travelTimes } as ServiceProperty
39-
})
40-
)
4136
}
4237

4338
public static async findById(id: number, searchId: number) {
4439
const property = await this.getById(id, searchId)
4540

46-
const search = await SearchService.getById(searchId)
47-
if (!search || !property) return super.response.notFound()
41+
const userLocations = await UserLocationService.getAllByUserId()
42+
if (!property) return super.response.notFound()
4843

49-
const travelTimes = await property.getTravelTimes({
50-
lat: search.location.lat,
51-
lng: search.location.lng,
52-
})
44+
let locations: Array<UserLocationWithTravelTime> = []
45+
46+
if (userLocations) {
47+
locations = await Promise.all(
48+
userLocations.map(async (location) => {
49+
const travelTimes = await property.getTravelTimes({
50+
lat: location.location.lat,
51+
lng: location.location.lng,
52+
})
53+
return {
54+
userLocation: location,
55+
travelTimes,
56+
}
57+
})
58+
)
59+
}
5360

5461
const propertyObj: Property = property.toJSON() as Property
5562

@@ -59,10 +66,10 @@ export default class PropertyService extends BaseService {
5966

6067
return {
6168
...propertyObj,
62-
travelTimes,
69+
locations,
6370
ratings,
6471
average_ratings: property.averageRating,
65-
} as ServiceProperty
72+
} as ServicePropertyWithLocations
6673
}
6774

6875
public static async getById(id: number, searchId: number) {

api/app/Services/SearchService.ts

Lines changed: 0 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,6 @@ import Search from 'App/Models/Search'
33
import SearchValidator from 'App/Validators/SearchValidator'
44
import AuthService from 'App/Services/AuthService'
55
import SearchUser from 'App/Models/SearchUser'
6-
import LocationService from 'App/Services/LocationService'
76
import User from 'App/Models/User'
87
import merge from 'lodash/merge'
98

@@ -21,7 +20,6 @@ export default class SearchService extends BaseService {
2120
public static async getById(id: number) {
2221
const search = await Search.query()
2322
.preload('users')
24-
.preload('location')
2523
.preload('creator')
2624
.where('id', id)
2725
.firstOrFail()
@@ -42,11 +40,9 @@ export default class SearchService extends BaseService {
4240
AuthService.unauthorized()
4341
return
4442
}
45-
const location = await LocationService.create(data.location)
4643
const search = await Search.create({
4744
...data,
4845
creator_id: super.auth.user?.id,
49-
location_id: location.id,
5046
})
5147
await super.sendPrivateSuccessNotification(`Votre recherche a été créée avec succès !`)
5248
return await this.getById(search.id)
@@ -69,7 +65,6 @@ export default class SearchService extends BaseService {
6965
query.where('user_id', user.id)
7066
})
7167
.orWhere('creator_id', user.id)
72-
.preload('location')
7368
.preload('creator')
7469
.preload('users')
7570
.orderBy('created_at', 'desc')
Lines changed: 60 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,60 @@
1+
import BaseService from 'App/Services/BaseService'
2+
import UserLocation from 'App/Models/UserLocation'
3+
import AuthService from 'App/Services/AuthService'
4+
import LocationService from 'App/Services/LocationService'
5+
import type { LocationCommand } from 'App/Services/LocationService'
6+
7+
interface UserLocationData {
8+
name?: string
9+
location: LocationCommand
10+
}
11+
12+
export default class UserLocationService extends BaseService {
13+
public static async update(data: UserLocationData[]) {
14+
const userId = super.auth.user?.id
15+
if (!userId) {
16+
AuthService.unauthorized()
17+
return
18+
}
19+
20+
// Get current user_locations
21+
const currentUserLocations = await this.getAllByUserId()
22+
23+
// Create new locations and user locations
24+
for (const dataItem of data) {
25+
// Create or find location
26+
const location = await LocationService.create(dataItem.location)
27+
28+
// Create new user location
29+
await UserLocation.create({
30+
name: dataItem.name ?? null,
31+
location_id: location.id,
32+
user_id: userId,
33+
})
34+
}
35+
36+
// If new locations were created successfully, delete old ones
37+
if (currentUserLocations) {
38+
for (const oldLocation of currentUserLocations) {
39+
await oldLocation.delete()
40+
}
41+
}
42+
43+
await super.sendPrivateSuccessNotification('Les lieux ont été mis à jour !')
44+
45+
return this.getAllByUserId()
46+
}
47+
48+
public static async getAllByUserId() {
49+
const user = super.auth.user
50+
if (!user) {
51+
AuthService.unauthorized()
52+
return
53+
}
54+
return await UserLocation.query()
55+
.where('user_id', user.id)
56+
.preload('location')
57+
.orderBy('created_at', 'desc')
58+
.exec()
59+
}
60+
}
Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,9 @@
11
import { schema } from '@ioc:Adonis/Core/Validator'
22
import { HttpContextContract } from '@ioc:Adonis/Core/HttpContext'
3-
import { locationSchema } from 'App/Validators/LocationValidator'
43

54
export default class SearchValidator {
65
constructor(protected ctx: HttpContextContract) {}
76
public schema = schema.create({
87
name: schema.string({ trim: true }),
9-
location: schema.object().members(locationSchema),
108
})
119
}
Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
import { schema } from '@ioc:Adonis/Core/Validator'
2+
import { HttpContextContract } from '@ioc:Adonis/Core/HttpContext'
3+
import { locationSchema } from 'App/Validators/LocationValidator'
4+
5+
export default class UserLocationValidator {
6+
constructor(protected ctx: HttpContextContract) {}
7+
public schema = schema.create({
8+
locations: schema.array().members(
9+
schema.object().members({
10+
name: schema.string.optional({ trim: true }),
11+
location: schema.object().members(locationSchema),
12+
})
13+
),
14+
})
15+
}

0 commit comments

Comments
 (0)