Skip to content

Commit ceec332

Browse files
committed
feat(article): add endpoint to retrieve user's items with search filter
feat(law): implement endpoint to fetch all laws with approval status fix(drawer): uncomment 'Hôtel de ventes' menu item for navigation fix(store): enhance item count display and improve search functionality
1 parent ee94d88 commit ceec332

File tree

6 files changed

+173
-12
lines changed

6 files changed

+173
-12
lines changed

apps/api/src/core/article/article.controller.ts

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,26 @@ export class ArticleController {
2020
})
2121
}
2222

23+
@Get('my/:playerName')
24+
public async my(
25+
@Param('playerName') playerName: string,
26+
@Query('recherche') recherche: string,
27+
@Req() req: Request & { user: any },
28+
@Res() res: Response,
29+
): Promise<Response> {
30+
if ((req.user as any).name !== playerName && !(req.user as any).roles.includes('op')) {
31+
return res.status(HttpStatus.FORBIDDEN).json({
32+
statusCode: HttpStatus.FORBIDDEN,
33+
message: 'You are not allowed to sell this item',
34+
})
35+
}
36+
37+
return res.status(HttpStatus.OK).json({
38+
statusCode: HttpStatus.OK,
39+
data: await this._service.findMy(playerName, recherche),
40+
})
41+
}
42+
2343
@Public()
2444
@Get('average')
2545
public async average(

apps/api/src/core/article/article.service.ts

Lines changed: 75 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -168,6 +168,81 @@ export class ArticleService {
168168
)
169169
}
170170

