@@ -21,9 +21,8 @@ import { defineMessages, useVIntl } from '@vintl/vintl'
2121import Fuse from ' fuse.js'
2222
2323import ModerationTechRevCard from ' ~/components/ui/moderation/ModerationTechRevCard.vue'
24-
2524// TEMPORARY: Mock data for development
26- import { generateMockProjectReviews } from ' ~/utils/mockTechReviewData'
25+ // import { generateMockSearchResponse } from '~/utils/mockTechReviewData'
2726
2827const client = injectModrinthClient ()
2928
@@ -88,9 +87,8 @@ const filterTypes = computed<ComboboxOption<string>[]>(() => {
8887 const issueTypes = new Set (
8988 reviewItems .value
9089 .flatMap ((review ) => review .reports )
91- .flatMap ((report ) => report .files )
92- .flatMap ((file ) => file .issues )
93- .map ((issue ) => issue .kind ),
90+ .flatMap ((report ) => report .issues )
91+ .map ((issue ) => issue .issue_type ),
9492 )
9593
9694 const sortedTypes = Array .from (issueTypes ).sort ()
@@ -112,8 +110,8 @@ const fuse = computed(() => {
112110 keys: [
113111 { name: ' project.title' , weight: 4 },
114112 { name: ' project.slug' , weight: 3 },
115- { name: ' reports.files. file_name' , weight: 2 },
116- { name: ' reports.files. issues.kind ' , weight: 3 },
113+ { name: ' reports.file_name' , weight: 2 },
114+ { name: ' reports.issues.issue_type ' , weight: 3 },
117115 { name: ' project_owner.name' , weight: 2 },
118116 ],
119117 includeScore: true ,
@@ -123,9 +121,7 @@ const fuse = computed(() => {
123121
124122const searchResults = computed (() => {
125123 if (! query .value || ! fuse .value ) return null
126- return fuse .value
127- .search (query .value )
128- .map ((result ) => result .item as Labrinth .TechReview .Internal .ProjectReview )
124+ return fuse .value .search (query .value ).map ((result ) => result .item )
129125})
130126
131127const baseFiltered = computed (() => {
@@ -138,31 +134,32 @@ const typeFiltered = computed(() => {
138134 const type = currentFilterType .value
139135
140136 return baseFiltered .value .filter ((review ) => {
141- return review .reports .some ((report ) =>
142- report .files .some ((file ) => file .issues .some ((issue ) => issue .kind === type )),
137+ return review .reports .some ((report : Labrinth .TechReview .Internal .FileReport ) =>
138+ report .issues .some (
139+ (issue : Labrinth .TechReview .Internal .FileIssue ) => issue .issue_type === type ,
140+ ),
143141 )
144142 })
145143})
146144
147- function getHighestSeverity(review : Labrinth .TechReview .Internal .ProjectReview ): string {
145+ function getHighestSeverity(review : {
146+ reports: Labrinth .TechReview .Internal .FileReport []
147+ }): string {
148148 const severities = review .reports
149- .flatMap ((r ) => r .files )
150- .flatMap ((f ) => f .issues )
149+ .flatMap ((r ) => r .issues )
151150 .flatMap ((i ) => i .details )
152151 .map ((d ) => d .severity )
153152
154153 const order = { SEVERE: 3 , HIGH: 2 , MEDIUM: 1 , LOW: 0 } as Record <string , number >
155154 return severities .sort ((a , b ) => (order [b ] ?? 0 ) - (order [a ] ?? 0 ))[0 ] || ' LOW'
156155}
157156
158- function hasPendingIssues(review : Labrinth .TechReview .Internal .ProjectReview ): boolean {
159- return review .reports .some ((report ) =>
160- report .files .some ((file ) => file .issues .some ((issue ) => issue .status === ' pending' )),
161- )
157+ function hasPendingIssues(review : { reports: Labrinth .TechReview .Internal .FileReport [] }): boolean {
158+ return review .reports .some ((report ) => report .issues .some ((issue ) => issue .status === ' pending' ))
162159}
163160
164- function getEarliestDate(review : Labrinth .TechReview .Internal .ProjectReview ): number {
165- const dates = review .reports .map ((r ) => new Date (r .created_at ).getTime ())
161+ function getEarliestDate(review : { reports : Labrinth .TechReview .Internal .FileReport [] } ): number {
162+ const dates = review .reports .map ((r ) => new Date (r .created ).getTime ())
166163 return Math .min (... dates )
167164}
168165
@@ -232,30 +229,78 @@ function toApiSort(label: string): Labrinth.TechReview.Internal.SearchProjectsSo
232229 }
233230}
234231
235- // const {
236- // data: reviewItems,
237- // isLoading,
238- // refetch,
239- // } = useQuery({
240- // queryKey: ['tech-reviews', currentSortType],
241- // queryFn: async () => {
242- // return await client.labrinth.tech_review_internal.searchProjects({
243- // limit: 350,
244- // page: 0,
245- // sort_by: toApiSort(currentSortType.value),
246- // })
247- // },
248- // initialData: [] as Labrinth.TechReview.Internal.ProjectReview[],
249- // })
232+ const {
233+ data : searchResponse,
234+ isLoading,
235+ refetch,
236+ } = useQuery ({
237+ queryKey: [' tech-reviews' , currentSortType ],
238+ queryFn : async () => {
239+ return await client .labrinth .tech_review_internal .searchProjects ({
240+ limit: 350 ,
241+ page: 0 ,
242+ sort_by: toApiSort (currentSortType .value ),
243+ })
244+ },
245+ initialData: {
246+ reports: [],
247+ projects: {},
248+ threads: {},
249+ ownership: {},
250+ } as Labrinth .TechReview .Internal .SearchResponse ,
251+ })
250252
251253// TEMPORARY: Mock data for development (58 items to match batch scan progress)
252- const reviewItems = ref <Labrinth .TechReview .Internal .ProjectReview []>(
253- generateMockProjectReviews (58 ),
254- )
255- const isLoading = ref (false )
256- const refetch = () => {
257- reviewItems .value = generateMockProjectReviews (58 )
258- }
254+ // const searchResponse = ref<Labrinth.TechReview.Internal.SearchResponse>(
255+ // generateMockSearchResponse(58),
256+ // )
257+ // const isLoading = ref(false)
258+ // const refetch = () => {
259+ // searchResponse.value = generateMockSearchResponse(58)
260+ // }
261+
262+ // Adapter: Transform flat SearchResponse into project-grouped structure
263+ // for easier consumption by existing UI components
264+ const reviewItems = computed (() => {
265+ if (! searchResponse .value || searchResponse .value .reports .length === 0 ) {
266+ return []
267+ }
268+
269+ const response = searchResponse .value
270+
271+ // Group reports by project
272+ const projectMap = new Map <
273+ string ,
274+ {
275+ project : Labrinth .Projects .v3 .Project
276+ project_owner : Labrinth .TechReview .Internal .Ownership
277+ thread : Labrinth .TechReview .Internal .DBThread
278+ reports : Labrinth .TechReview .Internal .FileReport []
279+ }
280+ > ()
281+
282+ for (const report of response .reports ) {
283+ const projectId = report .project_id
284+
285+ if (! projectMap .has (projectId )) {
286+ // Find the thread associated with this project
287+ const thread = Object .values (response .threads ).find ((t ) => t .project_id === projectId )
288+
289+ if (! thread ) continue // Skip if no thread found
290+
291+ projectMap .set (projectId , {
292+ project: response .projects [projectId ],
293+ project_owner: response .ownership [projectId ],
294+ thread ,
295+ reports: [],
296+ })
297+ }
298+
299+ projectMap .get (projectId )! .reports .push (report )
300+ }
301+
302+ return Array .from (projectMap .values ())
303+ })
259304
260305watch (currentSortType , () => {
261306 goToPage (1 )
0 commit comments