Repository: raystack/frontier
Pull request #897: feat(admin-ui): add org details layout with sidepanel
Author: login: rsbh
Created: 2025-03-13T06:26:35Z | Merged: 2025-03-19T08:20:52Z

This PR will add the layout for new org details pages.
It also add a side panel on right side, which show the following details.
1. Org details
2. KYC details
3. Plan details
4. Tokens
5. Billing Details


Diff:
diff --git a/pkg/server/config.go b/pkg/server/config.go
index 9725620d5..f72bde0c0 100644
--- a/pkg/server/config.go
+++ b/pkg/server/config.go
@@ -26,9 +26,10 @@ type GRPCConfig struct {
 func (cfg Config) grpcAddr() string { return fmt.Sprintf("%s:%d", cfg.Host, cfg.GRPC.Port) }
 
 type UIConfig struct {
-	Port  int    `yaml:"port" mapstructure:"port"`
-	Title string `yaml:"title" mapstructure:"title"`
-	Logo  string `yaml:"logo" mapstructure:"logo"`
+	Port   int    `yaml:"port" mapstructure:"port"`
+	Title  string `yaml:"title" mapstructure:"title"`
+	Logo   string `yaml:"logo" mapstructure:"logo"`
+	AppURL string `yaml:"app_url" mapstructure:"app_url"`
 }
 
 type Config struct {
diff --git a/pkg/server/server.go b/pkg/server/server.go
index 00f70fd77..e9a5f302d 100644
--- a/pkg/server/server.go
+++ b/pkg/server/server.go
@@ -58,8 +58,9 @@ const (
 )
 
 type UIConfigApiResponse struct {
-	Title string `json:"title"`
-	Logo  string `json:"logo"`
+	Title  string `json:"title"`
+	Logo   string `json:"logo"`
+	AppUrl string `json:"app_url"`
 }
 
 func ServeUI(ctx context.Context, logger log.Logger, uiConfig UIConfig, apiServerConfig Config) {
@@ -93,8 +94,9 @@ func ServeUI(ctx context.Context, logger log.Logger, uiConfig UIConfig, apiServe
 		http.HandleFunc("/configs", func(w http.ResponseWriter, r *http.Request) {
 			w.Header().Set("Content-Type", "application/json")
 			confResp := UIConfigApiResponse{
-				Title: uiConfig.Title,
-				Logo:  uiConfig.Logo,
+				Title:  uiConfig.Title,
+				Logo:   uiConfig.Logo,
+				AppUrl: uiConfig.AppURL,
 			}
 			json.NewEncoder(w).Encode(confResp)
 		})
diff --git a/ui/package-lock.json b/ui/package-lock.json
index a6ea40252..1d47e0f55 100644
--- a/ui/package-lock.json
+++ b/ui/package-lock.json
@@ -11,7 +11,7 @@
         "@hookform/resolvers": "^3.0.1",
         "@radix-ui/react-form": "^0.0.2",
         "@radix-ui/react-icons": "^1.3.0",
-        "@raystack/apsara": "^0.34.2",
+        "@raystack/apsara": "^0.37.0",
         "@raystack/frontier": "^0.49.0",
         "@stitches/react": "^1.2.8",
         "@tanstack/react-table": "^8.9.3",
@@ -28,6 +28,7 @@
         "slugify": "^1.6.6",
         "sonner": "^1.4.41",
         "swr": "^2.1.2",
+        "usehooks-ts": "^3.1.1",
         "vite-tsconfig-paths": "^4.0.7",
         "zod": "^3.22.3"
       },
@@ -40,7 +41,6 @@
         "eslint": "^8.56.0",
         "swagger-typescript-api": "^13.0.23",
         "typescript": "^5.8.2",
-        "usehooks-ts": "^3.1.1",
         "vite": "^4.5.9",
         "vite-plugin-svgr": "^4.3.0"
       }
@@ -5200,9 +5200,9 @@
       }
     },
     "node_modules/@raystack/apsara": {
-      "version": "0.34.2",
-      "resolved": "https://registry.npmjs.org/@raystack/apsara/-/apsara-0.34.2.tgz",
-      "integrity": "sha512-OwcLI3auw+maqJlHfHhwwV4h3id0T9hpfdX+Fen68s6f126hHFT7Eh4YjsH1q9zzzRyx/PoFkR1d4F8Svp3lUw==",
+      "version": "0.37.0",
+      "resolved": "https://registry.npmjs.org/@raystack/apsara/-/apsara-0.37.0.tgz",
+      "integrity": "sha512-v5X34bI90JxQmqz5OzdI2NQiwdrmfEwuckHgoHxJPwHM0h4QXa0updSYF0lc2hWZ9LcLJ2sKrq2L08q5YMo+lQ==",
       "dependencies": {
         "@radix-ui/react-accordion": "^1.1.2",
         "@radix-ui/react-avatar": "^1.0.3",
@@ -8584,7 +8584,6 @@
       "version": "3.1.1",
       "resolved": "https://registry.npmjs.org/usehooks-ts/-/usehooks-ts-3.1.1.tgz",
       "integrity": "sha512-I4diPp9Cq6ieSUH2wu+fDAVQO43xwtulo+fKEidHUwZPnYImbtkTjzIJYcDcJqxgmX31GVqNFURodvcgHcW0pA==",
-      "dev": true,
       "dependencies": {
         "lodash.debounce": "^4.0.8"
       },
diff --git a/ui/package.json b/ui/package.json
index 6cdf21443..b52f13f92 100644
--- a/ui/package.json
+++ b/ui/package.json
@@ -14,7 +14,7 @@
     "@hookform/resolvers": "^3.0.1",
     "@radix-ui/react-form": "^0.0.2",
     "@radix-ui/react-icons": "^1.3.0",
-    "@raystack/apsara": "^0.34.2",
+    "@raystack/apsara": "^0.37.0",
     "@raystack/frontier": "^0.49.0",
     "@stitches/react": "^1.2.8",
     "@tanstack/react-table": "^8.9.3",
@@ -31,6 +31,7 @@
     "slugify": "^1.6.6",
     "sonner": "^1.4.41",
     "swr": "^2.1.2",
+    "usehooks-ts": "^3.1.1",
     "vite-tsconfig-paths": "^4.0.7",
     "zod": "^3.22.3"
   },
@@ -43,7 +44,6 @@
     "eslint": "^8.56.0",
     "swagger-typescript-api": "^13.0.23",
     "typescript": "^5.8.2",
-    "usehooks-ts": "^3.1.1",
     "vite": "^4.5.9",
     "vite-plugin-svgr": "^4.3.0"
   }
diff --git a/ui/src/api/frontier.ts b/ui/src/api/frontier.ts
index db38ca129..7b2aaab07 100644
--- a/ui/src/api/frontier.ts
+++ b/ui/src/api/frontier.ts
@@ -189,7 +189,7 @@ export interface GooglerpcStatus {
  *       foo = any.unpack(Foo.getDefaultInstance());
  *     }
  *
- * Example 3: Pack and unpack a message in Python.
+ *  Example 3: Pack and unpack a message in Python.
  *
  *     foo = Foo(...)
  *     any = Any()