171+
public async findMy(playerName: string, filterSearch: string = '') {
172+
let filters = {}
173+
filters['state'] = ArticleState.ON_SALE
174+
if (filterSearch) {
175+
filters['name'] = new RegExp(filterSearch, 'i')
176+
}
177+
178+
const playerModel = await this._users.findOne({ name: playerName })
179+
if (!playerModel) throw new NotFoundException('Player not found')
180+
181+
filters['vendor.id'] = playerModel._id
182+
183+
const items = await this._model.aggregate([
184+
{ $match: filters },
185+
{
186+
$match: {
187+
state: ArticleState.ON_SALE,
188+
},
189+
},
190+
{
191+
$group: {
192+
_id: { name: '$name', stack: '$stack', nbt: '$nbt', state: '$state' },
193+
price: { $min: '$price' },
194+
quantity: { $sum: 1 },
195+
onSaleAt: { $max: '$metadata.onSaleAt' },
196+
},
197+
},
198+
{
199+
$group: {
200+
_id: '$_id.name',
201+
items: {
202+
$push: {
203+
name: '$_id.name',
204+
stack: '$_id.stack',
205+
nbt: '$_id.nbt',
206+
quantity: '$quantity',
207+
state: '$state',
208+
price: '$price',
209+
'onSaleAt': '$onSaleAt'
210+
},
211+
},
212+
// stack: '$stack',
213+
},
214+
},
215+
{
216+
$sort: {
217+
'_id': 1,
218+
},
219+
},
220+
])
221+
222+
return await Promise.all(
223+
items.map(async (item) => {
224+
let _data = {}
225+
const data = await this._redis.get(`oms:items:${item.id}`)
226+
try {
227+
_data = JSON.parse(data).model
228+
} catch (error) {
229+
}
230+
231+
if (existsSync(`../../storage/render/${item._id.replace(':', '__')}.png`)) {
232+
const file = readFileSync(`../../storage/render/${item._id.replace(':', '__')}.png`)
233+
_data['texture'] = `data:image/png;base64,${file.toString('base64')}`
234+
}
235+
236+
return {
237+
...item,
238+
name: item._id.split(':')[1],
239+
mod: item._id.split(':')[0],
240+
_data,
241+
}
242+
}),
243+
)
244+
}
245+
171246
public async sellItem(
172247
name: string,
173248
slot: number,

apps/api/src/core/democracy/law/law.controller.ts

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,17 @@ import { HasRoles } from '~/_common/_decorators/has-role.decorator'
88
export class LawController {
99
public constructor(private readonly _service: LawService) { }
1010

11+
@Public()
12+
@Get('all')
13+
public async getAllLaws(
14+
@Res() res: Response,
15+
): Promise<Response> {
16+
return res.status(HttpStatus.OK).json({
17+
statusCode: HttpStatus.OK,
18+
data: await this._service.getAllLaws(),
19+
})
20+
}
21+
1122
@Public()
1223
@Get(':playerName')
1324
public async getLaws(

apps/api/src/core/democracy/law/law.service.ts

Lines changed: 31 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -11,8 +11,38 @@ export class LawService {
1111
this._model = _model
1212
}
1313

14+
public async getAllLaws(): Promise<Law[]> {
15+
const currentDate = new Date()
16+
17+
const laws = await this._model.find({
18+
appliedAt: {
19+
$lte: currentDate,
20+
},
21+
}, {
22+
// votes: 0,
23+
}).exec()
24+
25+
return (laws || []).map((law) => {
26+
let approuved = 0
27+
for (const vote of law.votes) {
28+
approuved += vote.type
29+
}
30+
31+
return {
32+
...omit(law.toObject(), ['votes']),
33+
approuved: approuved > 0,
34+
}
35+
})
36+
}
37+
1438
public async getLaws(playerName: string = ''): Promise<Law[]> {
15-
const laws = await this._model.find({}, {
39+
const currentDate = new Date()
40+
41+
const laws = await this._model.find({
42+
appliedAt: {
43+
$gt: currentDate,
44+
},
45+
}, {
1646
// votes: 0,
1747
}).exec()
1848

apps/web/src/components/layout/default/drawer.vue

Lines changed: 2 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -31,12 +31,10 @@ export default {
3131
{ name: 'Accueil', to: '/', icon: 'mdi-view-dashboard' },
3232
{ name: 'Banque', to: '/bank', icon: 'mdi-bank' },
3333
{ name: 'Démocratie', to: '/democracy', icon: 'mdi-vote-outline' },
34-
// { name: 'Hôtel de ventes', to: '/store?mod=minecraft&recherche=', icon: 'mdi-store' },
34+
{ name: 'Hôtel de ventes', to: '/store?mod=minecraft&recherche=', icon: 'mdi-store' },
3535
{ name: 'Défis de la semaine', to: '/defi/upload', icon: 'mdi-bullseye-arrow' },
3636
],
37-
bottom: [
38-
/* { name: 'Inventaire', to: '/inventory', icon: 'mdi-archive' } */
39-
],
37+
bottom: [{ name: 'Inventaire', to: '/inventory', icon: 'mdi-archive' }],
4038
}
4139
},
4240
setup() {

apps/web/src/pages/store.vue

Lines changed: 34 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -2,18 +2,22 @@
22
q-card.flex.full-width(flat :style="{ flex: 1 }")
33
q-card-section(style='position: relative;bottom: 0;top: 0;max-height: 100%; min-width: 288px;')
44
q-tabs(v-model="mod" outside-arrows vertical style='max-height: 100%;position: absolute;top:0;bottom: 0;')
5+
q-tab(name="my")
6+
//- q-avatar.q-mr-sm(square color="secondary" text-color="white" size='25px') MY
7+
q-item-label Vos items
58
q-tab(v-for='mod in mods' :key="mod.name" :name="mod.name")
69
div.flex.row.items-center
710
q-avatar.q-mr-sm(square color="secondary" text-color="white" size='25px') {{ getModIcon(mod.name) }}
811
q-item-label {{ mod.name }}
9-
q-badge.q-ml-xs(color='info') {{ mod.count }}
12+
q-badge.q-ml-xs(color='red') {{ mod.count }}
1013

1114
q-card-section(:style="{ flex: 1 }")
1215
q-table(
1316
flat bordered
1417
title="Articles"
1518
:rows="search"
1619
:columns="searchColumns"
20+
@keyup.enter="enterSearch"
1721
row-key="name"
1822
)
1923
template(#top="props")
@@ -24,9 +28,18 @@
2428
v-model="recherche"
2529
debounce="300"
2630
placeholder="Search"
31+
clearable
32+
@keyup.enter="enterSearch"
2733
class="q-ml-sm"
2834
:style="{ width: '300px' }"
2935
)
36+
q-btn(
37+
@click="enterSearch"
38+
color="red"
39+
round
40+
dense
41+
icon="mdi-magnify"
42+
)
3043
template(#header="props")
3144
q-tr(:props="props")
3245
q-th(auto-width)
@@ -75,30 +88,32 @@ export default {
7588
get: () => {
7689
return `${useRoute().query.mod || 'all'}`
7790
},
78-
set: (value) => {
91+
set: async (value) => {
7992
const $route = useRoute()
8093
const $router = useRouter()
81-
$router.replace({
94+
await $router.replace({
8295
query: {
8396
...$route.query,
8497
mod: `${value}`,
8598
},
8699
})
100+
window.location.href = $route.fullPath
87101
},
88102
})
89103
const recherche = computed({
90104
get: () => {
91105
return `${useRoute().query.recherche || ''}`
92106
},
93-
set: (value) => {
107+
set: async (value) => {
94108
const $route = useRoute()
95109
const $router = useRouter()
96-
$router.replace({
110+
await $router.replace({
97111
query: {
98112
...$route.query,
99-
recherche: `${value}`,
113+
recherche: `${value || ''}`,
100114
},
101115
})
116+
// window.location.href = $route.fullPath
102117
},
103118
})
104119
@@ -118,11 +133,18 @@ export default {
118133
},
119134
})
120135
136+
const path = ref('/core/article/search')
137+
if (mod.value === 'my') {
138+
path.value = `/core/article/my/${getUsername.value}`
139+
} else {
140+
path.value = '/core/article/search'
141+
}
142+
121143
const {
122144
data: search,
123145
refresh: refreshSearch,
124146
error: errorSearch,
125-
} = await useHttp<any>('/core/article/search', {
147+
} = await useHttp<any>(path, {
126148
query: { mod, recherche: recherche.value },
127149
transform: (result) => {
128150
return result?.data || []
@@ -167,6 +189,11 @@ export default {
167189
},
168190
},
169191
methods: {
192+
enterSearch() {
193+
const $route = useRoute()
194+
this.refreshSearch()
195+
window.location.href = $route.fullPath
196+
},
170197
getAveragePrice(item) {
171198
const average = this.average.find((average) => average._id === item.name)
172199
if (average) {

0 commit comments

Comments
 (0)