Skip to content

Commit bbe37f6

Browse files
committed
Merge branch 'feat/openproject-claude-integration' into develop
2 parents 036a3f7 + 00429fa commit bbe37f6

8 files changed

Lines changed: 129 additions & 73 deletions

File tree

packages/admin/src/api/index.ts

Lines changed: 54 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -210,8 +210,10 @@ export const getUsers = async (): Promise<{ id: string; email: string; role: str
210210
return response.data
211211
}
212212

213-
export const createUser = async (user: { email: string; password: string; role: string; tenantIds?: string[] }) => {
214-
const response = await api().post(USERS_URL, user)
213+
export const createUser = async (user: { email: string; password: string; role: string; programIds?: string[] }) => {
214+
// Backend still expects tenantIds
215+
const { programIds, ...rest } = user
216+
const response = await api().post(USERS_URL, { ...rest, tenantIds: programIds })
215217
return response.data
216218
}
217219

@@ -220,9 +222,11 @@ export const updateUser = async (user: {
220222
email: string
221223
password?: string
222224
role: string
223-
tenantIds?: string[]
225+
programIds?: string[]
224226
}) => {
225-
const response = await api().put(`${USERS_URL}/${user.id}`, user)
227+
// Backend still expects tenantIds
228+
const { programIds, ...rest } = user
229+
const response = await api().put(`${USERS_URL}/${user.id}`, { ...rest, tenantIds: programIds })
226230
return response.data
227231
}
228232

@@ -306,7 +310,7 @@ export interface ReviewFormData {
306310
export interface ReviewRecord {
307311
id: string
308312
submissionGuid: string
309-
tenantId: string
313+
programId: string
310314
status: 'pending' | 'approved' | 'rejected'
311315
submittedBy: string
312316
reviewedBy: string | null
@@ -326,55 +330,71 @@ export interface ReviewConfigRecord {
326330
}
327331

328332
export const getReviews = async (
329-
tenantId: string,
333+
programId: string,
330334
status?: 'pending' | 'approved' | 'rejected',
331335
): Promise<{ reviews: ReviewRecord[] }> => {
332-
const params: Record<string, string> = { tenantId }
336+
// Backend still expects tenantId
337+
const params: Record<string, string> = { tenantId: programId }
333338
if (status) {
334339
params.status = status
335340
}
336341
const response = await api().get(REVIEWS_URL, { params })
337-
return response.data
342+
const data = response.data as { reviews: Array<Record<string, unknown>> }
343+
return {
344+
reviews: data.reviews.map(({ tenantId: tid, ...rest }) => ({
345+
...rest,
346+
programId: tid,
347+
})) as ReviewRecord[],
348+
}
338349
}
339350

340351
export const approveReview = async (
341352
id: string,
342-
tenantId: string,
353+
programId: string,
343354
): Promise<{ review: ReviewRecord }> => {
344-
const response = await api().post(`${REVIEWS_URL}/${id}/approve`, { tenantId })
345-
return response.data
355+
// Backend still expects tenantId
356+
const response = await api().post(`${REVIEWS_URL}/${id}/approve`, { tenantId: programId })
357+
const data = response.data as { review: Record<string, unknown> }
358+
const { tenantId: tid, ...rest } = data.review
359+
return { review: { ...rest, programId: tid } as ReviewRecord }
346360
}
347361

348362
export const rejectReview = async (
349363
id: string,
350-
tenantId: string,
364+
programId: string,
351365
reason: string,
352366
): Promise<{ review: ReviewRecord }> => {
353-
const response = await api().post(`${REVIEWS_URL}/${id}/reject`, { tenantId, reason })
354-
return response.data
367+
// Backend still expects tenantId
368+
const response = await api().post(`${REVIEWS_URL}/${id}/reject`, { tenantId: programId, reason })
369+
const data = response.data as { review: Record<string, unknown> }
370+
const { tenantId: tid, ...rest } = data.review
371+
return { review: { ...rest, programId: tid } as ReviewRecord }
355372
}
356373

357374
export const bulkApproveReviews = async (
358375
reviewIds: string[],
359-
tenantId: string,
376+
programId: string,
360377
): Promise<{ approved: number; failed: number; errors: Array<{ reviewId: string; error: string }> }> => {
361-
const response = await api().post(`${REVIEWS_URL}/bulk-approve`, { reviewIds, tenantId })
378+
// Backend still expects tenantId
379+
const response = await api().post(`${REVIEWS_URL}/bulk-approve`, { reviewIds, tenantId: programId })
362380
return response.data
363381
}
364382

365383
export const getReviewConfigs = async (
366-
tenantId: string,
384+
programId: string,
367385
): Promise<{ configs: ReviewConfigRecord[] }> => {
368-
const response = await api().get(`${REVIEWS_URL}/config/${tenantId}`)
386+
// Backend URL still uses tenantId path segment
387+
const response = await api().get(`${REVIEWS_URL}/config/${programId}`)
369388
return response.data
370389
}
371390

372391
export const setReviewConfig = async (
373-
tenantId: string,
392+
programId: string,
374393
eventType: string,
375394
config: { policy: string; requiredRole?: string; externalAdapterType?: string },
376395
): Promise<{ status: string; config: ReviewConfigRecord }> => {
377-
const response = await api().put(`${REVIEWS_URL}/config/${tenantId}/${eventType}`, config)
396+
// Backend URL still uses tenantId path segment
397+
const response = await api().put(`${REVIEWS_URL}/config/${programId}/${eventType}`, config)
378398
return response.data
379399
}
380400

@@ -464,9 +484,20 @@ export const getCurrentUser = async (): Promise<{
464484
id: string
465485
email: string
466486
role: string
467-
tenantIds: string[]
468-
roleAssignments?: Array<{ tenantId: string; role: string; areaId?: string }>
487+
programIds: string[]
488+
roleAssignments?: Array<{ programId: string; role: string; areaId?: string }>
469489
}> => {
470490
const response = await api().get(`${USERS_URL}/me`)
471-
return response.data
491+
const data = response.data
492+
// Backend returns tenantIds/tenantId — map to programIds/programId
493+
return {
494+
...data,
495+
programIds: data.tenantIds ?? data.programIds ?? [],
496+
roleAssignments: data.roleAssignments?.map(
497+
({ tenantId: tid, ...rest }: { tenantId?: string; programId?: string; role: string; areaId?: string }) => ({
498+
...rest,
499+
programId: tid ?? rest.programId,
500+
}),
501+
),
502+
}
472503
}

packages/admin/src/stores/__tests__/reviews.spec.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -23,7 +23,7 @@ function makeReview(overrides: Partial<ReviewRecord> = {}): ReviewRecord {
2323
return {
2424
id: 'review-1',
2525
submissionGuid: 'sub-1',
26-
tenantId: 'tenant-1',
26+
programId: 'tenant-1',
2727
status: 'pending',
2828
submittedBy: 'user-1',
2929
reviewedBy: null,

packages/admin/src/views/AppDetailsView.vue

Lines changed: 17 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -376,13 +376,26 @@ const triggerSync = async (credentials?: { username: string; password: string })
376376
377377
try {
378378
isSyncing.value = true
379-
await externalSyncApi(app.value.id, credentials)
380-
snackBarStore.showSnackbar('External sync completed successfully', 'success')
379+
const result = await externalSyncApi(app.value.id, credentials)
380+
const pushed = result?.pushed ?? 0
381+
const pulled = result?.pulled ?? 0
382+
snackBarStore.showSnackbar(
383+
`External sync completed: ${pushed} pushed, ${pulled} pulled`,
384+
'success',
385+
)
381386
// Refresh the page data after successful sync
382387
await fetchApp()
383388
} catch (err) {
384-
console.error('Failed to sync collection program', err)
385-
snackBarStore.showSnackbar('Failed to trigger external sync', 'red')
389+
if (err instanceof AxiosError && err.response?.status === 409) {
390+
snackBarStore.showSnackbar('A sync is already in progress. Please wait.', 'warning')
391+
} else if (err instanceof AxiosError && err.response?.status === 422) {
392+
const failed = err.response.data?.failed ?? 0
393+
snackBarStore.showSnackbar(`Sync completed with ${failed} failure(s)`, 'warning')
394+
await fetchApp()
395+
} else {
396+
console.error('Failed to sync collection program', err)
397+
snackBarStore.showSnackbar('Failed to trigger external sync', 'red')
398+
}
386399
} finally {
387400
isSyncing.value = false
388401
}

packages/admin/src/views/ReviewConfigView.vue

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -29,7 +29,7 @@ const router = useRouter()
2929
const reviewsStore = useReviewsStore()
3030
const snackBarStore = useSnackBarStore()
3131
32-
const tenantId = ref(route.params.id as string)
32+
const programId = ref(route.params.id as string)
3333
const loading = ref(false)
3434
3535
const policyOptions = [
@@ -67,7 +67,7 @@ const saveConfig = async () => {
6767
if (!editingConfig.value) return
6868
loading.value = true
6969
try {
70-
await reviewsStore.updateConfig(tenantId.value, editingConfig.value.eventType, {
70+
await reviewsStore.updateConfig(programId.value, editingConfig.value.eventType, {
7171
policy: editPolicy.value,
7272
requiredRole: editPolicy.value === 'internal-review' ? editRequiredRole.value : undefined,
7373
})
@@ -82,13 +82,13 @@ const saveConfig = async () => {
8282
}
8383
8484
const goBack = () => {
85-
router.push({ name: 'app-details', params: { id: tenantId.value } })
85+
router.push({ name: 'app-details', params: { id: programId.value } })
8686
}
8787
8888
onMounted(async () => {
8989
loading.value = true
9090
try {
91-
await reviewsStore.fetchConfigs(tenantId.value)
91+
await reviewsStore.fetchConfigs(programId.value)
9292
} catch (error) {
9393
snackBarStore.showSnackbar('Failed to load review configurations', 'error')
9494
console.error('Failed to load configs', error)

packages/admin/src/views/ReviewsView.vue

Lines changed: 13 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -27,8 +27,8 @@ import { useSnackBarStore } from '@/stores/snackBar'
2727
const reviewsStore = useReviewsStore()
2828
const snackBarStore = useSnackBarStore()
2929
30-
const tenants = ref<AppListItem[]>([])
31-
const selectedTenantId = ref<string | null>(null)
30+
const programs = ref<AppListItem[]>([])
31+
const selectedProgramId = ref<string | null>(null)
3232
const selectedStatus = ref<string | null>(null)
3333
const selectedReviewIds = ref<string[]>([])
3434
const showRejectDialog = ref(false)
@@ -124,19 +124,19 @@ const displayedReviews = computed(() => {
124124
125125
const hasSelectedReviews = computed(() => selectedReviewIds.value.length > 0)
126126
127-
const loadTenants = async () => {
127+
const loadPrograms = async () => {
128128
try {
129129
const response = await getApps()
130-
tenants.value = response.data
130+
programs.value = response.data
131131
} catch (error) {
132-
console.error('Failed to load tenants', error)
132+
console.error('Failed to load programs list', error)
133133
}
134134
}
135135
136136
const loadReviews = async () => {
137-
if (!selectedTenantId.value) return
137+
if (!selectedProgramId.value) return
138138
try {
139-
await reviewsStore.fetchReviews(selectedTenantId.value)
139+
await reviewsStore.fetchReviews(selectedProgramId.value)
140140
} catch (error) {
141141
snackBarStore.showSnackbar('Failed to load reviews', 'error')
142142
console.error('Failed to load reviews', error)
@@ -184,13 +184,13 @@ const handleBulkApprove = async () => {
184184
}
185185
}
186186
187-
watch(selectedTenantId, () => {
187+
watch(selectedProgramId, () => {
188188
selectedReviewIds.value = []
189189
loadReviews()
190190
})
191191
192192
onMounted(() => {
193-
loadTenants()
193+
loadPrograms()
194194
})
195195
</script>
196196

@@ -217,8 +217,8 @@ onMounted(() => {
217217
<v-row class="mb-4" align="center">
218218
<v-col cols="12" md="5">
219219
<v-select
220-
v-model="selectedTenantId"
221-
:items="tenants"
220+
v-model="selectedProgramId"
221+
:items="programs"
222222
item-title="name"
223223
item-value="id"
224224
label="Select Collection Program"
@@ -242,12 +242,12 @@ onMounted(() => {
242242
</v-col>
243243
</v-row>
244244

245-
<v-alert v-if="!selectedTenantId" type="info" variant="tonal" class="mb-4">
245+
<v-alert v-if="!selectedProgramId" type="info" variant="tonal" class="mb-4">
246246
Select a collection program to view its reviews.
247247
</v-alert>
248248

249249
<v-data-table
250-
v-if="selectedTenantId"
250+
v-if="selectedProgramId"
251251
v-model="selectedReviewIds"
252252
:headers="headers"
253253
:items="displayedReviews"

0 commit comments

Comments
 (0)