@djangocfg/layouts 1.0.4 → 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 (28) hide show
  1. package/package.json +5 -5
  2. package/src/layouts/PaymentsLayout/PaymentsLayout.tsx +47 -65
  3. package/src/layouts/PaymentsLayout/components/CreatePaymentDialog.tsx +118 -163
  4. package/src/layouts/PaymentsLayout/components/PaymentDetailsDialog.tsx +31 -45
  5. package/src/layouts/PaymentsLayout/components/index.ts +1 -4
  6. package/src/layouts/PaymentsLayout/events.ts +23 -84
  7. package/src/layouts/PaymentsLayout/index.ts +7 -11
  8. package/src/layouts/PaymentsLayout/types.ts +3 -16
  9. package/src/layouts/PaymentsLayout/views/overview/components/BalanceCard.tsx +45 -16
  10. package/src/layouts/PaymentsLayout/views/overview/components/RecentPayments.tsx +16 -12
  11. package/src/layouts/PaymentsLayout/views/overview/components/index.ts +0 -2
  12. package/src/layouts/PaymentsLayout/views/overview/index.tsx +3 -6
  13. package/src/layouts/PaymentsLayout/views/payments/components/PaymentsList.tsx +50 -30
  14. package/src/layouts/PaymentsLayout/views/payments/components/index.ts +0 -1
  15. package/src/layouts/PaymentsLayout/views/payments/index.tsx +1 -2
  16. package/src/layouts/PaymentsLayout/views/transactions/components/TransactionsList.tsx +273 -0
  17. package/src/layouts/PaymentsLayout/views/transactions/components/index.ts +1 -0
  18. package/src/layouts/PaymentsLayout/views/transactions/index.tsx +5 -17
  19. package/src/layouts/PaymentsLayout/README.md +0 -133
  20. package/src/layouts/PaymentsLayout/components/CreateApiKeyDialog.tsx +0 -172
  21. package/src/layouts/PaymentsLayout/components/DeleteApiKeyDialog.tsx +0 -100
  22. package/src/layouts/PaymentsLayout/context/RootPaymentsContext.tsx +0 -129
  23. package/src/layouts/PaymentsLayout/views/apikeys/components/ApiKeyMetrics.tsx +0 -109
  24. package/src/layouts/PaymentsLayout/views/apikeys/components/ApiKeysList.tsx +0 -194
  25. package/src/layouts/PaymentsLayout/views/apikeys/components/index.ts +0 -3
  26. package/src/layouts/PaymentsLayout/views/apikeys/index.tsx +0 -19
  27. package/src/layouts/PaymentsLayout/views/overview/components/MetricsCards.tsx +0 -103
  28. package/src/layouts/PaymentsLayout/views/tariffs/index.tsx +0 -29
@@ -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,129 +0,0 @@
1
- 'use client';
2
-
3
- import React, { createContext, useContext, useEffect, type ReactNode } from 'react';
4
- import { api, Hooks } from '@djangocfg/api';
5
- import type { API } from '@djangocfg/api';
6
- import type {
7
- Currency,
8
- PaginatedCurrencyListList,
9
- ProviderCurrency,
10
- PaginatedProviderCurrencyList,
11
- Network,
12
- PaginatedNetworkList,
13
- } from '@djangocfg/api';
14
-
15
- // ─────────────────────────────────────────────────────────────────────────
16
- // Context Type
17
- // ─────────────────────────────────────────────────────────────────────────
18
-
19
- export interface RootPaymentsContextValue {
20
- // Currencies
21
- currencies: PaginatedCurrencyListList | undefined;
22
- isLoadingCurrencies: boolean;
23
- currenciesError: Error | undefined;
24
- refreshCurrencies: () => Promise<void>;
25
-
26
- // Provider Currencies
27
- providerCurrencies: PaginatedProviderCurrencyList | undefined;
28
- isLoadingProviderCurrencies: boolean;
29
- providerCurrenciesError: Error | undefined;
30
- refreshProviderCurrencies: () => Promise<void>;
31
-
32
- // Networks
33
- networks: PaginatedNetworkList | undefined;
34
- isLoadingNetworks: boolean;
35
- networksError: Error | undefined;
36
- refreshNetworks: () => Promise<void>;
37
- }
38
-
39
- // ─────────────────────────────────────────────────────────────────────────
40
- // Context
41
- // ─────────────────────────────────────────────────────────────────────────
42
-
43
- const RootPaymentsContext = createContext<RootPaymentsContextValue | undefined>(undefined);
44
-
45
- // ─────────────────────────────────────────────────────────────────────────
46
- // Provider
47
- // ─────────────────────────────────────────────────────────────────────────
48
-
49
- export function RootPaymentsProvider({ children }: { children: ReactNode }) {
50
- // List all currencies
51
- const {
52
- data: currencies,
53
- error: currenciesError,
54
- isLoading: isLoadingCurrencies,
55
- mutate: mutateCurrencies,
56
- } = Hooks.usePaymentsCurrenciesList({}, api as unknown as API);
57
-
58
- // List all provider currencies
59
- const {
60
- data: providerCurrencies,
61
- error: providerCurrenciesError,
62
- isLoading: isLoadingProviderCurrencies,
63
- mutate: mutateProviderCurrencies,
64
- } = Hooks.usePaymentsProviderCurrenciesList({}, api as unknown as API);
65
-
66
- // List all networks
67
- const {
68
- data: networks,
69
- error: networksError,
70
- isLoading: isLoadingNetworks,
71
- mutate: mutateNetworks,
72
- } = Hooks.usePaymentsNetworksList({}, api as unknown as API);
73
-
74
- const refreshCurrencies = async () => {
75
- await mutateCurrencies();
76
- };
77
-
78
- const refreshProviderCurrencies = async () => {
79
- await mutateProviderCurrencies();
80
- };
81
-
82
- const refreshNetworks = async () => {
83
- await mutateNetworks();
84
- };
85
-
86
- const value: RootPaymentsContextValue = {
87
- currencies,
88
- isLoadingCurrencies,
89
- currenciesError,
90
- refreshCurrencies,
91
- providerCurrencies,
92
- isLoadingProviderCurrencies,
93
- providerCurrenciesError,
94
- refreshProviderCurrencies,
95
- networks,
96
- isLoadingNetworks,
97
- networksError,
98
- refreshNetworks,
99
- };
100
-
101
- return (
102
- <RootPaymentsContext.Provider value={value}>{children}</RootPaymentsContext.Provider>
103
- );
104
- }
105
-
106
- // ─────────────────────────────────────────────────────────────────────────
107
- // Hook
108
- // ─────────────────────────────────────────────────────────────────────────
109
-
110
- export function useRootPaymentsContext(): RootPaymentsContextValue {
111
- const context = useContext(RootPaymentsContext);
112
- if (!context) {
113
- throw new Error('useRootPaymentsContext must be used within RootPaymentsProvider');
114
- }
115
- return context;
116
- }
117
-
118
- // ─────────────────────────────────────────────────────────────────────────
119
- // Re-export types
120
- // ─────────────────────────────────────────────────────────────────────────
121
-
122
- export type {
123
- Currency,
124
- PaginatedCurrencyListList,
125
- ProviderCurrency,
126
- PaginatedProviderCurrencyList,
127
- Network,
128
- PaginatedNetworkList,
129
- };
@@ -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
-