Skip to content

Commit 67935ee

Browse files
committed
Entity Requests pagination + Lock / Unlock
1 parent 52639a1 commit 67935ee

16 files changed

Lines changed: 2171 additions & 66 deletions

File tree

src/lib/utils/roleChecker.ts

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -205,6 +205,12 @@ export const SITE_MAP: Record<string, PageRoleConfig> = {
205205
"/users/[provider]/[username]": {
206206
required: [{ role: "CanGetAnyUser" }],
207207
},
208+
"/users/[provider]/[username]/unlock": {
209+
required: [{ role: "CanUnlockUser" }],
210+
},
211+
"/users/[provider]/[username]/lock": {
212+
required: [{ role: "CanLockUser" }],
213+
},
208214

209215
// ── Dynamic Entities ──────────────────────────────────
210216
"/dynamic-entities/diagnostics": {

src/routes/(protected)/dynamic-entities/system/[id]/crud/+page.svelte

Lines changed: 0 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -915,11 +915,6 @@
915915
<table class="min-w-full divide-y divide-gray-200 dark:divide-gray-700">
916916
<thead class="bg-gray-50 dark:bg-gray-900">
917917
<tr>
918-
<th
919-
class="px-6 py-3 text-left text-xs font-medium uppercase tracking-wider text-gray-500 dark:text-gray-400"
920-
>
921-
ID
922-
</th>
923918
{#each Object.keys(properties).slice(0, 4) as fieldName}
924919
<th
925920
class="px-6 py-3 text-left text-xs font-medium uppercase tracking-wider text-gray-500 dark:text-gray-400"
@@ -939,13 +934,7 @@
939934
>
940935
{#each filteredRecords as record, index}
941936
{@const recordData = getRecordData(record)}
942-
{@const recordId = getRecordId(record)}
943937
<tr class="hover:bg-gray-50 dark:hover:bg-gray-700/50">
944-
<td
945-
class="max-w-xs truncate px-6 py-4 text-sm font-mono text-gray-600 dark:text-gray-400"
946-
>
947-
{recordId || "-"}
948-
</td>
949938
{#each Object.keys(properties).slice(0, 4) as fieldName}
950939
<td
951940
class="max-w-xs truncate px-6 py-4 text-sm text-gray-900 dark:text-gray-100"
@@ -1339,14 +1328,6 @@
13391328

13401329
<div class="bg-white p-6 dark:bg-gray-800">
13411330
<dl class="space-y-4">
1342-
<div>
1343-
<dt class="text-sm font-medium text-gray-500 dark:text-gray-400">
1344-
ID
1345-
</dt>
1346-
<dd class="mt-1 text-sm font-mono text-gray-600 dark:text-gray-400">
1347-
{getRecordId(selectedRecord) || "-"}
1348-
</dd>
1349-
</div>
13501331
{#each Object.entries(properties) as [fieldName, fieldDef]}
13511332
{@const recordData = getRecordData(selectedRecord)}
13521333
<div>

src/routes/(protected)/rbac/entitlement-requests/+page.server.ts

Lines changed: 14 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -21,7 +21,9 @@ interface EntitlementRequestsResponse {
2121
entitlement_requests: EntitlementRequest[];
2222
}
2323

24-
export const load: PageServerLoad = async ({ locals }) => {
24+
const DEFAULT_LIMIT = 50;
25+
26+
export const load: PageServerLoad = async ({ locals, url }) => {
2527
const session = locals.session;
2628

2729
if (!session?.data?.user) {
@@ -38,12 +40,18 @@ export const load: PageServerLoad = async ({ locals }) => {
3840
entitlementRequests: [],
3941
hasApiAccess: false,
4042
error: "No API access token available",
43+
limit: DEFAULT_LIMIT,
44+
offset: 0,
4145
};
4246
}
4347

48+
const limit = parseInt(url.searchParams.get("limit") || String(DEFAULT_LIMIT));
49+
const offset = parseInt(url.searchParams.get("offset") || "0");
50+
const sortDirection = url.searchParams.get("sort_direction") || "DESC";
51+
4452
try {
4553
logger.info("=== ENTITLEMENT REQUESTS API CALL ===");
46-
const endpoint = `/obp/v6.0.0/entitlement-requests`;
54+
const endpoint = `/obp/v6.0.0/entitlement-requests?limit=${limit}&offset=${offset}&sort_direction=${sortDirection}`;
4755
logger.info(`Request: ${endpoint}`);
4856

4957
const response: EntitlementRequestsResponse = await obp_requests.get(
@@ -58,6 +66,8 @@ export const load: PageServerLoad = async ({ locals }) => {
5866
return {
5967
entitlementRequests: response.entitlement_requests || [],
6068
hasApiAccess: true,
69+
limit,
70+
offset,
6171
};
6272
} catch (err) {
6373
logger.error("Error loading entitlement requests:", err);
@@ -69,6 +79,8 @@ export const load: PageServerLoad = async ({ locals }) => {
6979
err instanceof Error
7080
? err.message
7181
: "Failed to load entitlement requests",
82+
limit,
83+
offset,
7284
};
7385
}
7486
};

src/routes/(protected)/rbac/entitlement-requests/+page.svelte

Lines changed: 119 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,9 +8,12 @@
88
User,
99
Calendar,
1010
ArrowUpDown,
11+
ChevronLeft,
12+
ChevronRight,
1113
} from "@lucide/svelte";
1214
import { toast } from "$lib/utils/toastService";
1315
import { trackedFetch } from "$lib/utils/trackedFetch";
16+
import { goto, invalidateAll } from "$app/navigation";
1417
1518
interface EntitlementRequest {
1619
entitlement_request_id: string;
@@ -29,6 +32,10 @@
2932
let entitlementRequests = $derived(data.entitlementRequests || []);
3033
let hasApiAccess = $derived(data.hasApiAccess);
3134
let error = $derived(data.error);
35+
let limit = $derived(data.limit || 50);
36+
let offset = $derived(data.offset || 0);
37+
let hasNextPage = $derived(entitlementRequests.length >= limit);
38+
let hasPreviousPage = $derived(offset > 0);
3239
// Search state
3340
let searchQuery = $state("");
3441
@@ -68,6 +75,17 @@
6875
return sorted;
6976
});
7077
78+
// Pagination
79+
function nextPage() {
80+
const newOffset = offset + limit;
81+
goto(`?limit=${limit}&offset=${newOffset}&sort_direction=DESC`);
82+
}
83+
84+
function previousPage() {
85+
const newOffset = Math.max(0, offset - limit);
86+
goto(`?limit=${limit}&offset=${newOffset}&sort_direction=DESC`);
87+
}
88+
7189
// Helper function to format date
7290
function formatDate(dateString: string): string {
7391
try {
@@ -159,7 +177,7 @@
159177
);
160178
161179
setTimeout(() => {
162-
window.location.reload();
180+
invalidateAll();
163181
}, 1000);
164182
return;
165183
}
@@ -446,6 +464,34 @@
446464
</div>
447465
{/each}
448466
</div>
467+
468+
<!-- Pagination -->
469+
<div class="pagination">
470+
<div class="pagination-info">
471+
Showing {offset + 1}–{offset + entitlementRequests.length}
472+
{#if offset > 0}
473+
<span class="pagination-offset">(offset: {offset})</span>
474+
{/if}
475+
</div>
476+
<div class="pagination-buttons">
477+
<button
478+
class="btn-pagination"
479+
onclick={previousPage}
480+
disabled={!hasPreviousPage}
481+
>
482+
<ChevronLeft size={16} />
483+
Previous
484+
</button>
485+
<button
486+
class="btn-pagination"
487+
onclick={nextPage}
488+
disabled={!hasNextPage}
489+
>
490+
Next
491+
<ChevronRight size={16} />
492+
</button>
493+
</div>
494+
</div>
449495
</div>
450496
{/if}
451497
</div>
@@ -958,6 +1004,78 @@
9581004
background: rgb(var(--color-error-700));
9591005
}
9601006
1007+
.pagination {
1008+
display: flex;
1009+
justify-content: space-between;
1010+
align-items: center;
1011+
margin-top: 1.5rem;
1012+
padding-top: 1rem;
1013+
border-top: 1px solid #e5e7eb;
1014+
}
1015+
1016+
:global([data-mode="dark"]) .pagination {
1017+
border-top-color: rgb(var(--color-surface-700));
1018+
}
1019+
1020+
.pagination-info {
1021+
font-size: 0.875rem;
1022+
color: #6b7280;
1023+
}
1024+
1025+
:global([data-mode="dark"]) .pagination-info {
1026+
color: var(--color-surface-400);
1027+
}
1028+
1029+
.pagination-offset {
1030+
margin-left: 0.25rem;
1031+
color: #9ca3af;
1032+
}
1033+
1034+
:global([data-mode="dark"]) .pagination-offset {
1035+
color: var(--color-surface-500);
1036+
}
1037+
1038+
.pagination-buttons {
1039+
display: flex;
1040+
gap: 0.5rem;
1041+
}
1042+
1043+
.btn-pagination {
1044+
display: flex;
1045+
align-items: center;
1046+
gap: 0.25rem;
1047+
padding: 0.5rem 1rem;
1048+
border: 1px solid #d1d5db;
1049+
border-radius: 6px;
1050+
font-size: 0.875rem;
1051+
font-weight: 500;
1052+
background: white;
1053+
color: #374151;
1054+
cursor: pointer;
1055+
transition: all 0.2s;
1056+
}
1057+
1058+
.btn-pagination:hover:not(:disabled) {
1059+
background: #f9fafb;
1060+
border-color: #9ca3af;
1061+
}
1062+
1063+
.btn-pagination:disabled {
1064+
opacity: 0.5;
1065+
cursor: not-allowed;
1066+
}
1067+
1068+
:global([data-mode="dark"]) .btn-pagination {
1069+
background: rgb(var(--color-surface-700));
1070+
border-color: rgb(var(--color-surface-600));
1071+
color: var(--color-surface-200);
1072+
}
1073+
1074+
:global([data-mode="dark"]) .btn-pagination:hover:not(:disabled) {
1075+
background: rgb(var(--color-surface-600));
1076+
border-color: rgb(var(--color-surface-500));
1077+
}
1078+
9611079
@media (max-width: 768px) {
9621080
.header-top {
9631081
flex-direction: column;

src/routes/(protected)/users/[provider]/[username]/+page.server.ts

Lines changed: 11 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -5,17 +5,6 @@ import { obp_requests } from "$lib/obp/requests";
55
import { SessionOAuthHelper } from "$lib/oauth/sessionHelper";
66
import { error } from "@sveltejs/kit";
77

8-
interface UserDetail {
9-
user_id: string;
10-
username: string;
11-
email: string;
12-
provider: string;
13-
created_date: string;
14-
entitlements?: any[];
15-
views?: any[];
16-
accounts?: any[];
17-
}
18-
198
export const load: PageServerLoad = async ({ locals, params }) => {
209
const session = locals.session;
2110

@@ -42,8 +31,10 @@ export const load: PageServerLoad = async ({ locals, params }) => {
4231
};
4332
}
4433

34+
let user = null;
35+
4536
try {
46-
// Fetch user details from OBP API using v5.1.0 endpoint
37+
// Fetch user details from OBP API
4738
logger.info("=== USER DETAIL API CALL ===");
4839
const endpoint = `/obp/v6.0.0/users/provider/${encodeURIComponent(provider)}/username/${encodeURIComponent(username)}`;
4940
logger.info(`Request: ${endpoint}`);
@@ -53,12 +44,7 @@ export const load: PageServerLoad = async ({ locals, params }) => {
5344
logger.info(`Response: User ${username} from provider ${provider}`);
5445

5546
if (response) {
56-
return {
57-
user: response,
58-
provider,
59-
username,
60-
hasApiAccess: true,
61-
};
47+
user = response;
6248
} else {
6349
logger.warn("NO USER DATA IN RESPONSE");
6450
return {
@@ -84,4 +70,11 @@ export const load: PageServerLoad = async ({ locals, params }) => {
8470
error: err instanceof Error ? err.message : "Failed to load user details",
8571
};
8672
}
73+
74+
return {
75+
user,
76+
provider,
77+
username,
78+
hasApiAccess: true,
79+
};
8780
};

0 commit comments

Comments
 (0)