Skip to content

Commit f37ce6f

Browse files
committed
Mandates and Customer At
1 parent 6f1988c commit f37ce6f

File tree

21 files changed

+5340
-80
lines changed

21 files changed

+5340
-80
lines changed

src/lib/components/OpeyInsightBar.svelte

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -33,9 +33,9 @@
3333
// Ask Opey for a short insight based on notebook + current page
3434
const insight = await insightService.getInsight(pageContext, recentNotes);
3535
36-
insightText = insight || "";
36+
insightText = insight || pageContext;
3737
} catch {
38-
insightText = "";
38+
insightText = pageContext;
3939
} finally {
4040
loading = false;
4141
}

src/lib/config/insightMessages.ts

Lines changed: 49 additions & 49 deletions
Original file line numberDiff line numberDiff line change
@@ -4,60 +4,60 @@
44
*/
55

66
const routeDescriptions: Array<{ pattern: RegExp; description: string }> = [
7-
{ pattern: /^\/banks\/create$/, description: "Create Bank page" },
8-
{ pattern: /^\/banks\/([^/]+)\/([^/]+)/, description: "Bank detail page" },
9-
{ pattern: /^\/banks$/, description: "Banks list page" },
10-
{ pattern: /^\/users$/, description: "Users list page" },
11-
{ pattern: /^\/consumers$/, description: "API Consumers list page" },
12-
{ pattern: /^\/aggregate-metrics/, description: "Aggregate Metrics page" },
13-
{ pattern: /^\/connector-metrics/, description: "Connector Metrics page" },
14-
{ pattern: /^\/connector-traces/, description: "Connector Traces page" },
15-
{ pattern: /^\/connector-counts/, description: "Connector Counts page" },
16-
{ pattern: /^\/metrics/, description: "API Metrics page" },
17-
{ pattern: /^\/rbac\/entitlements\/create/, description: "Create Entitlement page" },
18-
{ pattern: /^\/rbac\/entitlements/, description: "Entitlements page" },
19-
{ pattern: /^\/rbac\/roles/, description: "Roles page" },
20-
{ pattern: /^\/rbac\/groups/, description: "Groups page" },
21-
{ pattern: /^\/rbac\/memberships/, description: "Memberships page" },
22-
{ pattern: /^\/rbac\/entitlement-requests/, description: "Entitlement Requests page" },
23-
{ pattern: /^\/rbac\/banks/, description: "RBAC Banks page" },
24-
{ pattern: /^\/system\/cache/, description: "System Cache page" },
25-
{ pattern: /^\/system\/config-props/, description: "System Config Props page" },
26-
{ pattern: /^\/system\/database-pool/, description: "Database Pool page" },
27-
{ pattern: /^\/system\/migrations/, description: "System Migrations page" },
28-
{ pattern: /^\/system\/webui-props/, description: "WebUI Props page" },
29-
{ pattern: /^\/system\/signal/, description: "Signals page" },
30-
{ pattern: /^\/system\//, description: "System settings page" },
31-
{ pattern: /^\/products\/financial/, description: "Financial Products page" },
32-
{ pattern: /^\/products\/collections/, description: "Product Collections page" },
33-
{ pattern: /^\/products\/bootstrap/, description: "Products Bootstrap page" },
34-
{ pattern: /^\/products/, description: "API Products page" },
35-
{ pattern: /^\/dynamic-entities\/diagnostics/, description: "Dynamic Entities Diagnostics page" },
36-
{ pattern: /^\/dynamic-entities\/personal/, description: "Personal Dynamic Entities page" },
37-
{ pattern: /^\/dynamic-entities/, description: "Dynamic Entities page" },
38-
{ pattern: /^\/dynamic-endpoints/, description: "Dynamic Endpoints page" },
39-
{ pattern: /^\/customers\/individual/, description: "Individual Customers page" },
40-
{ pattern: /^\/customers\/corporate/, description: "Corporate Customers page" },
41-
{ pattern: /^\/customers/, description: "Customers page" },
42-
{ pattern: /^\/account-access\/system-views/, description: "System Views page" },
43-
{ pattern: /^\/account-access\/custom-views/, description: "Custom Views page" },
44-
{ pattern: /^\/account-access\/account-directory/, description: "Account Directory page" },
45-
{ pattern: /^\/account-access\/accounts/, description: "My Accounts page" },
46-
{ pattern: /^\/account-access/, description: "Account Access page" },
47-
{ pattern: /^\/user\/consents/, description: "My Consents page" },
48-
{ pattern: /^\/user\/entitlements/, description: "My Entitlements page" },
49-
{ pattern: /^\/user$/, description: "My Profile page" },
50-
{ pattern: /^\/abac/, description: "ABAC Rules page" },
51-
{ pattern: /^\/integration/, description: "Integration / Method Routings page" },
52-
{ pattern: /^\/api-collections/, description: "API Collections page" },
53-
{ pattern: /^\/site-map$/, description: "Site Map page" },
54-
{ pattern: /^\/about$/, description: "About page" },
7+
{ pattern: /^\/banks\/create$/, description: "Create Bank" },
8+
{ pattern: /^\/banks\/([^/]+)\/([^/]+)/, description: "Bank detail" },
9+
{ pattern: /^\/banks$/, description: "Banks list" },
10+
{ pattern: /^\/users$/, description: "Users list" },
11+
{ pattern: /^\/consumers$/, description: "API Consumers list" },
12+
{ pattern: /^\/aggregate-metrics/, description: "Aggregate Metrics" },
13+
{ pattern: /^\/connector-metrics/, description: "Connector Metrics" },
14+
{ pattern: /^\/connector-traces/, description: "Connector Traces" },
15+
{ pattern: /^\/connector-counts/, description: "Connector Counts" },
16+
{ pattern: /^\/metrics/, description: "API Metrics" },
17+
{ pattern: /^\/rbac\/entitlements\/create/, description: "Create Entitlement" },
18+
{ pattern: /^\/rbac\/entitlements/, description: "Entitlements" },
19+
{ pattern: /^\/rbac\/roles/, description: "Roles" },
20+
{ pattern: /^\/rbac\/groups/, description: "Groups" },
21+
{ pattern: /^\/rbac\/memberships/, description: "Memberships" },
22+
{ pattern: /^\/rbac\/entitlement-requests/, description: "Entitlement Requests" },
23+
{ pattern: /^\/rbac\/banks/, description: "RBAC Banks" },
24+
{ pattern: /^\/system\/cache/, description: "System Cache" },
25+
{ pattern: /^\/system\/config-props/, description: "System Config Props" },
26+
{ pattern: /^\/system\/database-pool/, description: "Database Pool" },
27+
{ pattern: /^\/system\/migrations/, description: "System Migrations" },
28+
{ pattern: /^\/system\/webui-props/, description: "WebUI Props" },
29+
{ pattern: /^\/system\/signal/, description: "Signals" },
30+
{ pattern: /^\/system\//, description: "System settings" },
31+
{ pattern: /^\/products\/financial/, description: "Financial Products" },
32+
{ pattern: /^\/products\/collections/, description: "Product Collections" },
33+
{ pattern: /^\/products\/bootstrap/, description: "Products Bootstrap" },
34+
{ pattern: /^\/products/, description: "API Products" },
35+
{ pattern: /^\/dynamic-entities\/diagnostics/, description: "Dynamic Entities Diagnostics" },
36+
{ pattern: /^\/dynamic-entities\/personal/, description: "Personal Dynamic Entities" },
37+
{ pattern: /^\/dynamic-entities/, description: "Dynamic Entities" },
38+
{ pattern: /^\/dynamic-endpoints/, description: "Dynamic Endpoints" },
39+
{ pattern: /^\/customers\/individual/, description: "Individual Customers" },
40+
{ pattern: /^\/customers\/corporate/, description: "Corporate Customers" },
41+
{ pattern: /^\/customers/, description: "Customers" },
42+
{ pattern: /^\/account-access\/system-views/, description: "System Views" },
43+
{ pattern: /^\/account-access\/custom-views/, description: "Custom Views" },
44+
{ pattern: /^\/account-access\/account-directory/, description: "Account Directory" },
45+
{ pattern: /^\/account-access\/accounts/, description: "My Accounts" },
46+
{ pattern: /^\/account-access/, description: "Account Access" },
47+
{ pattern: /^\/user\/consents/, description: "My Consents" },
48+
{ pattern: /^\/user\/entitlements/, description: "My Entitlements" },
49+
{ pattern: /^\/user$/, description: "My Profile" },
50+
{ pattern: /^\/abac/, description: "ABAC Rules" },
51+
{ pattern: /^\/integration/, description: "Integration / Method Routings" },
52+
{ pattern: /^\/api-collections/, description: "API Collections" },
53+
{ pattern: /^\/site-map$/, description: "Site Map" },
54+
{ pattern: /^\/about$/, description: "About" },
5555
];
5656

5757
/**
5858
* Get a human-readable description of the current page for Opey context.
5959
*/
6060
export function describeRoute(pathname: string): string {
6161
const match = routeDescriptions.find((r) => r.pattern.test(pathname));
62-
return match ? match.description : `Page: ${pathname}`;
62+
return match ? match.description : pathname;
6363
}

src/lib/config/navigation.ts

Lines changed: 12 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -34,6 +34,7 @@ import {
3434
Hash,
3535
Map,
3636
Radio,
37+
FileSignature,
3738
} from "@lucide/svelte";
3839
import { env } from "$env/dynamic/public";
3940

@@ -336,11 +337,21 @@ function buildAccountAccessItems(): NavigationItem[] {
336337
label: "Custom Views",
337338
iconComponent: Eye,
338339
},
340+
{
341+
href: "/account-access/view-permissions",
342+
label: "View Permissions",
343+
iconComponent: Shield,
344+
},
339345
{
340346
href: "/account-access/account-directory",
341347
label: "Account Directory",
342348
iconComponent: FolderOpen,
343349
},
350+
{
351+
href: "/mandates",
352+
label: "Mandates",
353+
iconComponent: FileSignature,
354+
},
344355
];
345356

346357
return items;
@@ -584,7 +595,7 @@ export const navSections: NavigationSection[] = [
584595
{ id: "rbac", label: "RBAC", iconComponent: Shield, items: rbacItems, basePaths: ["/rbac"] },
585596
{ id: "banks", label: "Banks", iconComponent: Building2, items: banksItems, basePaths: ["/banks"] },
586597
{ id: "customers", label: "Customers", iconComponent: Users, items: customersItems, basePaths: ["/customers"] },
587-
{ id: "account-access", label: "Account Access", iconComponent: Landmark, items: accountAccessItems, basePaths: ["/account-access"] },
598+
{ id: "account-access", label: "Account Access", iconComponent: Landmark, items: accountAccessItems, basePaths: ["/account-access", "/mandates"] },
588599
{ id: "dynamic-entities", label: "Dynamic Entities", iconComponent: Box, items: dynamicEntitiesItems, basePaths: ["/dynamic-entities"] },
589600
{ id: "dynamic-endpoints", label: "Dynamic Endpoints", iconComponent: Plug, items: dynamicEndpointsItems, basePaths: ["/dynamic-endpoints"] },
590601
];

src/lib/utils/roleChecker.ts

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -228,6 +228,23 @@ export const SITE_MAP: Record<string, PageRoleConfig> = {
228228
optional: [{ role: "CanExecuteAbacRule" }],
229229
},
230230

231+
// ── Mandates ────────────────────────────────────────────
232+
"/mandates/[bank_id]/[account_id]": {
233+
required: [{ role: "CanGetMandate", bankScoped: true }],
234+
},
235+
"/mandates/[bank_id]/[account_id]/create": {
236+
required: [{ role: "CanCreateMandate", bankScoped: true }],
237+
},
238+
"/mandates/[bank_id]/[account_id]/[mandate_id]": {
239+
required: [{ role: "CanGetMandate", bankScoped: true }],
240+
optional: [
241+
{ role: "CanCreateSignatoryPanel", bankScoped: true },
242+
{ role: "CanCreateMandateProvision", bankScoped: true },
243+
{ role: "CanGetSignatoryPanel", bankScoped: true },
244+
{ role: "CanGetMandateProvision", bankScoped: true },
245+
],
246+
},
247+
231248
// ── Customers ───────────────────────────────────────────
232249
"/customers/individual": {
233250
required: [{ role: "CanGetCustomersAtOneBank", bankScoped: true }],

src/routes/(protected)/account-access/accounts/[bank_id]/[account_id]/[view_id]/+page.svelte

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
<script lang="ts">
22
import { page } from "$app/state";
3-
import { Landmark, ArrowLeft, Loader2, User, Tag, Route, Copy, Check, Plus } from "@lucide/svelte";
3+
import { Landmark, ArrowLeft, Loader2, User, Tag, Route, Copy, Check, Plus, FileSignature } from "@lucide/svelte";
44
import { trackedFetch } from "$lib/utils/trackedFetch";
55
import MissingRoleAlert from "$lib/components/MissingRoleAlert.svelte";
66
@@ -305,6 +305,10 @@
305305
</div>
306306
</div>
307307
<div class="header-actions">
308+
<a href="/mandates/{encodeURIComponent(bankId)}/{encodeURIComponent(accountId)}" class="btn-secondary" data-testid="view-mandates">
309+
<FileSignature size={16} />
310+
Mandates
311+
</a>
308312
<a href="/account-access/accounts" class="btn-secondary">
309313
<ArrowLeft size={16} />
310314
Back

src/routes/(protected)/account-access/custom-views/create/+page.svelte

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -169,9 +169,9 @@
169169
const result = await response.json();
170170
submitSuccess = true;
171171
172-
// Redirect to the new view's detail page after showing success message
172+
// Redirect to the custom views list after showing success message
173173
setTimeout(() => {
174-
goto(`/account-access/custom-views/${result.view_id}`);
174+
goto("/account-access/custom-views");
175175
}, 2500);
176176
} catch (err) {
177177
submitError =
Lines changed: 80 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,80 @@
1+
import type { PageServerLoad } from "./$types";
2+
import { error } from "@sveltejs/kit";
3+
import { createLogger } from "$lib/utils/logger";
4+
import { obp_requests } from "$lib/obp/requests";
5+
import { SessionOAuthHelper } from "$lib/oauth/sessionHelper";
6+
7+
const logger = createLogger("ViewPermissionsPageServer");
8+
9+
interface ViewPermission {
10+
permission: string;
11+
category: string;
12+
}
13+
14+
interface ViewPermissionsResponse {
15+
permissions: ViewPermission[];
16+
}
17+
18+
export const load: PageServerLoad = async ({ locals }) => {
19+
const session = locals.session;
20+
21+
if (!session?.data?.user) {
22+
throw error(401, "Unauthorized");
23+
}
24+
25+
const sessionOAuth = SessionOAuthHelper.getSessionOAuth(session);
26+
const accessToken = sessionOAuth?.accessToken;
27+
28+
if (!accessToken) {
29+
logger.warn("No access token available for view permissions page");
30+
return {
31+
hasApiAccess: false,
32+
permissionsByCategory: [],
33+
totalCount: 0,
34+
error: "No API access token available",
35+
};
36+
}
37+
38+
try {
39+
const endpoint = `/obp/v6.0.0/management/view-permissions`;
40+
logger.info(`Fetching view permissions from ${endpoint}`);
41+
42+
const response: ViewPermissionsResponse = await obp_requests.get(
43+
endpoint,
44+
accessToken,
45+
);
46+
47+
const categoryMap = new Map<string, string[]>();
48+
for (const p of response.permissions) {
49+
const perms = categoryMap.get(p.category) || [];
50+
perms.push(p.permission);
51+
categoryMap.set(p.category, perms);
52+
}
53+
54+
const permissionsByCategory = Array.from(categoryMap.entries()).map(
55+
([category, permissions]) => ({ category, permissions }),
56+
);
57+
58+
logger.info(
59+
`Loaded ${response.permissions.length} permissions in ${permissionsByCategory.length} categories`,
60+
);
61+
62+
return {
63+
hasApiAccess: true,
64+
permissionsByCategory,
65+
totalCount: response.permissions.length,
66+
};
67+
} catch (err) {
68+
logger.error("Error loading view permissions:", err);
69+
70+
return {
71+
hasApiAccess: true,
72+
permissionsByCategory: [],
73+
totalCount: 0,
74+
error:
75+
err instanceof Error
76+
? err.message
77+
: "Failed to load view permissions",
78+
};
79+
}
80+
};

0 commit comments

Comments
 (0)