diff --git a/src/app/dashboard/admin/root-ca/RootCaManagementClient.tsx b/src/app/dashboard/admin/root-ca/RootCaManagementClient.tsx index e3a08ba..64fa618 100644 --- a/src/app/dashboard/admin/root-ca/RootCaManagementClient.tsx +++ b/src/app/dashboard/admin/root-ca/RootCaManagementClient.tsx @@ -53,7 +53,7 @@ export default function RootCaManagementClient() { const { addToast } = useToast(); const { data, error, mutate, isLoading } = useSWR("/api/admin/ca-certificates", fetcher); const [isRenewing, setIsRenewing] = useState(false); - const [isSyncing, setIsSyncing] = useState(false); + const [activeSync, setActiveSync] = useState(null); const [isPromoting, setIsPromoting] = useState(false); const [confirmRenewUuid, setConfirmRenewUuid] = useState(null); @@ -83,7 +83,8 @@ export default function RootCaManagementClient() { }; const handleSyncCdn = async (type: 'all' | 'crt' | 'installers' | 'bundles', mode: 'latest' | 'archive' | 'both' = 'both') => { - setIsSyncing(true); + const syncKey = `${type}-${mode}`; + setActiveSync(syncKey); try { let endpoint = "/api/admin/ca-certificates/sync-cdn"; let msg = `Sync successful (Mode: ${mode})`; @@ -106,7 +107,7 @@ export default function RootCaManagementClient() { console.error(err); addToast(err.response?.data?.message || "Sync failed", "error"); } finally { - setIsSyncing(false); + setActiveSync(null); } }; @@ -129,61 +130,58 @@ export default function RootCaManagementClient() { const certificates = data?.data || []; return ( -
-
+
+
-
- {/* Left Column: Management & Renew Table */} -
- + {/* Main CA Management Table */} + + c.is_latest || certificates.length === 1)} + onRenew={setConfirmRenewUuid} + isRenewing={isRenewing} /> - - c.is_latest || certificates.length === 1)} - onRenew={setConfirmRenewUuid} - isRenewing={isRenewing} - /> + {(isLoading || isRenewing) && ( +
+ +
+ )} +
- {(isLoading || isRenewing) && ( -
- -
- )} -
+ {/* CDN Synchronization Controls */} + -
-

{t("info_title")}

-
    -
  • {t("info_point_1")}
  • -
  • {t("info_point_2")}
  • -
  • {t("info_point_3")}
  • -
  • {t("info_point_4")}
  • -
-
-
+ {/* Full-width Archive History */} + + + - {/* Right Column: Archives */} -
- - - +
+

{t("info_title")}

+
    +
  • {t("info_point_1")}
  • +
  • {t("info_point_2")}
  • +
  • {t("info_point_3")}
  • +
  • {t("info_point_4")}
  • +
