fix: improve code quality in soft-delete frontend (loading state, imports, confirm dialog)
This commit is contained in:
25
app/page.tsx
25
app/page.tsx
@@ -5,7 +5,7 @@ import Link from 'next/link'
|
|||||||
import { motion } from 'framer-motion'
|
import { motion } from 'framer-motion'
|
||||||
import { useAuth } from '@/lib/auth/context'
|
import { useAuth } from '@/lib/auth/context'
|
||||||
import { initiateOAuthFlow, initiateSignupFlow } from '@/lib/api/oauth'
|
import { initiateOAuthFlow, initiateSignupFlow } from '@/lib/api/oauth'
|
||||||
import { listSites, listDeletedSites, restoreSite, permanentDeleteSite, type Site } from '@/lib/api/sites'
|
import { listSites, listDeletedSites, restoreSite, type Site } from '@/lib/api/sites'
|
||||||
import { getStats } from '@/lib/api/stats'
|
import { getStats } from '@/lib/api/stats'
|
||||||
import type { Stats } from '@/lib/api/stats'
|
import type { Stats } from '@/lib/api/stats'
|
||||||
import { getSubscription, type SubscriptionDetails } from '@/lib/api/billing'
|
import { getSubscription, type SubscriptionDetails } from '@/lib/api/billing'
|
||||||
@@ -121,6 +121,7 @@ export default function HomePage() {
|
|||||||
const [showFinishSetupBanner, setShowFinishSetupBanner] = useState(true)
|
const [showFinishSetupBanner, setShowFinishSetupBanner] = useState(true)
|
||||||
const [deleteModalSite, setDeleteModalSite] = useState<Site | null>(null)
|
const [deleteModalSite, setDeleteModalSite] = useState<Site | null>(null)
|
||||||
const [deletedSites, setDeletedSites] = useState<Site[]>([])
|
const [deletedSites, setDeletedSites] = useState<Site[]>([])
|
||||||
|
const [permanentDeleteSiteModal, setPermanentDeleteSiteModal] = useState<Site | null>(null)
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
if (user?.org_id) {
|
if (user?.org_id) {
|
||||||
@@ -222,15 +223,9 @@ export default function HomePage() {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
const handlePermanentDelete = async (id: string) => {
|
const handlePermanentDelete = (id: string) => {
|
||||||
if (!confirm('Permanently delete this site and all data? This cannot be undone.')) return
|
const site = deletedSites.find((s) => s.id === id)
|
||||||
try {
|
if (site) setPermanentDeleteSiteModal(site)
|
||||||
await permanentDeleteSite(id)
|
|
||||||
toast.success('Site permanently deleted')
|
|
||||||
loadSites()
|
|
||||||
} catch (error: unknown) {
|
|
||||||
toast.error(getAuthErrorMessage(error) || 'Failed to delete site')
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if (authLoading) {
|
if (authLoading) {
|
||||||
@@ -550,6 +545,16 @@ export default function HomePage() {
|
|||||||
siteId={deleteModalSite?.id || ''}
|
siteId={deleteModalSite?.id || ''}
|
||||||
/>
|
/>
|
||||||
|
|
||||||
|
<DeleteSiteModal
|
||||||
|
open={!!permanentDeleteSiteModal}
|
||||||
|
onClose={() => setPermanentDeleteSiteModal(null)}
|
||||||
|
onDeleted={loadSites}
|
||||||
|
siteName={permanentDeleteSiteModal?.name || ''}
|
||||||
|
siteDomain={permanentDeleteSiteModal?.domain || ''}
|
||||||
|
siteId={permanentDeleteSiteModal?.id || ''}
|
||||||
|
permanentOnly
|
||||||
|
/>
|
||||||
|
|
||||||
{deletedSites.length > 0 && (
|
{deletedSites.length > 0 && (
|
||||||
<div className="mt-8">
|
<div className="mt-8">
|
||||||
<h3 className="text-sm font-medium text-neutral-500 dark:text-neutral-400 mb-4">Scheduled for Deletion</h3>
|
<h3 className="text-sm font-medium text-neutral-500 dark:text-neutral-400 mb-4">Scheduled for Deletion</h3>
|
||||||
|
|||||||
@@ -1,11 +1,9 @@
|
|||||||
'use client'
|
'use client'
|
||||||
|
|
||||||
import { useState } from 'react'
|
import { useState, useEffect } from 'react'
|
||||||
import { createPortal } from 'react-dom'
|
import { createPortal } from 'react-dom'
|
||||||
import { motion, AnimatePresence } from 'framer-motion'
|
import { motion, AnimatePresence } from 'framer-motion'
|
||||||
import { toast } from '@ciphera-net/ui'
|
import { toast, getAuthErrorMessage, AlertTriangleIcon, XIcon } from '@ciphera-net/ui'
|
||||||
import { getAuthErrorMessage } from '@ciphera-net/ui'
|
|
||||||
import { AlertTriangleIcon, XIcon } from '@ciphera-net/ui'
|
|
||||||
import { deleteSite, permanentDeleteSite } from '@/lib/api/sites'
|
import { deleteSite, permanentDeleteSite } from '@/lib/api/sites'
|
||||||
|
|
||||||
interface DeleteSiteModalProps {
|
interface DeleteSiteModalProps {
|
||||||
@@ -15,15 +13,22 @@ interface DeleteSiteModalProps {
|
|||||||
siteName: string
|
siteName: string
|
||||||
siteDomain: string
|
siteDomain: string
|
||||||
siteId: string
|
siteId: string
|
||||||
|
permanentOnly?: boolean
|
||||||
}
|
}
|
||||||
|
|
||||||
export default function DeleteSiteModal({ open, onClose, onDeleted, siteName, siteDomain, siteId }: DeleteSiteModalProps) {
|
export default function DeleteSiteModal({ open, onClose, onDeleted, siteName, siteDomain, siteId, permanentOnly }: DeleteSiteModalProps) {
|
||||||
const [deleteConfirm, setDeleteConfirm] = useState('')
|
const [deleteConfirm, setDeleteConfirm] = useState('')
|
||||||
const [isDeleting, setIsDeleting] = useState(false)
|
const [isDeleting, setIsDeleting] = useState(false)
|
||||||
const [showPermanent, setShowPermanent] = useState(false)
|
const [showPermanent, setShowPermanent] = useState(!!permanentOnly)
|
||||||
const [permanentConfirm, setPermanentConfirm] = useState('')
|
const [permanentConfirm, setPermanentConfirm] = useState('')
|
||||||
const [isPermanentDeleting, setIsPermanentDeleting] = useState(false)
|
const [isPermanentDeleting, setIsPermanentDeleting] = useState(false)
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
if (open && permanentOnly) {
|
||||||
|
setShowPermanent(true)
|
||||||
|
}
|
||||||
|
}, [open, permanentOnly])
|
||||||
|
|
||||||
const handleClose = () => {
|
const handleClose = () => {
|
||||||
setDeleteConfirm('')
|
setDeleteConfirm('')
|
||||||
setShowPermanent(false)
|
setShowPermanent(false)
|
||||||
@@ -43,7 +48,6 @@ export default function DeleteSiteModal({ open, onClose, onDeleted, siteName, si
|
|||||||
onDeleted()
|
onDeleted()
|
||||||
} catch (error: unknown) {
|
} catch (error: unknown) {
|
||||||
toast.error(getAuthErrorMessage(error) || 'Failed to delete site')
|
toast.error(getAuthErrorMessage(error) || 'Failed to delete site')
|
||||||
} finally {
|
|
||||||
setIsDeleting(false)
|
setIsDeleting(false)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -58,7 +62,6 @@ export default function DeleteSiteModal({ open, onClose, onDeleted, siteName, si
|
|||||||
onDeleted()
|
onDeleted()
|
||||||
} catch (error: unknown) {
|
} catch (error: unknown) {
|
||||||
toast.error(getAuthErrorMessage(error) || 'Failed to permanently delete site')
|
toast.error(getAuthErrorMessage(error) || 'Failed to permanently delete site')
|
||||||
} finally {
|
|
||||||
setIsPermanentDeleting(false)
|
setIsPermanentDeleting(false)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -193,13 +196,17 @@ export default function DeleteSiteModal({ open, onClose, onDeleted, siteName, si
|
|||||||
<button
|
<button
|
||||||
type="button"
|
type="button"
|
||||||
onClick={() => {
|
onClick={() => {
|
||||||
setShowPermanent(false)
|
if (permanentOnly) {
|
||||||
setPermanentConfirm('')
|
handleClose()
|
||||||
|
} else {
|
||||||
|
setShowPermanent(false)
|
||||||
|
setPermanentConfirm('')
|
||||||
|
}
|
||||||
}}
|
}}
|
||||||
className="flex-1 px-4 py-2 text-sm font-medium text-neutral-700 dark:text-neutral-300 hover:bg-neutral-100 dark:hover:bg-neutral-800 rounded-xl transition-colors"
|
className="flex-1 px-4 py-2 text-sm font-medium text-neutral-700 dark:text-neutral-300 hover:bg-neutral-100 dark:hover:bg-neutral-800 rounded-xl transition-colors"
|
||||||
disabled={isPermanentDeleting}
|
disabled={isPermanentDeleting}
|
||||||
>
|
>
|
||||||
Back
|
{permanentOnly ? 'Cancel' : 'Back'}
|
||||||
</button>
|
</button>
|
||||||
<button
|
<button
|
||||||
onClick={handlePermanentDelete}
|
onClick={handlePermanentDelete}
|
||||||
|
|||||||
Reference in New Issue
Block a user