Commit 692e285d authored by Dimitrios Gogos's avatar Dimitrios Gogos
Browse files

feat: refactor DeploymentCard tab to fetch deployed apps

parent 1ebf85a7
Loading
Loading
Loading
Loading
+30 −32
Original line number Diff line number Diff line
"use client";
import React from "react";
import styles from "./myApplications.module.scss";
import { IDeployment } from "../../utils/interfaces";
import { IAppInstance } from "../../utils/interfaces";
import { StatusChip } from "../../components/Chip/StatusChip";



export const DeploymentCard = ({ deployment }: { deployment: IDeployment }) => {
  const getStatusClass = (status: IDeployment["status"]) => {
    switch (status) {
      case "running":
        return styles.statusRunning;
      case "deploying":
        return styles.statusDeploying;
      case "error":
        return styles.statusError;
      case "stopped":
        return styles.statusStopped;
      default:
        return "";
    }
  };

export const DeploymentCard = ({ instance }: { instance: IAppInstance }) => {
  return (
    <div className={styles.deployCard}>
      <div className={styles.header}>
        <h3 className={styles.title}>{deployment.name}</h3>
       <StatusChip status={deployment.status} />
        <h3 className={styles.title}>{instance.name}</h3>
        <StatusChip status={instance.status} />
      </div>

      <p className={styles.region}>
        <strong>Region:</strong> {deployment.region}
      </p>

      <p className={styles.version}>
        <strong>Version:</strong> {deployment.version}
      </p>

      <p className={styles.created}>
        <strong>Created:</strong> {new Date(deployment.createdAt).toLocaleString()}
      </p>
      <dl className={styles.fields}>
        <div className={styles.field}>
          <dt>Provider</dt>
          <dd>{instance.appProvider || ""}</dd>
        </div>
        <div className={styles.field}>
          <dt>App ID</dt>
          <dd className={styles.mono}>{instance.appId}</dd>
        </div>
        <div className={styles.field}>
          <dt>Instance ID</dt>
          <dd className={styles.mono}>{instance.appInstanceId}</dd>
        </div>
        {instance.edgeCloudZoneId && (
          <div className={styles.field}>
            <dt>Edge Zone</dt>
            <dd className={styles.mono}>{instance.edgeCloudZoneId}</dd>
          </div>
        )}
        {instance.kubernetesClusterRef && (
          <div className={styles.field}>
            <dt>K8s Cluster</dt>
            <dd className={styles.mono}>{instance.kubernetesClusterRef}</dd>
          </div>
        )}
      </dl>
    </div>
  );
};
+49 −26
Original line number Diff line number Diff line
@@ -250,40 +250,63 @@
  box-shadow: 0 4px 14px rgba(0, 0, 0, 0.08);
  transition: transform 0.15s ease, box-shadow 0.15s ease;
  cursor: pointer;
  display: flex;
  flex-direction: column;
  gap: 0.75rem;

  &:hover {
    transform: translateY(-3px);
    box-shadow: 0 6px 20px rgba(0, 0, 0, 0.12);
  }


  .header {
    display: flex;
    align-items: center;
    justify-content: space-between;
    gap: 0.5rem;
  }

  .title {
    font-size: 1.15rem;
    font-weight: 600;
  color: var(--text-heading);
    color: var(--blue-color);
    margin: 0;
  }

.statusChip {
  padding: 4px 10px;
  border-radius: 10px;
  font-size: 0.75rem;
  font-weight: 600;
  .fields {
    display: flex;
    flex-direction: column;
    gap: 0.4rem;
    margin: 0;
    padding: 0;

    .field {
      display: grid;
      grid-template-columns: 100px 1fr;
      gap: 0.5rem;
      align-items: baseline;

      dt {
        font-size: 0.72rem;
        font-weight: 700;
        text-transform: uppercase;
  color: white;
        letter-spacing: 0.06em;
        color: #999;
        white-space: nowrap;
      }

.region,
.version,
.created {
  font-size: 0.9rem;
  color: var(--text-secondary);
  margin: 6px 0;
      dd {
        font-size: 0.85rem;
        color: #333;
        margin: 0;
      }
    }
  }

  .mono {
    font-family: monospace;
    font-size: 0.78rem;
    color: #555;
    word-break: break-all;
  }
}
 No newline at end of file
+45 −16
Original line number Diff line number Diff line
@@ -4,28 +4,49 @@ import { Button, SegmentedControl } from "rsuite";
import styles from "./myApplications.module.scss";
import buttons from "@/app/styles/buttons.module.scss";
import { ProfileCard } from "./ProfileCard";
import { IApplicationProfile, MyApplicationsTabKey } from "../../utils/interfaces";
import { IApplicationProfile, IAppInstance, MyApplicationsTabKey } from "../../utils/interfaces";
import { tabs } from "../../utils/constants";
import { deployments } from "../../utils/tableHelpers";
import { DeploymentCard } from "./DeploymentCard";
import { CreateProfileModal } from "../../components/CreateProfileModal/CreateProfileModal";

const page = () => {
  const [activeTab, setActiveTab] = useState<MyApplicationsTabKey>("profile");

  const [apps, setApps] = useState<IApplicationProfile[]>([]);
  const [loading, setLoading] = useState(true);
  const [error, setError] = useState<string | null>(null);
  const [appsLoading, setAppsLoading] = useState(true);
  const [appsError, setAppsError] = useState<string | null>(null);

  const [instances, setInstances] = useState<IAppInstance[]>([]);
  const [instancesLoading, setInstancesLoading] = useState(false);
  const [instancesError, setInstancesError] = useState<string | null>(null);

  const [createOpen, setCreateOpen] = useState(false);

  useEffect(() => {
    if (activeTab !== "profile") return;
    setLoading(true);
    setError(null);
    setAppsLoading(true);
    setAppsError(null);
    fetch("/api/apps")
      .then((r) => r.json())
      .then((data) => setApps(Array.isArray(data) ? data : []))
      .catch(() => setError("Failed to load applications."))
      .finally(() => setLoading(false));
      .catch(() => setAppsError("Failed to load applications."))
      .finally(() => setAppsLoading(false));
  }, [activeTab]);

  useEffect(() => {
    if (activeTab !== "deployments") return;
    setInstancesLoading(true);
    setInstancesError(null);
    fetch("/api/appinstances")
      .then((r) => r.json())
      .then((data) => {
        const list: IAppInstance[] = Array.isArray(data?.appInstanceInfo)
          ? data.appInstanceInfo
          : [];
        setInstances(list);
      })
      .catch(() => setInstancesError("Failed to load deployments."))
      .finally(() => setInstancesLoading(false));
  }, [activeTab]);

  return (
@@ -47,9 +68,9 @@ const page = () => {
        {/* ---------- PROFILE TAB ---------- */}
        {activeTab === "profile" && (
          <>
            {loading && <p className={styles.status}>Loading applications…</p>}
            {error && <p className={styles.statusError}>{error}</p>}
            {!loading && !error && apps.length === 0 && (
            {appsLoading && <p className={styles.status}>Loading applications…</p>}
            {appsError && <p className={styles.statusError}>{appsError}</p>}
            {!appsLoading && !appsError && apps.length === 0 && (
              <p className={styles.status}>No applications registered yet.</p>
            )}
            <div className={styles.cards}>
@@ -66,13 +87,21 @@ const page = () => {

        {/* ---------- DEPLOYMENTS TAB ---------- */}
        {activeTab === "deployments" && (
          <>
            {instancesLoading && <p className={styles.status}>Loading deployments…</p>}
            {instancesError && <p className={styles.statusError}>{instancesError}</p>}
            {!instancesLoading && !instancesError && instances.length === 0 && (
              <p className={styles.status}>No running deployments found.</p>
            )}
            <div className={styles.cards}>
            {deployments.map((deployment) => (
              <DeploymentCard key={deployment.id} deployment={deployment} />
              {instances.map((instance) => (
                <DeploymentCard key={instance.appInstanceId} instance={instance} />
              ))}
            </div>
          </>
        )}
      </div>

      <CreateProfileModal
        open={createOpen}
        onClose={() => setCreateOpen(false)}
+23 −0
Original line number Diff line number Diff line
import { NextResponse } from "next/server";
import { oeg } from "@/app/utils/constants";

export async function GET() {
  try {
    const res = await fetch(`${oeg.baseUrl}/appinstances`, {
      cache: "no-store",
    });

    if (!res.ok) {
      return NextResponse.json(
        { error: `OEG returned ${res.status}` },
        { status: res.status }
      );
    }

    const data = await res.json();
    return NextResponse.json(data);
  } catch {
    return NextResponse.json(
      { error: "Could not reach OEG" },
      { status: 502 }
    );
  }
}

export async function POST(req: Request) {
  try {
    const body = await req.json();
+3 −0
Original line number Diff line number Diff line
@@ -20,10 +20,13 @@ export const StatusChip = ({ status }: { status: string }) => {

      // 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;
Loading