Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Wip optimization performance 264197 #29

Merged
merged 7 commits into from
Dec 15, 2024
1 change: 1 addition & 0 deletions public/safedep.svg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
3 changes: 3 additions & 0 deletions src/app/layout.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,9 @@ export const metadata: Metadata = {
title: "SafeDep | Open Source Software Supply Chain Security Platform",
description:
"Welcome to SafeDep. Onboard to SafeDep cloud, generate authentication credentials and access platform APIs",
icons: {
icon: '/safedep.svg',
},
};

export default function RootLayout({
Expand Down
10 changes: 6 additions & 4 deletions src/app/platform/components/navbar.tsx
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
import React, { memo } from 'react';
import { Disclosure, DisclosureButton, DisclosurePanel, Menu, MenuButton, MenuItem, MenuItems } from '@headlessui/react';
import { UserCircleIcon } from "@heroicons/react/16/solid";
import { Bars3Icon, XMarkIcon } from '@heroicons/react/24/outline';
Expand All @@ -10,7 +11,7 @@ function classNames(...classes: string[]) {
return classes.filter(Boolean).join(' ')
}

export default function Navbar() {
const Navbar = () => {
const navigation = [
{ name: 'API Keys', href: '/platform/keys', current: false },
]
Expand All @@ -31,8 +32,8 @@ export default function Navbar() {
<DisclosureButton className="group relative inline-flex items-center justify-center rounded-md p-2 text-gray-400 hover:bg-gray-700 hover:text-white focus:outline-none focus:ring-2 focus:ring-inset focus:ring-white">
<span className="absolute -inset-0.5" />
<span className="sr-only">Open main menu</span>
<Bars3Icon aria-hidden="true" className="block size-6 group-data-[open]:hidden" />
<XMarkIcon aria-hidden="true" className="hidden size-6 group-data-[open]:block" />
<Bars3Icon aria-hidden="true" className="block h-8 w-8 group-data-[open]:hidden" />
<XMarkIcon aria-hidden="true" className="hidden h-8 w-8 group-data-[open]:block" />
</DisclosureButton>
</div>
<div className="flex flex-1 items-center justify-center sm:items-stretch sm:justify-start">
Expand Down Expand Up @@ -66,7 +67,7 @@ export default function Navbar() {
<MenuButton className="relative flex rounded-full bg-gray-800 text-sm focus:outline-none focus:ring-2 focus:ring-white focus:ring-offset-2 focus:ring-offset-gray-800">
<span className="absolute -inset-1.5" />
<span className="sr-only">Open user menu</span>
<UserCircleIcon className="h-8 w-8 text-gray-300" />
<UserCircleIcon className="h-10 w-10 text-gray-300" />
</MenuButton>
</div>
<MenuItems
Expand Down Expand Up @@ -117,3 +118,4 @@ export default function Navbar() {
)
}

export default memo(Navbar);
139 changes: 71 additions & 68 deletions src/app/platform/keys/create/page.tsx
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
"use client";

import { useState } from "react";
import { useState, useCallback, useMemo } from "react";
import { KeyIcon, ClipboardCopyIcon, CheckIcon } from "lucide-react";
import MainHeader from "../../components/header";
import Sidebar from "../../components/sidebar";
Expand All @@ -15,7 +15,7 @@ const Page = () => {
const [copied, setCopied] = useState(false);
const [loading, setLoading] = useState(false);

const handleSubmit = async (e: React.FormEvent) => {
const handleSubmit = useCallback(async (e: React.FormEvent) => {
e.preventDefault();

if (!name || !description || !expiryDays) {
Expand Down Expand Up @@ -43,7 +43,6 @@ const Page = () => {
setApiKey(data.key);
setNotification("success");


setName("");
setDescription("");
setExpiry("");
Expand All @@ -54,13 +53,78 @@ const Page = () => {
} finally {
setLoading(false);
}
};
}, [name, description, expiryDays]);

const handleCopy = () => {
const handleCopy = useCallback(() => {
navigator.clipboard.writeText(apiKey);
setCopied(true);
setTimeout(() => setCopied(false), 2000);
};
}, [apiKey]);

const formContent = useMemo(() => (
<form
onSubmit={handleSubmit}
className="mt-8 space-y-8 bg-white rounded-xl shadow-sm border border-gray-200 p-8"
>
<div className="space-y-6">
<div>
<label className="block text-sm font-medium text-gray-700 mb-2">
Name
</label>
<input
type="text"
value={name}
onChange={(e) => setName(e.target.value)}
className="w-full px-4 py-3 rounded-lg border border-gray-300 focus:ring-2 focus:ring-blue-500 focus:border-transparent transition duration-200"
placeholder="e.g. API Key"
required
/>
</div>

<div>
<label className="block text-sm font-medium text-gray-700 mb-2">
Description
</label>
<textarea
value={description}
onChange={(e) => setDescription(e.target.value)}
rows={3}
className="w-full px-4 py-3 rounded-lg border border-gray-300 focus:ring-2 focus:ring-blue-500 focus:border-transparent transition duration-200"
placeholder="What will this API key be used for?"
required
/>
</div>

<div>
<label className="block text-sm font-medium text-gray-700 mb-2">
Expiration Period
</label>
<select
value={expiryDays}
onChange={(e) => setExpiry(e.target.value)}
className="w-full px-4 py-3 rounded-lg border border-gray-300 focus:ring-2 focus:ring-blue-500 focus:border-transparent transition duration-200"
required
>
<option value="">Select duration</option>
<option value="30">30 days</option>
<option value="60">60 days</option>
<option value="90">90 days</option>
<option value="365">1 year</option>
</select>
</div>
</div>

<div className="pt-6 border-t border-gray-200"></div>
<button
type="submit"
disabled={loading}
className="w-full px-6 py-3 text-white bg-blue-600 hover:bg-blue-700 rounded-lg shadow transition duration-200 flex items-center justify-center space-x-2"
>
<KeyIcon className="h-5 w-5" />
<span>{loading ? "Generating..." : "Generate API Key"}</span>
</button>
</form>
), [handleSubmit, name, description, expiryDays, loading]);

return (
<div className="flex min-h-screen bg-gray-50">
Expand All @@ -77,68 +141,7 @@ const Page = () => {
</div>
</MainHeader>

<form
onSubmit={handleSubmit}
className="mt-8 space-y-8 bg-white rounded-xl shadow-sm border border-gray-200 p-8"
>
<div className="space-y-6">
<div>
<label className="block text-sm font-medium text-gray-700 mb-2">
Name
</label>
<input
type="text"
value={name}
onChange={(e) => setName(e.target.value)}
className="w-full px-4 py-3 rounded-lg border border-gray-300 focus:ring-2 focus:ring-blue-500 focus:border-transparent transition duration-200"
placeholder="e.g. API Key"
required
/>
</div>

<div>
<label className="block text-sm font-medium text-gray-700 mb-2">
Description
</label>
<textarea
value={description}
onChange={(e) => setDescription(e.target.value)}
rows={3}
className="w-full px-4 py-3 rounded-lg border border-gray-300 focus:ring-2 focus:ring-blue-500 focus:border-transparent transition duration-200"
placeholder="What will this API key be used for?"
required
/>
</div>

<div>
<label className="block text-sm font-medium text-gray-700 mb-2">
Expiration Period
</label>
<select
value={expiryDays}
onChange={(e) => setExpiry(e.target.value)}
className="w-full px-4 py-3 rounded-lg border border-gray-300 focus:ring-2 focus:ring-blue-500 focus:border-transparent transition duration-200"
required
>
<option value="">Select duration</option>
<option value="30">30 days</option>
<option value="60">60 days</option>
<option value="90">90 days</option>
<option value="365">1 year</option>
</select>
</div>
</div>

<div className="pt-6 border-t border-gray-200"></div>
<button
type="submit"
disabled={loading}
className="w-full px-6 py-3 text-white bg-blue-600 hover:bg-blue-700 rounded-lg shadow transition duration-200 flex items-center justify-center space-x-2"
>
<KeyIcon className="h-5 w-5" />
<span>{loading ? "Generating..." : "Generate API Key"}</span>
</button>
</form>
{formContent}

{notification === "success" && (
<div className="mt-6 p-6 rounded-xl bg-green-50 border border-green-200">
Expand Down
21 changes: 12 additions & 9 deletions src/app/platform/keys/list/page.tsx
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
"use client";

import { useEffect, useState } from "react";
import { useEffect, useState, useCallback } from "react";
import { KeyIcon, MoreVertical } from "lucide-react";
import MainHeader from "../../components/header";
import Sidebar from "../../components/sidebar";
Expand Down Expand Up @@ -81,7 +81,8 @@ const Page = () => {
return id.length > 6 ? `${id.slice(0, 4)}...` : id;
};

const handleDelete = async (id: string) => {
const handleDelete = useCallback(async (id: string) => {
setLoading(true);
try {
const response = await fetch("/api/platform/keys", {
method: "DELETE",
Expand All @@ -98,15 +99,17 @@ const Page = () => {

setApiKeys((prevKeys) => prevKeys.filter((key) => key.id !== id));
setShowDeleteModal(false);
} catch (err) {
if (err instanceof Error) {
console.error("Error deleting API key:", err.message);
} else {
console.error("Unknown error occurred while deleting API key.");
}
} catch (error) {
console.error(error);
} finally {
setLoading(false);
}
};
}, []);

useEffect(() => {
if (loading) {
}
}, [loading]);

return (
<div className="flex min-h-screen bg-gray-50">
Expand Down
Loading