[RAPID-micro] 메일관리 IMAP 서비스 프리셋 추가 (Gmail, Naver, Outlook 등)

Co-Authored-By: Claude Sonnet 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
2026-04-01 15:02:25 +09:00
parent 8ddb7319ab
commit 545b206f4c

View File

@@ -25,6 +25,10 @@ import {
} from "@/components/ui/alert-dialog";
import { toast } from "sonner";
import { Switch } from "@/components/ui/switch";
import { Popover, PopoverContent, PopoverTrigger } from "@/components/ui/popover";
import { Command, CommandEmpty, CommandGroup, CommandInput, CommandItem, CommandList } from "@/components/ui/command";
import { cn } from "@/lib/utils";
import { ChevronsUpDown, Check } from "lucide-react";
import {
ResizablePanelGroup,
ResizablePanel,
@@ -85,6 +89,15 @@ import {
} from "@/lib/api/userMail";
const ComposeDialogDynamic = dynamic(() => import("./ComposeDialog"), { ssr: false });
const IMAP_PRESETS = [
{ label: "직접 입력", value: "custom", host: "", port: 993, useTls: true },
{ label: "Gmail", value: "gmail", host: "imap.gmail.com", port: 993, useTls: true },
{ label: "Naver", value: "naver", host: "imap.naver.com", port: 993, useTls: true },
{ label: "Outlook / Hotmail", value: "outlook", host: "outlook.office365.com", port: 993, useTls: true },
{ label: "Kakao", value: "kakao", host: "imap.kakao.com", port: 993, useTls: true },
{ label: "Daum", value: "daum", host: "imap.daum.net", port: 993, useTls: true },
];
const DEFAULT_FORM: CreateUserMailAccountDto = {
displayName: "",
email: "",
@@ -131,6 +144,7 @@ export default function ImapMailPage() {
const [pendingDeleteMail, setPendingDeleteMail] = useState<ReceivedMail | null>(null);
const [pendingDeleteAccount, setPendingDeleteAccount] = useState<UserMailAccount | null>(null);
const [showPassword, setShowPassword] = useState(false);
const [hostPopoverOpen, setHostPopoverOpen] = useState(false);
const detailCacheRef = useRef<Map<string, MailDetail>>(new Map());
const prefetchingRef = useRef<Set<string>>(new Set());
@@ -416,6 +430,7 @@ export default function ImapMailPage() {
setForm(DEFAULT_FORM);
setTestResult(null);
setShowPassword(false);
setHostPopoverOpen(false);
setShowDialog(true);
}
@@ -913,11 +928,46 @@ export default function ImapMailPage() {
<div className="grid grid-cols-2 gap-3">
<div className="space-y-1">
<Label className="text-xs">IMAP </Label>
<Input
value={form.host}
onChange={(e) => setForm((p) => ({ ...p, host: e.target.value }))}
placeholder="imap.gmail.com"
/>
<Popover open={hostPopoverOpen} onOpenChange={setHostPopoverOpen}>
<PopoverTrigger asChild>
<Button variant="outline" role="combobox" className="w-full justify-between font-normal">
<span className={cn("truncate", !form.host && "text-muted-foreground")}>
{form.host || "imap.gmail.com"}
</span>
<ChevronsUpDown className="ml-2 h-4 w-4 shrink-0 opacity-50" />
</Button>
</PopoverTrigger>
<PopoverContent className="w-[220px] p-0" align="start">
<Command>
<CommandInput
placeholder="호스트 직접 입력..."
value={form.host}
onValueChange={(v) => setForm((p) => ({ ...p, host: v }))}
/>
<CommandList>
<CommandEmpty> </CommandEmpty>
<CommandGroup>
{IMAP_PRESETS.filter((p) => p.value !== "custom").map((preset) => (
<CommandItem
key={preset.value}
value={preset.host}
onSelect={() => {
setForm((p) => ({ ...p, host: preset.host, port: preset.port, useTls: preset.useTls }));
setHostPopoverOpen(false);
}}
>
<Check className={cn("mr-2 h-4 w-4", form.host === preset.host ? "opacity-100" : "opacity-0")} />
<div>
<div className="text-sm font-medium">{preset.label}</div>
<div className="text-xs text-muted-foreground">{preset.host}</div>
</div>
</CommandItem>
))}
</CommandGroup>
</CommandList>
</Command>
</PopoverContent>
</Popover>
</div>
<div className="space-y-1">
<Label className="text-xs"></Label>