Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
61 changes: 42 additions & 19 deletions web/src/api/hooks/useStoreHooks.ts
Original file line number Diff line number Diff line change
@@ -1,16 +1,18 @@
import { useMutation, useQuery, useQueryClient } from "@tanstack/react-query";
import { useApiClients } from "../apiClient";
import { cacheKeys, cacheSettings, CACHE_TIMES } from "../utils/cache";
import {
import type {
// Use type-only import for Error
ApiKeyCreate,
StoreUpdate,
StoreBasicInfo,
Error,
SearchStoresParams,
GetApiKeyUsagePayload, // <-- Import payload type for usage stats
GetApiUsageLogParams, // <-- Import params type for usage log
ApiUsageLogEntry, // <-- Import log entry type
PaginationInfo, // <-- Import pagination info type
GetApiKeyUsagePayload,
GetApiUsageLogParams,
ApiUsageLogEntry,
PaginationInfo,
ApiKeyUsage, // <-- Add ApiKeyUsage import
} from "../types/data-contracts";
import { useAuth } from "../../hooks/useAuth";
import { useState, useEffect } from "react";
Expand Down Expand Up @@ -90,24 +92,45 @@ export function useRevokeApiKey() {

// Update useApiKeyUsage to accept payload
export function useApiKeyUsage(
keyId: string,
payload?: GetApiKeyUsagePayload, // Accept payload
keyId: string | undefined, // Allow undefined here
payload?: GetApiKeyUsagePayload,
) {
const { apiClients, clientsReady } = useApiClients();
const { isAuthenticated, isLoading: authLoading } = useAuth();

// Include payload in the query key if present
// This object now matches the expected parameter type for the updated cache key
const queryKeyParams = { keyId, ...payload };

return useQuery({
// This call should now be valid
queryKey: cacheKeys.stores.apiKeyUsage(queryKeyParams),
queryFn: () =>
// Pass payload to the API call
apiClients.stores.getApiKeyUsage(keyId, payload).then((res) => res.data),
enabled: !!keyId && isAuthenticated && !authLoading && clientsReady,
...cacheSettings.apiKeys, // Consider specific cache settings for usage
// Determine if the query should be enabled
// It should only run if a specific keyId is provided (not undefined, null, or "all")
const isEnabled =
isAuthenticated &&
!authLoading &&
clientsReady &&
!!keyId && // Ensure keyId is truthy (not undefined, null, or empty string)
keyId !== "all"; // Explicitly disable if it's "all"

return useQuery<ApiKeyUsage, Error>({
// Use a more specific query key that includes the keyId and payload
queryKey: cacheKeys.stores.apiKeyUsage({
// Pass a single object argument
keyId: keyId || "all",
startDate: payload?.startDate,
endDate: payload?.endDate,
}),
queryFn: () => {
// Add a check here just in case, although 'enabled' should prevent it
if (!keyId || keyId === "all") {
// Should not happen if 'enabled' works correctly, but good failsafe
return Promise.reject(
new Error("No specific API key ID provided for usage stats."),
);
}
return apiClients.stores
.getApiKeyUsage(keyId, payload) // Pass keyId and payload
.then((res) => res.data);
},
enabled: isEnabled, // Use the calculated enabled state
// Optional: Add placeholderData or staleTime if desired
// placeholderData: (previousData) => previousData,
// staleTime: 5 * 60 * 1000, // 5 minutes
});
}

Expand Down
6 changes: 6 additions & 0 deletions web/src/index.css
Original file line number Diff line number Diff line change
Expand Up @@ -2,4 +2,10 @@
@plugin "flowbite-react/plugin/tailwindcss";
@source "../.flowbite-react/class-list.json";

@layer base {
body {
@apply bg-gray-50 dark:bg-gray-900;
}
}

@custom-variant dark (&:where(.dark, .dark *));
12 changes: 7 additions & 5 deletions web/src/pages/StoreDashboard/ApiKeyManagement.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -215,11 +215,13 @@ export function ApiKeyManagement() {
<div className="overflow-x-auto">
<Table hoverable>
<TableHead>
<TableHeadCell>Name</TableHeadCell>
<TableHeadCell>Prefix</TableHeadCell>
<TableHeadCell>Status</TableHeadCell>
<TableHeadCell>Created At</TableHeadCell>
<TableHeadCell>Actions</TableHeadCell>
<TableRow>
<TableHeadCell>Name</TableHeadCell>
<TableHeadCell>Prefix</TableHeadCell>
<TableHeadCell>Status</TableHeadCell>
<TableHeadCell>Created At</TableHeadCell>
<TableHeadCell>Actions</TableHeadCell>
</TableRow>
</TableHead>
<TableBody className="divide-y">
{apiKeysData.map((key) => (
Expand Down
51 changes: 36 additions & 15 deletions web/src/pages/StoreDashboard/ApiUsageDashboard.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -86,7 +86,8 @@ export function ApiUsageDashboard() {
data: usageStats,
isLoading: usageStatsLoading,
error: usageStatsError,
} = useApiKeyUsage(usageFilters.keyId || "all", {
} = useApiKeyUsage(usageFilters.keyId, {
// Pass usageFilters.keyId directly (can be undefined)
startDate: usageFilters.startDate,
endDate: usageFilters.endDate,
});
Expand Down Expand Up @@ -167,8 +168,12 @@ export function ApiUsageDashboard() {
<Datepicker
id="startDateFilter"
icon={HiCalendar}
value={usageFilters.startDate}
onSelectedDateChanged={(date) =>
value={
usageFilters.startDate
? new Date(usageFilters.startDate)
: undefined
}
onChange={(date: Date | null) =>
handleFilterChange("startDate", date)
}
maxDate={
Expand All @@ -183,8 +188,13 @@ export function ApiUsageDashboard() {
<Datepicker
id="endDateFilter"
icon={HiCalendar}
value={usageFilters.endDate}
onSelectedDateChanged={(date) =>
// Convert string back to Date for the component's value prop
value={
usageFilters.endDate
? new Date(usageFilters.endDate)
: undefined
}
onChange={(date: Date | null) =>
handleFilterChange("endDate", date)
}
minDate={
Expand All @@ -202,19 +212,28 @@ export function ApiUsageDashboard() {
<h4 className="mb-4 text-lg font-medium text-gray-900 dark:text-white">
Usage Statistics
</h4>
{/* Show loading spinner ONLY if the query is actually running */}
{usageStatsLoading ? (
<div className="flex justify-center py-8">
<Spinner size="lg" />
</div>
) : usageStatsError ? (
) : usageStatsError ? ( // Show error if the query failed
<Alert color="failure" icon={HiInformationCircle}>
Failed to load usage statistics: {usageStatsError.message}
</Alert>
) : !usageStats || usageStats.totalRequests === 0 ? (
) : // Add a specific check for when no key is selected (stats are undefined but not loading/error)
!usageFilters.keyId ? (
<p className="py-4 text-center text-gray-500 dark:text-gray-400">
Select an API key from the filter above to view its usage
statistics.
</p>
) : // Original check for no data *after* a key was selected and the query ran
!usageStats || usageStats.totalRequests === 0 ? (
<p className="py-4 text-center text-gray-500 dark:text-gray-400">
No usage data found for the selected filters.
No usage data found for the selected key and date range.
</p>
) : (
// Render the stats and charts only when data is available
<div className="space-y-6">
<p className="text-gray-700 dark:text-gray-400">
Total Requests:{" "}
Expand Down Expand Up @@ -272,7 +291,7 @@ export function ApiUsageDashboard() {
}
>
{Object.entries(usageStats.methodBreakdown).map(
(entry, index) => (
(_, index) => (
<Cell
key={`cell-${index}`}
fill={COLORS[index % COLORS.length]}
Expand All @@ -290,7 +309,7 @@ export function ApiUsageDashboard() {
)}
</Card>

{/* Detailed Log Section */}
{/* Detailed Log Section (Should be okay as is) */}
<Card>
<h4 className="mb-4 text-lg font-medium text-gray-900 dark:text-white">
Detailed Request Log
Expand All @@ -314,11 +333,13 @@ export function ApiUsageDashboard() {
>
<Table hoverable>
<TableHead>
<TableHeadCell>Timestamp</TableHeadCell>
<TableHeadCell>Method</TableHeadCell>
<TableHeadCell>Endpoint</TableHeadCell>
<TableHeadCell>Key Prefix</TableHeadCell>
<TableHeadCell>User Agent</TableHeadCell>
<TableRow>
<TableHeadCell>Timestamp</TableHeadCell>
<TableHeadCell>Method</TableHeadCell>
<TableHeadCell>Endpoint</TableHeadCell>
<TableHeadCell>Key Prefix</TableHeadCell>
<TableHeadCell>User Agent</TableHeadCell>
</TableRow>
</TableHead>
<TableBody className="divide-y">
{usageLogData.logs.map((log) => (
Expand Down
Loading