refactor: 전체 프론트엔드 하드코딩 색상 → CSS 변수 일괄 치환

447+ 파일, 4500+ 줄 변경:
- gray-* → border/bg-muted/text-foreground/text-muted-foreground
- blue-* → primary/ring
- red-* → destructive
- green-* → emerald (일관성)
- indigo-* → primary
- yellow/orange → amber (통일)
- dark mode 변형도 시맨틱 토큰으로 변환

Made-with: Cursor
This commit is contained in:
DDD1542
2026-03-09 14:31:59 +09:00
parent d967cf0a0d
commit 4f10b5e42d
447 changed files with 4520 additions and 4520 deletions

View File

@@ -409,7 +409,7 @@ example2@example.com,김철수,XYZ회사`;
{recipients.length > 0 && (
<div className="rounded-md border bg-muted p-4">
<div className="flex items-center gap-2 text-sm">
<CheckCircle2 className="h-4 w-4 text-green-600" />
<CheckCircle2 className="h-4 w-4 text-emerald-600" />
<span className="font-medium">{recipients.length} </span>
</div>
<p className="mt-1 text-xs text-muted-foreground">

View File

@@ -101,8 +101,8 @@ export default function MailDashboardPage() {
value: stats.totalAccounts,
icon: Users,
color: "blue",
bgColor: "bg-blue-100",
iconColor: "text-blue-600",
bgColor: "bg-primary/10",
iconColor: "text-primary",
href: "/admin/mail/accounts",
},
{
@@ -110,8 +110,8 @@ export default function MailDashboardPage() {
value: stats.totalTemplates,
icon: FileText,
color: "green",
bgColor: "bg-green-100",
iconColor: "text-green-600",
bgColor: "bg-emerald-100",
iconColor: "text-emerald-600",
href: "/admin/mail/templates",
},
{
@@ -119,8 +119,8 @@ export default function MailDashboardPage() {
value: stats.sentToday,
icon: Send,
color: "orange",
bgColor: "bg-orange-100",
iconColor: "text-orange-600",
bgColor: "bg-amber-100",
iconColor: "text-amber-600",
href: "/admin/mail/sent",
},
{

View File

@@ -438,8 +438,8 @@ export default function MailReceivePage() {
<div
className={`mt-4 p-3 rounded-lg flex items-center gap-2 ${
testResult.success
? "bg-green-50 text-green-800 border border-green-200"
: "bg-red-50 text-red-800 border border-red-200"
? "bg-emerald-50 text-emerald-800 border border-emerald-200"
: "bg-destructive/10 text-red-800 border border-destructive/20"
}`}
>
{testResult.success ? (
@@ -460,7 +460,7 @@ export default function MailReceivePage() {
<div className="flex flex-col md:flex-row gap-3">
{/* 검색 */}
<div className="flex-1 relative">
<Search className="absolute left-3 top-1/2 -translate-y-1/2 w-4 h-4 text-gray-400" />
<Search className="absolute left-3 top-1/2 -translate-y-1/2 w-4 h-4 text-muted-foreground/70" />
<input
type="text"
value={searchTerm}
@@ -511,7 +511,7 @@ export default function MailReceivePage() {
{filteredAndSortedMails.length}
{searchTerm && (
<span className="ml-2">
(: <span className="font-medium text-orange-600">{searchTerm}</span>)
(: <span className="font-medium text-amber-600">{searchTerm}</span>)
</span>
)}
</div>
@@ -527,14 +527,14 @@ export default function MailReceivePage() {
{loading ? (
<Card className="">
<CardContent className="flex justify-center items-center py-16">
<Loader2 className="w-8 h-8 animate-spin text-orange-500" />
<Loader2 className="w-8 h-8 animate-spin text-amber-500" />
<span className="ml-3 text-muted-foreground"> ...</span>
</CardContent>
</Card>
) : filteredAndSortedMails.length === 0 ? (
<Card className="text-center py-16 bg-card ">
<CardContent className="pt-6">
<Mail className="w-16 h-16 mx-auto mb-4 text-gray-300" />
<Mail className="w-16 h-16 mx-auto mb-4 text-muted-foreground/50" />
<p className="text-muted-foreground mb-4">
{!selectedAccountId
? "메일 계정을 선택하세요"
@@ -560,9 +560,9 @@ export default function MailReceivePage() {
</Card>
) : (
<Card className="">
<CardHeader className="bg-gradient-to-r from-slate-50 to-gray-50 border-b">
<CardHeader className="bg-gradient-to-r from-slate-50 to-muted border-b">
<CardTitle className="flex items-center gap-2">
<Inbox className="w-5 h-5 text-orange-500" />
<Inbox className="w-5 h-5 text-amber-500" />
({filteredAndSortedMails.length}/{mails.length})
</CardTitle>
</CardHeader>
@@ -573,14 +573,14 @@ export default function MailReceivePage() {
key={mail.id}
onClick={() => handleMailClick(mail)}
className={`p-4 hover:bg-background transition-colors cursor-pointer ${
!mail.isRead ? "bg-blue-50/30" : ""
!mail.isRead ? "bg-primary/10/30" : ""
} ${selectedMailId === mail.id ? "bg-accent border-l-4 border-l-primary" : ""}`}
>
<div className="flex items-start gap-4">
{/* 읽음 표시 */}
<div className="flex-shrink-0 w-2 h-2 mt-2">
{!mail.isRead && (
<div className="w-2 h-2 bg-blue-500 rounded-full"></div>
<div className="w-2 h-2 bg-primary rounded-full"></div>
)}
</div>
@@ -598,7 +598,7 @@ export default function MailReceivePage() {
</span>
<div className="flex items-center gap-2">
{mail.hasAttachments && (
<Paperclip className="w-4 h-4 text-gray-400" />
<Paperclip className="w-4 h-4 text-muted-foreground/70" />
)}
<span className="text-xs text-muted-foreground">
{formatDate(mail.date)}
@@ -882,14 +882,14 @@ export default function MailReceivePage() {
) : loadingDetail ? (
<Card className="sticky top-6">
<CardContent className="flex justify-center items-center py-16">
<Loader2 className="w-8 h-8 animate-spin text-orange-500" />
<Loader2 className="w-8 h-8 animate-spin text-amber-500" />
<span className="ml-3 text-muted-foreground"> ...</span>
</CardContent>
</Card>
) : (
<Card className="sticky top-6">
<CardContent className="flex flex-col justify-center items-center py-16 text-center">
<Mail className="w-16 h-16 mb-4 text-gray-300" />
<Mail className="w-16 h-16 mb-4 text-muted-foreground/50" />
<p className="text-muted-foreground">
</p>
@@ -900,10 +900,10 @@ export default function MailReceivePage() {
</div>
{/* 안내 정보 */}
<Card className="bg-gradient-to-r from-green-50 to-emerald-50 border-green-200 ">
<Card className="bg-gradient-to-r from-green-50 to-emerald-50 border-emerald-200 ">
<CardHeader>
<CardTitle className="text-lg flex items-center">
<CheckCircle className="w-5 h-5 mr-2 text-green-600" />
<CheckCircle className="w-5 h-5 mr-2 text-emerald-600" />
! 🎉
</CardTitle>
</CardHeader>
@@ -913,81 +913,81 @@ export default function MailReceivePage() {
</p>
<div className="grid grid-cols-1 md:grid-cols-2 gap-3">
<div>
<p className="font-medium text-gray-800 mb-2">📬 </p>
<p className="font-medium text-foreground mb-2">📬 </p>
<ul className="space-y-1 text-sm text-muted-foreground">
<li className="flex items-start">
<span className="text-green-500 mr-2"></span>
<span className="text-emerald-500 mr-2"></span>
<span>IMAP </span>
</li>
<li className="flex items-start">
<span className="text-green-500 mr-2"></span>
<span className="text-emerald-500 mr-2"></span>
<span> </span>
</li>
<li className="flex items-start">
<span className="text-green-500 mr-2"></span>
<span className="text-emerald-500 mr-2"></span>
<span>/ </span>
</li>
<li className="flex items-start">
<span className="text-green-500 mr-2"></span>
<span className="text-emerald-500 mr-2"></span>
<span> </span>
</li>
</ul>
</div>
<div>
<p className="font-medium text-gray-800 mb-2">📄 </p>
<p className="font-medium text-foreground mb-2">📄 </p>
<ul className="space-y-1 text-sm text-muted-foreground">
<li className="flex items-start">
<span className="text-green-500 mr-2"></span>
<span className="text-emerald-500 mr-2"></span>
<span>HTML </span>
</li>
<li className="flex items-start">
<span className="text-green-500 mr-2"></span>
<span className="text-emerald-500 mr-2"></span>
<span> </span>
</li>
<li className="flex items-start">
<span className="text-green-500 mr-2"></span>
<span className="text-emerald-500 mr-2"></span>
<span> </span>
</li>
<li className="flex items-start">
<span className="text-green-500 mr-2"></span>
<span className="text-emerald-500 mr-2"></span>
<span> </span>
</li>
</ul>
</div>
<div>
<p className="font-medium text-gray-800 mb-2">🔍 </p>
<p className="font-medium text-foreground mb-2">🔍 </p>
<ul className="space-y-1 text-sm text-muted-foreground">
<li className="flex items-start">
<span className="text-green-500 mr-2"></span>
<span className="text-emerald-500 mr-2"></span>
<span> (//)</span>
</li>
<li className="flex items-start">
<span className="text-green-500 mr-2"></span>
<span className="text-emerald-500 mr-2"></span>
<span> (/)</span>
</li>
<li className="flex items-start">
<span className="text-green-500 mr-2"></span>
<span className="text-emerald-500 mr-2"></span>
<span> (/)</span>
</li>
<li className="flex items-start">
<span className="text-green-500 mr-2"></span>
<span className="text-emerald-500 mr-2"></span>
<span> (30)</span>
</li>
</ul>
</div>
<div>
<p className="font-medium text-gray-800 mb-2">🔒 </p>
<p className="font-medium text-foreground mb-2">🔒 </p>
<ul className="space-y-1 text-sm text-muted-foreground">
<li className="flex items-start">
<span className="text-green-500 mr-2"></span>
<span className="text-emerald-500 mr-2"></span>
<span>XSS (DOMPurify)</span>
</li>
<li className="flex items-start">
<span className="text-green-500 mr-2"></span>
<span className="text-emerald-500 mr-2"></span>
<span> </span>
</li>
<li className="flex items-start">
<span className="text-green-500 mr-2"></span>
<span className="text-emerald-500 mr-2"></span>
<span> </span>
</li>
</ul>

View File

@@ -516,12 +516,12 @@ ${data.originalBody}`;
toast({
title: (
<div className="flex items-center gap-2">
<CheckCircle2 className="w-5 h-5 text-green-500" />
<CheckCircle2 className="w-5 h-5 text-emerald-500" />
<span> !</span>
</div>
) as any,
description: `${to.length}${cc.length > 0 ? ` (참조 ${cc.length}명)` : ""}${bcc.length > 0 ? ` (숨은참조 ${bcc.length}명)` : ""}${attachments.length > 0 ? ` (첨부파일 ${attachments.length}개)` : ""}에게 메일이 성공적으로 발송되었습니다.`,
className: "border-green-500 bg-green-50",
className: "border-emerald-500 bg-emerald-50",
});
// 알림 갱신 이벤트 발생
@@ -544,7 +544,7 @@ ${data.originalBody}`;
toast({
title: (
<div className="flex items-center gap-2">
<AlertCircle className="w-5 h-5 text-red-500" />
<AlertCircle className="w-5 h-5 text-destructive" />
<span> </span>
</div>
) as any,
@@ -781,7 +781,7 @@ ${data.originalBody}`;
</>
) : (
<>
<CheckCircle2 className="w-4 h-4 text-green-500" />
<CheckCircle2 className="w-4 h-4 text-emerald-500" />
<span>
{new Date(lastSaved).toLocaleTimeString('ko-KR', {
hour: '2-digit',
@@ -895,7 +895,7 @@ ${data.originalBody}`;
{to.map((email) => (
<div
key={email}
className="flex items-center gap-1 px-2 py-1 bg-blue-100 text-blue-700 rounded-md text-sm"
className="flex items-center gap-1 px-2 py-1 bg-primary/10 text-primary rounded-md text-sm"
>
<span>{email}</span>
<button
@@ -933,12 +933,12 @@ ${data.originalBody}`;
{cc.map((email) => (
<div
key={email}
className="flex items-center gap-1 px-2 py-1 bg-green-100 text-green-700 rounded-md text-sm"
className="flex items-center gap-1 px-2 py-1 bg-emerald-100 text-emerald-700 rounded-md text-sm"
>
<span>{email}</span>
<button
onClick={() => removeEmail(email, "cc")}
className="hover:bg-green-200 rounded p-0.5"
className="hover:bg-emerald-200 rounded p-0.5"
>
<X className="w-3 h-3" />
</button>
@@ -1222,7 +1222,7 @@ ${data.originalBody}`;
<div
key={component.id}
style={{ height: `${component.height || 20}px` }}
className="bg-background rounded flex items-center justify-center text-xs text-gray-400"
className="bg-background rounded flex items-center justify-center text-xs text-muted-foreground/70"
>
</div>
@@ -1236,7 +1236,7 @@ ${data.originalBody}`;
{component.logoSrc && <img src={component.logoSrc} alt="로고" className="h-10" />}
<span className="font-bold text-lg">{component.brandName}</span>
</div>
<span className="text-sm text-gray-500">{component.sendDate}</span>
<span className="text-sm text-muted-foreground">{component.sendDate}</span>
</div>
</div>
);
@@ -1245,13 +1245,13 @@ ${data.originalBody}`;
return (
<div key={component.id} className="border rounded-lg overflow-hidden">
{component.tableTitle && (
<div className="bg-gray-50 px-4 py-2 font-semibold border-b">{component.tableTitle}</div>
<div className="bg-muted px-4 py-2 font-semibold border-b">{component.tableTitle}</div>
)}
<table className="w-full">
<tbody>
{component.rows?.map((row: any, i: number) => (
<tr key={i} className={i % 2 === 0 ? 'bg-white' : 'bg-gray-50'}>
<td className="px-4 py-2 font-medium text-gray-600 w-1/3 border-r">{row.label}</td>
<tr key={i} className={i % 2 === 0 ? 'bg-white' : 'bg-muted'}>
<td className="px-4 py-2 font-medium text-muted-foreground w-1/3 border-r">{row.label}</td>
<td className="px-4 py-2">{row.value}</td>
</tr>
))}
@@ -1263,9 +1263,9 @@ ${data.originalBody}`;
case 'alertBox':
return (
<div key={component.id} className={`p-4 rounded-lg border-l-4 ${
component.alertType === 'info' ? 'bg-blue-50 border-blue-500 text-blue-800' :
component.alertType === 'info' ? 'bg-primary/10 border-primary text-primary' :
component.alertType === 'warning' ? 'bg-amber-50 border-amber-500 text-amber-800' :
component.alertType === 'danger' ? 'bg-red-50 border-red-500 text-red-800' :
component.alertType === 'danger' ? 'bg-destructive/10 border-destructive text-red-800' :
'bg-emerald-50 border-emerald-500 text-emerald-800'
}`}>
{component.alertTitle && <div className="font-bold mb-1">{component.alertTitle}</div>}
@@ -1275,13 +1275,13 @@ ${data.originalBody}`;
case 'divider':
return (
<hr key={component.id} className="border-gray-300" style={{ borderWidth: `${component.height || 1}px` }} />
<hr key={component.id} className="border-input" style={{ borderWidth: `${component.height || 1}px` }} />
);
case 'footer':
return (
<div key={component.id} className="text-center text-sm text-gray-500 py-4 border-t bg-gray-50">
{component.companyName && <div className="font-semibold text-gray-700">{component.companyName}</div>}
<div key={component.id} className="text-center text-sm text-muted-foreground py-4 border-t bg-muted">
{component.companyName && <div className="font-semibold text-foreground">{component.companyName}</div>}
{(component.ceoName || component.businessNumber) && (
<div className="mt-1">
{component.ceoName && <span>: {component.ceoName}</span>}
@@ -1297,7 +1297,7 @@ ${data.originalBody}`;
{component.email && <span>Email: {component.email}</span>}
</div>
)}
{component.copyright && <div className="mt-2 text-xs text-gray-400">{component.copyright}</div>}
{component.copyright && <div className="mt-2 text-xs text-muted-foreground/70">{component.copyright}</div>}
</div>
);
@@ -1318,9 +1318,9 @@ ${data.originalBody}`;
}
})}
</div>
<div className="bg-gradient-to-r from-green-50 to-emerald-50 px-4 py-3 border-t border-green-200">
<p className="text-sm text-green-800 flex items-center gap-2 font-medium">
<CheckCircle2 className="w-4 h-4 text-green-600" />
<div className="bg-gradient-to-r from-green-50 to-emerald-50 px-4 py-3 border-t border-emerald-200">
<p className="text-sm text-emerald-800 flex items-center gap-2 font-medium">
<CheckCircle2 className="w-4 h-4 text-emerald-600" />
</p>
</div>
@@ -1396,7 +1396,7 @@ ${data.originalBody}`;
onChange={handleFileSelect}
className="hidden"
/>
<Upload className="w-12 h-12 mx-auto text-gray-400 mb-3" />
<Upload className="w-12 h-12 mx-auto text-muted-foreground/70 mb-3" />
<p className="text-sm text-muted-foreground mb-1">
</p>
@@ -1430,7 +1430,7 @@ ${data.originalBody}`;
variant="ghost"
size="sm"
onClick={() => removeFile(index)}
className="flex-shrink-0 text-red-500 hover:text-red-600 hover:bg-red-50"
className="flex-shrink-0 text-destructive hover:text-destructive hover:bg-destructive/10"
>
<X className="w-4 h-4" />
</Button>
@@ -1530,7 +1530,7 @@ ${data.originalBody}`;
<div key={index} className="flex items-center gap-2 text-xs text-muted-foreground">
<File className="w-3 h-3" />
<span className="truncate">{file.name}</span>
<span className="text-gray-400">({formatFileSize(file.size)})</span>
<span className="text-muted-foreground/70">({formatFileSize(file.size)})</span>
</div>
))}
</div>

View File

@@ -371,8 +371,8 @@ export default function SentMailPage() {
{stats.successCount}
</p>
</div>
<div className="p-3 bg-green-500/10 rounded-lg">
<CheckCircle2 className="w-6 h-6 text-green-600" />
<div className="p-3 bg-emerald-500/10 rounded-lg">
<CheckCircle2 className="w-6 h-6 text-emerald-600" />
</div>
</div>
</CardContent>
@@ -387,8 +387,8 @@ export default function SentMailPage() {
{stats.failedCount}
</p>
</div>
<div className="p-3 bg-red-500/10 rounded-lg">
<XCircle className="w-6 h-6 text-red-600" />
<div className="p-3 bg-destructive/10 rounded-lg">
<XCircle className="w-6 h-6 text-destructive" />
</div>
</div>
</CardContent>
@@ -403,8 +403,8 @@ export default function SentMailPage() {
{stats.todayCount}
</p>
</div>
<div className="p-3 bg-blue-500/10 rounded-lg">
<Calendar className="w-6 h-6 text-blue-600" />
<div className="p-3 bg-primary/10 rounded-lg">
<Calendar className="w-6 h-6 text-primary" />
</div>
</div>
</CardContent>