Loading portal-gui/src/app/(app)/app-profiles/[appId]/page.tsx +28 −25 Original line number Diff line number Diff line Loading @@ -51,7 +51,7 @@ const ProfilePage = () => { <div className={styles.header}> <div className={styles.headerTop}> <div className={styles.badges}> <span className={styles.badge}>{app.packageType}</span> <span className={styles.badgeSecondary}>{app.packageType}</span> {app.appProvider && ( <span className={styles.badgeSecondary}>{app.appProvider}</span> )} Loading @@ -61,8 +61,9 @@ const ProfilePage = () => { <p className={styles.appId}>appID: {app.appId}</p> </div> {app.appRepo && ( <> <Divider label="Repository" color="#004a8d" /> <div className={styles.section}> <div className={styles.field}> <span className={styles.label}>Type</span> Loading @@ -85,10 +86,12 @@ const ProfilePage = () => { </div> )} </div> </> )} <Divider label="Components" color="#004a8d" /> {app.componentSpec.map((component) => ( {(app.componentSpec ?? []).map((component) => ( <div key={component.componentName} className={styles.componentBox}> <h3 className={styles.componentName}>{component.componentName}</h3> <table className={styles.table}> Loading portal-gui/src/app/(app)/my-applications/ProfileCard.tsx +13 −9 Original line number Diff line number Diff line Loading @@ -47,7 +47,7 @@ export const ProfileCard = ({ app, onDelete }: Props) => { <div className={styles.cardHeader}> <div className={styles.badges}> <span className={styles.badge}>{app.packageType}</span> <span className={styles.badgeSecondary}>{app.packageType}</span> {app.appProvider && ( <span className={styles.badgeSecondary}>{app.appProvider}</span> )} Loading Loading @@ -80,15 +80,19 @@ export const ProfileCard = ({ app, onDelete }: Props) => { <dd>{app.version}</dd> </div> {app.appRepo && ( <div className={styles.field}> <dt>Image</dt> <dd className={styles.mono}>{app.appRepo.imagePath}</dd> </div> )} {app.appRepo && ( <div className={styles.field}> <dt>Repo</dt> <dd>{app.appRepo.type}</dd> </div> )} <div className={styles.field}> <dt>Components</dt> Loading portal-gui/src/app/api/apps/route.ts +6 −7 Original line number Diff line number Diff line Loading @@ -11,15 +11,14 @@ export async function POST(req: Request) { cache: "no-store", }); if (!res.ok) { const err = await res.json().catch(() => ({})); return NextResponse.json( { error: err.message ?? `OEG returned ${res.status}` }, { status: res.status } ); const data = await res.json().catch(() => ({})); // OEG sometimes returns 200 with {"error": "...", "status_code": N} instead of a proper 4xx/5xx if (!res.ok || data.error) { const message = data.error ?? data.message ?? `OEG returned ${res.status}`; return NextResponse.json({ error: message }, { status: res.ok ? 502 : res.status }); } const data = await res.json(); return NextResponse.json(data, { status: 201 }); } catch { return NextResponse.json( Loading portal-gui/src/app/components/CreateProfileModal/CreateProfileModal.tsx +31 −2 Original line number Diff line number Diff line Loading @@ -130,7 +130,10 @@ export const CreateProfileModal = ({ open, onClose, onCreated }: Props) => { setError(null); setSubmitting(true); const appId = crypto.randomUUID(); const body: Record<string, unknown> = { appId, name: form.name, version: form.version, packageType: form.packageType, Loading Loading @@ -163,8 +166,34 @@ export const CreateProfileModal = ({ open, onClose, onCreated }: Props) => { }); if (res.ok) { const created: IApplicationProfile = await res.json(); onCreated(created); const responseData = await res.json().catch(() => ({})); const resolvedAppId: string = responseData.appId ?? appId; const newApp: IApplicationProfile = { appId: resolvedAppId, name: form.name, version: form.version, packageType: form.packageType, ...(form.appProvider && { appProvider: form.appProvider }), appRepo: { type: form.repoType, imagePath: form.imagePath, ...(form.repoType === "PRIVATEREPO" && { ...(form.userName && { userName: form.userName }), ...(form.credentials && { credentials: form.credentials }), ...(form.authType !== "NONE" && { authType: form.authType }), }), }, componentSpec: form.componentSpec.map((c) => ({ componentName: c.componentName, networkInterfaces: c.networkInterfaces.map((n) => ({ interfaceId: n.interfaceId, protocol: n.protocol, port: parseInt(n.port, 10), visibilityType: n.visibilityType, })), })), }; onCreated(newApp); setForm(initialForm()); onClose(); } else { Loading portal-gui/src/app/utils/interfaces.ts +1 −1 Original line number Diff line number Diff line Loading @@ -25,7 +25,7 @@ export interface IApplicationProfile { version: string; packageType: "QCOW2" | "OVA" | "CONTAINER" | "HELM"; appProvider?: string; appRepo: IOEGAppRepo; appRepo?: IOEGAppRepo; componentSpec?: IOEGComponentSpec[]; requiredResources?: Record<string, unknown>; } Loading Loading
portal-gui/src/app/(app)/app-profiles/[appId]/page.tsx +28 −25 Original line number Diff line number Diff line Loading @@ -51,7 +51,7 @@ const ProfilePage = () => { <div className={styles.header}> <div className={styles.headerTop}> <div className={styles.badges}> <span className={styles.badge}>{app.packageType}</span> <span className={styles.badgeSecondary}>{app.packageType}</span> {app.appProvider && ( <span className={styles.badgeSecondary}>{app.appProvider}</span> )} Loading @@ -61,8 +61,9 @@ const ProfilePage = () => { <p className={styles.appId}>appID: {app.appId}</p> </div> {app.appRepo && ( <> <Divider label="Repository" color="#004a8d" /> <div className={styles.section}> <div className={styles.field}> <span className={styles.label}>Type</span> Loading @@ -85,10 +86,12 @@ const ProfilePage = () => { </div> )} </div> </> )} <Divider label="Components" color="#004a8d" /> {app.componentSpec.map((component) => ( {(app.componentSpec ?? []).map((component) => ( <div key={component.componentName} className={styles.componentBox}> <h3 className={styles.componentName}>{component.componentName}</h3> <table className={styles.table}> Loading
portal-gui/src/app/(app)/my-applications/ProfileCard.tsx +13 −9 Original line number Diff line number Diff line Loading @@ -47,7 +47,7 @@ export const ProfileCard = ({ app, onDelete }: Props) => { <div className={styles.cardHeader}> <div className={styles.badges}> <span className={styles.badge}>{app.packageType}</span> <span className={styles.badgeSecondary}>{app.packageType}</span> {app.appProvider && ( <span className={styles.badgeSecondary}>{app.appProvider}</span> )} Loading Loading @@ -80,15 +80,19 @@ export const ProfileCard = ({ app, onDelete }: Props) => { <dd>{app.version}</dd> </div> {app.appRepo && ( <div className={styles.field}> <dt>Image</dt> <dd className={styles.mono}>{app.appRepo.imagePath}</dd> </div> )} {app.appRepo && ( <div className={styles.field}> <dt>Repo</dt> <dd>{app.appRepo.type}</dd> </div> )} <div className={styles.field}> <dt>Components</dt> Loading
portal-gui/src/app/api/apps/route.ts +6 −7 Original line number Diff line number Diff line Loading @@ -11,15 +11,14 @@ export async function POST(req: Request) { cache: "no-store", }); if (!res.ok) { const err = await res.json().catch(() => ({})); return NextResponse.json( { error: err.message ?? `OEG returned ${res.status}` }, { status: res.status } ); const data = await res.json().catch(() => ({})); // OEG sometimes returns 200 with {"error": "...", "status_code": N} instead of a proper 4xx/5xx if (!res.ok || data.error) { const message = data.error ?? data.message ?? `OEG returned ${res.status}`; return NextResponse.json({ error: message }, { status: res.ok ? 502 : res.status }); } const data = await res.json(); return NextResponse.json(data, { status: 201 }); } catch { return NextResponse.json( Loading
portal-gui/src/app/components/CreateProfileModal/CreateProfileModal.tsx +31 −2 Original line number Diff line number Diff line Loading @@ -130,7 +130,10 @@ export const CreateProfileModal = ({ open, onClose, onCreated }: Props) => { setError(null); setSubmitting(true); const appId = crypto.randomUUID(); const body: Record<string, unknown> = { appId, name: form.name, version: form.version, packageType: form.packageType, Loading Loading @@ -163,8 +166,34 @@ export const CreateProfileModal = ({ open, onClose, onCreated }: Props) => { }); if (res.ok) { const created: IApplicationProfile = await res.json(); onCreated(created); const responseData = await res.json().catch(() => ({})); const resolvedAppId: string = responseData.appId ?? appId; const newApp: IApplicationProfile = { appId: resolvedAppId, name: form.name, version: form.version, packageType: form.packageType, ...(form.appProvider && { appProvider: form.appProvider }), appRepo: { type: form.repoType, imagePath: form.imagePath, ...(form.repoType === "PRIVATEREPO" && { ...(form.userName && { userName: form.userName }), ...(form.credentials && { credentials: form.credentials }), ...(form.authType !== "NONE" && { authType: form.authType }), }), }, componentSpec: form.componentSpec.map((c) => ({ componentName: c.componentName, networkInterfaces: c.networkInterfaces.map((n) => ({ interfaceId: n.interfaceId, protocol: n.protocol, port: parseInt(n.port, 10), visibilityType: n.visibilityType, })), })), }; onCreated(newApp); setForm(initialForm()); onClose(); } else { Loading
portal-gui/src/app/utils/interfaces.ts +1 −1 Original line number Diff line number Diff line Loading @@ -25,7 +25,7 @@ export interface IApplicationProfile { version: string; packageType: "QCOW2" | "OVA" | "CONTAINER" | "HELM"; appProvider?: string; appRepo: IOEGAppRepo; appRepo?: IOEGAppRepo; componentSpec?: IOEGComponentSpec[]; requiredResources?: Record<string, unknown>; } Loading