@djangocfg/layouts 1.0.3 → 1.0.5

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (41) hide show
  1. package/package.json +5 -5
  2. package/src/layouts/AppLayout/layouts/AuthLayout/AuthHelp.tsx +2 -2
  3. package/src/layouts/AppLayout/layouts/AuthLayout/OTPForm.tsx +6 -6
  4. package/src/layouts/AppLayout/layouts/PrivateLayout/components/DashboardSidebar.tsx +1 -1
  5. package/src/layouts/AppLayout/layouts/PublicLayout/components/DesktopUserMenu.tsx +6 -6
  6. package/src/layouts/AppLayout/layouts/PublicLayout/components/Footer.tsx +1 -1
  7. package/src/layouts/AppLayout/layouts/PublicLayout/components/MobileMenu.tsx +43 -133
  8. package/src/layouts/AppLayout/layouts/PublicLayout/components/MobileMenuUserCard.tsx +150 -0
  9. package/src/layouts/AppLayout/layouts/PublicLayout/components/Navigation.tsx +2 -2
  10. package/src/layouts/PaymentsLayout/PaymentsLayout.tsx +47 -65
  11. package/src/layouts/PaymentsLayout/components/CreatePaymentDialog.tsx +121 -144
  12. package/src/layouts/PaymentsLayout/components/PaymentDetailsDialog.tsx +103 -48
  13. package/src/layouts/PaymentsLayout/components/index.ts +1 -4
  14. package/src/layouts/PaymentsLayout/events.ts +23 -84
  15. package/src/layouts/PaymentsLayout/index.ts +7 -11
  16. package/src/layouts/PaymentsLayout/types.ts +3 -16
  17. package/src/layouts/PaymentsLayout/views/overview/components/BalanceCard.tsx +45 -16
  18. package/src/layouts/PaymentsLayout/views/overview/components/RecentPayments.tsx +18 -14
  19. package/src/layouts/PaymentsLayout/views/overview/components/index.ts +0 -2
  20. package/src/layouts/PaymentsLayout/views/overview/index.tsx +3 -6
  21. package/src/layouts/PaymentsLayout/views/payments/components/PaymentsList.tsx +51 -31
  22. package/src/layouts/PaymentsLayout/views/payments/components/index.ts +0 -1
  23. package/src/layouts/PaymentsLayout/views/payments/index.tsx +1 -2
  24. package/src/layouts/PaymentsLayout/views/transactions/components/TransactionsList.tsx +273 -0
  25. package/src/layouts/PaymentsLayout/views/transactions/components/index.ts +1 -0
  26. package/src/layouts/PaymentsLayout/views/transactions/index.tsx +5 -17
  27. package/src/layouts/SupportLayout/hooks/useInfiniteMessages.ts +2 -3
  28. package/src/layouts/SupportLayout/hooks/useInfiniteTickets.ts +2 -3
  29. package/src/snippets/Chat/components/SessionList.tsx +1 -1
  30. package/src/snippets/Chat/hooks/useInfiniteSessions.ts +2 -2
  31. package/src/snippets/VideoPlayer/VideoPlayer.tsx +1 -1
  32. package/src/layouts/PaymentsLayout/README.md +0 -133
  33. package/src/layouts/PaymentsLayout/components/CreateApiKeyDialog.tsx +0 -172
  34. package/src/layouts/PaymentsLayout/components/DeleteApiKeyDialog.tsx +0 -100
  35. package/src/layouts/PaymentsLayout/context/RootPaymentsContext.tsx +0 -134
  36. package/src/layouts/PaymentsLayout/views/apikeys/components/ApiKeyMetrics.tsx +0 -109
  37. package/src/layouts/PaymentsLayout/views/apikeys/components/ApiKeysList.tsx +0 -194
  38. package/src/layouts/PaymentsLayout/views/apikeys/components/index.ts +0 -3
  39. package/src/layouts/PaymentsLayout/views/apikeys/index.tsx +0 -19
  40. package/src/layouts/PaymentsLayout/views/overview/components/MetricsCards.tsx +0 -103
  41. package/src/layouts/PaymentsLayout/views/tariffs/index.tsx +0 -29