diff --git a/src/components/admin/CdnManagementCard.tsx b/src/components/admin/CdnManagementCard.tsx index 469cce7..4deb6f4 100644 --- a/src/components/admin/CdnManagementCard.tsx +++ b/src/components/admin/CdnManagementCard.tsx @@ -6,54 +6,61 @@ import { useTranslations } from "next-intl"; interface CdnManagementCardProps { onSync: (type: "all" | "crt" | "installers" | "bundles", mode: "latest" | "archive" | "both") => void; - isSyncing: boolean; + activeSync: string | null; disabled: boolean; } -export default function CdnManagementCard({ onSync, isSyncing, disabled }: CdnManagementCardProps) { +export default function CdnManagementCard({ onSync, activeSync, disabled }: CdnManagementCardProps) { const t = useTranslations("RootCA"); const SyncButton = ({ - onClick, + type, + mode, label, desc, variant = "blue" }: { - onClick: () => void; + type: "all" | "crt" | "installers" | "bundles"; + mode: "latest" | "archive" | "both"; label: string; desc: string; - variant?: "blue" | "purple" | "indigo" | "gray" + variant?: "blue" | "purple" | "indigo" | "gray" | "success" | "warning"; }) => { + const syncKey = `${type}-${mode}`; + const isLoading = activeSync === syncKey; + const variantClasses = { blue: "bg-blue-600 hover:bg-blue-700 disabled:bg-blue-400", purple: "bg-purple-600 hover:bg-purple-700 disabled:bg-purple-400", indigo: "bg-indigo-600 hover:bg-indigo-700 disabled:bg-indigo-400", gray: "bg-gray-600 hover:bg-gray-700 disabled:bg-gray-400", + success: "bg-success-600 hover:bg-success-700 disabled:bg-success-400", + warning: "bg-warning-500 hover:bg-warning-600 disabled:bg-warning-400", }; return ( -
-
-
+
+
+
{label} -

{desc}

+

{desc}

@@ -61,36 +68,68 @@ export default function CdnManagementCard({ onSync, isSyncing, disabled }: CdnMa }; return ( - -
- onSync("all", "both")} - variant="indigo" - /> - onSync("all", "latest")} - variant="blue" - /> - onSync("all", "archive")} - variant="purple" - /> - onSync("bundles", "latest")} - variant="gray" - /> +
+
+ {/* Specific Assets Column */} + +
+ + + +
+
+ + {/* Sync Modes Column */} + +
+ + + +
+
- +
); } diff --git a/src/components/admin/RootCaTable.tsx b/src/components/admin/RootCaTable.tsx index 871d891..81e1807 100644 --- a/src/components/admin/RootCaTable.tsx +++ b/src/components/admin/RootCaTable.tsx @@ -109,7 +109,7 @@ export default function RootCaTable({
{new Date(cert.valid_from).toLocaleString()} - {t("to")} + {t("validity_to_label")} {new Date(cert.valid_to).toLocaleString()}
diff --git a/src/messages/en.json b/src/messages/en.json index c9b46a3..1a5f4b9 100644 --- a/src/messages/en.json +++ b/src/messages/en.json @@ -639,6 +639,10 @@ "management_title": "Root CA Management", "card_title": "CA Certificates", "card_desc": "Manage Root and Intermediate Certification Authorities", + "cdn_group_assets": "Sync Specific Assets", + "cdn_group_assets_desc": "Update specific file types to the CDN (Uses 'Both' mode by default).", + "cdn_group_modes": "Dual CDN Strategy", + "cdn_group_modes_desc": "Control asset distribution paths (Clean URLs vs Global Archives).", "toast_renew_success": "CA Certificate renewed successfully.", "toast_renew_failed": "Failed to renew CA certificate", "load_failed": "Failed to load CA certificates. Admin access required.", @@ -661,7 +665,7 @@ "validity_to_label": "to", "status_valid": "Valid", "status_expired": "Expired", - "renew_button": "Renew (10Y)", + "renew_button": "Renew Now", "no_ca_search": "No CAs matched \"{term}\"", "no_ca_found": "No Root CA certificates found. Run CA Setup from Certificates page." }, diff --git a/src/messages/id.json b/src/messages/id.json index 6fffd10..7ae63d1 100644 --- a/src/messages/id.json +++ b/src/messages/id.json @@ -639,6 +639,10 @@ "management_title": "Manajemen Root CA", "card_title": "Sertifikat CA", "card_desc": "Kelola Otoritas Sertifikasi Root dan Intermediat", + "cdn_group_assets": "Aset Sinkronisasi (Asset-specific)", + "cdn_group_assets_desc": "Perbarui file spesifik ke CDN (Menggunakan mode 'Both' secara default).", + "cdn_group_modes": "Strategi Dual CDN (Mode-specific)", + "cdn_group_modes_desc": "Kontrol jalur distribusi aset (Clean URLs vs Global Archives).", "toast_renew_success": "Sertifikat CA berhasil diperbarui.", "toast_renew_failed": "Gagal memperbarui sertifikat CA", "load_failed": "Gagal memuat sertifikat CA. Diperlukan akses admin.", @@ -661,7 +665,7 @@ "validity_to_label": "ke", "status_valid": "Valid", "status_expired": "Kedaluwarsa", - "renew_button": "Perbarui (10T)", + "renew_button": "Perbarui", "no_ca_search": "Tidak ada CA yang cocok dengan \"{term}\"", "no_ca_found": "Sertifikat Root CA tidak ditemukan. Jalankan Pengaturan CA dari halaman Sertifikat." },