Brijesh's Git Server — argus-web @ fd843b172d679a1b2a32ad4430829b41c65f015f

Web Ul for argus

src/app/dashboard/api-keys/page.js (view raw)

 1
 2
 3
 4
 5
 6
 7
 8
 9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
 100
 101
 102
 103
 104
 105
 106
 107
 108
 109
 110
 111
 112
 113
 114
 115
 116
 117
 118
 119
 120
 121
 122
 123
 124
 125
 126
 127
 128
 129
 130
 131
 132
 133
 134
 135
 136
 137
 138
 139
 140
 141
 142
 143
 144
 145
 146
 147
 148
 149
 150
 151
 152
 153
 154
 155
 156
 157
 158
 159
 160
 161
 162
 163
 164
 165
 166
 167
"use client";

import { useState, useEffect } from "react";
import Link from "next/link";
import Button from "@/components/shared/Button";

export default function APIKeysPage() {
  const [apiKeys, setApiKeys] = useState([]);
  const [isLoading, setIsLoading] = useState(true);
  const [error, setError] = useState(null);
  const [deletingKeys, setDeletingKeys] = useState(new Set());

  useEffect(() => {
    fetchAPIKeys();
  }, []);

  async function fetchAPIKeys() {
    try {
      const response = await fetch(
        "http://localhost:8080/twirp/apikeys.APIKeysService/ListAPIKeys",
        {
          method: "POST",
          headers: {
            "Content-Type": "application/json",
          },
          body: JSON.stringify({
            token: localStorage.getItem("token"),
          }),
        },
      );

      if (!response.ok) throw new Error("Failed to fetch API keys");

      const data = await response.json();
      setApiKeys(data.api_keys || []);
    } catch (error) {
      setError("Failed to load API keys");
      console.error("Error:", error);
    } finally {
      setIsLoading(false);
    }
  }

  if (isLoading) {
    return (
      <div className="flex items-center justify-center h-64">
        <div className="text-gray-500">Loading API keys...</div>
      </div>
    );
  }

  if (error) {
    return (
      <div className="rounded-lg bg-white shadow p-6">
        <div className="text-red-600">{error}</div>
      </div>
    );
  }

  async function handleDeleteKey(keyId) {
    if (
      !confirm(
        "Are you sure you want to delete this API key? This action cannot be undone.",
      )
    ) {
      return;
    }

    setDeletingKeys((prev) => new Set([...prev, keyId]));

    try {
      const response = await fetch(
        `http://localhost:8080/twirp/apikeys.APIKeysService/DeleteAPIKey`,
        {
          method: "POST",
          headers: {
            "Content-Type": "application/json",
          },
          body: JSON.stringify({
            token: localStorage.getItem("token"),
            key_id: keyId,
          }),
        },
      );

      if (!response.ok) throw new Error("Failed to delete API key: ", response);

      await fetchAPIKeys(); // Refresh the list
    } catch (error) {
      setError("Failed to delete API key");
      console.error("Error:", error);
    } finally {
      setDeletingKeys((prev) => {
        const newSet = new Set(prev);
        newSet.delete(keyId);
        return newSet;
      });
    }
  }

  if (!apiKeys.length) {
    return (
      <div className="rounded-lg bg-white shadow">
        <div className="px-4 py-12 my-0">
          <div className="text-center">
            <h3 className="mt-2 text-lg font-semibold text-gray-900">
              No API keys
            </h3>
            <p className="mt-1 text-sm text-gray-500">
              Create an API key to get started with programmatic access.
            </p>
            <div className="mt-6">
              <Link href="/dashboard/api-keys/new">
                <Button variant="secondary">Create API Key</Button>
              </Link>
            </div>
          </div>
        </div>
      </div>
    );
  }

  return (
    <>
      <div className="bg-white shadow rounded-lg divide-y divide-gray-200">
        {apiKeys &&
          apiKeys.map((key) => (
            <APIKeyItem
              key={key.id}
              apiKey={key}
              onDelete={() => handleDeleteKey(key.id)}
              isDeleting={deletingKeys.has(key.id)}
            />
          ))}
      </div>
    </>
  );
}

function APIKeyItem({ apiKey, onDelete, isDeleting }) {
  return (
    <div className="px-6 py-4">
      <div className="flex items-center justify-between">
        <div className="flex items-center gap-4">
          <h3 className="text-lg font-medium text-gray-900 capitalize w-48 truncate">
            {apiKey.name}
          </h3>
          <div className="mt-1 text-sm text-gray-500">
            Created on {new Date(apiKey.created_at).toLocaleDateString()}
          </div>
          {apiKey.last_used_at && (
            <div className="mt-1 text-sm text-gray-500">
              Last used on {new Date(apiKey.last_used_at).toLocaleDateString()}
            </div>
          )}
        </div>
        <button
          onClick={onDelete}
          disabled={isDeleting}
          className={`text-sm text-red-600 hover:text-red-900 ${isDeleting ? "opacity-50 cursor-not-allowed" : ""}`}
        >
          {isDeleting ? "Deleting..." : "Delete"}
        </button>
      </div>
    </div>
  );
}