{api.category}
+ +{api.description}
++ Version: {api.version} +
++ Protocol: REST +
++ Status: Active +
+{api.instructions}
+Available operations
+ {api.functionalities.some((f) => f.length > 30) ? ( +Swagger spec not available
+ } */} +appID: {app.appId}
+| Protocol | +Port | +Visibility | +
|---|---|---|
| {iface.protocol} | +{iface.port} | ++ + {iface.visibilityType === "VISIBILITY_EXTERNAL" + ? "External" + : "Internal"} + + | +
+ {JSON.stringify(app.requiredResources, null, 2)}
+
+ >
+ )}
+ Loading applications…
} + {appsError &&{appsError}
} + {!appsLoading && !appsError && apps.length === 0 && ( +No applications registered yet.
+ )} +Loading deployments…
} + {instancesError &&{instancesError}
} + {!instancesLoading && !instancesError && instances.length === 0 && ( +No running deployments found.
+ )} ++ Name: + {user.name} +
++ Email: {user.email} +
++ Role: {user.role} +
++ Organization: {user.organization} +
++ Plan: + Enterprize +
++ Billing Card: 1234 - xxxx - xxxx - 1234 +
+ ++ eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJlNzhkYjViNy1hMjU0LTQ5MDctOTFhYy1jN2ZlNWNkZDhjZWIiLCJuYW1lIjoiSm9obiBEb2UiLCJlbWFpbCI6ImplYW5AZXhhbXBsZS5jb20iLCJyb2xlcyI6WyJkb2N0b3IiLCJhZG1pbiJdLCJvcmdhbml6YXRpb24iOiJIZWFsdGhDYXJlIiwiaWF0IjoxNjk3MzAwODAwLCJleHAiOjE2OTczMDQ0MDB9.nXv6bA_RdSxqP2Zz0xQ9P8TVL2lfH3zdrRix_M46YRM +
++ Send invitation to a new developer +
+{api.category}
+ {/*+ {" "} + {truncate(api.description, { by: "chars", length: 110 })} +
+ +); diff --git a/portal-gui/src/app/components/Card/card.module.scss b/portal-gui/src/app/components/Card/card.module.scss new file mode 100644 index 0000000000000000000000000000000000000000..60cb4d9a1ecc2d736805a79bb685a01e6dbbc8d1 --- /dev/null +++ b/portal-gui/src/app/components/Card/card.module.scss @@ -0,0 +1,66 @@ + +.card { + background: linear-gradient(314deg, #ffffff 0%, #f4f4f4 100%); + padding: 1.5rem; + border-radius: 8px; + border: 1px solid var(--orange-color); + box-shadow: 4px 4px 8px rgba(0, 0, 0, 0.05); + display: flex; + cursor: pointer; + flex-direction: column; + gap: 0.6rem; + transition: all 0.2s ease; + + &:hover { + box-shadow: 6px 6px 12px rgba(0, 0, 0, 0.1); + transform: translateY(-2px); + } + + h2 { + color: var(--blue-color); + margin-bottom: 0.25rem; + font-size: 1.5rem; + } +} + +.title { + font-size: 1.2rem; + font-weight: 600; + margin: 0; +} + +.category { + font-size: 0.85rem; + color: var(--orange-color); + font-weight: 700; +} + +.provider, +.size { + font-size: 0.85rem; +} + +.functionalities { + display: flex; + flex-wrap: wrap; + gap: 0.4rem; + margin-top: 0.4rem; +} + +.badge { + background: linear-gradient(45deg, #f159225b 0%, #fdbb135b 100%); + color: var(--blue-color); + font-size: 0.6rem; + padding: 0.3rem 0.7rem; + border-radius: 50px; + font-weight: 500; + white-space: nowrap; + opacity: 0.9; +} + +.description { + font-size: 0.85rem; + color: #444; + margin-top: 0.4rem; + line-height: 1.35rem; +} \ No newline at end of file diff --git a/portal-gui/src/app/components/Chip/StatusChip.tsx b/portal-gui/src/app/components/Chip/StatusChip.tsx new file mode 100644 index 0000000000000000000000000000000000000000..40dec82d5c7105ee6531dc357188adad0e634fbc --- /dev/null +++ b/portal-gui/src/app/components/Chip/StatusChip.tsx @@ -0,0 +1,47 @@ +"use client"; + +import React from "react"; +import styles from "./chip.module.scss"; + +export const StatusChip = ({ status }: { status: string }) => { + const normalized = status.toLowerCase(); + + const colorClass = (() => { + switch (normalized) { + // API statuses + case "active": + return styles.active; + case "inactive": + return styles.inactive; + case "maintenance": + return styles.maintenance; + case "deprecated": + return styles.deprecated; + + // Deployment statuses + case "running": + case "ready": + return styles.running; + case "deploying": + case "pending": + return styles.deploying; + case "error": + case "failed": + return styles.error; + case "stopped": + return styles.stopped; + // Api Functionalities + case "registration": + return styles.registration; + case "retrieval": + return styles.retrieval; + case "removal": + return styles.removal; + + default: + return styles.defaultChip; + } + })(); + + return {status.toLocaleUpperCase()}; +}; diff --git a/portal-gui/src/app/components/Chip/chip.module.scss b/portal-gui/src/app/components/Chip/chip.module.scss new file mode 100644 index 0000000000000000000000000000000000000000..deb337cd366e59d11ec63c925ecd244233d22e64 --- /dev/null +++ b/portal-gui/src/app/components/Chip/chip.module.scss @@ -0,0 +1,61 @@ + +.chip { + display: inline-block; + padding: 4px 10px; + border-radius: 20px; + font-size: 0.6rem; + font-weight: 500; + color: #fff; +} + +.active { + background-color: #7dcd7ddf; +} + +.inactive { + background-color: #6c757db9; +} + +.maintenance { + background-color: #fdbb13a5; + color: #333; +} + +.deprecated { + background-color: #dd4a59d7; +} +.running, +.ready { + background-color: #7dcd7ddf; +} + +.deploying { + background-color: #fdbb13a5; + +} + +.error { + background-color: #dd4a59d7; + +} + +.stopped { + background-color: #EDEDED; + color: #333; +} +.registration, .retrieval, .removal { + background: linear-gradient(45deg, #f159225b 0%, #fdbb135b 100%); + color: var(--blue-color); + font-size: 0.6rem; + padding: 0.3rem 0.7rem; + border-radius: 50px; + font-weight: 500; + white-space: nowrap; + opacity: 0.9; +} + + +/* fallback */ +.defaultChip { + background-color: #6c757db9; +} \ No newline at end of file diff --git a/portal-gui/src/app/components/ConfirmModal/ConfirmModal.tsx b/portal-gui/src/app/components/ConfirmModal/ConfirmModal.tsx new file mode 100644 index 0000000000000000000000000000000000000000..3a234dee972b04fca6f5e7c02ce10d45165b83f4 --- /dev/null +++ b/portal-gui/src/app/components/ConfirmModal/ConfirmModal.tsx @@ -0,0 +1,45 @@ +"use client"; + +import { Modal, Button } from "rsuite"; +import styles from "./confirmModal.module.scss"; +import buttons from "@/app/styles/buttons.module.scss"; + +type Props = { + open: boolean; + title: string; + message: string; + confirmLabel?: string; + loading?: boolean; + error?: string | null; + onConfirm: () => void; + onClose: () => void; +}; + +export const ConfirmModal = ({ + open, + title, + message, + confirmLabel = "Confirm", + loading = false, + error, + onConfirm, + onClose, +}: Props) => ( +{message}
+ {error &&{error}
} +Repository
+ +Components *
+ +Network interfaces
+ + {comp.networkInterfaces.map((iface, ii) => ( +Required Resources (optional)
+ +{error}
} +Select the edge cloud zone(s) to deploy to:
+ + {loadingZones &&Loading zones…
} + + {!loadingZones && zones.length === 0 && !error && ( +No edge cloud zones available.
+ )} + +{error}
} +{message}
+ {href && {linkText}} +Loading...
+