@@ -1,133 +0,0 @@
1
- # Payments Layout
2
-
3
- Modern payments layout with tabbed interface for managing payments, balances, API keys, and subscriptions.
4
-
5
- ## Structure
6
-
7
- ```
8
- PaymentsLayout/
9
- ├── PaymentsLayout.tsx # Main layout with tabs and providers
10
- ├── events.ts # Event-based dialog communication
11
- ├── types.ts # TypeScript types
12
- ├── index.ts # Public exports
13
- └── views/ # Tab views
14
- ├── overview/ # Dashboard with metrics and balance
15
- │ ├── components/
16
- │ │ ├── MetricsCards.tsx
17
- │ │ ├── BalanceCard.tsx
18
- │ │ ├── RecentPayments.tsx
19
- │ │ └── index.ts
20
- │ └── index.tsx
21
- ├── payments/ # Payment transactions list
22
- │ ├── components/
23
- │ │ ├── PaymentsList.tsx
24
- │ │ └── index.ts
25
- │ └── index.tsx
26
- ├── transactions/ # Transaction history (placeholder)
27
- │ └── index.tsx
28
- ├── apikeys/ # API keys management
29
- │ ├── components/
30
- │ │ ├── ApiKeysList.tsx
31
- │ │ ├── ApiKeyMetrics.tsx
32
- │ │ └── index.ts
33
- │ └── index.tsx
34
- └── tariffs/ # Tariff plans (placeholder)
35
- └── index.tsx
36
- ```
37
-
38
- ## Usage
39
-
40
- ```tsx
41
- import { PaymentsLayout } from '@djangocfg/layouts';
42
-
43
- function PaymentsPage() {
44
- return <PaymentsLayout />;
45
- }
46
- ```
47
-
48
- ## Features
49
-
50
- ### Tabs
51
- - **Overview** - Dashboard with key metrics, balance card, and recent payments
52
- - **Payments** - Full payment history with search and filters
53
- - **Transactions** - Balance transaction history (coming soon)
54
- - **API Keys** - Create, manage, and monitor API keys
55
- - **Tariffs** - Subscription plans and pricing (coming soon)
56
-
57
- ### Contexts
58
- The layout automatically provides all necessary contexts:
59
- - `OverviewProvider` - Dashboard metrics and overview data
60
- - `PaymentsProvider` - Payment CRUD operations
61
- - `BalancesProvider` - User balance management
62
- - `ApiKeysProvider` - API key management
63
-
64
- ### Event System
65
- Uses event-based communication for dialogs:
66
- - `openCreateApiKeyDialog()` - Open create API key dialog
67
- - `openEditApiKeyDialog(id)` - Open edit API key dialog
68
- - `openDeleteApiKeyDialog(id)` - Open delete confirmation dialog
69
- - `openCreatePaymentDialog()` - Open create payment dialog
70
- - `openPaymentDetailsDialog(id)` - Open payment details dialog
71
- - `openCancelPaymentDialog(id)` - Open cancel payment dialog
72
- - `closePaymentsDialog()` - Close any active dialog
73
-
74
- ## Components
75
-
76
- ### Overview View
77
- - **MetricsCards** - Display key metrics (balance, payments, API keys)
78
- - **BalanceCard** - Show current balance with quick actions
79
- - **RecentPayments** - List of recent payment transactions
80
-
81
- ### Payments View
82
- - **PaymentsList** - Paginated payment list with filters and search
83
-
84
- ### API Keys View
85
- - **ApiKeyMetrics** - API key usage statistics
86
- - **ApiKeysList** - List of API keys with management actions
87
-
88
- ## Responsive Design
89
- - Mobile-first responsive layout
90
- - Adaptive tabs (icons only on mobile, text on desktop)
91
- - Responsive grid layouts for cards and metrics
92
-
93
- ## Architecture
94
-
95
- ### Decomposed Contexts
96
- Uses specialized contexts from `@djangocfg/api/cfg/contexts`:
97
- - `PaymentsContext` - Payment operations
98
- - `BalancesContext` - Balance queries
99
- - `ApiKeysContext` - API key CRUD
100
- - `OverviewContext` - Dashboard data
101
-
102
- ### View-based Structure
103
- Each tab is a separate view with:
104
- - Dedicated components folder
105
- - Dedicated hooks folder (for future custom hooks)
106
- - Clean separation of concerns
107
-
108
- ### Event-driven Dialogs
109
- Dialogs are controlled via events, not props:
110
- - Loose coupling between components
111
- - Easy to trigger from anywhere
112
- - Centralized dialog management
113
-
114
- ## Migration from Old Layout
115
-
116
- The old `PaymentsLayout_old` has been replaced with this new structure. Key improvements:
117
-
118
- 1. **Better Organization** - Views are logically separated
119
- 2. **Decomposed Contexts** - Each context has single responsibility
120
- 3. **Event System** - Better dialog management
121
- 4. **Scalability** - Easy to add new tabs and features
122
- 5. **Type Safety** - Full TypeScript support
123
-
124
- ## Future Enhancements
125
-
126
- - [ ] Complete Transactions view with filtering
127
- - [ ] Complete Tariffs view with plan comparison
128
- - [ ] Add payment creation dialogs
129
- - [ ] Add API key creation/edit dialogs
130
- - [ ] Add real-time payment status updates
131
- - [ ] Add charts and analytics
132
- - [ ] Add export functionality
133
-
@@ -1,172 +0,0 @@
1
- /**
2
- * Create API Key Dialog
3
- * Dialog for creating new API keys
4
- */
5
-
6
- 'use client';
7
-
8
- import React, { useState, useEffect } from 'react';
9
- import {
10
- Dialog,
11
- DialogContent,
12
- DialogDescription,
13
- DialogFooter,
14
- DialogHeader,
15
- DialogTitle,
16
- Form,
17
- FormControl,
18
- FormDescription,
19
- FormField,
20
- FormItem,
21
- FormLabel,
22
- FormMessage,
23
- Input,
24
- Button,
25
- useEventListener,
26
- } from '@djangocfg/ui';
27
- import { Plus, RefreshCw } from 'lucide-react';
28
- import { useForm } from 'react-hook-form';
29
- import { zodResolver } from '@hookform/resolvers/zod';
30
- import { useApiKeysContext } from '@djangocfg/api/cfg/contexts';
31
- import { Schemas } from '@djangocfg/api/cfg/generated';
32
- import { paymentsLogger } from '../../../utils/logger';
33
-
34
- const { APIKeyCreateRequestSchema } = Schemas;
35
- type APIKeyCreateRequest = Schemas.APIKeyCreateRequest;
36
- import { PAYMENTS_DIALOG_EVENTS, closePaymentsDialog } from '../events';
37
-
38
- export const CreateApiKeyDialog: React.FC = () => {
39
- const [open, setOpen] = useState(false);
40
- const [initialData, setInitialData] = useState<any>();
41
- const { createApiKey } = useApiKeysContext();
42
- const [isSubmitting, setIsSubmitting] = useState(false);
43
-
44
- const form = useForm<APIKeyCreateRequest>({
45
- resolver: zodResolver(APIKeyCreateRequestSchema),
46
- defaultValues: {
47
- name: '',
48
- expires_in_days: null,
49
- },
50
- });
51
-
52
- // Event listeners
53
- useEventListener(PAYMENTS_DIALOG_EVENTS.OPEN_CREATE_APIKEY_DIALOG, (event: any) => {
54
- setInitialData(event.payload?.initialKeyData);
55
- setOpen(true);
56
- });
57
-
58
- useEventListener(PAYMENTS_DIALOG_EVENTS.CLOSE_PAYMENTS_DIALOG, () => {
59
- setOpen(false);
60
- });
61
-
62
- // Reset form when initial data changes
63
- useEffect(() => {
64
- if (initialData) {
65
- form.reset({
66
- name: initialData.name || '',
67
- expires_in_days: initialData.expires_in_days || undefined,
68
- });
69
- }
70
- }, [initialData, form]);
71
-
72
- const handleClose = () => {
73
- setOpen(false);
74
- form.reset();
75
- setInitialData(undefined);
76
- };
77
-
78
- const handleSubmit = async (data: APIKeyCreateRequest) => {
79
- try {
80
- setIsSubmitting(true);
81
- await createApiKey(data);
82
- handleClose();
83
- closePaymentsDialog();
84
- } catch (error) {
85
- paymentsLogger.error('Failed to create API key:', error);
86
- } finally {
87
- setIsSubmitting(false);
88
- }
89
- };
90
-
91
- return (
92
- <Dialog open={open} onOpenChange={(isOpen) => !isOpen && handleClose()}>
93
- <DialogContent className="sm:max-w-md">
94
- <DialogHeader>
95
- <DialogTitle>Create API Key</DialogTitle>
96
- <DialogDescription>
97
- Create a new API key for secure access to the platform.
98
- </DialogDescription>
99
- </DialogHeader>
100
-
101
- <Form {...form}>
102
- <form onSubmit={form.handleSubmit(handleSubmit)} className="space-y-4">
103
- <FormField
104
- control={form.control}
105
- name="name"
106
- render={({ field }) => (
107
- <FormItem>
108
- <FormLabel>Name</FormLabel>
109
- <FormControl>
110
- <Input placeholder="My API Key" {...field} />
111
- </FormControl>
112
- <FormDescription>
113
- A descriptive name for your API key.
114
- </FormDescription>
115
- <FormMessage />
116
- </FormItem>
117
- )}
118
- />
119
-
120
- <FormField
121
- control={form.control}
122
- name="expires_in_days"
123
- render={({ field }) => (
124
- <FormItem>
125
- <FormLabel>Expiration (Days)</FormLabel>
126
- <FormControl>
127
- <Input
128
- type="number"
129
- placeholder="30"
130
- min="1"
131
- max="365"
132
- {...field}
133
- onChange={(e) => {
134
- const value = e.target.value;
135
- field.onChange(value ? parseInt(value, 10) : null);
136
- }}
137
- value={field.value || ''}
138
- />
139
- </FormControl>
140
- <FormDescription>
141
- Number of days until expiration. Leave empty for no expiration.
142
- </FormDescription>
143
- <FormMessage />
144
- </FormItem>
145
- )}
146
- />
147
-
148
- <DialogFooter>
149
- <Button type="button" variant="outline" onClick={handleClose}>
150
- Cancel
151
- </Button>
152
- <Button type="submit" disabled={isSubmitting || !form.formState.isValid}>
153
- {isSubmitting ? (
154
- <>
155
- <RefreshCw className="h-4 w-4 mr-2 animate-spin" />
156
- Creating...
157
- </>
158
- ) : (
159
- <>
160
- <Plus className="h-4 w-4 mr-2" />
161
- Create Key
162
- </>
163
- )}
164
- </Button>
165
- </DialogFooter>
166
- </form>
167
- </Form>
168
- </DialogContent>
169
- </Dialog>
170
- );
171
- };
172
-
@@ -1,100 +0,0 @@
1
- /**
2
- * Delete API Key Dialog
3
- * Confirmation dialog for deleting API keys
4
- */
5
-
6
- 'use client';
7
-
8
- import React, { useState } from 'react';
9
- import {
10
- Dialog,
11
- DialogContent,
12
- DialogDescription,
13
- DialogFooter,
14
- DialogHeader,
15
- DialogTitle,
16
- Button,
17
- useEventListener,
18
- } from '@djangocfg/ui';
19
- import { Trash2, RefreshCw } from 'lucide-react';
20
- import { useApiKeysContext } from '@djangocfg/api/cfg/contexts';
21
- import { paymentsLogger } from '../../../utils/logger';
22
- import { PAYMENTS_DIALOG_EVENTS, closePaymentsDialog } from '../events';
23
-
24
- export const DeleteApiKeyDialog: React.FC = () => {
25
- const [open, setOpen] = useState(false);
26
- const [keyId, setKeyId] = useState<string>('');
27
- const [isDeleting, setIsDeleting] = useState(false);
28
- const { deleteApiKey } = useApiKeysContext();
29
-
30
- useEventListener(PAYMENTS_DIALOG_EVENTS.OPEN_DELETE_APIKEY_DIALOG, (payload: any) => {
31
- const id = payload?.keyId || '';
32
- setKeyId(id);
33
- setOpen(true);
34
- });
35
-
36
- useEventListener(PAYMENTS_DIALOG_EVENTS.CLOSE_PAYMENTS_DIALOG, () => {
37
- setOpen(false);
38
- });
39
-
40
- const handleClose = () => {
41
- setOpen(false);
42
- setKeyId('');
43
- };
44
-
45
- const handleDelete = async () => {
46
- if (!keyId) return;
47
-
48
- try {
49
- setIsDeleting(true);
50
- await deleteApiKey(keyId);
51
- // Add a small delay to ensure the list refreshes before closing
52
- await new Promise(resolve => setTimeout(resolve, 100));
53
- handleClose();
54
- closePaymentsDialog();
55
- } catch (error) {
56
- paymentsLogger.error('Failed to delete API key:', error);
57
- } finally {
58
- setIsDeleting(false);
59
- }
60
- };
61
-
62
- return (
63
- <Dialog open={open} onOpenChange={(isOpen) => !isOpen && handleClose()}>
64
- <DialogContent className="sm:max-w-md">
65
- <DialogHeader>
66
- <DialogTitle>Delete API Key</DialogTitle>
67
- <DialogDescription>
68
- Are you sure you want to delete this API key? This action cannot be undone and will
69
- immediately revoke access for any applications using this key.
70
- </DialogDescription>
71
- </DialogHeader>
72
-
73
- <DialogFooter>
74
- <Button type="button" variant="outline" onClick={handleClose} disabled={isDeleting}>
75
- Cancel
76
- </Button>
77
- <Button
78
- type="button"
79
- variant="destructive"
80
- onClick={handleDelete}
81
- disabled={isDeleting}
82
- >
83
- {isDeleting ? (
84
- <>
85
- <RefreshCw className="h-4 w-4 mr-2 animate-spin" />
86
- Deleting...
87
- </>
88
- ) : (
89
- <>
90
- <Trash2 className="h-4 w-4 mr-2" />
91
- Delete Key
92
- </>
93
- )}
94
- </Button>
95
- </DialogFooter>
96
- </DialogContent>
97
- </Dialog>
98
- );
99
- };
100
-
@@ -1,134 +0,0 @@
1
- 'use client';
2
-
3
- import React, { createContext, useContext, useEffect, type ReactNode } from 'react';
4
- import {
5
- api,
6
- usePaymentsCurrenciesList,
7
- usePaymentsProviderCurrenciesList,
8
- usePaymentsNetworksList,
9
- } from '@djangocfg/api/cfg';
10
- import type { API } from '@djangocfg/api/cfg';
11
- import type {
12
- Currency,
13
- PaginatedCurrencyListList,
14
- ProviderCurrency,
15
- PaginatedProviderCurrencyList,
16
- Network,
17
- PaginatedNetworkList,
18
- } from '@djangocfg/api/cfg';
19
-
20
- // ─────────────────────────────────────────────────────────────────────────
21
- // Context Type
22
- // ─────────────────────────────────────────────────────────────────────────
23
-
24
- export interface RootPaymentsContextValue {
25
- // Currencies
26
- currencies: PaginatedCurrencyListList | undefined;
27
- isLoadingCurrencies: boolean;
28
- currenciesError: Error | undefined;
29
- refreshCurrencies: () => Promise<void>;
30
-
31
- // Provider Currencies
32
- providerCurrencies: PaginatedProviderCurrencyList | undefined;
33
- isLoadingProviderCurrencies: boolean;
34
- providerCurrenciesError: Error | undefined;
35
- refreshProviderCurrencies: () => Promise<void>;
36
-
37
- // Networks
38
- networks: PaginatedNetworkList | undefined;
39
- isLoadingNetworks: boolean;
40
- networksError: Error | undefined;
41
- refreshNetworks: () => Promise<void>;
42
- }
43
-
44
- // ─────────────────────────────────────────────────────────────────────────
45
- // Context
46
- // ─────────────────────────────────────────────────────────────────────────
47
-
48
- const RootPaymentsContext = createContext<RootPaymentsContextValue | undefined>(undefined);
49
-
50
- // ─────────────────────────────────────────────────────────────────────────
51
- // Provider
52
- // ─────────────────────────────────────────────────────────────────────────
53
-
54
- export function RootPaymentsProvider({ children }: { children: ReactNode }) {
55
- // List all currencies
56
- const {
57
- data: currencies,
58
- error: currenciesError,
59
- isLoading: isLoadingCurrencies,
60
- mutate: mutateCurrencies,
61
- } = usePaymentsCurrenciesList({}, api as unknown as API);
62
-
63
- // List all provider currencies
64
- const {
65
- data: providerCurrencies,
66
- error: providerCurrenciesError,
67
- isLoading: isLoadingProviderCurrencies,
68
- mutate: mutateProviderCurrencies,
69
- } = usePaymentsProviderCurrenciesList({}, api as unknown as API);
70
-
71
- // List all networks
72
- const {
73
- data: networks,
74
- error: networksError,
75
- isLoading: isLoadingNetworks,
76
- mutate: mutateNetworks,
77
- } = usePaymentsNetworksList({}, api as unknown as API);
78
-
79
- const refreshCurrencies = async () => {
80
- await mutateCurrencies();
81
- };
82
-
83
- const refreshProviderCurrencies = async () => {
84
- await mutateProviderCurrencies();
85
- };
86
-
87
- const refreshNetworks = async () => {
88
- await mutateNetworks();
89
- };
90
-
91
- const value: RootPaymentsContextValue = {
92
- currencies,
93
- isLoadingCurrencies,
94
- currenciesError,
95
- refreshCurrencies,
96
- providerCurrencies,
97
- isLoadingProviderCurrencies,
98
- providerCurrenciesError,
99
- refreshProviderCurrencies,
100
- networks,
101
- isLoadingNetworks,
102
- networksError,
103
- refreshNetworks,
104
- };
105
-
106
- return (
107
- <RootPaymentsContext.Provider value={value}>{children}</RootPaymentsContext.Provider>
108
- );
109
- }
110
-
111
- // ─────────────────────────────────────────────────────────────────────────
112
- // Hook
113
- // ─────────────────────────────────────────────────────────────────────────
114
-
115
- export function useRootPaymentsContext(): RootPaymentsContextValue {
116
- const context = useContext(RootPaymentsContext);
117
- if (!context) {
118
- throw new Error('useRootPaymentsContext must be used within RootPaymentsProvider');
119
- }
120
- return context;
121
- }
122
-
123
- // ─────────────────────────────────────────────────────────────────────────
124
- // Re-export types
125
- // ─────────────────────────────────────────────────────────────────────────
126
-
127
- export type {
128
- Currency,
129
- PaginatedCurrencyListList,
130
- ProviderCurrency,
131
- PaginatedProviderCurrencyList,
132
- Network,
133
- PaginatedNetworkList,
134
- };
@@ -1,109 +0,0 @@
1
- /**
2
- * API Key Metrics Component
3
- * Display API key usage statistics
4
- */
5
-
6
- 'use client';
7
-
8
- import React from 'react';
9
- import {
10
- Card,
11
- CardContent,
12
- CardHeader,
13
- CardTitle,
14
- Skeleton,
15
- } from '@djangocfg/ui';
16
- import { Key, CheckCircle2, XCircle, Activity } from 'lucide-react';
17
- import { useApiKeysContext } from '@djangocfg/api/cfg/contexts';
18
-
19
- export const ApiKeyMetrics: React.FC = () => {
20
- const { apiKeys, isLoadingApiKeys } = useApiKeysContext();
21
-
22
- const keysList = apiKeys?.results || [];
23
-
24
- const metrics = React.useMemo(() => {
25
- const total = keysList.length;
26
- const active = keysList.filter(key => key.is_active).length;
27
- const inactive = total - active;
28
- const recentlyUsed = keysList.filter(key => {
29
- if (!key.last_used_at) return false;
30
- const lastUsed = new Date(key.last_used_at);
31
- const dayAgo = new Date(Date.now() - 24 * 60 * 60 * 1000);
32
- return lastUsed > dayAgo;
33
- }).length;
34
-
35
- return { total, active, inactive, recentlyUsed };
36
- }, [keysList]);
37
-
38
- if (isLoadingApiKeys) {
39
- return (
40
- <div className="grid gap-4 md:grid-cols-2 lg:grid-cols-4">
41
- {Array.from({ length: 4 }).map((_, i) => (
42
- <Card key={i}>
43
- <CardHeader className="flex flex-row items-center justify-between space-y-0 pb-2">
44
- <Skeleton className="h-4 w-24" />
45
- <Skeleton className="h-4 w-4 rounded-full" />
46
- </CardHeader>
47
- <CardContent>
48
- <Skeleton className="h-7 w-12 mb-1" />
49
- <Skeleton className="h-3 w-32" />
50
- </CardContent>
51
- </Card>
52
- ))}
53
- </div>
54
- );
55
- }
56
-
57
- const metricsData = [
58
- {
59
- title: 'Total Keys',
60
- value: metrics.total.toString(),
61
- description: 'All API keys',
62
- icon: Key,
63
- },
64
- {
65
- title: 'Active Keys',
66
- value: metrics.active.toString(),
67
- description: 'Currently active',
68
- icon: CheckCircle2,
69
- iconColor: 'text-green-600',
70
- },
71
- {
72
- title: 'Inactive Keys',
73
- value: metrics.inactive.toString(),
74
- description: 'Not in use',
75
- icon: XCircle,
76
- iconColor: 'text-muted-foreground',
77
- },
78
- {
79
- title: 'Recently Used',
80
- value: metrics.recentlyUsed.toString(),
81
- description: 'Last 24 hours',
82
- icon: Activity,
83
- iconColor: 'text-blue-600',
84
- },
85
- ];
86
-
87
- return (
88
- <div className="grid gap-4 md:grid-cols-2 lg:grid-cols-4">
89
- {metricsData.map((metric, index) => {
90
- const Icon = metric.icon;
91
- return (
92
- <Card key={index}>
93
- <CardHeader className="flex flex-row items-center justify-between space-y-0 pb-2">
94
- <CardTitle className="text-sm font-medium">{metric.title}</CardTitle>
95
- <Icon className={`h-4 w-4 ${metric.iconColor || 'text-muted-foreground'}`} />
96
- </CardHeader>
97
- <CardContent>
98
- <div className="text-2xl font-bold">{metric.value}</div>
99
- <p className="text-xs text-muted-foreground mt-1">
100
- {metric.description}
101
- </p>
102
- </CardContent>
103
- </Card>
104
- );
105
- })}
106
- </div>
107
- );
108
- };
109
-