@@ -199,7 +199,7 @@ export interface GooglerpcStatus {
  *       any.Unpack(foo)
  *       ...
  *
- * Example 4: Pack and unpack a message in Go
+ *  Example 4: Pack and unpack a message in Go
  *
  *      foo := &pb.Foo{...}
  *      any, err := anypb.New(foo)
@@ -219,7 +219,7 @@ export interface GooglerpcStatus {
  * name "y.z".
  *
  * JSON
- *
+ * ====
  * The JSON representation of an `Any` value uses the regular
  * representation of the deserialized, embedded message, with an
  * additional field `@type` which contains the type URL. Example:
@@ -271,7 +271,8 @@ export interface ProtobufAny {
    *
    * Note: this functionality is not currently available in the official
    * protobuf release, and it is not used for type URLs beginning with
-   * type.googleapis.com.
+   * type.googleapis.com. As of May 2023, there are no widely used type server
+   * implementations and no plans to implement one.
    *
    * Schemes other than `http`, `https` (or the empty scheme) might be
    * used with implementation specific semantics.
@@ -284,7 +285,7 @@ export interface ProtobufAny {
  * `NullValue` is a singleton enumeration to represent the null value for the
  * `Value` type union.
  *
- *  The JSON representation for `NullValue` is JSON `null`.
+ * The JSON representation for `NullValue` is JSON `null`.
  *
  *  - NULL_VALUE: Null value.
  * @default "NULL_VALUE"
@@ -1440,6 +1441,8 @@ export interface V1Beta1ListProjectsResponse {
 
 export interface V1Beta1ListProspectsResponse {
   prospects?: V1Beta1Prospect[];
+  pagination?: V1Beta1RQLQueryPaginationResponse;
+  group?: V1Beta1RQLQueryGroupResponse;
 }
 
 export interface V1Beta1ListRelationsResponse {
@@ -2217,6 +2220,11 @@ export interface V1Beta1Subscription {
   plan?: V1Beta1Plan;
 }
 
+export interface V1Beta1TotalDebitedTransactionsResponse {
+  /** Total debited amount in the billing account */
+  debited?: BillingAccountBalance;
+}
+
 export type V1Beta1UpdateBillingAccountLimitsResponse = object;
 
 export interface V1Beta1UpdateBillingAccountResponse {
@@ -4607,6 +4615,24 @@ export class Api<SecurityDataType extends unknown> extends HttpClient<SecurityDa
         ...params,
       }),
 
+    /**
+     * @description Sum of amount of debited transactions including refunds
+     *
+     * @tags Transaction
+     * @name FrontierServiceTotalDebitedTransactions
+     * @summary Sum of amount of debited transactions including refunds
+     * @request GET:/v1beta1/organizations/{org_id}/billing/{billing_id}/debited_transactions_total
+     * @secure
+     */
+    frontierServiceTotalDebitedTransactions: (orgId: string, billingId: string, params: RequestParams = {}) =>
+      this.request<V1Beta1TotalDebitedTransactionsResponse, GooglerpcStatus>({
+        path: `/v1beta1/organizations/${orgId}/billing/${billingId}/debited_transactions_total`,
+        method: "GET",
+        secure: true,
+        format: "json",
+        ...params,
+      }),
+
     /**
      * @description List all invoices of a billing account.
      *
diff --git a/ui/src/assets/icons/coin-colored.svg b/ui/src/assets/icons/coin-colored.svg
new file mode 100644
index 000000000..2bf73c2be
--- /dev/null
+++ b/ui/src/assets/icons/coin-colored.svg
@@ -0,0 +1,11 @@
+<svg width="16" height="16" viewBox="0 0 16 16" fill="none" xmlns="http://www.w3.org/2000/svg">
+<g id="Credit coin">
+<path id="credit coin" d="M16 8.0065C15.9994 12.4409 12.4249 16.0017 7.97544 16C3.55904 15.9983 -0.00328503 12.4167 2.27331e-06 7.98151C0.00333792 3.55091 3.58307 -0.00425033 8.03664 3.81393e-06C12.439 0.00425796 16.0006 3.58509 16 8.0065ZM3.7629 8.03256C5.56222 9.09629 6.88009 10.6178 8.06512 12.2863C9.05412 10.4291 10.6565 9.21792 12.2909 8.02328C10.4607 6.98797 9.18885 5.43231 8.01919 3.74578C6.83596 5.41394 5.58083 6.97768 3.7629 8.03256Z" fill="url(#paint0_linear_4609_45749)"/>
+</g>
+<defs>
+<linearGradient id="paint0_linear_4609_45749" x1="13.25" y1="2.49999" x2="3.25004" y2="14.25" gradientUnits="userSpaceOnUse">
+<stop stop-color="#E82F72" stop-opacity="0.7"/>
+<stop offset="1" stop-color="#ED25B5" stop-opacity="0.7"/>
+</linearGradient>
+</defs>
+</svg>
diff --git a/ui/src/assets/icons/coin.svg b/ui/src/assets/icons/coin.svg
new file mode 100644
index 000000000..822227d01
--- /dev/null
+++ b/ui/src/assets/icons/coin.svg
@@ -0,0 +1,5 @@
+<svg width="16" height="16" viewBox="0 0 16 16" fill="none" xmlns="http://www.w3.org/2000/svg">
+<g id="Credit coin">
+<path id="credit coin" d="M16 8.0065C15.9994 12.4409 12.4249 16.0017 7.97544 16C3.55904 15.9983 -0.00328503 12.4167 2.27331e-06 7.98151C0.00333792 3.55091 3.58307 -0.00425033 8.03664 3.81393e-06C12.439 0.00425796 16.0006 3.58509 16 8.0065ZM3.7629 8.03256C5.56222 9.09629 6.88009 10.6178 8.06512 12.2863C9.05412 10.4291 10.6565 9.21792 12.2909 8.02328C10.4607 6.98797 9.18885 5.43231 8.01919 3.74578C6.83596 5.41394 5.58083 6.97768 3.7629 8.03256Z" fill="currentColor"/>
+</g>
+</svg>
diff --git a/ui/src/assets/icons/organization.svg b/ui/src/assets/icons/organization.svg
deleted file mode 100644
index 6aa427cfd..000000000
--- a/ui/src/assets/icons/organization.svg
+++ /dev/null
@@ -1,5 +0,0 @@
-<svg width="16" height="16" viewBox="0 0 16 16" fill="none" xmlns="http://www.w3.org/2000/svg">
-<g id="buildings">
-<path id="Vector" fill-rule="evenodd" clip-rule="evenodd" d="M0.799988 2.2C0.799988 2.04087 0.863202 1.88826 0.975724 1.77573C1.08825 1.66321 1.24086 1.6 1.39999 1.6H9.79999C9.95912 1.6 10.1117 1.66321 10.2243 1.77573C10.3368 1.88826 10.4 2.04087 10.4 2.2C10.4 2.35913 10.3368 2.51174 10.2243 2.62426C10.1117 2.73678 9.95912 2.8 9.79999 2.8H9.59999V13.8C9.59999 13.9591 9.53677 14.1117 9.42425 14.2243C9.31173 14.3368 9.15912 14.4 8.99999 14.4H7.79999C7.64086 14.4 7.48825 14.3368 7.37572 14.2243C7.2632 14.1117 7.19999 13.9591 7.19999 13.8V11.8C7.19999 11.6409 7.13677 11.4883 7.02425 11.3757C6.91173 11.2632 6.75912 11.2 6.59999 11.2H4.59999C4.44086 11.2 4.28825 11.2632 4.17572 11.3757C4.0632 11.4883 3.99999 11.6409 3.99999 11.8V13.8C3.99999 13.9591 3.93677 14.1117 3.82425 14.2243C3.71173 14.3368 3.55912 14.4 3.39999 14.4H1.39999C1.24086 14.4 1.08825 14.3368 0.975724 14.2243C0.863202 14.1117 0.799988 13.9591 0.799988 13.8C0.799988 13.6409 0.863202 13.4883 0.975724 13.3757C1.08825 13.2632 1.24086 13.2 1.39999 13.2H1.59999V2.8H1.39999C1.24086 2.8 1.08825 2.73678 0.975724 2.62426C0.863202 2.51174 0.799988 2.35913 0.799988 2.2ZM3.19999 4.4C3.19999 4.29391 3.24213 4.19217 3.31715 4.11716C3.39216 4.04214 3.4939 4 3.59999 4H4.39999C4.50607 4 4.60782 4.04214 4.68283 4.11716C4.75784 4.19217 4.79999 4.29391 4.79999 4.4V5.2C4.79999 5.30609 4.75784 5.40783 4.68283 5.48284C4.60782 5.55786 4.50607 5.6 4.39999 5.6H3.59999C3.4939 5.6 3.39216 5.55786 3.31715 5.48284C3.24213 5.40783 3.19999 5.30609 3.19999 5.2V4.4ZM3.59999 7.2C3.4939 7.2 3.39216 7.24214 3.31715 7.31716C3.24213 7.39217 3.19999 7.49391 3.19999 7.6V8.4C3.19999 8.50609 3.24213 8.60783 3.31715 8.68284C3.39216 8.75786 3.4939 8.8 3.59999 8.8H4.39999C4.50607 8.8 4.60782 8.75786 4.68283 8.68284C4.75784 8.60783 4.79999 8.50609 4.79999 8.4V7.6C4.79999 7.49391 4.75784 7.39217 4.68283 7.31716C4.60782 7.24214 4.50607 7.2 4.39999 7.2H3.59999ZM6.39999 4.4C6.39999 4.29391 6.44213 4.19217 6.51714 4.11716C6.59216 4.04214 6.6939 4 6.79999 4H7.59999C7.70607 4 7.80782 4.04214 7.88283 4.11716C7.95784 4.19217 7.99999 4.29391 7.99999 4.4V5.2C7.99999 5.30609 7.95784 5.40783 7.88283 5.48284C7.80782 5.55786 7.70607 5.6 7.59999 5.6H6.79999C6.6939 5.6 6.59216 5.55786 6.51714 5.48284C6.44213 5.40783 6.39999 5.30609 6.39999 5.2V4.4ZM6.79999 7.2C6.6939 7.2 6.59216 7.24214 6.51714 7.31716C6.44213 7.39217 6.39999 7.49391 6.39999 7.6V8.4C6.39999 8.50609 6.44213 8.60783 6.51714 8.68284C6.59216 8.75786 6.6939 8.8 6.79999 8.8H7.59999C7.70607 8.8 7.80782 8.75786 7.88283 8.68284C7.95784 8.60783 7.99999 8.50609 7.99999 8.4V7.6C7.99999 7.49391 7.95784 7.39217 7.88283 7.31716C7.80782 7.24214 7.70607 7.2 7.59999 7.2H6.79999ZM11.4 4.8C11.2409 4.8 11.0882 4.86321 10.9757 4.97573C10.8632 5.08826 10.8 5.24087 10.8 5.4V13.6C10.8 13.8122 10.8843 14.0157 11.0343 14.1657C11.1843 14.3157 11.3878 14.4 11.6 14.4H14.6C14.7591 14.4 14.9117 14.3368 15.0243 14.2243C15.1368 14.1117 15.2 13.9591 15.2 13.8C15.2 13.6409 15.1368 13.4883 15.0243 13.3757C14.9117 13.2632 14.7591 13.2 14.6 13.2H14.4V6H14.6C14.7591 6 14.9117 5.93678 15.0243 5.82426C15.1368 5.71174 15.2 5.55913 15.2 5.4C15.2 5.24087 15.1368 5.08826 15.0243 4.97573C14.9117 4.86321 14.7591 4.8 14.6 4.8H11.4ZM11.8 7.6C11.8 7.49391 11.8421 7.39217 11.9171 7.31716C11.9922 7.24214 12.0939 7.2 12.2 7.2H13C13.1061 7.2 13.2078 7.24214 13.2828 7.31716C13.3578 7.39217 13.4 7.49391 13.4 7.6V8.4C13.4 8.50609 13.3578 8.60783 13.2828 8.68284C13.2078 8.75786 13.1061 8.8 13 8.8H12.2C12.0939 8.8 11.9922 8.75786 11.9171 8.68284C11.8421 8.60783 11.8 8.50609 11.8 8.4V7.6ZM12.2 10.4C12.0939 10.4 11.9922 10.4421 11.9171 10.5172C11.8421 10.5922 11.8 10.6939 11.8 10.8V11.6C11.8 11.7061 11.8421 11.8078 11.9171 11.8828C11.9922 11.9579 12.0939 12 12.2 12H13C13.1061 12 13.2078 11.9579 13.2828 11.8828C13.3578 11.8078 13.4 11.7061 13.4 11.6V10.8C13.4 10.6939 13.3578 10.5922 13.2828 10.5172C13.2078 10.4421 13.1061 10.4 13 10.4H12.2Z" fill="#646464"/>
-</g>
-</svg>
diff --git a/ui/src/components/Sidebar/index.tsx b/ui/src/components/Sidebar/index.tsx
index d3cedf7ad..018fe637c 100644
--- a/ui/src/components/Sidebar/index.tsx
+++ b/ui/src/components/Sidebar/index.tsx
@@ -9,9 +9,8 @@ import {
 import { api } from "~/api";
 
 import styles from "./sidebar.module.css";
-
+import { OrganizationIcon } from "@raystack/apsara/icons";
 import IAMIcon from "~/assets/icons/iam.svg?react";
-import OrganizationsIcon from "~/assets/icons/organization.svg?react";
 import UserIcon from "~/assets/icons/users.svg?react";
 import InvoicesIcon from "~/assets/icons/invoices.svg?react";
 import RolesIcon from "~/assets/icons/roles.svg?react";
@@ -37,7 +36,7 @@ const navigationItems: NavigationItemsTypes[] = [
   {
     name: "Organizations",
     to: `/organisations`,
-    icon: <OrganizationsIcon />,
+    icon: <OrganizationIcon />,
   },
   {
     name: "Users",
diff --git a/ui/src/components/states/Loading.tsx b/ui/src/components/states/Loading.tsx
index 71de6a6e6..2a8b35c43 100644
--- a/ui/src/components/states/Loading.tsx
+++ b/ui/src/components/states/Loading.tsx
@@ -2,7 +2,11 @@ import { Flex, Spinner } from "@raystack/apsara/v1";
 
 export default function LoadingState() {
   return (
-    <Flex style={{ height: "100vh" }} justify={"center"} align={"center"}>
+    <Flex
+      style={{ height: "100vh", width: "100%" }}
+      justify={"center"}
+      align={"center"}
+    >
       <Spinner size={6} />
     </Flex>
   );
diff --git a/ui/src/pages/organizations/details/details.module.css b/ui/src/pages/organizations/details/details.module.css
new file mode 100644
index 000000000..974a14a19
--- /dev/null
+++ b/ui/src/pages/organizations/details/details.module.css
@@ -0,0 +1,30 @@
+.page {
+  width: 100%;
+  height: 100vh;
+  overflow: hidden;
+}
+
+.main_content {
+  width: 100%;
+}
+
+.main_content_with_sidebar {
+  width: calc(100% - 400px);
+}
+
+.navbar {
+  padding: var(--rs-space-4) var(--rs-space-7);
+  border-bottom: 0.5px solid var(--rs-color-border-base-primary);
+  background: var(--rs-color-background-base-primary);
+  display: flex;
+  align-items: center;
+  justify-content: space-between;
+}
+
+.navbar-action-menu-content {
+  min-width: 160px;
+}
+
+.side-panel-section-item-label {
+  width: 120px;
+}
diff --git a/ui/src/pages/organizations/details/index.tsx b/ui/src/pages/organizations/details/index.tsx
new file mode 100644
index 000000000..12f896042
--- /dev/null
+++ b/ui/src/pages/organizations/details/index.tsx
@@ -0,0 +1,77 @@
+import { OrganizationsDetailsNavabar } from "./navbar";
+import styles from "./details.module.css";
+import { EmptyState, Flex } from "@raystack/apsara/v1";
+import { OrgSidePanel } from "./side-panel/";
+import { V1Beta1Organization } from "~/api/frontier";
+import { useEffect, useState } from "react";
+import { api } from "~/api";
+import { useParams } from "react-router-dom";
+import LoadingState from "~/components/states/Loading";
+import { OrganizationIcon } from "@raystack/apsara/icons";
+import PageTitle from "~/components/page-title";
+
+export const OrganizationDetails = () => {
+  const [organization, setOrganization] = useState<V1Beta1Organization>();
+  const [isOrganizationLoading, setIsOrganizationLoading] = useState(true);
+  const { organizationId } = useParams();
+  const [showSidePanel, setShowSidePanel] = useState(true);
+
+  useEffect(() => {
+    async function fetchOrganization(id: string) {
+      try {
+        const response = await api?.frontierServiceGetOrganization(id);
+        const org = response.data?.organization;
+        setOrganization(org);
+      } catch (error) {
+        console.error(error);
+      } finally {
+        setIsOrganizationLoading(false);
+      }
+    }
+    if (organizationId) {
+      fetchOrganization(organizationId);
+    }
+  }, [organizationId]);
+
+  function toggleSidePanel() {
+    setShowSidePanel(!showSidePanel);
+  }
+
+  return isOrganizationLoading ? (
+    // TODO: make better loading state for page
+    <LoadingState />
+  ) : organization ? (
+    <Flex direction="column" className={styles.page}>
+      <PageTitle title={organization?.title} />
+      <OrganizationsDetailsNavabar
+        organization={organization}
+        toggleSidePanel={toggleSidePanel}
+      />
+      <Flex justify="between" style={{ height: "100%" }}>
+        <Flex
+          className={
+            showSidePanel
+              ? styles["main_content_with_sidebar"]
+              : styles["main_content"]
+          }
+        >
+          <EmptyState icon={<OrganizationIcon />} heading="Coming Soon" />
+        </Flex>
+        {showSidePanel ? <OrgSidePanel organization={organization} /> : null}
+      </Flex>
+    </Flex>
+  ) : (
+    <Flex
+      style={{ height: "100vh", width: "100%" }}
+      align="center"
+      justify="center"
+    >
+      <PageTitle title={"Organization not found"} />
+      <EmptyState
+        icon={<OrganizationIcon />}
+        heading="Organization not found"
+        subHeading="The organization you are looking for does not exist."
+      />
+    </Flex>
+  );
+};
diff --git a/ui/src/pages/organizations/details/navbar.tsx b/ui/src/pages/organizations/details/navbar.tsx
new file mode 100644
index 000000000..fbe8dd333
--- /dev/null
+++ b/ui/src/pages/organizations/details/navbar.tsx
@@ -0,0 +1,126 @@
+import { SidebarIcon, OrganizationIcon } from "@raystack/apsara/icons";
+
+import {
+  Flex,
+  Text,
+  Breadcrumb,
+  Avatar,
+  IconButton,
+  DropdownMenu,
+  Chip,
+} from "@raystack/apsara/v1";
+
+import styles from "./details.module.css";
+import {
+  ChevronRightIcon,
+  DotsHorizontalIcon,
+  MagnifyingGlassIcon,
+} from "@radix-ui/react-icons";
+import { V1Beta1Organization } from "~/api/frontier";
+
+const NavbarActionMenu = () => {
+  return (
+    <DropdownMenu>
+      <DropdownMenu.Trigger asChild>
+        <IconButton size={2} data-test-id="admin-ui-nav-action-menu-button">
+          <DotsHorizontalIcon />
+        </IconButton>
+      </DropdownMenu.Trigger>
+      <DropdownMenu.Content
+        className={styles["navbar-action-menu-content"]}
+        align="start"
+      >
+        <DropdownMenu.Item>
+          <Text>Edit...</Text>
+        </DropdownMenu.Item>
+        <DropdownMenu.Item>
+          <Text>Invite users...</Text>
+        </DropdownMenu.Item>
+        <DropdownMenu.Item>
+          <Text>Add project...</Text>
+        </DropdownMenu.Item>
+        <DropdownMenu.Item>
+          <Text>Add tokens...</Text>
+        </DropdownMenu.Item>
+        <DropdownMenu.Item>
+          <Text>Change plan...</Text>
+        </DropdownMenu.Item>
+        {/* TODO: use submenus for exports */}
+        {/* <DropdownMenu.Group>
+          <DropdownMenu.Item>
+            <Text>Members</Text>
+          </DropdownMenu.Item>
+          <DropdownMenu.Item>
+            <Text>Projects</Text>
+          </DropdownMenu.Item>
+          <DropdownMenu.Item>
+            <Text>Tokens</Text>
+          </DropdownMenu.Item>
+          <DropdownMenu.Item>
+            <Text>Audit log</Text>
+          </DropdownMenu.Item>
+        </DropdownMenu.Group> */}
+      </DropdownMenu.Content>
+    </DropdownMenu>
+  );
+};
+
+interface OrganizationDetailsNavbarProps {
+  organization: V1Beta1Organization;
+  toggleSidePanel: () => void;
+}
+
+export const OrganizationsDetailsNavabar = ({
+  organization,
+  toggleSidePanel,
+}: OrganizationDetailsNavbarProps) => {
+  return (
+    <nav className={styles.navbar}>
+      <Flex gap={4} align="center">
+        <Breadcrumb
+          size="small"
+          separator={<ChevronRightIcon style={{ display: "flex" }} />}
+          items={[
+            {
+              label: "Organizations",
+              href: "/organisations",
+              icon: <OrganizationIcon />,
+            },
+            {
+              label: organization?.title || "NA",
+              href: `/organigations/${organization?.id}`,
+              icon: (
+                <Avatar
+                  src={organization?.avatar}
+                  fallback={organization?.title?.[0]}
+                  size={1}
+                />
+              ),
+            },
+          ]}
+        />
+        <NavbarActionMenu />
+        <Flex gap={3}>
+          <Chip>Members</Chip>
+          <Chip>Projects</Chip>
+          <Chip>Tokens</Chip>
+          <Chip>API</Chip>
+          <Chip>Audit log</Chip>
+          <Chip>Security</Chip>
+        </Flex>
+      </Flex>
+      <Flex align="center" gap={4}>
+        <IconButton size={3} data-test-id="admin-ui-nav-search-button">
+          <MagnifyingGlassIcon />
+        </IconButton>
+        <IconButton
+          size={3}
+          data-test-id="admin-ui-nav-sidepanel-button"
+          onClick={toggleSidePanel}
+        >
+          <SidebarIcon />
+        </IconButton>
+      </Flex>
+    </nav>
+  );
+};
diff --git a/ui/src/pages/organizations/details/side-panel/billing-details-section.tsx b/ui/src/pages/organizations/details/side-panel/billing-details-section.tsx
new file mode 100644
index 000000000..eab091df0
--- /dev/null
+++ b/ui/src/pages/organizations/details/side-panel/billing-details-section.tsx
@@ -0,0 +1,159 @@
+import { Flex, Link, List, Text } from "@raystack/apsara/v1";
+import styles from "./side-panel.module.css";
+import { V1Beta1BillingAccount, V1Beta1Invoice } from "~/api/frontier";
+import { converBillingAddressToString } from "~/utils/helper";
+import Skeleton from "react-loading-skeleton";
+import { useEffect, useState } from "react";
+import { api } from "~/api";
+import dayjs from "dayjs";
+import { CalendarIcon } from "@radix-ui/react-icons";
+import { Amount } from "@raystack/frontier/react";
+
+interface BillingDetailsSectionProps {
+  organizationId: string;
+  billingAccountId: string;
+  isLoading: boolean;
+  billingAccount?: V1Beta1BillingAccount;
+}
+
+export const BillingDetailsSection = ({
+  isLoading,
+  billingAccount,
+  organizationId,
+}: BillingDetailsSectionProps) => {
+  const [upcomingInvoice, setUpcomingInvoice] = useState<V1Beta1Invoice>();
+  const [isUpcomingInvoiceLoading, setIsUpcomingInvoiceLoading] =
+    useState(false);
+
+  useEffect(() => {
+    async function getUpcomingInvoice(orgId: string, billingId: string) {
+      setIsUpcomingInvoiceLoading(true);
+      try {
+        const resp = await api?.frontierServiceGetUpcomingInvoice(
+          orgId,
+          billingId,
+        );
+        const invoice = resp?.data?.invoice;
+        if (invoice && invoice.state) {
+          setUpcomingInvoice(invoice);
+        }
+      } catch (err: any) {
+        console.error(err);
+      } finally {
+        setIsUpcomingInvoiceLoading(false);
+      }
+    }
+
+    if (organizationId && billingAccount?.id) {
+      getUpcomingInvoice(organizationId, billingAccount.id);
+    }
+  }, [organizationId, billingAccount]);
+
+  const isDataLoading = isLoading || isUpcomingInvoiceLoading;
+
+  const due_date = upcomingInvoice?.due_date || upcomingInvoice?.period_end_at;
+
+  const stripeLink = billingAccount?.provider_id
+    ? "https://dashboard.stripe.com/customers/" + billingAccount?.provider_id
+    : "";
+
+  return (
+    <List.Root>
+      <List.Header>Billing</List.Header>
+      <List.Item>
+        <List.Label className={styles["side-panel-section-item-label"]}>
+          Name
+        </List.Label>
+        <List.Value className={styles["side-panel-section-item-value"]}>
+          {isDataLoading ? (
+            <Skeleton />
+          ) : (
+            <Text>{billingAccount?.name || "N/A"}</Text>
+          )}
+        </List.Value>
+      </List.Item>
+      <List.Item>
+        <List.Label className={styles["side-panel-section-item-label"]}>
+          Email
+        </List.Label>
+        <List.Value className={styles["side-panel-section-item-value"]}>
+          {isDataLoading ? (
+            <Skeleton />
+          ) : (
+            <Text>{billingAccount?.email || "N/A"}</Text>
+          )}
+        </List.Value>
+      </List.Item>
+      <List.Item>
+        <List.Label className={styles["side-panel-section-item-label"]}>
+          Address
+        </List.Label>
+        <List.Value className={styles["side-panel-section-item-value"]}>
+          {isDataLoading ? (
+            <Skeleton />
+          ) : (
+            <Text>
+              {billingAccount?.address
+                ? converBillingAddressToString(billingAccount.address)
+                : "N/A"}
+            </Text>
+          )}
+        </List.Value>
+      </List.Item>
+      <List.Item>
+        <List.Label className={styles["side-panel-section-item-label"]}>
+          Next billing date
+        </List.Label>
+        <List.Value className={styles["side-panel-section-item-value"]}>
+          {isDataLoading ? (
+            <Skeleton />
+          ) : due_date ? (
+            <Flex gap={3}>
+              <CalendarIcon />
+              <Text>{dayjs(due_date).format("DD MMM YYYY")}</Text>
+            </Flex>
+          ) : (
+            <Text>-</Text>
+          )}
+        </List.Value>
+      </List.Item>
+      <List.Item>
+        <List.Label className={styles["side-panel-section-item-label"]}>
+          Amount
+        </List.Label>
+        <List.Value className={styles["side-panel-section-item-value"]}>
+          {isDataLoading ? (
+            <Skeleton />
+          ) : upcomingInvoice?.amount ? (
+            <Amount
+              currency={upcomingInvoice?.currency}
+              value={Number(upcomingInvoice?.amount)}
+            />
+          ) : (
+            <Text>-</Text>
+          )}
+        </List.Value>
+      </List.Item>
+      <List.Item>
+        <List.Label className={styles["side-panel-section-item-label"]}>
+          Profile
+        </List.Label>
+        <List.Value className={styles["side-panel-section-item-value"]}>
+          {isDataLoading ? (
+            <Skeleton />
+          ) : stripeLink ? (
+            <Link
+              href={stripeLink}
+              data-test-id="stripe-dashboard-link"
+              target="_blank"
+            >
+              Stripe
+            </Link>
+          ) : (
+            <Text>N/A</Text>
+          )}
+        </List.Value>
+      </List.Item>
+    </List.Root>
+  );
+};
diff --git a/ui/src/pages/organizations/details/side-panel/index.tsx b/ui/src/pages/organizations/details/side-panel/index.tsx
new file mode 100644
index 000000000..baf2b5ebd
--- /dev/null
+++ b/ui/src/pages/organizations/details/side-panel/index.tsx
@@ -0,0 +1,82 @@
+import { Avatar, SidePanel } from "@raystack/apsara/v1";
+import { V1Beta1Organization, V1Beta1BillingAccount } from "~/api/frontier";
+import { OrganizationDetailsSection } from "./org-details-section";
+import { KYCDetailsSection } from "./kyc-section";
+import { PlanDetailsSection } from "./plan-details-section";
+import { TokensDetailsSection } from "./tokens-details-section";
+import { useEffect, useState } from "react";
+import { api } from "~/api";
+import { BillingDetailsSection } from "./billing-details-section";
+
+export const SUBSCRIPTION_STATES = {
+  active: "Active",
+  past_due: "Past due",
+  trialing: "Trialing",
+  canceled: "Canceled",
+  "": "NA",
+} as const;
+
+interface SidePanelProps {
+  organization: V1Beta1Organization;
+}
+
+export function OrgSidePanel({ organization }: SidePanelProps) {
+  const [isBillingAccountLoading, setIsBillingAccountLoading] = useState(true);
+  const [billingAccount, setBillingAccount] = useState<V1Beta1BillingAccount>();
+
+  useEffect(() => {
+    async function fetchBillingAccount(orgId: string) {
+      try {
+        setIsBillingAccountLoading(true);
+        const resp = await api?.frontierServiceListBillingAccounts(orgId);
+        const newBillingAccount = resp.data?.billing_accounts?.[0];
+        setBillingAccount(newBillingAccount);
+      } catch (error) {
+        console.error("Error fetching billing account:", error);
+      } finally {
+        setIsBillingAccountLoading(false);
+      }
+    }
+
+    if (organization?.id) {
+      fetchBillingAccount(organization.id);
+    }
+  }, [organization?.id]);
+
+  return (
+    <SidePanel data-test-id="admin-ui-sidepanel">
+      <SidePanel.Header
+        title={organization?.title || "Organization"}
+        icon={<Avatar fallback={organization?.title?.[0]} />}
+      />
+      <SidePanel.Section>
+        <OrganizationDetailsSection organization={organization} />
+      </SidePanel.Section>
+      <SidePanel.Section>
+        <KYCDetailsSection organizationId={organization.id || ""} />
+      </SidePanel.Section>
+      <SidePanel.Section>
+        <PlanDetailsSection
+          organizationId={organization.id || ""}
+          billingAccountId={billingAccount?.id || ""}
+          isLoading={isBillingAccountLoading}
+        />
+      </SidePanel.Section>
+      <SidePanel.Section>
+        <TokensDetailsSection
+          organizationId={organization.id || ""}
+          billingAccountId={billingAccount?.id || ""}
+          isLoading={isBillingAccountLoading}
+        />
+      </SidePanel.Section>
+      <SidePanel.Section>
+        <BillingDetailsSection
+          organizationId={organization.id || ""}
+          billingAccountId={billingAccount?.id || ""}
+          isLoading={isBillingAccountLoading}
+          billingAccount={billingAccount}
+        />
+      </SidePanel.Section>
+    </SidePanel>
+  );
+}
diff --git a/ui/src/pages/organizations/details/side-panel/kyc-section.tsx b/ui/src/pages/organizations/details/side-panel/kyc-section.tsx
new file mode 100644
index 000000000..a275729fd
--- /dev/null
+++ b/ui/src/pages/organizations/details/side-panel/kyc-section.tsx
@@ -0,0 +1,93 @@
+import { Flex, List, Text } from "@raystack/apsara/v1";
+import { useEffect, useState } from "react";
+import { api } from "~/api";
+import { V1Beta1OrganizationKyc } from "~/api/frontier";
+import styles from "./side-panel.module.css";
+import Skeleton from "react-loading-skeleton";
+import {
+  CheckCircleFilledIcon,
+  CrossCircleFilledIcon,
+} from "@raystack/apsara/icons";
+import { Link2Icon } from "@radix-ui/react-icons";
+
+export const KYCDetailsSection = ({
+  organizationId,
+}: {
+  organizationId: string;
+}) => {
+  const [KYCDetails, setKYCDetails] = useState<V1Beta1OrganizationKyc | null>(
+    null,
+  );
+  const [isKYCLoading, setIsKYCLoading] = useState(true);
+
+  useEffect(() => {
+    async function fetchKYCDetails(id: string) {
+      setIsKYCLoading(true);
+      try {
+        const response = await api?.frontierServiceGetOrganizationKyc(id);
+        const kyc = response?.data?.organization_kyc || null;
+        setKYCDetails(kyc);
+      } catch (error) {
+        console.error("Error fetching KYC details:", error);
+      } finally {
+        setIsKYCLoading(false);
+      }
+    }
+    if (organizationId) {
+      fetchKYCDetails(organizationId);
+    }
+  }, [organizationId]);
+
+  return (
+    <List.Root>
+      <List.Header>KYC Details</List.Header>
+      <List.Item>
+        <List.Label className={styles["side-panel-section-item-label"]}>
+          Status
+        </List.Label>
+        <List.Value>
+          {isKYCLoading ? (
+            <Skeleton />
+          ) : KYCDetails?.status ? (
+            <Flex justifyContent="center" alignItems="center" gap={3}>
+              <CheckCircleFilledIcon
+                color={"var(--rs-color-foreground-success-primary)"}
+                className={styles["kyc-status-icon"]}
+              />
+              <Text>Verified</Text>
+            </Flex>
+          ) : (
+            <Flex justifyContent="center" alignItems="center" gap={3}>
+              <CrossCircleFilledIcon
+                color={"var(--rs-color-foreground-danger-primary)"}
+                className={styles["kyc-status-icon"]}
+              />
+              <Text>Not verified</Text>
+            </Flex>
+          )}
+        </List.Value>
+      </List.Item>
+      <List.Item>
+        <List.Label className={styles["side-panel-section-item-label"]}>
+          Documents Link
+        </List.Label>
+        <List.Value>
+          {isKYCLoading ? (
+            <Skeleton />
+          ) : (
+            <Flex justifyContent="center" alignItems="center" gap={3}>
+              {KYCDetails?.link ? (
+                <>
+                  <Link2Icon />
+                  <Text>{KYCDetails?.link}</Text>
+                </>
+              ) : (
+                <Text>N/A</Text>
+              )}
+            </Flex>
+          )}
+        </List.Value>
+      </List.Item>
+    </List.Root>
+  );
+};
diff --git a/ui/src/pages/organizations/details/side-panel/org-details-section.tsx b/ui/src/pages/organizations/details/side-panel/org-details-section.tsx
new file mode 100644
index 000000000..0de926830
--- /dev/null
+++ b/ui/src/pages/organizations/details/side-panel/org-details-section.tsx
@@ -0,0 +1,108 @@
+import { V1Beta1Organization } from "@raystack/frontier";
+import { CalendarIcon } from "@radix-ui/react-icons";
+import { Flex, List, Text, CopyButton, Tooltip } from "@raystack/apsara/v1";
+import styles from "./side-panel.module.css";
+import dayjs from "dayjs";
+import { useContext } from "react";
+import { AppContext } from "~/contexts/App";
+
+interface OrganizationDetailsSectionProps {
+  organization: V1Beta1Organization;
+}
+
+type Metadata = Record<string, any>;
+
+export const OrganizationDetailsSection = ({
+  organization,
+}: OrganizationDetailsSectionProps) => {
+  const { config } = useContext(AppContext);
+
+  return (
+    <List.Root>
+      <List.Header>Organization Details</List.Header>
+      <List.Item>
+        <List.Label className={styles["side-panel-section-item-label"]}>
+          URL
+        </List.Label>
+        <List.Value>
+          <Text>
+            {config?.app_url}/{organization.name}
+          </Text>
+        </List.Value>
+      </List.Item>
+      <List.Item>
+        <List.Label className={styles["side-panel-section-item-label"]}>
+          Organization ID
+        </List.Label>
+        <List.Value>
+          <Flex gap={3}>
+            <CopyButton
+              text={organization.id || ""}
+              data-test-id="copy-button"
+            />
+            <Tooltip message={organization.id}>
+              <Text className={styles["org-details-section-org-id"]}>
+                {organization.id}
+              </Text>
+            </Tooltip>
+          </Flex>
+        </List.Value>
+      </List.Item>
+      <List.Item>
+        <List.Label className={styles["side-panel-section-item-label"]}>
+          Created on
+        </List.Label>
+        <List.Value>
+          <Flex gap={3}>
+            <CalendarIcon />
+            <Text>{dayjs(organization?.created_at).format("DD MMM YYYY")}</Text>
+          </Flex>
+        </List.Value>
+      </List.Item>
+      {/* <List.Item>
+        <List.Label className={styles["side-panel-section-item-label"]}>
+          Created by
+        </List.Label>
+        <List.Value>
+          <span />
+        </List.Value>
+      </List.Item>
+         */}
+      <List.Item>
+        <List.Label className={styles["side-panel-section-item-label"]}>
+          Organization size
+        </List.Label>
+        <List.Value>
+          <List.Value>
+            <Text>{(organization?.metadata as Metadata)?.["size"] || "-"}</Text>
+          </List.Value>
+        </List.Value>
+      </List.Item>
+      <List.Item>
+        <List.Label className={styles["side-panel-section-item-label"]}>
+          Industry
+        </List.Label>
+        <List.Value>
+          <Text>{(organization?.metadata as Metadata)?.["type"] || "-"}</Text>
+        </List.Value>
+      </List.Item>
+
+      <List.Item>
+        <List.Label className={styles["side-panel-section-item-label"]}>
+          Country
+        </List.Label>
+        <List.Value>
+          <Text>
+            {(organization?.metadata as Metadata)?.["country"] || "-"}
+          </Text>
+        </List.Value>
+      </List.Item>
+      <List.Item>
+        <List.Label className={styles["side-panel-section-item-label"]}>
+          Status
+        </List.Label>
+        <List.Value>{organization?.state}</List.Value>
+      </List.Item>
+    </List.Root>
+  );
+};
diff --git a/ui/src/pages/organizations/details/side-panel/plan-details-section.tsx b/ui/src/pages/organizations/details/side-panel/plan-details-section.tsx
new file mode 100644
index 000000000..d2bdb27fb
--- /dev/null
+++ b/ui/src/pages/organizations/details/side-panel/plan-details-section.tsx
@@ -0,0 +1,123 @@
+import { V1Beta1Subscription, V1Beta1Plan } from "~/api/frontier";
+import { useEffect, useState } from "react";
+import { api } from "~/api";
+import { List, Text, Flex } from "@raystack/apsara/v1";
+import styles from "./side-panel.module.css";
+import dayjs from "dayjs";
+import { CalendarIcon } from "@radix-ui/react-icons";
+import Skeleton from "react-loading-skeleton";
+
+interface PlanDetailsSectionProps {
+  organizationId: string;
+  billingAccountId: string;
+  isLoading: boolean;
+}
+
+export const PlanDetailsSection = ({
+  organizationId,
+  billingAccountId,
+  isLoading,
+}: PlanDetailsSectionProps) => {
+  const [isSubscriptionLoading, setIsSubscriptionLoading] = useState(false);
+  const [subscription, setSubscription] = useState<V1Beta1Subscription>();
+  const [plan, setPlan] = useState<V1Beta1Plan>();
+
+  useEffect(() => {
+    async function fetchBillingDetails(id: string, billingAccountId: string) {
+      try {
+        setIsSubscriptionLoading(true);
+        const subResponse = await api?.frontierServiceListSubscriptions(
+          id,
+          billingAccountId,
+        );
+        const subscriptions = subResponse?.data?.subscriptions || [];
+        const sub = subscriptions.find(
+          (sub) => sub.state === "active" || sub.state === "trialing",
+        );
+        if (sub && sub.plan_id) {
+          setSubscription(sub);
+          const planResponse = await api?.frontierServiceGetPlan(sub.plan_id);
+          const plan = planResponse?.data?.plan;
+          setPlan(plan);
+        }
+      } catch (error) {
+        console.error("Error fetching billing details:", error);
+      } finally {
+        setIsSubscriptionLoading(false);
+      }
+    }
+    if (organizationId && billingAccountId) {
+      fetchBillingDetails(organizationId, billingAccountId);
+    }
+  }, [organizationId, billingAccountId]);
+
+  const isDataLoading = isSubscriptionLoading || isLoading;
+
+  return (
+    <List.Root>
+      <List.Header>Plan details</List.Header>
+      <List.Item>
+        <List.Label className={styles["side-panel-section-item-label"]}>
+          Name
+        </List.Label>
+        <List.Value>
+          {isDataLoading ? (
+            <Skeleton />
+          ) : (
+            <Text>{plan?.title || "Standard"}</Text>
+          )}
+        </List.Value>
+      </List.Item>
+      <List.Item>
+        <List.Label className={styles["side-panel-section-item-label"]}>
+          Started from
+        </List.Label>
+        <List.Value>
+          {isDataLoading ? (
+            <Skeleton />
+          ) : subscription?.current_period_start_at ? (
+            <Flex gap={3}>
+              <CalendarIcon />
+              <Text>
+                {dayjs(subscription?.current_period_start_at).format(
+                  "DD MMM YYYY",
+                )}
+              </Text>
+            </Flex>
+          ) : (
+            <Text>-</Text>
+          )}
+        </List.Value>
+      </List.Item>
+      <List.Item>
+        <List.Label className={styles["side-panel-section-item-label"]}>
+          Ends on
+        </List.Label>
+        <List.Value>
+          {isDataLoading ? (
+            <Skeleton />
+          ) : subscription?.current_period_end_at ? (
+            <Flex gap={3}>
+              <CalendarIcon />
+              <Text>
+                {dayjs(subscription?.current_period_end_at).format(
+                  "DD MMM YYYY",
+                )}
+              </Text>
+            </Flex>
+          ) : (
+            <Text>-</Text>
+          )}
+        </List.Value>
+      </List.Item>
+      <List.Item>
+        <List.Label className={styles["side-panel-section-item-label"]}>
+          Payment mode
+        </List.Label>
+        <List.Value>
+          <Text>Prepaid</Text>
+        </List.Value>
+      </List.Item>
+    </List.Root>
+  );
+};
diff --git a/ui/src/pages/organizations/details/side-panel/side-panel.module.css b/ui/src/pages/organizations/details/side-panel/side-panel.module.css
new file mode 100644
index 000000000..4596b66f5
--- /dev/null
+++ b/ui/src/pages/organizations/details/side-panel/side-panel.module.css
@@ -0,0 +1,15 @@
+.side-panel-section-item-label {
+  width: 120px;
+}
+
+.org-details-section-org-id {
+  max-width: 160px;
+  overflow: hidden;
+  text-overflow: ellipsis;
+  white-space: nowrap;
+}
+
+.kyc-status-icon {
+  height: var(--rs-space-5);
+  width: var(--rs-space-5);
+}
diff --git a/ui/src/pages/organizations/details/side-panel/tokens-details-section.tsx b/ui/src/pages/organizations/details/side-panel/tokens-details-section.tsx
new file mode 100644
index 000000000..2ca176b43
--- /dev/null
+++ b/ui/src/pages/organizations/details/side-panel/tokens-details-section.tsx
@@ -0,0 +1,85 @@
+import { List, Text, Flex } from "@raystack/apsara/v1";
+import styles from "./side-panel.module.css";
+import CoinIcon from "~/assets/icons/coin.svg?react";
+import CoinColoredIcon from "~/assets/icons/coin-colored.svg?react";
+import { useEffect, useState } from "react";
+import Skeleton from "react-loading-skeleton";
+import { api } from "~/api";
+
+interface TokensDetailsSectionProps {
+  organizationId: string;
+  isLoading: boolean;
+  billingAccountId: string;
+}
+
+export const TokensDetailsSection = ({
+  organizationId,
+  billingAccountId,
+  isLoading,
+}: TokensDetailsSectionProps) => {
+  const [balance, setBalance] = useState("0");
+  const [tokensUsed, setTokensUsed] = useState("0");
+  const [isTokensLoading, setIsTokensLoading] = useState(false);
+
+  useEffect(() => {
+    async function fetchTokenDetails(id: string, billingAccountId: string) {
+      try {
+        setIsTokensLoading(true);
+        const [balanceResp, tokensUsedResp] = await Promise.all([
+          api.frontierServiceGetBillingBalance(id, billingAccountId),
+          api.frontierServiceTotalDebitedTransactions(id, billingAccountId),
+        ]);
+        const newBalance = balanceResp.data.balance?.amount || "0";
+        setBalance(newBalance);
+
+        const newTokensUsed = tokensUsedResp.data.debited?.amount || "0";
+        setTokensUsed(newTokensUsed);
+      } catch (error) {
+        console.error(error);
+      } finally {
+        setIsTokensLoading(false);
+      }
+    }
+    if (organizationId && billingAccountId) {
+      fetchTokenDetails(organizationId, billingAccountId);
+    }
+  }, [organizationId, billingAccountId]);
+
+  const isDataLoading = isLoading || isTokensLoading;
+
+  return (
+    <List.Root>
+      <List.Header>Tokens</List.Header>
+      <List.Item>
+        <List.Label className={styles["side-panel-section-item-label"]}>
+          Available tokens
+        </List.Label>
+        <List.Value>
+          {isDataLoading ? (
+            <Skeleton />
+          ) : (
+            <Flex gap={3}>
+              <CoinColoredIcon />
+              <Text>{balance}</Text>
+            </Flex>
+          )}
+        </List.Value>
+      </List.Item>
+      <List.Item>
+        <List.Label className={styles["side-panel-section-item-label"]}>
+          Used till date
+        </List.Label>
+        <List.Value>
+          {isDataLoading ? (
+            <Skeleton />
+          ) : (
+            <Flex gap={3}>
+              <CoinIcon color="var(--rs-color-foreground-base-tertiary)" />
+              <Text>{tokensUsed}</Text>
+            </Flex>
+          )}
+        </List.Value>
+      </List.Item>
+    </List.Root>
+  );
+};
diff --git a/ui/src/pages/organizations/list/index.tsx b/ui/src/pages/organizations/list/index.tsx
index 9dbb7f6cf..b8c21b588 100644
--- a/ui/src/pages/organizations/list/index.tsx
+++ b/ui/src/pages/organizations/list/index.tsx
@@ -5,11 +5,11 @@ import {
   DataTableQuery,
   EmptyFilterValue,
 } from "@raystack/apsara/v1";
+import { OrganizationIcon } from "@raystack/apsara/icons";
 import { V1Beta1Organization, V1Beta1Plan } from "@raystack/frontier";
 import { useDebounceCallback } from "usehooks-ts";
 import { useCallback, useEffect, useState } from "react";
 import { OrganizationsNavabar } from "./navbar";
-import OrganizationsIcon from "~/assets/icons/organization.svg?react";
 import styles from "./list.module.css";
 import { getColumns } from "./columns";
 import { api } from "~/api";
@@ -25,7 +25,7 @@ const NoOrganizations = () => {
       }}
       heading="No Organization Found"
       subHeading="We couldn’t find any matches for that keyword or filter. Try alternative terms or check for typos."
-      icon={<OrganizationsIcon />}
+      icon={<OrganizationIcon />}
     />
   );
 };
