EverydayTech Platform - Developer Reference
Complete Source Code Documentation - All Applications
Loading...
Searching...
No Matches
page.tsx
Go to the documentation of this file.
1"use client";
2
3import React, { useState, useEffect } from "react";
4import Link from "next/link";
5
6interface MessagingAccount {
7 f100: string; // Account ID
8 f109: string; // Account Name
9 f110: string; // Type (SMTP, POP3, IMAP, SMS, FTP, UPLOAD, AWSS3)
10 f101?: string; // Server
11 f102?: string; // Port
12 f103?: string; // User/Email
13 f104?: string; // Password
14 f111?: string; // Auth schemes
15 f112?: string; // From Name
16 f118?: string; // Reply To
17 f120?: number; // Status flags (1=hold)
18 f120s?: string; // Status string
19 f120s_bkcolor?: string; // Status background color
20 f128?: number; // Well known server ID
21 f129?: string; // Account headers
22 f130?: string; // Provider type (for SMS) or FTP folder
23 f131?: string; // File types (FTP)
24 f132?: string; // Output folder (FTP)
25 f133?: string; // API Key (SMS)
26 f134?: string; // API Secret (SMS)
27 f140?: string; // Bucket name (S3) or Country code (SMS)
28 f141?: string; // Access key (S3)
29 f142?: string; // Secret key (S3)
30 f143?: string; // Region (S3)
31 f160?: string; // Max per hour
32 f190?: number; // Transactional email flag
33 f191?: number; // Bulk email flag
34 f192?: number; // Confirm TX flag
35 f193?: number; // Save message flag
36 f280?: string; // Network password
37 f300?: number; // Used for transactions (UPLOAD)
38 f301?: number; // Used for images (UPLOAD)
39 f310?: string; // Allow list (SMS)
40 f311?: string; // Block list (SMS)
41 STAT?: any; // Status/log data
42}
43
44type AccountType = "SMTP" | "POP3" | "IMAP" | "SMS" | "FTP" | "UPLOAD" | "AWSS3";
45
46export default function ConfigureAccountsPage() {
47 const [accounts, setAccounts] = useState<MessagingAccount[]>([]);
48 const [loading, setLoading] = useState(true);
49 const [showNewAccountModal, setShowNewAccountModal] = useState(false);
50 const [showEditModal, setShowEditModal] = useState(false);
51 const [selectedAccount, setSelectedAccount] = useState<MessagingAccount | null>(null);
52 const [newAccountType, setNewAccountType] = useState<AccountType | null>(null);
53
54 useEffect(() => {
55 loadAccounts();
56 const interval = setInterval(loadAccounts, 30000); // Refresh every 30 seconds
57 return () => clearInterval(interval);
58 }, []);
59
60 const loadAccounts = async () => {
61 try {
62 const response = await fetch("/api/v1/messaging/config");
63 const data = await response.json();
64
65 if (data.success && data.data?.DATS) {
66 const accountsList = Array.isArray(data.data.DATS) ? data.data.DATS : [data.data.DATS];
67
68 // Process accounts
69 const processed = accountsList.map((acc: MessagingAccount) => {
70 const status = acc.f120 || 0;
71 if (status & 1) {
72 acc.f120s = "Hold";
73 acc.f120s_bkcolor = "#ff8080";
74 } else {
75 acc.f120s = "Active";
76 acc.f120s_bkcolor = "#00ff00";
77 }
78
79 // Mark known internally
80 if (acc.f128 && acc.f128 !== 9998 && acc.f128 !== 9999) {
81 acc.f101 = "Known Internally";
82 }
83
84 return acc;
85 });
86
87 setAccounts(processed);
88 }
89 } catch (error) {
90 console.error("Error loading accounts:", error);
91 } finally {
92 setLoading(false);
93 }
94 };
95
96 const handleCreateAccount = () => {
97 setShowNewAccountModal(true);
98 };
99
100 const handleEditAccount = (account: MessagingAccount) => {
101 setSelectedAccount(account);
102 setShowEditModal(true);
103 };
104
105 const handleSetStatus = async (account: MessagingAccount, hold: boolean) => {
106 try {
107 await fetch("/api/v1/messaging/config", {
108 method: "PUT",
109 headers: { "Content-Type": "application/json" },
110 body: JSON.stringify({
111 f100: account.f100,
112 f120: hold ? 1 : 0,
113 }),
114 });
115
116 setTimeout(loadAccounts, 1000);
117 } catch (error) {
118 console.error("Error updating status:", error);
119 }
120 };
121
122 const getAccountInfo = (account: MessagingAccount) => {
123 const info: string[] = [];
124 if (account.f110 === "SMTP") {
125 if (account.f190 && account.f190 > 0) info.push("Txn Email");
126 if (account.f191 && account.f191 > 0) info.push("Bulk Email");
127 if (account.f192 && account.f192 > 0) info.push("Confirm TX");
128 if (account.f193 && account.f193 > 0) info.push("Save Message");
129 }
130 return info.join(", ");
131 };
132
133 return (
134 <div className="min-h-screen bg-gray-50">
135 <div className="max-w-7xl mx-auto p-6">
136 {/* Header */}
137 <div className="mb-8">
138 <div className="flex items-center gap-3 mb-2">
139 <Link href="/marketing" className="text-gray-600 hover:text-gray-800">
140 Marketing
141 </Link>
142 <span className="text-gray-400">/</span>
143 <Link href="/marketing/messaging" className="text-gray-600 hover:text-gray-800">
144 Email & Interfaces
145 </Link>
146 <span className="text-gray-400">/</span>
147 <span className="text-gray-900 font-semibold">Configure Accounts</span>
148 </div>
149 <h1 className="text-3xl font-bold text-gray-900 mb-2">Messaging Configuration</h1>
150 <p className="text-gray-600 text-sm">
151 Manage email, SMS, and interface accounts for external communications
152 </p>
153 </div>
154
155 {/* New Account Button */}
156 <div className="mb-6">
157 <button
158 onClick={handleCreateAccount}
159 className="px-6 py-3 bg-[#00946b] text-white rounded-lg hover:bg-[#007555] transition shadow-md flex items-center gap-2"
160 >
161 <span className="text-xl">➕</span>
162 New Account
163 </button>
164 </div>
165
166 {/* Accounts Table */}
167 <div className="bg-white rounded-lg shadow-md overflow-hidden">
168 <div className="overflow-x-auto">
169 {loading ? (
170 <div className="p-8 text-center text-gray-600">Loading accounts...</div>
171 ) : accounts.length === 0 ? (
172 <div className="p-8 text-center text-gray-500">
173 No accounts configured. Click "New Account" to create one.
174 </div>
175 ) : (
176 <table className="w-full text-sm">
177 <thead className="bg-gray-50 border-b border-gray-200">
178 <tr>
179 <th className="px-4 py-3 text-left font-semibold text-gray-700">Account Name</th>
180 <th className="px-4 py-3 text-left font-semibold text-gray-700">Type</th>
181 <th className="px-4 py-3 text-left font-semibold text-gray-700">Server</th>
182 <th className="px-4 py-3 text-left font-semibold text-gray-700">Port</th>
183 <th className="px-4 py-3 text-left font-semibold text-gray-700">User</th>
184 <th className="px-4 py-3 text-left font-semibold text-gray-700">Info</th>
185 <th className="px-4 py-3 text-left font-semibold text-gray-700">Status</th>
186 <th className="px-4 py-3 text-left font-semibold text-gray-700">Actions</th>
187 </tr>
188 </thead>
189 <tbody>
190 {accounts.map((account, idx) => (
191 <tr key={idx} className="border-b border-gray-100 hover:bg-gray-50">
192 <td className="px-4 py-3 font-medium text-gray-900">{account.f109}</td>
193 <td className="px-4 py-3 text-gray-700">
194 <span className="px-2 py-1 bg-blue-100 text-blue-800 rounded text-xs font-medium">
195 {account.f110}
196 </span>
197 </td>
198 <td className="px-4 py-3 text-gray-700 font-mono text-xs">{account.f101 || "-"}</td>
199 <td className="px-4 py-3 text-gray-700">{account.f102 || "-"}</td>
200 <td className="px-4 py-3 text-gray-700 font-mono text-xs truncate max-w-xs">
201 {account.f103 || "-"}
202 </td>
203 <td className="px-4 py-3 text-gray-600 text-xs">{getAccountInfo(account)}</td>
204 <td className="px-4 py-3">
205 <span
206 className="px-3 py-1 rounded-full text-xs font-medium"
207 style={{ backgroundColor: account.f120s_bkcolor, color: "#000" }}
208 >
209 {account.f120s}
210 </span>
211 </td>
212 <td className="px-4 py-3">
213 <div className="flex gap-2">
214 <button
215 onClick={() => handleEditAccount(account)}
216 className="px-3 py-1 bg-blue-600 text-white rounded text-xs hover:bg-blue-700"
217 >
218 Edit
219 </button>
220 {account.f120s === "Hold" ? (
221 <button
222 onClick={() => handleSetStatus(account, false)}
223 className="px-3 py-1 bg-green-600 text-white rounded text-xs hover:bg-green-700"
224 >
225 Activate
226 </button>
227 ) : (
228 <button
229 onClick={() => handleSetStatus(account, true)}
230 className="px-3 py-1 bg-orange-600 text-white rounded text-xs hover:bg-orange-700"
231 >
232 Hold
233 </button>
234 )}
235 </div>
236 </td>
237 </tr>
238 ))}
239 </tbody>
240 </table>
241 )}
242 </div>
243 </div>
244
245 {/* New Account Modal */}
246 {showNewAccountModal && (
247 <NewAccountModal
248 onClose={() => setShowNewAccountModal(false)}
249 onSuccess={() => {
250 setShowNewAccountModal(false);
251 setTimeout(loadAccounts, 1000);
252 }}
253 />
254 )}
255
256 {/* Edit Account Modal */}
257 {showEditModal && selectedAccount && (
258 <EditAccountModal
259 account={selectedAccount}
260 onClose={() => {
261 setShowEditModal(false);
262 setSelectedAccount(null);
263 }}
264 onSuccess={() => {
265 setShowEditModal(false);
266 setSelectedAccount(null);
267 setTimeout(loadAccounts, 1000);
268 }}
269 />
270 )}
271 </div>
272 </div>
273 );
274}
275
276// New Account Modal Component
277function NewAccountModal({ onClose, onSuccess }: { onClose: () => void; onSuccess: () => void }) {
278 const [accountType, setAccountType] = useState<AccountType>("SMTP");
279 const [formData, setFormData] = useState<any>({});
280
281 const handleSubmit = async (e: React.FormEvent) => {
282 e.preventDefault();
283
284 try {
285 await fetch("/api/v1/messaging/config", {
286 method: "POST",
287 headers: { "Content-Type": "application/json" },
288 body: JSON.stringify({
289 ...formData,
290 f110: accountType,
291 }),
292 });
293
294 onSuccess();
295 } catch (error) {
296 console.error("Error creating account:", error);
297 alert("Failed to create account");
298 }
299 };
300
301 return (
302 <div className="modal-overlay">
303 <div className="modal-content bg-white rounded-lg shadow-xl max-w-2xl w-full max-h-[90vh] overflow-y-auto">
304 <div className="sticky top-0 bg-[#5094fb] text-white p-4 rounded-t-lg flex justify-between items-center">
305 <h2 className="text-xl font-bold">Add New Messaging Account</h2>
306 <button onClick={onClose} className="text-white hover:text-gray-200 text-2xl leading-none">
307 ×
308 </button>
309 </div>
310
311 <div className="p-6">
312 {/* Account Type Tabs */}
313 <div className="flex gap-2 mb-6 flex-wrap">
314 {(["SMTP", "IMAP", "POP3", "SMS", "FTP", "UPLOAD", "AWSS3"] as AccountType[]).map((type) => (
315 <button
316 key={type}
317 onClick={() => setAccountType(type)}
318 className={`px-4 py-2 rounded ${
319 accountType === type
320 ? "bg-[#5094fb] text-white"
321 : "bg-gray-200 text-gray-700 hover:bg-gray-300"
322 }`}
323 >
324 {type}
325 </button>
326 ))}
327 </div>
328
329 <form onSubmit={handleSubmit}>
330 <AccountForm
331 accountType={accountType}
332 formData={formData}
333 onChange={(data) => setFormData(data)}
334 isNew={true}
335 />
336
337 <div className="mt-6 flex gap-3">
338 <button
339 type="submit"
340 className="px-6 py-2 bg-[#00946b] text-white rounded hover:bg-[#007555]"
341 >
342 Create {accountType}
343 </button>
344 <button
345 type="button"
346 onClick={onClose}
347 className="px-6 py-2 bg-gray-300 text-gray-700 rounded hover:bg-gray-400"
348 >
349 Cancel
350 </button>
351 </div>
352 </form>
353 </div>
354 </div>
355 </div>
356 );
357}
358
359// Edit Account Modal Component
360function EditAccountModal({
361 account,
362 onClose,
363 onSuccess,
364}: {
365 account: MessagingAccount;
366 onClose: () => void;
367 onSuccess: () => void;
368}) {
369 const [formData, setFormData] = useState<any>(account);
370
371 const handleSubmit = async (e: React.FormEvent) => {
372 e.preventDefault();
373
374 try {
375 await fetch("/api/v1/messaging/config", {
376 method: "PUT",
377 headers: { "Content-Type": "application/json" },
378 body: JSON.stringify(formData),
379 });
380
381 onSuccess();
382 } catch (error) {
383 console.error("Error updating account:", error);
384 alert("Failed to update account");
385 }
386 };
387
388 return (
389 <div className="modal-overlay">
390 <div className="modal-content bg-white rounded-lg shadow-xl max-w-3xl w-full max-h-[90vh] overflow-y-auto">
391 <div className="sticky top-0 bg-[#00946b] text-white p-4 rounded-t-lg flex justify-between items-center">
392 <h2 className="text-xl font-bold">{account.f109}</h2>
393 <button onClick={onClose} className="text-white hover:text-gray-200 text-2xl leading-none">
394 ×
395 </button>
396 </div>
397
398 <div className="p-6">
399 <form onSubmit={handleSubmit}>
400 <AccountForm
401 accountType={account.f110 as AccountType}
402 formData={formData}
403 onChange={(data) => setFormData(data)}
404 isNew={false}
405 />
406
407 <div className="mt-6 flex gap-3">
408 <button
409 type="submit"
410 className="px-6 py-2 bg-[#00946b] text-white rounded hover:bg-[#007555]"
411 >
412 Save Changes
413 </button>
414 <button
415 type="button"
416 onClick={onClose}
417 className="px-6 py-2 bg-gray-300 text-gray-700 rounded hover:bg-gray-400"
418 >
419 Cancel
420 </button>
421 </div>
422 </form>
423 </div>
424 </div>
425 </div>
426 );
427}
428
429// Reusable Account Form Component
430function AccountForm({
431 accountType,
432 formData,
433 onChange,
434 isNew,
435}: {
436 accountType: AccountType;
437 formData: any;
438 onChange: (data: any) => void;
439 isNew: boolean;
440}) {
441 const updateField = (field: string, value: any) => {
442 onChange({ ...formData, [field]: value });
443 };
444
445 return (
446 <div className="space-y-4">
447 <div>
448 <label className="block text-sm font-medium text-gray-700 mb-1">Account Name</label>
449 <input
450 type="text"
451 value={formData.f109 || ""}
452 onChange={(e) => updateField("f109", e.target.value)}
453 className="w-full px-3 py-2 border border-gray-300 rounded focus:ring-2 focus:ring-[#00946b] focus:border-transparent"
454 required
455 />
456 </div>
457
458 {(accountType === "SMTP" || accountType === "POP3" || accountType === "IMAP") && (
459 <>
460 {(accountType === "SMTP" || accountType === "POP3") && (
461 <div>
462 <label className="block text-sm font-medium text-gray-700 mb-1">Well Known Server</label>
463 <select
464 value={formData.f128 || ""}
465 onChange={(e) => updateField("f128", e.target.value)}
466 className="w-full px-3 py-2 border border-gray-300 rounded focus:ring-2 focus:ring-[#00946b]"
467 >
468 <option value="">Custom</option>
469 <option value="1">Microsoft Office 365</option>
470 <option value="2">Outlook.com</option>
471 <option value="9998">Microsoft Exchange Online</option>
472 <option value="12003">iServe.net.nz</option>
473 <option value="9999">Corporate Connector</option>
474 </select>
475 </div>
476 )}
477
478 <div className="grid grid-cols-2 gap-4">
479 <div>
480 <label className="block text-sm font-medium text-gray-700 mb-1">Server</label>
481 <input
482 type="text"
483 value={formData.f101 || ""}
484 onChange={(e) => updateField("f101", e.target.value)}
485 className="w-full px-3 py-2 border border-gray-300 rounded focus:ring-2 focus:ring-[#00946b]"
486 />
487 </div>
488 <div>
489 <label className="block text-sm font-medium text-gray-700 mb-1">Port</label>
490 <input
491 type="text"
492 value={formData.f102 || ""}
493 onChange={(e) => updateField("f102", e.target.value)}
494 className="w-full px-3 py-2 border border-gray-300 rounded focus:ring-2 focus:ring-[#00946b]"
495 />
496 </div>
497 </div>
498
499 <div>
500 <label className="block text-sm font-medium text-gray-700 mb-1">User/Email</label>
501 <input
502 type="text"
503 value={formData.f103 || ""}
504 onChange={(e) => updateField("f103", e.target.value)}
505 className="w-full px-3 py-2 border border-gray-300 rounded focus:ring-2 focus:ring-[#00946b]"
506 />
507 </div>
508
509 <div>
510 <label className="block text-sm font-medium text-gray-700 mb-1">Password</label>
511 <input
512 type="password"
513 value={formData.f104 || ""}
514 onChange={(e) => updateField("f104", e.target.value)}
515 className="w-full px-3 py-2 border border-gray-300 rounded focus:ring-2 focus:ring-[#00946b]"
516 />
517 </div>
518
519 {accountType === "SMTP" && (
520 <>
521 <div>
522 <label className="block text-sm font-medium text-gray-700 mb-1">From Name</label>
523 <input
524 type="text"
525 value={formData.f112 || ""}
526 onChange={(e) => updateField("f112", e.target.value)}
527 placeholder="My Store <email@example.com>"
528 className="w-full px-3 py-2 border border-gray-300 rounded focus:ring-2 focus:ring-[#00946b]"
529 />
530 </div>
531
532 <div>
533 <label className="block text-sm font-medium text-gray-700 mb-1">Reply To</label>
534 <input
535 type="text"
536 value={formData.f118 || ""}
537 onChange={(e) => updateField("f118", e.target.value)}
538 className="w-full px-3 py-2 border border-gray-300 rounded focus:ring-2 focus:ring-[#00946b]"
539 />
540 </div>
541
542 <div className="space-y-2">
543 <label className="flex items-center gap-2">
544 <input
545 type="checkbox"
546 checked={formData.f190 !== undefined ? formData.f190 > 0 : true}
547 onChange={(e) => updateField("f190", e.target.checked ? 1 : 0)}
548 className="rounded"
549 />
550 <span className="text-sm text-gray-700">Transactional emails (Receipts, POs, etc.)</span>
551 </label>
552 <label className="flex items-center gap-2">
553 <input
554 type="checkbox"
555 checked={formData.f191 > 0}
556 onChange={(e) => updateField("f191", e.target.checked ? 1 : 0)}
557 className="rounded"
558 />
559 <span className="text-sm text-gray-700">Bulk marketing emails</span>
560 </label>
561 <label className="flex items-center gap-2">
562 <input
563 type="checkbox"
564 checked={formData.f192 > 0}
565 onChange={(e) => updateField("f192", e.target.checked ? 1 : 0)}
566 className="rounded"
567 />
568 <span className="text-sm text-gray-700">Send BCC to Fieldpine for confirmation</span>
569 </label>
570 <label className="flex items-center gap-2">
571 <input
572 type="checkbox"
573 checked={formData.f193 > 0}
574 onChange={(e) => updateField("f193", e.target.checked ? 1 : 0)}
575 className="rounded"
576 />
577 <span className="text-sm text-gray-700">Save sent email body</span>
578 </label>
579 </div>
580 </>
581 )}
582 </>
583 )}
584
585 {accountType === "SMS" && (
586 <>
587 <div>
588 <label className="block text-sm font-medium text-gray-700 mb-1">SMS Provider</label>
589 <select
590 value={formData.f130 || ""}
591 onChange={(e) => updateField("f130", e.target.value)}
592 className="w-full px-3 py-2 border border-gray-300 rounded focus:ring-2 focus:ring-[#00946b]"
593 >
594 <option value="">--Select One--</option>
595 <option value="2">Fieldpine Gateway</option>
596 <option value="1">Email Gateway</option>
597 <option value="3">Output to File</option>
598 <option value="4">Burst SMS</option>
599 <option value="6401">NZ. Spark eTxt</option>
600 </select>
601 </div>
602
603 {formData.f130 === "4" && (
604 <>
605 <div>
606 <label className="block text-sm font-medium text-gray-700 mb-1">API Key</label>
607 <input
608 type="text"
609 value={formData.f133 || ""}
610 onChange={(e) => updateField("f133", e.target.value)}
611 className="w-full px-3 py-2 border border-gray-300 rounded focus:ring-2 focus:ring-[#00946b]"
612 />
613 </div>
614 <div>
615 <label className="block text-sm font-medium text-gray-700 mb-1">API Secret</label>
616 <input
617 type="password"
618 value={formData.f134 || ""}
619 onChange={(e) => updateField("f134", e.target.value)}
620 className="w-full px-3 py-2 border border-gray-300 rounded focus:ring-2 focus:ring-[#00946b]"
621 />
622 </div>
623 </>
624 )}
625 </>
626 )}
627
628 {accountType === "FTP" && (
629 <>
630 <div className="grid grid-cols-2 gap-4">
631 <div>
632 <label className="block text-sm font-medium text-gray-700 mb-1">Server</label>
633 <input
634 type="text"
635 value={formData.f101 || ""}
636 onChange={(e) => updateField("f101", e.target.value)}
637 className="w-full px-3 py-2 border border-gray-300 rounded focus:ring-2 focus:ring-[#00946b]"
638 />
639 </div>
640 <div>
641 <label className="block text-sm font-medium text-gray-700 mb-1">Port</label>
642 <input
643 type="text"
644 value={formData.f102 || ""}
645 onChange={(e) => updateField("f102", e.target.value)}
646 className="w-full px-3 py-2 border border-gray-300 rounded focus:ring-2 focus:ring-[#00946b]"
647 />
648 </div>
649 </div>
650 <div className="grid grid-cols-2 gap-4">
651 <div>
652 <label className="block text-sm font-medium text-gray-700 mb-1">Username</label>
653 <input
654 type="text"
655 value={formData.f103 || ""}
656 onChange={(e) => updateField("f103", e.target.value)}
657 className="w-full px-3 py-2 border border-gray-300 rounded focus:ring-2 focus:ring-[#00946b]"
658 />
659 </div>
660 <div>
661 <label className="block text-sm font-medium text-gray-700 mb-1">Password</label>
662 <input
663 type="password"
664 value={formData.f104 || ""}
665 onChange={(e) => updateField("f104", e.target.value)}
666 className="w-full px-3 py-2 border border-gray-300 rounded focus:ring-2 focus:ring-[#00946b]"
667 />
668 </div>
669 </div>
670 </>
671 )}
672
673 {accountType === "UPLOAD" && (
674 <>
675 <div>
676 <label className="block text-sm font-medium text-gray-700 mb-1">Local Directory</label>
677 <input
678 type="text"
679 value={formData.f101 || ""}
680 onChange={(e) => updateField("f101", e.target.value)}
681 className="w-full px-3 py-2 border border-gray-300 rounded focus:ring-2 focus:ring-[#00946b]"
682 />
683 </div>
684 <div className="space-y-2">
685 <label className="flex items-center gap-2">
686 <input
687 type="checkbox"
688 checked={formData.f300 > 0}
689 onChange={(e) => updateField("f300", e.target.checked ? 1 : 0)}
690 className="rounded"
691 />
692 <span className="text-sm text-gray-700">Used for transactions</span>
693 </label>
694 <label className="flex items-center gap-2">
695 <input
696 type="checkbox"
697 checked={formData.f301 > 0}
698 onChange={(e) => updateField("f301", e.target.checked ? 1 : 0)}
699 className="rounded"
700 />
701 <span className="text-sm text-gray-700">Used for images</span>
702 </label>
703 </div>
704 </>
705 )}
706
707 {accountType === "AWSS3" && (
708 <>
709 <div>
710 <label className="block text-sm font-medium text-gray-700 mb-1">Bucket Name</label>
711 <input
712 type="text"
713 value={formData.f140 || ""}
714 onChange={(e) => updateField("f140", e.target.value)}
715 className="w-full px-3 py-2 border border-gray-300 rounded focus:ring-2 focus:ring-[#00946b]"
716 />
717 </div>
718 <div>
719 <label className="block text-sm font-medium text-gray-700 mb-1">Access Key</label>
720 <input
721 type="text"
722 value={formData.f141 || ""}
723 onChange={(e) => updateField("f141", e.target.value)}
724 className="w-full px-3 py-2 border border-gray-300 rounded focus:ring-2 focus:ring-[#00946b]"
725 />
726 </div>
727 <div>
728 <label className="block text-sm font-medium text-gray-700 mb-1">Secret Key</label>
729 <input
730 type="password"
731 value={formData.f142 || ""}
732 onChange={(e) => updateField("f142", e.target.value)}
733 className="w-full px-3 py-2 border border-gray-300 rounded focus:ring-2 focus:ring-[#00946b]"
734 />
735 </div>
736 <div>
737 <label className="block text-sm font-medium text-gray-700 mb-1">Region</label>
738 <input
739 type="text"
740 value={formData.f143 || ""}
741 onChange={(e) => updateField("f143", e.target.value)}
742 className="w-full px-3 py-2 border border-gray-300 rounded focus:ring-2 focus:ring-[#00946b]"
743 />
744 </div>
745 </>
746 )}
747 </div>
748 );
749}