Try
dfssdfsfdsf
This commit is contained in:
@@ -188,6 +188,8 @@ export function Settings() {
|
||||
const [showImapForm, setShowImapForm] = useState(false)
|
||||
const [imapForm, setImapForm] = useState({ email: '', password: '', imapHost: 'imap.porkbun.com', imapPort: 993, imapSecure: true })
|
||||
const [imapConnecting, setImapConnecting] = useState(false)
|
||||
const [recoveringAccountId, setRecoveringAccountId] = useState<string | null>(null)
|
||||
const [reSortingAccountId, setReSortingAccountId] = useState<string | null>(null)
|
||||
const [vipSenders, setVipSenders] = useState<VIPSender[]>([])
|
||||
const [newVipEmail, setNewVipEmail] = useState('')
|
||||
const [subscription, setSubscription] = useState<Subscription | null>(null)
|
||||
@@ -224,6 +226,7 @@ export function Settings() {
|
||||
const [showNameLabelPanel, setShowNameLabelPanel] = useState(false)
|
||||
const [referralData, setReferralData] = useState<{ referralCode: string; referralCount: number } | null>(null)
|
||||
const [loadingReferral, setLoadingReferral] = useState(false)
|
||||
const [resettingSort, setResettingSort] = useState(false)
|
||||
|
||||
// Control Panel Sub-Tabs
|
||||
const [controlPanelTab, setControlPanelTab] = useState<'rules' | 'cleanup' | 'labels'>('rules')
|
||||
@@ -612,6 +615,51 @@ export function Settings() {
|
||||
}
|
||||
}
|
||||
|
||||
const handleRecoverEmails = async (accountId: string) => {
|
||||
if (!user?.$id) return
|
||||
setRecoveringAccountId(accountId)
|
||||
try {
|
||||
const res = await api.recoverEmails(accountId)
|
||||
if (res.error) {
|
||||
showMessage('error', res.error.message || 'Recovery failed')
|
||||
return
|
||||
}
|
||||
const data = res.data as { recovered?: number; message?: string } | undefined
|
||||
const n = data?.recovered ?? 0
|
||||
const text =
|
||||
data?.message ||
|
||||
(n > 0 ? `${n} emails recovered to inbox` : 'No emails found outside inbox')
|
||||
showMessage('success', text)
|
||||
} finally {
|
||||
setRecoveringAccountId(null)
|
||||
}
|
||||
}
|
||||
|
||||
const handleReSortEmails = async (accountId: string) => {
|
||||
if (!user?.$id) return
|
||||
if (
|
||||
!window.confirm(
|
||||
'Move messages from Junk, Archive, MailFlow/EmailSorter folders (and similar sort targets) back to INBOX and remove MailFlow category tags? Then run Sort again. Sent/Drafts/Trash are not touched.',
|
||||
)
|
||||
) {
|
||||
return
|
||||
}
|
||||
setReSortingAccountId(accountId)
|
||||
try {
|
||||
const res = await api.reSortEmails(accountId)
|
||||
if (res.error) {
|
||||
showMessage('error', res.error.message || 'Re-sort prep failed')
|
||||
return
|
||||
}
|
||||
const data = res.data as
|
||||
| { recovered?: number; mailFlowKeywordsStripped?: number; message?: string }
|
||||
| undefined
|
||||
showMessage('success', data?.message || 'Re-sort prep completed')
|
||||
} finally {
|
||||
setReSortingAccountId(null)
|
||||
}
|
||||
}
|
||||
|
||||
const handleConnectImap = async (e: React.FormEvent) => {
|
||||
e.preventDefault()
|
||||
if (!user?.$id || !imapForm.email.trim() || !imapForm.password) return
|
||||
@@ -1025,10 +1073,45 @@ export function Settings() {
|
||||
<p className="text-sm text-slate-500 dark:text-slate-400 capitalize">{account.provider === 'imap' ? 'IMAP' : account.provider}</p>
|
||||
</div>
|
||||
</div>
|
||||
<div className="flex items-center gap-3">
|
||||
<div className="flex items-center gap-3 flex-wrap justify-end">
|
||||
<Badge variant={account.connected ? 'success' : 'secondary'}>
|
||||
{account.connected ? 'Connected' : 'Disconnected'}
|
||||
</Badge>
|
||||
{account.provider === 'imap' && (
|
||||
<>
|
||||
<Button
|
||||
type="button"
|
||||
variant="outline"
|
||||
size="sm"
|
||||
disabled={
|
||||
recoveringAccountId === account.id ||
|
||||
reSortingAccountId === account.id
|
||||
}
|
||||
onClick={() => handleRecoverEmails(account.id)}
|
||||
>
|
||||
{recoveringAccountId === account.id ? (
|
||||
<Loader2 className="w-4 h-4 animate-spin mr-2" />
|
||||
) : null}
|
||||
Recover Emails
|
||||
</Button>
|
||||
<Button
|
||||
type="button"
|
||||
variant="secondary"
|
||||
size="sm"
|
||||
disabled={
|
||||
recoveringAccountId === account.id ||
|
||||
reSortingAccountId === account.id
|
||||
}
|
||||
onClick={() => handleReSortEmails(account.id)}
|
||||
title="Reset wrongly sorted mail: move from Junk/Archive/MailFlow folders to INBOX and clear MailFlow tags"
|
||||
>
|
||||
{reSortingAccountId === account.id ? (
|
||||
<Loader2 className="w-4 h-4 animate-spin mr-2" />
|
||||
) : null}
|
||||
Re-sort all
|
||||
</Button>
|
||||
</>
|
||||
)}
|
||||
<Button variant="ghost" size="icon" onClick={() => handleDisconnectAccount(account.id)}>
|
||||
<Trash2 className="w-4 h-4 text-red-500" />
|
||||
</Button>
|
||||
@@ -2325,6 +2408,67 @@ export function Settings() {
|
||||
|
||||
{activeTab === 'name-labels' && isAdmin && (
|
||||
<div className="space-y-6">
|
||||
<Card className="border-amber-200 dark:border-amber-900">
|
||||
<CardHeader>
|
||||
<div className="flex items-center gap-2">
|
||||
<Shield className="w-5 h-5 text-amber-600 dark:text-amber-400" />
|
||||
<CardTitle>Reset sort data (admin)</CardTitle>
|
||||
</div>
|
||||
<CardDescription>
|
||||
Clears email stats, digests, usage, and onboarding progress for the chosen user. Removes the{' '}
|
||||
<code className="text-xs">$MailFlow-sorted</code> flag from all messages in each IMAP account's INBOX.
|
||||
Does not remove email connections or subscriptions.
|
||||
</CardDescription>
|
||||
</CardHeader>
|
||||
<CardContent>
|
||||
<Button
|
||||
type="button"
|
||||
variant="destructive"
|
||||
disabled={resettingSort}
|
||||
onClick={async () => {
|
||||
const email = window.prompt('User email to reset:', 'support@webklar.com')
|
||||
if (email == null) return
|
||||
const trimmed = email.trim()
|
||||
if (!trimmed) {
|
||||
showMessage('error', 'Email is required')
|
||||
return
|
||||
}
|
||||
if (
|
||||
!window.confirm(
|
||||
`Reset ALL sort-related data for ${trimmed}? This cannot be undone.`,
|
||||
)
|
||||
) {
|
||||
return
|
||||
}
|
||||
setResettingSort(true)
|
||||
try {
|
||||
const res = await api.resetSortData(trimmed)
|
||||
if (res.error) {
|
||||
showMessage('error', res.error.message)
|
||||
return
|
||||
}
|
||||
const d = res.data
|
||||
showMessage(
|
||||
'success',
|
||||
`Reset OK. Stats/digests/usage cleared; IMAP $MailFlow-sorted removed from ${d?.imapCleared ?? 0} message(s).`,
|
||||
)
|
||||
} finally {
|
||||
setResettingSort(false)
|
||||
}
|
||||
}}
|
||||
>
|
||||
{resettingSort ? (
|
||||
<>
|
||||
<Loader2 className="w-4 h-4 mr-2 animate-spin" />
|
||||
Resetting…
|
||||
</>
|
||||
) : (
|
||||
'Reset sort data'
|
||||
)}
|
||||
</Button>
|
||||
</CardContent>
|
||||
</Card>
|
||||
|
||||
<Card>
|
||||
<CardHeader>
|
||||
<div className="flex items-center gap-2">
|
||||
|
||||
Reference in New Issue
Block a user