@@ -71,14 +71,14 @@ export const OrganizationList = () => {
               acc[group.name || ""] = group.count || 0;
               return acc;
             },
-            {} as Record<string, number>
+            {} as Record<string, number>,
           ) || {};
         const groupKey = response.data.group?.name;
         if (groupKey) {
           setGroupCountMap((prev) => ({ ...prev, [groupKey]: groupCount }));
         }
         setHasMoreData(
-          organizations.length !== 0 && organizations.length === LIMIT
+          organizations.length !== 0 && organizations.length === LIMIT,
         );
       } catch (error) {
         console.error(error);
@@ -86,7 +86,7 @@ export const OrganizationList = () => {
         setIsDataLoading(false);
       }
     },
-    []
+    [],
   );
 
   const fetchPlans = useCallback(async () => {
@@ -146,15 +146,14 @@ export const OrganizationList = () => {
         <Flex direction="column" style={{ width: "100%" }}>
           <OrganizationsNavabar seachQuery={query.search} />
           <DataTable.Toolbar />
-          <div className={styles["table-wrapper"]}>
-            <DataTable.Content
-              classNames={{
-                table: tableClassName,
-                header: styles["table-header"],
-              }}
-              emptyState={<NoOrganizations />}
-            />
-          </div>
+          <DataTable.Content
+            classNames={{
+              root: styles["table-wrapper"],
+              table: tableClassName,
+              header: styles["table-header"],
+            }}
+            emptyState={<NoOrganizations />}
+          />
         </Flex>
       </DataTable>
     </>
diff --git a/ui/src/pages/organizations/list/navbar.tsx b/ui/src/pages/organizations/list/navbar.tsx
index a8a0b367f..1bcbc354e 100644
--- a/ui/src/pages/organizations/list/navbar.tsx
+++ b/ui/src/pages/organizations/list/navbar.tsx
@@ -1,4 +1,3 @@
-import OrganizationsIcon from "~/assets/icons/organization.svg?react";
 import {
   Button,
   DataTable,
@@ -8,7 +7,7 @@ import {
   IconButton,
   Spinner,
 } from "@raystack/apsara/v1";
-
+import { OrganizationIcon } from "@raystack/apsara/icons";
 import styles from "./list.module.css";
 import {
   DownloadIcon,
@@ -67,7 +66,7 @@ export const OrganizationsNavabar = ({
   return (
     <nav className={styles.navbar}>
       <Flex gap={2}>
-        <OrganizationsIcon />
+        <OrganizationIcon />
         <Text size={2} weight={500}>
           Organizations
         </Text>
diff --git a/ui/src/routes.tsx b/ui/src/routes.tsx
index 154d39d49..f53e775e5 100644
--- a/ui/src/routes.tsx
+++ b/ui/src/routes.tsx
@@ -1,5 +1,5 @@
 import "@raystack/apsara/style.css";
-import { MagicLinkVerify } from "@raystack/frontier/react";
+// import { MagicLinkVerify } from "@raystack/frontier/react";
 import * as R from "ramda";
 import { memo, useContext } from "react";
 import { Route, Routes } from "react-router-dom";
@@ -10,19 +10,19 @@ import UnauthorizedState from "./components/states/Unauthorized";
 import App from "./App";
 import PlanList from "./containers/billingplans.list";
 import PlanDetails from "./containers/billingplans.list/details";
-import Dashboard from "./containers/dashboard";
+// import Dashboard from "./containers/dashboard";
 import NewGroup from "./containers/groups.create";
 import Groups from "./containers/groups.list";
 import GroupDetails from "./containers/groups.list/details";
 import Login from "./containers/login";
 import MagicLink from "./containers/magiclink";
 import NewOrganisation from "./containers/organisations.create";
-import Organisations from "./containers/organisations.list";
+// import Organisations from "./containers/organisations.list";
 import OrganisationBillingAccounts from "./containers/organisations.list/billingaccounts";
 import BillingAccountDetails from "./containers/organisations.list/billingaccounts/details";
 import OrganisationBAInvoices from "./containers/organisations.list/billingaccounts/invoices";
 import OrganisationBASubscriptions from "./containers/organisations.list/billingaccounts/subscriptions";
-import OrganisationDetails from "./containers/organisations.list/details";
+// import OrganisationDetails from "./containers/organisations.list/details";
 import OrganisationProjects from "./containers/organisations.list/projects";
 import OrganisationServiceUsers from "./containers/organisations.list/serviceusers";
 import OrgSettingPage from "./containers/organisations.list/settings";
@@ -59,6 +59,7 @@ import UpdateWebhooks from "./containers/webhooks/update";
 import AuthLayout from "./layout/auth";
 
 import { OrganizationList } from "./pages/organizations/list";
+import { OrganizationDetails } from "./pages/organizations/details";
 
 export default memo(function AppRoutes() {
   const { isAdmin, isLoading, user } = useContext(AppContext);
@@ -84,8 +85,8 @@ export default memo(function AppRoutes() {
           <Route path="create" element={<NewOrganisation />} />
         </Route>
         <Route
-          path="organisations/:organisationId"
-          element={<OrganisationDetails />}
+          path="organisations/:organizationId"
+          element={<OrganizationDetails />}
         />
         <Route
           path="organisations/:organisationId/users"
diff --git a/ui/src/utils/constants.ts b/ui/src/utils/constants.ts
index cc76b838c..afa03d85d 100644
--- a/ui/src/utils/constants.ts
+++ b/ui/src/utils/constants.ts
@@ -15,10 +15,12 @@ export const SUBSCRIPTION_STATUSES = [
 export interface Config {
   title: string;
   logo?: string;
+  app_url?: string;
 }
 
 export const defaultConfig: Config = {
   title: "Frontier Admin",
+  app_url: "example.com",
 };
 
 export const NULL_DATE = "0001-01-01T00:00:00Z";
diff --git a/ui/src/utils/helper.ts b/ui/src/utils/helper.ts
index ed0a8e7c6..14586eb97 100644
--- a/ui/src/utils/helper.ts
+++ b/ui/src/utils/helper.ts
@@ -1,6 +1,7 @@
 import dayjs from "dayjs";
 import type { TableColumnMetadata } from "~/types/types";
 import { DEFAULT_DATE_FORMAT } from "./constants";
+import { BillingAccountAddress } from "~/api/frontier";
 
 const currencySymbolMap: Record<string, string> = {
   usd: "$",
@@ -15,7 +16,7 @@ const DEFAULT_DECIMAL = 0;
 
 export const getCurrencyValue = (
   value: string = "",
-  currency: string = "usd"
+  currency: string = "usd",
 ) => {
   const symbol =
     (currency?.toLowerCase() && currencySymbolMap[currency?.toLowerCase()]) ||
@@ -48,9 +49,20 @@ export const capitalizeFirstLetter = (str: string) => {
 export const fetcher = (...args) => fetch(...args).then((res) => res.json());
 
 export const keyToColumnMetaObject = (key: any) =>
-  ({ key: key, name: key, value: key } as TableColumnMetadata);
+  ({ key: key, name: key, value: key }) as TableColumnMetadata;
 
 /*
  * @desc returns date string - Eg, June 13, 2025. return '-' if the date in the argument is invalid.
  */
-export const getFormattedDateString = (date: string) => date ? dayjs(date).format(DEFAULT_DATE_FORMAT) : '-'
+export const getFormattedDateString = (date: string) =>
+  date ? dayjs(date).format(DEFAULT_DATE_FORMAT) : "-";
+
+export const converBillingAddressToString = (
+  address?: BillingAccountAddress,
+) => {
+  if (!address) return "";
+  const { line1, line2, city, state, country, postal_code } = address;
+  return [line1, line2, city, state, country, postal_code]
+    .filter((v) => v)
+    .join(", ");
+};


Reviews:
user: login: paanSinghCoder
state: CHANGES_REQUESTED
author_association: CONTRIBUTOR
submitted_at: 2025-03-19T07:32:06Z
user: login: paanSinghCoder
state: APPROVED
author_association: CONTRIBUTOR
submitted_at: 2025-03-19T07:37:43Z
user: login: rsbh
state: COMMENTED
author_association: MEMBER
submitted_at: 2025-03-19T08:20:45Z

Review comments:
diff_hunk: @@ -14,7 +14,7 @@
     "@hookform/resolvers": "^3.0.1",
     "@radix-ui/react-form": "^0.0.2",
     "@radix-ui/react-icons": "^1.3.0",
-    "@raystack/apsara": "^0.34.2",
+    "@raystack/apsara": "^0.37.0",
path: ui/package.json
user: login: paanSinghCoder
body: Can be updated to latest version. It has some fixes.
created_at: 2025-03-19T07:06:26Z
author_association: CONTRIBUTOR
diff_hunk: @@ -14,7 +14,7 @@
     "@hookform/resolvers": "^3.0.1",
     "@radix-ui/react-form": "^0.0.2",
     "@radix-ui/react-icons": "^1.3.0",
-    "@raystack/apsara": "^0.34.2",
+    "@raystack/apsara": "^0.37.0",
path: ui/package.json
user: login: rsbh
body: i will do in next PR
created_at: 2025-03-19T08:20:45Z
author_association: MEMBER

PR comments:
user: login: vercel[bot]
created_at: 2025-03-13T06:26:39Z
body: [vc]: #/9gja8hp9vmuZK56dKNb8TVYRprxd7uLo1ADkLv63M0=:eyJpc01vbm9yZXBvIjp0cnVlLCJ0eXBlIjoiZ2l0aHViIiwicHJvamVjdHMiOlt7Im5hbWUiOiJmcm9udGllciIsImluc3BlY3RvclVybCI6Imh0dHBzOi8vdmVyY2VsLmNvbS9yYXlzdGFjay9mcm9udGllci9FUjhOVDZUTHZzcG1Bb3pFaVVqQ2V3azdHeUVRIiwicHJldmlld1VybCI6ImZyb250aWVyLWdpdC1mZWF0YWRtaW51aW9yZ2RldGFpbHNwYWdlLXJheXN0YWNrLnZlcmNlbC5hcHAiLCJuZXh0Q29tbWl0U3RhdHVzIjoiREVQTE9ZRUQiLCJsaXZlRmVlZGJhY2siOnsicmVzb2x2ZWQiOjAsInVucmVzb2x2ZWQiOjAsInRvdGFsIjowLCJsaW5rIjoiZnJvbnRpZXItZ2l0LWZlYXRhZG1pbnVpb3JnZGV0YWlsc3BhZ2UtcmF5c3RhY2sudmVyY2VsLmFwcCJ9LCJyb290RGlyZWN0b3J5IjoiZG9jcyJ9XX0=
**The latest updates on your projects**. Learn more about [Vercel for Git ↗︎](https://vercel.link/github-learn-more)

| Name | Status | Preview | Comments | Updated (UTC) |
| :--- | :----- | :------ | :------- | :------ |
| **frontier** | ✅ Ready ([Inspect](https://vercel.com/raystack/frontier/ER8NT6TLvspmAozEiUjCewk7GyEQ)) | [Visit Preview](https://frontier-git-featadminuiorgdetailspage-raystack.vercel.app) | 💬 [**Add feedback**](https://vercel.live/open-feedback/frontier-git-featadminuiorgdetailspage-raystack.vercel.app?via=pr-comment-feedback-link) | Mar 18, 2025 11:50am |


author_association: NONE
user: login: coveralls
created_at: 2025-03-13T06:31:46Z
body: ## Pull Request Test Coverage Report for [Build 13922420245](https://coveralls.io/builds/72784905)


### Details

* **0** of **3**   **(0.0%)**  changed or added relevant lines in **1** file are covered.
* No unchanged relevant lines lost coverage.
* Overall first build on feat_admin_ui_org_details_page at **31.753%**

---

|  Changes Missing Coverage | Covered Lines | Changed/Added Lines | % |
| :-----|--------------|--------|---: |
| [pkg/server/server.go](https://coveralls.io/builds/72784905/source?filename=pkg%2Fserver%2Fserver.go#L97) | 0 | 3 | 0.0%
<!-- | **Total:** | **0** | **3** | **0.0%** | -->


|  Totals | [![Coverage Status](https://coveralls.io/builds/72784905/badge)](https://coveralls.io/builds/72784905) |
| :-- | --: |
| Change from base [Build 13895898889](https://coveralls.io/builds/72757451): |  31.8% |
| Covered Lines: | 8540 |
| Relevant Lines: | 26895 |

---
##### 💛  - [Coveralls](https://coveralls.io)

author_association: NONE