@djangocfg/layouts 1.0.1
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.
- package/LICENSE +21 -0
- package/README.md +77 -0
- package/package.json +86 -0
- package/src/auth/README.md +962 -0
- package/src/auth/context/AuthContext.tsx +458 -0
- package/src/auth/context/index.ts +2 -0
- package/src/auth/context/types.ts +63 -0
- package/src/auth/hooks/index.ts +6 -0
- package/src/auth/hooks/useAuthForm.ts +329 -0
- package/src/auth/hooks/useAuthGuard.ts +23 -0
- package/src/auth/hooks/useAuthRedirect.ts +51 -0
- package/src/auth/hooks/useAutoAuth.ts +42 -0
- package/src/auth/hooks/useLocalStorage.ts +211 -0
- package/src/auth/hooks/useSessionStorage.ts +186 -0
- package/src/auth/index.ts +10 -0
- package/src/auth/middlewares/index.ts +1 -0
- package/src/auth/middlewares/proxy.ts +24 -0
- package/src/auth/server.ts +6 -0
- package/src/auth/utils/errors.ts +34 -0
- package/src/auth/utils/index.ts +2 -0
- package/src/auth/utils/validation.ts +14 -0
- package/src/index.ts +15 -0
- package/src/layouts/AppLayout/AppLayout.tsx +123 -0
- package/src/layouts/AppLayout/README.md +204 -0
- package/src/layouts/AppLayout/SUMMARY.md +240 -0
- package/src/layouts/AppLayout/USAGE.md +312 -0
- package/src/layouts/AppLayout/components/PageProgress.tsx +104 -0
- package/src/layouts/AppLayout/components/Seo.tsx +87 -0
- package/src/layouts/AppLayout/components/index.ts +6 -0
- package/src/layouts/AppLayout/context/AppContext.tsx +146 -0
- package/src/layouts/AppLayout/context/index.ts +5 -0
- package/src/layouts/AppLayout/hooks/index.ts +6 -0
- package/src/layouts/AppLayout/hooks/useLayoutMode.ts +26 -0
- package/src/layouts/AppLayout/hooks/useNavigation.ts +49 -0
- package/src/layouts/AppLayout/index.ts +31 -0
- package/src/layouts/AppLayout/layouts/AuthLayout/AuthContext.tsx +51 -0
- package/src/layouts/AppLayout/layouts/AuthLayout/AuthHelp.tsx +111 -0
- package/src/layouts/AppLayout/layouts/AuthLayout/AuthLayout.tsx +40 -0
- package/src/layouts/AppLayout/layouts/AuthLayout/IdentifierForm.tsx +330 -0
- package/src/layouts/AppLayout/layouts/AuthLayout/OTPForm.tsx +158 -0
- package/src/layouts/AppLayout/layouts/AuthLayout/index.ts +13 -0
- package/src/layouts/AppLayout/layouts/AuthLayout/types.ts +61 -0
- package/src/layouts/AppLayout/layouts/PrivateLayout/PrivateLayout.tsx +92 -0
- package/src/layouts/AppLayout/layouts/PrivateLayout/components/DashboardContent.tsx +60 -0
- package/src/layouts/AppLayout/layouts/PrivateLayout/components/DashboardHeader.tsx +170 -0
- package/src/layouts/AppLayout/layouts/PrivateLayout/components/DashboardSidebar.tsx +164 -0
- package/src/layouts/AppLayout/layouts/PrivateLayout/components/index.ts +7 -0
- package/src/layouts/AppLayout/layouts/PrivateLayout/index.ts +5 -0
- package/src/layouts/AppLayout/layouts/PublicLayout/PublicLayout.tsx +44 -0
- package/src/layouts/AppLayout/layouts/PublicLayout/components/DesktopUserMenu.tsx +136 -0
- package/src/layouts/AppLayout/layouts/PublicLayout/components/Footer.tsx +262 -0
- package/src/layouts/AppLayout/layouts/PublicLayout/components/MobileMenu.tsx +289 -0
- package/src/layouts/AppLayout/layouts/PublicLayout/components/Navigation.tsx +159 -0
- package/src/layouts/AppLayout/layouts/PublicLayout/index.ts +5 -0
- package/src/layouts/AppLayout/layouts/index.ts +7 -0
- package/src/layouts/AppLayout/providers/CoreProviders.tsx +47 -0
- package/src/layouts/AppLayout/providers/index.ts +5 -0
- package/src/layouts/AppLayout/types/config.ts +40 -0
- package/src/layouts/AppLayout/types/index.ts +10 -0
- package/src/layouts/AppLayout/types/layout.ts +47 -0
- package/src/layouts/AppLayout/types/navigation.ts +41 -0
- package/src/layouts/AppLayout/types/routes.ts +45 -0
- package/src/layouts/AppLayout/utils/index.ts +5 -0
- package/src/layouts/AppLayout/utils/routeDetection.ts +31 -0
- package/src/layouts/PaymentsLayout/PaymentsLayout.tsx +125 -0
- package/src/layouts/PaymentsLayout/README.md +133 -0
- package/src/layouts/PaymentsLayout/components/CreateApiKeyDialog.tsx +172 -0
- package/src/layouts/PaymentsLayout/components/CreatePaymentDialog.tsx +203 -0
- package/src/layouts/PaymentsLayout/components/DeleteApiKeyDialog.tsx +100 -0
- package/src/layouts/PaymentsLayout/components/index.ts +4 -0
- package/src/layouts/PaymentsLayout/events.ts +106 -0
- package/src/layouts/PaymentsLayout/index.ts +20 -0
- package/src/layouts/PaymentsLayout/types.ts +19 -0
- package/src/layouts/PaymentsLayout/views/apikeys/components/ApiKeyMetrics.tsx +109 -0
- package/src/layouts/PaymentsLayout/views/apikeys/components/ApiKeysList.tsx +194 -0
- package/src/layouts/PaymentsLayout/views/apikeys/components/index.ts +3 -0
- package/src/layouts/PaymentsLayout/views/apikeys/index.tsx +19 -0
- package/src/layouts/PaymentsLayout/views/overview/components/BalanceCard.tsx +99 -0
- package/src/layouts/PaymentsLayout/views/overview/components/MetricsCards.tsx +103 -0
- package/src/layouts/PaymentsLayout/views/overview/components/RecentPayments.tsx +138 -0
- package/src/layouts/PaymentsLayout/views/overview/components/index.ts +4 -0
- package/src/layouts/PaymentsLayout/views/overview/index.tsx +23 -0
- package/src/layouts/PaymentsLayout/views/payments/components/PaymentsList.tsx +282 -0
- package/src/layouts/PaymentsLayout/views/payments/components/index.ts +2 -0
- package/src/layouts/PaymentsLayout/views/payments/index.tsx +18 -0
- package/src/layouts/PaymentsLayout/views/tariffs/index.tsx +29 -0
- package/src/layouts/PaymentsLayout/views/transactions/index.tsx +29 -0
- package/src/layouts/ProfileLayout/ProfileLayout.tsx +110 -0
- package/src/layouts/ProfileLayout/components/AvatarSection.tsx +146 -0
- package/src/layouts/ProfileLayout/components/ProfileForm.tsx +208 -0
- package/src/layouts/ProfileLayout/components/index.ts +3 -0
- package/src/layouts/ProfileLayout/index.ts +3 -0
- package/src/layouts/SupportLayout/README.md +91 -0
- package/src/layouts/SupportLayout/SupportLayout.tsx +178 -0
- package/src/layouts/SupportLayout/components/CreateTicketDialog.tsx +154 -0
- package/src/layouts/SupportLayout/components/MessageInput.tsx +92 -0
- package/src/layouts/SupportLayout/components/MessageList.tsx +312 -0
- package/src/layouts/SupportLayout/components/TicketCard.tsx +96 -0
- package/src/layouts/SupportLayout/components/TicketList.tsx +152 -0
- package/src/layouts/SupportLayout/components/index.ts +6 -0
- package/src/layouts/SupportLayout/context/SupportLayoutContext.tsx +260 -0
- package/src/layouts/SupportLayout/context/index.ts +2 -0
- package/src/layouts/SupportLayout/events.ts +31 -0
- package/src/layouts/SupportLayout/hooks/index.ts +2 -0
- package/src/layouts/SupportLayout/hooks/useInfiniteMessages.ts +118 -0
- package/src/layouts/SupportLayout/hooks/useInfiniteTickets.ts +91 -0
- package/src/layouts/SupportLayout/index.ts +6 -0
- package/src/layouts/SupportLayout/types.ts +23 -0
- package/src/layouts/index.ts +9 -0
- package/src/snippets/AuthDialog/AuthDialog.tsx +88 -0
- package/src/snippets/AuthDialog/events.ts +21 -0
- package/src/snippets/AuthDialog/index.ts +3 -0
- package/src/snippets/AuthDialog/useAuthDialog.ts +27 -0
- package/src/snippets/Breadcrumbs.tsx +80 -0
- package/src/snippets/Chat/ChatUIContext.tsx +110 -0
- package/src/snippets/Chat/ChatWidget.tsx +476 -0
- package/src/snippets/Chat/README.md +122 -0
- package/src/snippets/Chat/components/MessageInput.tsx +124 -0
- package/src/snippets/Chat/components/MessageList.tsx +168 -0
- package/src/snippets/Chat/components/SessionList.tsx +192 -0
- package/src/snippets/Chat/components/index.ts +9 -0
- package/src/snippets/Chat/hooks/index.ts +6 -0
- package/src/snippets/Chat/hooks/useInfiniteSessions.ts +83 -0
- package/src/snippets/Chat/index.tsx +44 -0
- package/src/snippets/Chat/types.ts +79 -0
- package/src/snippets/VideoPlayer/README.md +203 -0
- package/src/snippets/VideoPlayer/VideoControls.tsx +133 -0
- package/src/snippets/VideoPlayer/VideoPlayer.tsx +114 -0
- package/src/snippets/VideoPlayer/index.ts +8 -0
- package/src/snippets/VideoPlayer/types.ts +61 -0
- package/src/snippets/index.ts +10 -0
- package/src/styles/dashboard.css +41 -0
- package/src/styles/index.css +20 -0
- package/src/styles/sources.css +6 -0
- package/src/types/index.ts +1 -0
- package/src/types/pageConfig.ts +103 -0
- package/src/utils/index.ts +6 -0
- package/src/utils/logger.ts +57 -0
|
@@ -0,0 +1,133 @@
|
|
|
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
|
+
|
|
@@ -0,0 +1,172 @@
|
|
|
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
|
+
|
|
@@ -0,0 +1,203 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Create Payment Dialog
|
|
3
|
+
* Dialog for creating new payments
|
|
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
|
+
Form,
|
|
17
|
+
FormControl,
|
|
18
|
+
FormDescription,
|
|
19
|
+
FormField,
|
|
20
|
+
FormItem,
|
|
21
|
+
FormLabel,
|
|
22
|
+
FormMessage,
|
|
23
|
+
Input,
|
|
24
|
+
Select,
|
|
25
|
+
SelectContent,
|
|
26
|
+
SelectItem,
|
|
27
|
+
SelectTrigger,
|
|
28
|
+
SelectValue,
|
|
29
|
+
Button,
|
|
30
|
+
useEventListener,
|
|
31
|
+
} from '@djangocfg/ui';
|
|
32
|
+
import { Plus, RefreshCw } from 'lucide-react';
|
|
33
|
+
import { useForm } from 'react-hook-form';
|
|
34
|
+
import { zodResolver } from '@hookform/resolvers/zod';
|
|
35
|
+
import { usePaymentsContext } from '@djangocfg/api/cfg/contexts';
|
|
36
|
+
import { Schemas, Enums } from '@djangocfg/api/cfg/generated';
|
|
37
|
+
import { paymentsLogger } from '../../../utils/logger';
|
|
38
|
+
import { PAYMENTS_DIALOG_EVENTS, closePaymentsDialog } from '../events';
|
|
39
|
+
|
|
40
|
+
const { PaymentCreateRequestSchema } = Schemas;
|
|
41
|
+
type PaymentCreateRequest = Schemas.PaymentCreateRequest;
|
|
42
|
+
const { PaymentCreateRequestCurrencyCode, PaymentCreateRequestProvider } = Enums;
|
|
43
|
+
|
|
44
|
+
export const CreatePaymentDialog: React.FC = () => {
|
|
45
|
+
const [open, setOpen] = useState(false);
|
|
46
|
+
const [isSubmitting, setIsSubmitting] = useState(false);
|
|
47
|
+
const { createPayment } = usePaymentsContext();
|
|
48
|
+
|
|
49
|
+
const form = useForm<PaymentCreateRequest>({
|
|
50
|
+
resolver: zodResolver(PaymentCreateRequestSchema),
|
|
51
|
+
defaultValues: {
|
|
52
|
+
amount_usd: 10,
|
|
53
|
+
currency_code: PaymentCreateRequestCurrencyCode.USDT,
|
|
54
|
+
provider: PaymentCreateRequestProvider.NOWPAYMENTS,
|
|
55
|
+
},
|
|
56
|
+
});
|
|
57
|
+
|
|
58
|
+
useEventListener(PAYMENTS_DIALOG_EVENTS.OPEN_CREATE_PAYMENT_DIALOG, () => {
|
|
59
|
+
setOpen(true);
|
|
60
|
+
});
|
|
61
|
+
|
|
62
|
+
useEventListener(PAYMENTS_DIALOG_EVENTS.CLOSE_PAYMENTS_DIALOG, () => {
|
|
63
|
+
setOpen(false);
|
|
64
|
+
});
|
|
65
|
+
|
|
66
|
+
const handleClose = () => {
|
|
67
|
+
setOpen(false);
|
|
68
|
+
form.reset();
|
|
69
|
+
};
|
|
70
|
+
|
|
71
|
+
const handleSubmit = async (data: PaymentCreateRequest) => {
|
|
72
|
+
try {
|
|
73
|
+
setIsSubmitting(true);
|
|
74
|
+
await createPayment(data);
|
|
75
|
+
handleClose();
|
|
76
|
+
closePaymentsDialog();
|
|
77
|
+
} catch (error) {
|
|
78
|
+
paymentsLogger.error('Failed to create payment:', error);
|
|
79
|
+
} finally {
|
|
80
|
+
setIsSubmitting(false);
|
|
81
|
+
}
|
|
82
|
+
};
|
|
83
|
+
|
|
84
|
+
const currencyOptions = Object.entries(PaymentCreateRequestCurrencyCode).map(([key, value]) => ({
|
|
85
|
+
value,
|
|
86
|
+
label: key,
|
|
87
|
+
}));
|
|
88
|
+
|
|
89
|
+
return (
|
|
90
|
+
<Dialog open={open} onOpenChange={(isOpen) => !isOpen && handleClose()}>
|
|
91
|
+
<DialogContent className="sm:max-w-md">
|
|
92
|
+
<DialogHeader>
|
|
93
|
+
<DialogTitle>Create Payment</DialogTitle>
|
|
94
|
+
<DialogDescription>
|
|
95
|
+
Create a new payment to add funds to your account.
|
|
96
|
+
</DialogDescription>
|
|
97
|
+
</DialogHeader>
|
|
98
|
+
|
|
99
|
+
<Form {...form}>
|
|
100
|
+
<form onSubmit={form.handleSubmit(handleSubmit)} className="space-y-4">
|
|
101
|
+
<FormField
|
|
102
|
+
control={form.control}
|
|
103
|
+
name="amount_usd"
|
|
104
|
+
render={({ field }) => (
|
|
105
|
+
<FormItem>
|
|
106
|
+
<FormLabel>Amount (USD)</FormLabel>
|
|
107
|
+
<FormControl>
|
|
108
|
+
<Input
|
|
109
|
+
type="number"
|
|
110
|
+
step="0.01"
|
|
111
|
+
min="0.01"
|
|
112
|
+
placeholder="10.00"
|
|
113
|
+
{...field}
|
|
114
|
+
onChange={(e) => field.onChange(parseFloat(e.target.value) || 0)}
|
|
115
|
+
/>
|
|
116
|
+
</FormControl>
|
|
117
|
+
<FormDescription>
|
|
118
|
+
The amount you want to pay in USD.
|
|
119
|
+
</FormDescription>
|
|
120
|
+
<FormMessage />
|
|
121
|
+
</FormItem>
|
|
122
|
+
)}
|
|
123
|
+
/>
|
|
124
|
+
|
|
125
|
+
<FormField
|
|
126
|
+
control={form.control}
|
|
127
|
+
name="currency_code"
|
|
128
|
+
render={({ field }) => (
|
|
129
|
+
<FormItem>
|
|
130
|
+
<FormLabel>Currency</FormLabel>
|
|
131
|
+
<Select value={field.value} onValueChange={field.onChange}>
|
|
132
|
+
<FormControl>
|
|
133
|
+
<SelectTrigger>
|
|
134
|
+
<SelectValue placeholder="Select currency" />
|
|
135
|
+
</SelectTrigger>
|
|
136
|
+
</FormControl>
|
|
137
|
+
<SelectContent>
|
|
138
|
+
{currencyOptions.map((option) => (
|
|
139
|
+
<SelectItem key={option.value} value={option.value}>
|
|
140
|
+
{option.label}
|
|
141
|
+
</SelectItem>
|
|
142
|
+
))}
|
|
143
|
+
</SelectContent>
|
|
144
|
+
</Select>
|
|
145
|
+
<FormDescription>
|
|
146
|
+
The cryptocurrency to use for payment.
|
|
147
|
+
</FormDescription>
|
|
148
|
+
<FormMessage />
|
|
149
|
+
</FormItem>
|
|
150
|
+
)}
|
|
151
|
+
/>
|
|
152
|
+
|
|
153
|
+
<FormField
|
|
154
|
+
control={form.control}
|
|
155
|
+
name="provider"
|
|
156
|
+
render={({ field }) => (
|
|
157
|
+
<FormItem>
|
|
158
|
+
<FormLabel>Payment Provider</FormLabel>
|
|
159
|
+
<Select value={field.value} onValueChange={field.onChange}>
|
|
160
|
+
<FormControl>
|
|
161
|
+
<SelectTrigger>
|
|
162
|
+
<SelectValue placeholder="Select provider" />
|
|
163
|
+
</SelectTrigger>
|
|
164
|
+
</FormControl>
|
|
165
|
+
<SelectContent>
|
|
166
|
+
<SelectItem value={PaymentCreateRequestProvider.NOWPAYMENTS}>
|
|
167
|
+
NOWPayments
|
|
168
|
+
</SelectItem>
|
|
169
|
+
</SelectContent>
|
|
170
|
+
</Select>
|
|
171
|
+
<FormDescription>
|
|
172
|
+
The payment gateway to process your payment.
|
|
173
|
+
</FormDescription>
|
|
174
|
+
<FormMessage />
|
|
175
|
+
</FormItem>
|
|
176
|
+
)}
|
|
177
|
+
/>
|
|
178
|
+
|
|
179
|
+
<DialogFooter>
|
|
180
|
+
<Button type="button" variant="outline" onClick={handleClose} disabled={isSubmitting}>
|
|
181
|
+
Cancel
|
|
182
|
+
</Button>
|
|
183
|
+
<Button type="submit" disabled={isSubmitting || !form.formState.isValid}>
|
|
184
|
+
{isSubmitting ? (
|
|
185
|
+
<>
|
|
186
|
+
<RefreshCw className="h-4 w-4 mr-2 animate-spin" />
|
|
187
|
+
Creating...
|
|
188
|
+
</>
|
|
189
|
+
) : (
|
|
190
|
+
<>
|
|
191
|
+
<Plus className="h-4 w-4 mr-2" />
|
|
192
|
+
Create Payment
|
|
193
|
+
</>
|
|
194
|
+
)}
|
|
195
|
+
</Button>
|
|
196
|
+
</DialogFooter>
|
|
197
|
+
</form>
|
|
198
|
+
</Form>
|
|
199
|
+
</DialogContent>
|
|
200
|
+
</Dialog>
|
|
201
|
+
);
|
|
202
|
+
};
|
|
203
|
+
|
|
@@ -0,0 +1,100 @@
|
|
|
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
|
+
|