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

@ -69,33 +69,33 @@ const RESOURCE_TYPE_CONFIG: Record<
string, string,
{ label: string; icon: React.ElementType; color: string } { label: string; icon: React.ElementType; color: string }
> = { > = {
MENU: { label: "메뉴", icon: Layout, color: "bg-blue-100 text-blue-700" }, MENU: { label: "메뉴", icon: Layout, color: "bg-primary/10 text-primary" },
SCREEN: { label: "화면", icon: Monitor, color: "bg-purple-100 text-purple-700" }, SCREEN: { label: "화면", icon: Monitor, color: "bg-purple-100 text-purple-700" },
SCREEN_LAYOUT: { label: "레이아웃", icon: Monitor, color: "bg-purple-100 text-purple-700" }, SCREEN_LAYOUT: { label: "레이아웃", icon: Monitor, color: "bg-purple-100 text-purple-700" },
FLOW: { label: "플로우", icon: GitBranch, color: "bg-green-100 text-green-700" }, FLOW: { label: "플로우", icon: GitBranch, color: "bg-emerald-100 text-emerald-700" },
FLOW_STEP: { label: "플로우 스텝", icon: GitBranch, color: "bg-green-100 text-green-700" }, FLOW_STEP: { label: "플로우 스텝", icon: GitBranch, color: "bg-emerald-100 text-emerald-700" },
USER: { label: "사용자", icon: User, color: "bg-orange-100 text-orange-700" }, USER: { label: "사용자", icon: User, color: "bg-amber-100 text-orange-700" },
ROLE: { label: "권한", icon: Shield, color: "bg-red-100 text-red-700" }, ROLE: { label: "권한", icon: Shield, color: "bg-destructive/10 text-destructive" },
PERMISSION: { label: "권한", icon: Shield, color: "bg-red-100 text-red-700" }, PERMISSION: { label: "권한", icon: Shield, color: "bg-destructive/10 text-destructive" },
COMPANY: { label: "회사", icon: Building2, color: "bg-indigo-100 text-indigo-700" }, COMPANY: { label: "회사", icon: Building2, color: "bg-indigo-100 text-indigo-700" },
CODE_CATEGORY: { label: "코드 카테고리", icon: Hash, color: "bg-cyan-100 text-cyan-700" }, CODE_CATEGORY: { label: "코드 카테고리", icon: Hash, color: "bg-cyan-100 text-cyan-700" },
CODE: { label: "코드", icon: Hash, color: "bg-cyan-100 text-cyan-700" }, CODE: { label: "코드", icon: Hash, color: "bg-cyan-100 text-cyan-700" },
DATA: { label: "데이터", icon: Database, color: "bg-gray-100 text-gray-700" }, DATA: { label: "데이터", icon: Database, color: "bg-muted text-foreground" },
TABLE: { label: "테이블", icon: Database, color: "bg-gray-100 text-gray-700" }, TABLE: { label: "테이블", icon: Database, color: "bg-muted text-foreground" },
NUMBERING_RULE: { label: "채번 규칙", icon: FileText, color: "bg-amber-100 text-amber-700" }, NUMBERING_RULE: { label: "채번 규칙", icon: FileText, color: "bg-amber-100 text-amber-700" },
BATCH: { label: "배치", icon: RefreshCw, color: "bg-teal-100 text-teal-700" }, BATCH: { label: "배치", icon: RefreshCw, color: "bg-teal-100 text-teal-700" },
}; };
const ACTION_CONFIG: Record<string, { label: string; color: string }> = { const ACTION_CONFIG: Record<string, { label: string; color: string }> = {
CREATE: { label: "생성", color: "bg-emerald-100 text-emerald-700" }, CREATE: { label: "생성", color: "bg-emerald-100 text-emerald-700" },
UPDATE: { label: "수정", color: "bg-blue-100 text-blue-700" }, UPDATE: { label: "수정", color: "bg-primary/10 text-primary" },
DELETE: { label: "삭제", color: "bg-red-100 text-red-700" }, DELETE: { label: "삭제", color: "bg-destructive/10 text-destructive" },
COPY: { label: "복사", color: "bg-violet-100 text-violet-700" }, COPY: { label: "복사", color: "bg-violet-100 text-violet-700" },
LOGIN: { label: "로그인", color: "bg-gray-100 text-gray-700" }, LOGIN: { label: "로그인", color: "bg-muted text-foreground" },
STATUS_CHANGE: { label: "상태변경", color: "bg-amber-100 text-amber-700" }, STATUS_CHANGE: { label: "상태변경", color: "bg-amber-100 text-amber-700" },
BATCH_CREATE: { label: "배치생성", color: "bg-emerald-100 text-emerald-700" }, BATCH_CREATE: { label: "배치생성", color: "bg-emerald-100 text-emerald-700" },
BATCH_UPDATE: { label: "배치수정", color: "bg-blue-100 text-blue-700" }, BATCH_UPDATE: { label: "배치수정", color: "bg-primary/10 text-primary" },
BATCH_DELETE: { label: "배치삭제", color: "bg-red-100 text-red-700" }, BATCH_DELETE: { label: "배치삭제", color: "bg-destructive/10 text-destructive" },
}; };
function formatDateTime(dateStr: string): string { function formatDateTime(dateStr: string): string {
@ -203,12 +203,12 @@ function renderChanges(changes: Record<string, unknown>) {
<tr className="bg-muted/50"> <tr className="bg-muted/50">
<th className="px-3 py-1.5 text-left font-medium"></th> <th className="px-3 py-1.5 text-left font-medium"></th>
{hasBefore && ( {hasBefore && (
<th className="px-3 py-1.5 text-left font-medium text-red-600"> <th className="px-3 py-1.5 text-left font-medium text-destructive">
</th> </th>
)} )}
{hasAfter && ( {hasAfter && (
<th className="px-3 py-1.5 text-left font-medium text-blue-600"> <th className="px-3 py-1.5 text-left font-medium text-primary">
</th> </th>
)} )}
@ -234,7 +234,7 @@ function renderChanges(changes: Record<string, unknown>) {
{hasBefore && ( {hasBefore && (
<td className="px-3 py-1.5"> <td className="px-3 py-1.5">
{row.beforeVal !== null ? ( {row.beforeVal !== null ? (
<span className="rounded bg-red-50 px-1.5 py-0.5 text-red-700"> <span className="rounded bg-destructive/10 px-1.5 py-0.5 text-destructive">
{row.beforeVal} {row.beforeVal}
</span> </span>
) : ( ) : (
@ -245,7 +245,7 @@ function renderChanges(changes: Record<string, unknown>) {
{hasAfter && ( {hasAfter && (
<td className="px-3 py-1.5"> <td className="px-3 py-1.5">
{row.afterVal !== null ? ( {row.afterVal !== null ? (
<span className="rounded bg-blue-50 px-1.5 py-0.5 text-blue-700"> <span className="rounded bg-primary/10 px-1.5 py-0.5 text-primary">
{row.afterVal} {row.afterVal}
</span> </span>
) : ( ) : (

View File

@ -311,10 +311,10 @@ export default function BatchCreatePage() {
{/* 매핑 설정 */} {/* 매핑 설정 */}
<div className="grid grid-cols-1 lg:grid-cols-2 gap-6"> <div className="grid grid-cols-1 lg:grid-cols-2 gap-6">
{/* FROM 섹션 */} {/* FROM 섹션 */}
<Card className="border-green-200"> <Card className="border-emerald-200">
<CardHeader className="bg-green-50"> <CardHeader className="bg-emerald-50">
<CardTitle className="text-green-700">FROM ( )</CardTitle> <CardTitle className="text-emerald-700">FROM ( )</CardTitle>
<p className="text-sm text-green-600"> <p className="text-sm text-emerald-600">
1단계: 커넥션을 2단계: 테이블을 3단계: 컬럼을 1단계: 커넥션을 2단계: 테이블을 3단계: 컬럼을
</p> </p>
</CardHeader> </CardHeader>
@ -365,7 +365,7 @@ export default function BatchCreatePage() {
{/* FROM 컬럼 목록 */} {/* FROM 컬럼 목록 */}
{fromTable && ( {fromTable && (
<div className="space-y-2"> <div className="space-y-2">
<Label className="text-blue-600 font-semibold">{fromTable} </Label> <Label className="text-primary font-semibold">{fromTable} </Label>
<div className="border rounded-lg p-4 max-h-80 overflow-y-auto space-y-2"> <div className="border rounded-lg p-4 max-h-80 overflow-y-auto space-y-2">
{fromColumns.map((column) => ( {fromColumns.map((column) => (
<div <div
@ -373,16 +373,16 @@ export default function BatchCreatePage() {
onClick={() => handleFromColumnClick(column)} onClick={() => handleFromColumnClick(column)}
className={`p-3 border rounded cursor-pointer transition-colors ${ className={`p-3 border rounded cursor-pointer transition-colors ${
selectedFromColumn?.column_name === column.column_name selectedFromColumn?.column_name === column.column_name
? 'bg-green-100 border-green-300' ? 'bg-emerald-100 border-green-300'
: 'hover:bg-gray-50 border-gray-200' : 'hover:bg-muted border-border'
}`} }`}
> >
<div className="font-medium">{column.column_name}</div> <div className="font-medium">{column.column_name}</div>
<div className="text-sm text-gray-500">{column.data_type}</div> <div className="text-sm text-muted-foreground">{column.data_type}</div>
</div> </div>
))} ))}
{fromColumns.length === 0 && fromTable && ( {fromColumns.length === 0 && fromTable && (
<div className="text-center text-gray-500 py-4"> <div className="text-center text-muted-foreground py-4">
... ...
</div> </div>
)} )}
@ -393,10 +393,10 @@ export default function BatchCreatePage() {
</Card> </Card>
{/* TO 섹션 */} {/* TO 섹션 */}
<Card className="border-red-200"> <Card className="border-destructive/20">
<CardHeader className="bg-red-50"> <CardHeader className="bg-destructive/10">
<CardTitle className="text-red-700">TO ( )</CardTitle> <CardTitle className="text-destructive">TO ( )</CardTitle>
<p className="text-sm text-red-600"> <p className="text-sm text-destructive">
FROM에서 , FROM에서 ,
</p> </p>
</CardHeader> </CardHeader>
@ -447,7 +447,7 @@ export default function BatchCreatePage() {
{/* TO 컬럼 목록 */} {/* TO 컬럼 목록 */}
{toTable && ( {toTable && (
<div className="space-y-2"> <div className="space-y-2">
<Label className="text-blue-600 font-semibold">{toTable} </Label> <Label className="text-primary font-semibold">{toTable} </Label>
<div className="border rounded-lg p-4 max-h-80 overflow-y-auto space-y-2"> <div className="border rounded-lg p-4 max-h-80 overflow-y-auto space-y-2">
{toColumns.map((column) => ( {toColumns.map((column) => (
<div <div
@ -455,16 +455,16 @@ export default function BatchCreatePage() {
onClick={() => handleToColumnClick(column)} onClick={() => handleToColumnClick(column)}
className={`p-3 border rounded cursor-pointer transition-colors ${ className={`p-3 border rounded cursor-pointer transition-colors ${
selectedFromColumn selectedFromColumn
? 'hover:bg-red-50 border-gray-200' ? 'hover:bg-destructive/10 border-border'
: 'bg-gray-100 border-gray-300 cursor-not-allowed' : 'bg-muted border-input cursor-not-allowed'
}`} }`}
> >
<div className="font-medium">{column.column_name}</div> <div className="font-medium">{column.column_name}</div>
<div className="text-sm text-gray-500">{column.data_type}</div> <div className="text-sm text-muted-foreground">{column.data_type}</div>
</div> </div>
))} ))}
{toColumns.length === 0 && toTable && ( {toColumns.length === 0 && toTable && (
<div className="text-center text-gray-500 py-4"> <div className="text-center text-muted-foreground py-4">
... ...
</div> </div>
)} )}
@ -484,22 +484,22 @@ export default function BatchCreatePage() {
<CardContent> <CardContent>
<div className="space-y-3"> <div className="space-y-3">
{mappings.map((mapping, index) => ( {mappings.map((mapping, index) => (
<div key={index} className="flex items-center justify-between p-4 border rounded-lg bg-yellow-50"> <div key={index} className="flex items-center justify-between p-4 border rounded-lg bg-amber-50">
<div className="flex items-center space-x-4"> <div className="flex items-center space-x-4">
<div className="text-sm"> <div className="text-sm">
<div className="font-medium"> <div className="font-medium">
{mapping.from_table_name}.{mapping.from_column_name} {mapping.from_table_name}.{mapping.from_column_name}
</div> </div>
<div className="text-gray-500"> <div className="text-muted-foreground">
{mapping.from_column_type} {mapping.from_column_type}
</div> </div>
</div> </div>
<ArrowRight className="h-4 w-4 text-gray-400" /> <ArrowRight className="h-4 w-4 text-muted-foreground/70" />
<div className="text-sm"> <div className="text-sm">
<div className="font-medium"> <div className="font-medium">
{mapping.to_table_name}.{mapping.to_column_name} {mapping.to_table_name}.{mapping.to_column_name}
</div> </div>
<div className="text-gray-500"> <div className="text-muted-foreground">
{mapping.to_column_type} {mapping.to_column_type}
</div> </div>
</div> </div>
@ -508,7 +508,7 @@ export default function BatchCreatePage() {
variant="ghost" variant="ghost"
size="sm" size="sm"
onClick={() => removeMapping(index)} onClick={() => removeMapping(index)}
className="text-red-600 hover:text-red-700" className="text-destructive hover:text-destructive"
> >
<Trash2 className="h-4 w-4" /> <Trash2 className="h-4 w-4" />
</Button> </Button>

View File

@ -815,7 +815,7 @@ export default function BatchEditPage() {
</SelectContent> </SelectContent>
</Select> </Select>
)} )}
<p className="mt-1 text-xs text-gray-500"> <p className="mt-1 text-xs text-muted-foreground">
{authTokenMode === "direct" {authTokenMode === "direct"
? "API 호출 시 Authorization 헤더에 사용할 토큰을 입력하세요." ? "API 호출 시 Authorization 헤더에 사용할 토큰을 입력하세요."
: "auth_tokens 테이블에서 선택한 서비스의 최신 토큰을 사용합니다."} : "auth_tokens 테이블에서 선택한 서비스의 최신 토큰을 사용합니다."}
@ -874,7 +874,7 @@ export default function BatchEditPage() {
onChange={(e) => setDataArrayPath(e.target.value)} onChange={(e) => setDataArrayPath(e.target.value)}
placeholder="response (예: data.items, results)" placeholder="response (예: data.items, results)"
/> />
<p className="mt-1 text-xs text-gray-500"> <p className="mt-1 text-xs text-muted-foreground">
API . . API . .
<br /> <br />
예시: response, data.items, result.list 예시: response, data.items, result.list
@ -902,7 +902,7 @@ export default function BatchEditPage() {
className="min-h-[100px]" className="min-h-[100px]"
rows={5} rows={5}
/> />
<p className="mt-1 text-xs text-gray-500">API JSON .</p> <p className="mt-1 text-xs text-muted-foreground">API JSON .</p>
</div> </div>
)} )}
@ -910,7 +910,7 @@ export default function BatchEditPage() {
<div className="space-y-4"> <div className="space-y-4">
<div className="border-t pt-4"> <div className="border-t pt-4">
<Label className="text-base font-medium">API </Label> <Label className="text-base font-medium">API </Label>
<p className="mt-1 text-sm text-gray-600"> .</p> <p className="mt-1 text-sm text-muted-foreground"> .</p>
</div> </div>
<div> <div>
@ -967,26 +967,26 @@ export default function BatchEditPage() {
} }
/> />
{apiParamSource === "dynamic" && ( {apiParamSource === "dynamic" && (
<p className="mt-1 text-xs text-gray-500"> <p className="mt-1 text-xs text-muted-foreground">
. : {"{{user_id}}"} ID . : {"{{user_id}}"} ID
</p> </p>
)} )}
</div> </div>
{apiParamType === "url" && ( {apiParamType === "url" && (
<div className="rounded-lg bg-blue-50 p-3"> <div className="rounded-lg bg-primary/10 p-3">
<div className="text-sm font-medium text-blue-800">URL </div> <div className="text-sm font-medium text-primary">URL </div>
<div className="mt-1 text-sm text-blue-700"> <div className="mt-1 text-sm text-primary">
: /api/users/{`{${apiParamName || "userId"}}`} : /api/users/{`{${apiParamName || "userId"}}`}
</div> </div>
<div className="text-sm text-blue-700"> : /api/users/{apiParamValue || "123"}</div> <div className="text-sm text-primary"> : /api/users/{apiParamValue || "123"}</div>
</div> </div>
)} )}
{apiParamType === "query" && ( {apiParamType === "query" && (
<div className="rounded-lg bg-green-50 p-3"> <div className="rounded-lg bg-emerald-50 p-3">
<div className="text-sm font-medium text-green-800"> </div> <div className="text-sm font-medium text-emerald-800"> </div>
<div className="mt-1 text-sm text-green-700"> <div className="mt-1 text-sm text-emerald-700">
: {mappings[0]?.from_table_name || "/api/users"}?{apiParamName || "userId"}= : {mappings[0]?.from_table_name || "/api/users"}?{apiParamName || "userId"}=
{apiParamValue || "123"} {apiParamValue || "123"}
</div> </div>

View File

@ -294,7 +294,7 @@ export default function FlowEditorPage() {
onNodeDragStop={handleNodeDragStop} onNodeDragStop={handleNodeDragStop}
nodeTypes={nodeTypes} nodeTypes={nodeTypes}
fitView fitView
className="bg-gray-50" className="bg-muted"
> >
<Background /> <Background />
<Controls /> <Controls />

View File

@ -996,7 +996,7 @@ export default function FlowManagementPage() {
<div className="flex flex-col"> <div className="flex flex-col">
<span className="font-medium">{table.displayName || table.tableName}</span> <span className="font-medium">{table.displayName || table.tableName}</span>
{table.description && ( {table.description && (
<span className="text-[10px] text-gray-500">{table.description}</span> <span className="text-[10px] text-muted-foreground">{table.description}</span>
)} )}
</div> </div>
</CommandItem> </CommandItem>

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@ -592,19 +592,19 @@ export default function BatchManagementNewPage() {
<div <div
key={option.value} key={option.value}
className={`cursor-pointer rounded-lg border p-3 transition-all ${ className={`cursor-pointer rounded-lg border p-3 transition-all ${
batchType === option.value ? "border-blue-500 bg-blue-50" : "border-gray-200 hover:border-gray-300" batchType === option.value ? "border-primary bg-primary/10" : "border-border hover:border-input"
}`} }`}
onClick={() => setBatchType(option.value)} onClick={() => setBatchType(option.value)}
> >
<div className="flex items-center space-x-2"> <div className="flex items-center space-x-2">
{option.value === "restapi-to-db" ? ( {option.value === "restapi-to-db" ? (
<Globe className="h-4 w-4 text-blue-600" /> <Globe className="h-4 w-4 text-primary" />
) : ( ) : (
<Database className="h-4 w-4 text-green-600" /> <Database className="h-4 w-4 text-emerald-600" />
)} )}
<div> <div>
<div className="text-sm font-medium">{option.label}</div> <div className="text-sm font-medium">{option.label}</div>
<div className="mt-1 text-xs text-gray-500">{option.description}</div> <div className="mt-1 text-xs text-muted-foreground">{option.description}</div>
</div> </div>
</div> </div>
</div> </div>
@ -739,7 +739,7 @@ export default function BatchManagementNewPage() {
</SelectContent> </SelectContent>
</Select> </Select>
)} )}
<p className="mt-1 text-xs text-gray-500"> <p className="mt-1 text-xs text-muted-foreground">
{authTokenMode === "direct" {authTokenMode === "direct"
? "API 호출 시 Authorization 헤더에 사용할 토큰을 입력하세요." ? "API 호출 시 Authorization 헤더에 사용할 토큰을 입력하세요."
: "auth_tokens 테이블에서 선택한 서비스의 최신 토큰을 사용합니다."} : "auth_tokens 테이블에서 선택한 서비스의 최신 토큰을 사용합니다."}
@ -782,7 +782,7 @@ export default function BatchManagementNewPage() {
onChange={(e) => setDataArrayPath(e.target.value)} onChange={(e) => setDataArrayPath(e.target.value)}
placeholder="response (예: data.items, results)" placeholder="response (예: data.items, results)"
/> />
<p className="mt-1 text-xs text-gray-500"> <p className="mt-1 text-xs text-muted-foreground">
API . . API . .
<br /> <br />
예시: response, data.items, result.list 예시: response, data.items, result.list
@ -801,7 +801,7 @@ export default function BatchManagementNewPage() {
className="min-h-[100px]" className="min-h-[100px]"
rows={5} rows={5}
/> />
<p className="mt-1 text-xs text-gray-500">API JSON .</p> <p className="mt-1 text-xs text-muted-foreground">API JSON .</p>
</div> </div>
)} )}
@ -809,7 +809,7 @@ export default function BatchManagementNewPage() {
<div className="space-y-4"> <div className="space-y-4">
<div className="border-t pt-4"> <div className="border-t pt-4">
<Label className="text-base font-medium">API </Label> <Label className="text-base font-medium">API </Label>
<p className="mt-1 text-sm text-gray-600"> .</p> <p className="mt-1 text-sm text-muted-foreground"> .</p>
</div> </div>
<div> <div>
@ -868,26 +868,26 @@ export default function BatchManagementNewPage() {
} }
/> />
{apiParamSource === "dynamic" && ( {apiParamSource === "dynamic" && (
<p className="mt-1 text-xs text-gray-500"> <p className="mt-1 text-xs text-muted-foreground">
. : {"{{user_id}}"} ID . : {"{{user_id}}"} ID
</p> </p>
)} )}
</div> </div>
{apiParamType === "url" && ( {apiParamType === "url" && (
<div className="rounded-lg bg-blue-50 p-3"> <div className="rounded-lg bg-primary/10 p-3">
<div className="text-sm font-medium text-blue-800">URL </div> <div className="text-sm font-medium text-primary">URL </div>
<div className="mt-1 text-sm text-blue-700"> <div className="mt-1 text-sm text-primary">
: /api/users/{`{${apiParamName || "userId"}}`} : /api/users/{`{${apiParamName || "userId"}}`}
</div> </div>
<div className="text-sm text-blue-700"> : /api/users/{apiParamValue || "123"}</div> <div className="text-sm text-primary"> : /api/users/{apiParamValue || "123"}</div>
</div> </div>
)} )}
{apiParamType === "query" && ( {apiParamType === "query" && (
<div className="rounded-lg bg-green-50 p-3"> <div className="rounded-lg bg-emerald-50 p-3">
<div className="text-sm font-medium text-green-800"> </div> <div className="text-sm font-medium text-emerald-800"> </div>
<div className="mt-1 text-sm text-green-700"> <div className="mt-1 text-sm text-emerald-700">
: {fromEndpoint || "/api/users"}?{apiParamName || "userId"}= : {fromEndpoint || "/api/users"}?{apiParamName || "userId"}=
{apiParamValue || "123"} {apiParamValue || "123"}
</div> </div>
@ -899,9 +899,9 @@ export default function BatchManagementNewPage() {
{/* API 호출 미리보기 정보 */} {/* API 호출 미리보기 정보 */}
{fromApiUrl && fromEndpoint && ( {fromApiUrl && fromEndpoint && (
<div className="rounded-lg bg-gray-50 p-3"> <div className="rounded-lg bg-muted p-3">
<div className="text-sm font-medium text-gray-700">API </div> <div className="text-sm font-medium text-foreground">API </div>
<div className="mt-1 text-sm text-gray-600"> <div className="mt-1 text-sm text-muted-foreground">
{fromApiMethod} {fromApiUrl} {fromApiMethod} {fromApiUrl}
{apiParamType === "url" && apiParamName && apiParamValue {apiParamType === "url" && apiParamName && apiParamValue
? fromEndpoint.replace(`{${apiParamName}}`, apiParamValue) || fromEndpoint + `/${apiParamValue}` ? fromEndpoint.replace(`{${apiParamName}}`, apiParamValue) || fromEndpoint + `/${apiParamValue}`
@ -911,14 +911,14 @@ export default function BatchManagementNewPage() {
: ""} : ""}
</div> </div>
{((authTokenMode === "direct" && fromApiKey) || (authTokenMode === "db" && authServiceName)) && ( {((authTokenMode === "direct" && fromApiKey) || (authTokenMode === "db" && authServiceName)) && (
<div className="mt-1 text-xs text-gray-500"> <div className="mt-1 text-xs text-muted-foreground">
{authTokenMode === "direct" {authTokenMode === "direct"
? `Authorization: Bearer ${fromApiKey.substring(0, 15)}...` ? `Authorization: Bearer ${fromApiKey.substring(0, 15)}...`
: `Authorization: DB 토큰 (${authServiceName})`} : `Authorization: DB 토큰 (${authServiceName})`}
</div> </div>
)} )}
{apiParamType !== "none" && apiParamName && apiParamValue && ( {apiParamType !== "none" && apiParamName && apiParamValue && (
<div className="mt-1 text-xs text-blue-600"> <div className="mt-1 text-xs text-primary">
: {apiParamName} = {apiParamValue} ({apiParamSource === "static" ? "고정값" : "동적값"}) : {apiParamName} = {apiParamValue} ({apiParamSource === "static" ? "고정값" : "동적값"})
</div> </div>
)} )}
@ -988,7 +988,7 @@ export default function BatchManagementNewPage() {
setSelectedColumns(selectedColumns.filter((col) => col !== column.column_name)); setSelectedColumns(selectedColumns.filter((col) => col !== column.column_name));
} }
}} }}
className="rounded border-gray-300" className="rounded border-input"
/> />
<label <label
htmlFor={`col-${column.column_name}`} htmlFor={`col-${column.column_name}`}
@ -1002,14 +1002,14 @@ export default function BatchManagementNewPage() {
</div> </div>
{/* 선택된 컬럼 개수 표시 */} {/* 선택된 컬럼 개수 표시 */}
<div className="mt-2 text-xs text-gray-500"> <div className="mt-2 text-xs text-muted-foreground">
: {selectedColumns.length} / : {fromColumns.length} : {selectedColumns.length} / : {fromColumns.length}
</div> </div>
{/* 빠른 매핑 버튼들 */} {/* 빠른 매핑 버튼들 */}
{selectedColumns.length > 0 && toApiFields.length > 0 && ( {selectedColumns.length > 0 && toApiFields.length > 0 && (
<div className="mt-3 rounded-lg border border-green-200 bg-green-50 p-3"> <div className="mt-3 rounded-lg border border-emerald-200 bg-emerald-50 p-3">
<div className="mb-2 text-sm font-medium text-green-800"> </div> <div className="mb-2 text-sm font-medium text-emerald-800"> </div>
<div className="flex flex-wrap gap-2"> <div className="flex flex-wrap gap-2">
<button <button
type="button" type="button"
@ -1051,7 +1051,7 @@ export default function BatchManagementNewPage() {
setDbToApiFieldMapping(mapping); setDbToApiFieldMapping(mapping);
toast.success(`${Object.keys(mapping).length}개 컬럼이 자동 매핑되었습니다.`); toast.success(`${Object.keys(mapping).length}개 컬럼이 자동 매핑되었습니다.`);
}} }}
className="rounded bg-blue-600 px-3 py-1 text-xs text-white hover:bg-blue-700" className="rounded bg-primary px-3 py-1 text-xs text-white hover:bg-primary/90"
> >
</button> </button>
@ -1061,7 +1061,7 @@ export default function BatchManagementNewPage() {
setDbToApiFieldMapping({}); setDbToApiFieldMapping({});
toast.success("매핑이 초기화되었습니다."); toast.success("매핑이 초기화되었습니다.");
}} }}
className="rounded bg-gray-600 px-3 py-1 text-xs text-white hover:bg-gray-700" className="rounded bg-foreground/80 px-3 py-1 text-xs text-white hover:bg-foreground/90"
> >
</button> </button>
@ -1071,9 +1071,9 @@ export default function BatchManagementNewPage() {
{/* 자동 생성된 JSON 미리보기 */} {/* 자동 생성된 JSON 미리보기 */}
{selectedColumns.length > 0 && ( {selectedColumns.length > 0 && (
<div className="mt-3 rounded-lg border border-blue-200 bg-blue-50 p-3"> <div className="mt-3 rounded-lg border border-primary/20 bg-primary/10 p-3">
<div className="mb-2 text-sm font-medium text-blue-800"> JSON </div> <div className="mb-2 text-sm font-medium text-primary"> JSON </div>
<pre className="overflow-x-auto font-mono text-xs text-blue-600"> <pre className="overflow-x-auto font-mono text-xs text-primary">
{JSON.stringify( {JSON.stringify(
selectedColumns.reduce( selectedColumns.reduce(
(obj, col) => { (obj, col) => {
@ -1105,7 +1105,7 @@ export default function BatchManagementNewPage() {
setToApiBody(autoJson); setToApiBody(autoJson);
toast.success("Request Body에 자동 생성된 JSON이 적용되었습니다."); toast.success("Request Body에 자동 생성된 JSON이 적용되었습니다.");
}} }}
className="mt-2 rounded bg-blue-600 px-3 py-1 text-xs text-white hover:bg-blue-700" className="mt-2 rounded bg-primary px-3 py-1 text-xs text-white hover:bg-primary/90"
> >
Request Body에 Request Body에
</button> </button>
@ -1231,7 +1231,7 @@ export default function BatchManagementNewPage() {
))} ))}
</SelectContent> </SelectContent>
</Select> </Select>
<p className="mt-1 text-xs text-gray-500"> <p className="mt-1 text-xs text-muted-foreground">
UPDATE, INSERT . (: device_serial_number) UPDATE, INSERT . (: device_serial_number)
</p> </p>
</div> </div>
@ -1273,7 +1273,7 @@ export default function BatchManagementNewPage() {
placeholder="/api/users" placeholder="/api/users"
/> />
{(toApiMethod === "PUT" || toApiMethod === "DELETE") && ( {(toApiMethod === "PUT" || toApiMethod === "DELETE") && (
<p className="mt-1 text-xs text-gray-500"> <p className="mt-1 text-xs text-muted-foreground">
URL: {toEndpoint}/{urlPathColumn ? `{${urlPathColumn}}` : "{ID}"} URL: {toEndpoint}/{urlPathColumn ? `{${urlPathColumn}}` : "{ID}"}
</p> </p>
)} )}
@ -1310,7 +1310,7 @@ export default function BatchManagementNewPage() {
))} ))}
</SelectContent> </SelectContent>
</Select> </Select>
<p className="mt-1 text-xs text-gray-500"> <p className="mt-1 text-xs text-muted-foreground">
PUT/DELETE URL . (: USER_ID /api/users/user123) PUT/DELETE URL . (: USER_ID /api/users/user123)
</p> </p>
</div> </div>
@ -1321,7 +1321,7 @@ export default function BatchManagementNewPage() {
<button <button
type="button" type="button"
onClick={previewToApiData} onClick={previewToApiData}
className="flex items-center space-x-2 rounded-md bg-green-600 px-4 py-2 text-white hover:bg-green-700" className="flex items-center space-x-2 rounded-md bg-emerald-600 px-4 py-2 text-white hover:bg-green-700"
> >
<Eye className="h-4 w-4" /> <Eye className="h-4 w-4" />
<span>API </span> <span>API </span>
@ -1330,13 +1330,13 @@ export default function BatchManagementNewPage() {
{/* TO API 필드 표시 */} {/* TO API 필드 표시 */}
{toApiFields.length > 0 && ( {toApiFields.length > 0 && (
<div className="rounded-lg border border-green-200 bg-green-50 p-3"> <div className="rounded-lg border border-emerald-200 bg-emerald-50 p-3">
<div className="mb-2 text-sm font-medium text-green-800"> <div className="mb-2 text-sm font-medium text-emerald-800">
API ({toApiFields.length}) API ({toApiFields.length})
</div> </div>
<div className="flex flex-wrap gap-2"> <div className="flex flex-wrap gap-2">
{toApiFields.map((field) => ( {toApiFields.map((field) => (
<span key={field} className="rounded bg-green-100 px-2 py-1 text-xs text-green-700"> <span key={field} className="rounded bg-emerald-100 px-2 py-1 text-xs text-emerald-700">
{field} {field}
</span> </span>
))} ))}
@ -1355,7 +1355,7 @@ export default function BatchManagementNewPage() {
placeholder='{"id": "{{id}}", "name": "{{name}}", "email": "{{email}}"}' placeholder='{"id": "{{id}}", "name": "{{name}}", "email": "{{email}}"}'
className="h-24 w-full rounded-md border p-2 font-mono text-sm" className="h-24 w-full rounded-md border p-2 font-mono text-sm"
/> />
<div className="mt-1 text-xs text-gray-500"> <div className="mt-1 text-xs text-muted-foreground">
DB {"{{컬럼명}}"} . : {"{{user_id}}, {{user_name}}"} DB {"{{컬럼명}}"} . : {"{{user_id}}, {{user_name}}"}
</div> </div>
</div> </div>
@ -1363,15 +1363,15 @@ export default function BatchManagementNewPage() {
{/* API 호출 정보 */} {/* API 호출 정보 */}
{toApiUrl && toApiKey && toEndpoint && ( {toApiUrl && toApiKey && toEndpoint && (
<div className="rounded-lg bg-gray-50 p-3"> <div className="rounded-lg bg-muted p-3">
<div className="text-sm font-medium text-gray-700">API </div> <div className="text-sm font-medium text-foreground">API </div>
<div className="mt-1 text-sm text-gray-600"> <div className="mt-1 text-sm text-muted-foreground">
{toApiMethod} {toApiUrl} {toApiMethod} {toApiUrl}
{toEndpoint} {toEndpoint}
</div> </div>
<div className="mt-1 text-xs text-gray-500">Headers: X-API-Key: {toApiKey.substring(0, 10)}...</div> <div className="mt-1 text-xs text-muted-foreground">Headers: X-API-Key: {toApiKey.substring(0, 10)}...</div>
{toApiBody && ( {toApiBody && (
<div className="mt-1 text-xs text-blue-600">Body: {toApiBody.substring(0, 50)}...</div> <div className="mt-1 text-xs text-primary">Body: {toApiBody.substring(0, 50)}...</div>
)} )}
</div> </div>
)} )}
@ -1394,7 +1394,7 @@ export default function BatchManagementNewPage() {
</Button> </Button>
{(!fromApiUrl || !fromEndpoint || !toTable) && ( {(!fromApiUrl || !fromEndpoint || !toTable) && (
<p className="ml-4 flex items-center text-xs text-gray-500"> <p className="ml-4 flex items-center text-xs text-muted-foreground">
FROM TO . FROM TO .
</p> </p>
)} )}
@ -1693,17 +1693,17 @@ const DbToRestApiMappingCard = memo(function DbToRestApiMappingCard({
<CardContent> <CardContent>
<div className="max-h-96 space-y-3 overflow-y-auto rounded-lg border p-4"> <div className="max-h-96 space-y-3 overflow-y-auto rounded-lg border p-4">
{selectedColumnObjects.map((column) => ( {selectedColumnObjects.map((column) => (
<div key={column.column_name} className="flex items-center space-x-4 rounded-lg bg-gray-50 p-3"> <div key={column.column_name} className="flex items-center space-x-4 rounded-lg bg-muted p-3">
{/* DB 컬럼 정보 */} {/* DB 컬럼 정보 */}
<div className="flex-1"> <div className="flex-1">
<div className="text-sm font-medium">{column.column_name}</div> <div className="text-sm font-medium">{column.column_name}</div>
<div className="text-xs text-gray-500"> <div className="text-xs text-muted-foreground">
: {column.data_type} | NULL: {column.is_nullable ? "Y" : "N"} : {column.data_type} | NULL: {column.is_nullable ? "Y" : "N"}
</div> </div>
</div> </div>
{/* 화살표 */} {/* 화살표 */}
<div className="text-gray-400"></div> <div className="text-muted-foreground/70"></div>
{/* API 필드 선택 드롭다운 */} {/* API 필드 선택 드롭다운 */}
<div className="flex-1"> <div className="flex-1">
@ -1735,7 +1735,7 @@ const DbToRestApiMappingCard = memo(function DbToRestApiMappingCard({
<input <input
type="text" type="text"
placeholder="API 필드명을 직접 입력하세요" placeholder="API 필드명을 직접 입력하세요"
className="mt-2 w-full rounded-md border border-gray-300 px-3 py-2 text-sm focus:ring-2 focus:ring-blue-500 focus:outline-none" className="mt-2 w-full rounded-md border border-input px-3 py-2 text-sm focus:ring-2 focus:ring-ring focus:outline-none"
onChange={(e) => { onChange={(e) => {
setDbToApiFieldMapping((prev) => ({ setDbToApiFieldMapping((prev) => ({
...prev, ...prev,
@ -1745,7 +1745,7 @@ const DbToRestApiMappingCard = memo(function DbToRestApiMappingCard({
/> />
)} )}
<div className="mt-1 text-xs text-gray-500"> <div className="mt-1 text-xs text-muted-foreground">
{dbToApiFieldMapping[column.column_name] {dbToApiFieldMapping[column.column_name]
? `매핑: ${column.column_name}${dbToApiFieldMapping[column.column_name]}` ? `매핑: ${column.column_name}${dbToApiFieldMapping[column.column_name]}`
: `기본값: ${column.column_name} (DB 컬럼명 사용)`} : `기본값: ${column.column_name} (DB 컬럼명 사용)`}
@ -1755,32 +1755,32 @@ const DbToRestApiMappingCard = memo(function DbToRestApiMappingCard({
{/* 템플릿 미리보기 */} {/* 템플릿 미리보기 */}
<div className="flex-1"> <div className="flex-1">
<div className="rounded border bg-white p-2 font-mono text-sm">{`{{${column.column_name}}}`}</div> <div className="rounded border bg-white p-2 font-mono text-sm">{`{{${column.column_name}}}`}</div>
<div className="mt-1 text-xs text-gray-500"> DB </div> <div className="mt-1 text-xs text-muted-foreground"> DB </div>
</div> </div>
</div> </div>
))} ))}
</div> </div>
{selectedColumns.length > 0 && ( {selectedColumns.length > 0 && (
<div className="mt-4 rounded-lg border border-blue-200 bg-blue-50 p-3"> <div className="mt-4 rounded-lg border border-primary/20 bg-primary/10 p-3">
<div className="text-sm font-medium text-blue-800"> JSON </div> <div className="text-sm font-medium text-primary"> JSON </div>
<pre className="mt-1 overflow-x-auto font-mono text-xs text-blue-600">{autoJsonPreview}</pre> <pre className="mt-1 overflow-x-auto font-mono text-xs text-primary">{autoJsonPreview}</pre>
<button <button
type="button" type="button"
onClick={() => { onClick={() => {
setToApiBody(autoJsonPreview); setToApiBody(autoJsonPreview);
toast.success("Request Body에 자동 생성된 JSON이 적용되었습니다."); toast.success("Request Body에 자동 생성된 JSON이 적용되었습니다.");
}} }}
className="mt-2 rounded bg-blue-600 px-3 py-1 text-xs text-white hover:bg-blue-700" className="mt-2 rounded bg-primary px-3 py-1 text-xs text-white hover:bg-primary/90"
> >
Request Body에 Request Body에
</button> </button>
</div> </div>
)} )}
<div className="mt-4 rounded-lg border border-blue-200 bg-blue-50 p-3"> <div className="mt-4 rounded-lg border border-primary/20 bg-primary/10 p-3">
<div className="text-sm font-medium text-blue-800"> </div> <div className="text-sm font-medium text-primary"> </div>
<div className="mt-1 font-mono text-xs text-blue-600"> <div className="mt-1 font-mono text-xs text-primary">
{'{"id": "{{id}}", "name": "{{user_name}}", "email": "{{email}}"}'} {'{"id": "{{id}}", "name": "{{user_name}}", "email": "{{email}}"}'}
</div> </div>
</div> </div>

View File

@ -381,7 +381,7 @@ export default function AutoFillTab() {
<Pencil className="h-4 w-4" /> <Pencil className="h-4 w-4" />
</Button> </Button>
<Button variant="ghost" size="icon" onClick={() => handleDeleteConfirm(group.groupCode)}> <Button variant="ghost" size="icon" onClick={() => handleDeleteConfirm(group.groupCode)}>
<Trash2 className="h-4 w-4 text-red-500" /> <Trash2 className="h-4 w-4 text-destructive" />
</Button> </Button>
</TableCell> </TableCell>
</TableRow> </TableRow>
@ -676,7 +676,7 @@ export default function AutoFillTab() {
</AlertDialogHeader> </AlertDialogHeader>
<AlertDialogFooter> <AlertDialogFooter>
<AlertDialogCancel></AlertDialogCancel> <AlertDialogCancel></AlertDialogCancel>
<AlertDialogAction onClick={handleDelete} className="bg-red-500 hover:bg-red-600"> <AlertDialogAction onClick={handleDelete} className="bg-destructive hover:bg-destructive">
</AlertDialogAction> </AlertDialogAction>
</AlertDialogFooter> </AlertDialogFooter>

View File

@ -577,9 +577,9 @@ export default function CascadingRelationsTab() {
</TableCell> </TableCell>
<TableCell> <TableCell>
<div className="flex items-center gap-2 text-sm"> <div className="flex items-center gap-2 text-sm">
<span className="rounded bg-blue-100 px-2 py-0.5 text-blue-700">{relation.parent_table}</span> <span className="rounded bg-primary/10 px-2 py-0.5 text-primary">{relation.parent_table}</span>
<ChevronRight className="text-muted-foreground h-4 w-4" /> <ChevronRight className="text-muted-foreground h-4 w-4" />
<span className="rounded bg-green-100 px-2 py-0.5 text-green-700"> <span className="rounded bg-emerald-100 px-2 py-0.5 text-emerald-700">
{relation.child_table} {relation.child_table}
</span> </span>
</div> </div>
@ -619,7 +619,7 @@ export default function CascadingRelationsTab() {
<div className="space-y-4"> <div className="space-y-4">
{/* Step 1: 부모 테이블 */} {/* Step 1: 부모 테이블 */}
<div className="rounded-lg border p-4"> <div className="rounded-lg border p-4">
<h4 className="mb-3 text-sm font-semibold text-blue-600">1. ( )</h4> <h4 className="mb-3 text-sm font-semibold text-primary">1. ( )</h4>
<div className="grid grid-cols-2 gap-3"> <div className="grid grid-cols-2 gap-3">
<div className="space-y-1.5"> <div className="space-y-1.5">
<Label className="text-xs"></Label> <Label className="text-xs"></Label>
@ -696,7 +696,7 @@ export default function CascadingRelationsTab() {
{/* Step 2: 자식 테이블 */} {/* Step 2: 자식 테이블 */}
<div className="rounded-lg border p-4"> <div className="rounded-lg border p-4">
<h4 className="mb-3 text-sm font-semibold text-green-600">2. ( )</h4> <h4 className="mb-3 text-sm font-semibold text-emerald-600">2. ( )</h4>
<div className="grid grid-cols-2 gap-3"> <div className="grid grid-cols-2 gap-3">
<div className="space-y-1.5"> <div className="space-y-1.5">
<Label className="text-xs"></Label> <Label className="text-xs"></Label>

View File

@ -304,7 +304,7 @@ export default function ConditionTab() {
<TableCell> <TableCell>
<div className="text-sm"> <div className="text-sm">
<span className="text-muted-foreground">{condition.conditionField}</span> <span className="text-muted-foreground">{condition.conditionField}</span>
<span className="mx-1 text-blue-600">{getOperatorLabel(condition.conditionOperator)}</span> <span className="mx-1 text-primary">{getOperatorLabel(condition.conditionOperator)}</span>
<span className="font-medium">{condition.conditionValue}</span> <span className="font-medium">{condition.conditionValue}</span>
</div> </div>
</TableCell> </TableCell>
@ -329,7 +329,7 @@ export default function ConditionTab() {
size="icon" size="icon"
onClick={() => handleDeleteConfirm(condition.conditionId!)} onClick={() => handleDeleteConfirm(condition.conditionId!)}
> >
<Trash2 className="h-4 w-4 text-red-500" /> <Trash2 className="h-4 w-4 text-destructive" />
</Button> </Button>
</TableCell> </TableCell>
</TableRow> </TableRow>
@ -491,7 +491,7 @@ export default function ConditionTab() {
</AlertDialogHeader> </AlertDialogHeader>
<AlertDialogFooter> <AlertDialogFooter>
<AlertDialogCancel></AlertDialogCancel> <AlertDialogCancel></AlertDialogCancel>
<AlertDialogAction onClick={handleDelete} className="bg-red-500 hover:bg-red-600"> <AlertDialogAction onClick={handleDelete} className="bg-destructive hover:bg-destructive">
</AlertDialogAction> </AlertDialogAction>
</AlertDialogFooter> </AlertDialogFooter>

View File

@ -501,7 +501,7 @@ export default function HierarchyTab() {
<Pencil className="h-4 w-4" /> <Pencil className="h-4 w-4" />
</Button> </Button>
<Button variant="ghost" size="icon" onClick={() => handleDeleteConfirm(group.groupCode)}> <Button variant="ghost" size="icon" onClick={() => handleDeleteConfirm(group.groupCode)}>
<Trash2 className="h-4 w-4 text-red-500" /> <Trash2 className="h-4 w-4 text-destructive" />
</Button> </Button>
</div> </div>
</div> </div>
@ -548,7 +548,7 @@ export default function HierarchyTab() {
size="icon" size="icon"
onClick={() => handleDeleteLevel(level.levelId!, group.groupCode)} onClick={() => handleDeleteLevel(level.levelId!, group.groupCode)}
> >
<Trash2 className="h-4 w-4 text-red-500" /> <Trash2 className="h-4 w-4 text-destructive" />
</Button> </Button>
</TableCell> </TableCell>
</TableRow> </TableRow>
@ -836,7 +836,7 @@ export default function HierarchyTab() {
</AlertDialogHeader> </AlertDialogHeader>
<AlertDialogFooter> <AlertDialogFooter>
<AlertDialogCancel></AlertDialogCancel> <AlertDialogCancel></AlertDialogCancel>
<AlertDialogAction onClick={handleDelete} className="bg-red-500 hover:bg-red-600"> <AlertDialogAction onClick={handleDelete} className="bg-destructive hover:bg-destructive">
</AlertDialogAction> </AlertDialogAction>
</AlertDialogFooter> </AlertDialogFooter>

View File

@ -354,7 +354,7 @@ export default function MutualExclusionTab() {
<Pencil className="h-4 w-4" /> <Pencil className="h-4 w-4" />
</Button> </Button>
<Button variant="ghost" size="icon" onClick={() => handleDeleteConfirm(exclusion.exclusionId!)}> <Button variant="ghost" size="icon" onClick={() => handleDeleteConfirm(exclusion.exclusionId!)}>
<Trash2 className="h-4 w-4 text-red-500" /> <Trash2 className="h-4 w-4 text-destructive" />
</Button> </Button>
</TableCell> </TableCell>
</TableRow> </TableRow>
@ -404,7 +404,7 @@ export default function MutualExclusionTab() {
/> />
{fieldList.length > 2 && ( {fieldList.length > 2 && (
<Button variant="ghost" size="icon" onClick={() => removeField(index)}> <Button variant="ghost" size="icon" onClick={() => removeField(index)}>
<Trash2 className="h-4 w-4 text-red-500" /> <Trash2 className="h-4 w-4 text-destructive" />
</Button> </Button>
)} )}
</div> </div>
@ -571,7 +571,7 @@ export default function MutualExclusionTab() {
</AlertDialogHeader> </AlertDialogHeader>
<AlertDialogFooter> <AlertDialogFooter>
<AlertDialogCancel></AlertDialogCancel> <AlertDialogCancel></AlertDialogCancel>
<AlertDialogAction onClick={handleDelete} className="bg-red-500 hover:bg-red-600"> <AlertDialogAction onClick={handleDelete} className="bg-destructive hover:bg-destructive">
</AlertDialogAction> </AlertDialogAction>
</AlertDialogFooter> </AlertDialogFooter>

View File

@ -133,7 +133,7 @@ export default function DebugLayoutPage() {
<h1 className="mb-4 text-2xl font-bold"> </h1> <h1 className="mb-4 text-2xl font-bold"> </h1>
<div className="space-y-4"> <div className="space-y-4">
<div className="rounded bg-green-100 p-4"> <div className="rounded bg-emerald-100 p-4">
<h2 className="mb-2 font-semibold"> </h2> <h2 className="mb-2 font-semibold"> </h2>
<p> : {debugInfo.hasToken ? "✅ 예" : "❌ 아니오"}</p> <p> : {debugInfo.hasToken ? "✅ 예" : "❌ 아니오"}</p>
<p> : {debugInfo.tokenLength}</p> <p> : {debugInfo.tokenLength}</p>
@ -142,14 +142,14 @@ export default function DebugLayoutPage() {
<p>SessionStorage : {debugInfo.sessionToken ? "✅ 존재" : "❌ 없음"}</p> <p>SessionStorage : {debugInfo.sessionToken ? "✅ 존재" : "❌ 없음"}</p>
</div> </div>
<div className="rounded bg-blue-100 p-4"> <div className="rounded bg-primary/10 p-4">
<h2 className="mb-2 font-semibold"> </h2> <h2 className="mb-2 font-semibold"> </h2>
<p> URL: {debugInfo.currentUrl}</p> <p> URL: {debugInfo.currentUrl}</p>
<p>Pathname: {debugInfo.pathname}</p> <p>Pathname: {debugInfo.pathname}</p>
<p>: {debugInfo.timestamp}</p> <p>: {debugInfo.timestamp}</p>
</div> </div>
<div className="rounded bg-yellow-100 p-4"> <div className="rounded bg-amber-100 p-4">
<h2 className="mb-2 font-semibold"> </h2> <h2 className="mb-2 font-semibold"> </h2>
<div className="space-x-2"> <div className="space-x-2">
<button <button
@ -157,11 +157,11 @@ export default function DebugLayoutPage() {
const token = localStorage.getItem("authToken"); const token = localStorage.getItem("authToken");
alert(`토큰: ${token ? "존재" : "없음"}\n길이: ${token ? token.length : 0}`); alert(`토큰: ${token ? "존재" : "없음"}\n길이: ${token ? token.length : 0}`);
}} }}
className="rounded bg-blue-500 px-4 py-2 text-white hover:bg-blue-600" className="rounded bg-primary px-4 py-2 text-white hover:bg-primary"
> >
</button> </button>
<button onClick={handleTokenSync} className="rounded bg-green-500 px-4 py-2 text-white hover:bg-green-600"> <button onClick={handleTokenSync} className="rounded bg-emerald-500 px-4 py-2 text-white hover:bg-emerald-600">
</button> </button>
<button <button
@ -173,13 +173,13 @@ export default function DebugLayoutPage() {
</div> </div>
</div> </div>
<div className="rounded bg-orange-100 p-4"> <div className="rounded bg-amber-100 p-4">
<h2 className="mb-2 font-semibold">API </h2> <h2 className="mb-2 font-semibold">API </h2>
<div className="mb-4 space-x-2"> <div className="mb-4 space-x-2">
<button onClick={handleApiTest} className="rounded bg-orange-500 px-4 py-2 text-white hover:bg-orange-600"> <button onClick={handleApiTest} className="rounded bg-amber-500 px-4 py-2 text-white hover:bg-orange-600">
API API
</button> </button>
<button onClick={handleUserApiTest} className="rounded bg-red-500 px-4 py-2 text-white hover:bg-red-600"> <button onClick={handleUserApiTest} className="rounded bg-destructive px-4 py-2 text-white hover:bg-destructive">
API API
</button> </button>
<button <button
@ -194,7 +194,7 @@ export default function DebugLayoutPage() {
<div <div
className={`rounded p-3 ${ className={`rounded p-3 ${
apiTestResult.status === "success" apiTestResult.status === "success"
? "bg-green-200" ? "bg-emerald-200"
: apiTestResult.status === "error" : apiTestResult.status === "error"
? "bg-red-200" ? "bg-red-200"
: "bg-yellow-200" : "bg-yellow-200"

View File

@ -28,27 +28,27 @@ export default function SimpleDebugPage() {
<h1 className="mb-4 text-2xl font-bold"> </h1> <h1 className="mb-4 text-2xl font-bold"> </h1>
<div className="space-y-4"> <div className="space-y-4">
<div className="rounded bg-green-100 p-4"> <div className="rounded bg-emerald-100 p-4">
<h2 className="mb-2 font-semibold"> </h2> <h2 className="mb-2 font-semibold"> </h2>
<p> : {tokenInfo.hasToken ? "✅ 예" : "❌ 아니오"}</p> <p> : {tokenInfo.hasToken ? "✅ 예" : "❌ 아니오"}</p>
<p> : {tokenInfo.tokenLength}</p> <p> : {tokenInfo.tokenLength}</p>
<p> : {tokenInfo.tokenStart}</p> <p> : {tokenInfo.tokenStart}</p>
</div> </div>
<div className="rounded bg-blue-100 p-4"> <div className="rounded bg-primary/10 p-4">
<h2 className="mb-2 font-semibold"> </h2> <h2 className="mb-2 font-semibold"> </h2>
<p> URL: {tokenInfo.currentUrl}</p> <p> URL: {tokenInfo.currentUrl}</p>
<p>: {tokenInfo.timestamp}</p> <p>: {tokenInfo.timestamp}</p>
</div> </div>
<div className="rounded bg-yellow-100 p-4"> <div className="rounded bg-amber-100 p-4">
<h2 className="mb-2 font-semibold"> </h2> <h2 className="mb-2 font-semibold"> </h2>
<button <button
onClick={() => { onClick={() => {
const token = localStorage.getItem("authToken"); const token = localStorage.getItem("authToken");
alert(`토큰: ${token ? "존재" : "없음"}\n길이: ${token ? token.length : 0}`); alert(`토큰: ${token ? "존재" : "없음"}\n길이: ${token ? token.length : 0}`);
}} }}
className="rounded bg-blue-500 px-4 py-2 text-white hover:bg-blue-600" className="rounded bg-primary px-4 py-2 text-white hover:bg-primary"
> >
</button> </button>

View File

@ -15,16 +15,16 @@ export default function AdminDebugPage() {
<h1 className="mb-4 text-2xl font-bold"> </h1> <h1 className="mb-4 text-2xl font-bold"> </h1>
<div className="space-y-4"> <div className="space-y-4">
<div className="rounded bg-gray-100 p-4"> <div className="rounded bg-muted p-4">
<h2 className="mb-2 font-semibold"> </h2> <h2 className="mb-2 font-semibold"> </h2>
<p>: {loading ? "예" : "아니오"}</p> <p>: {loading ? "예" : "아니오"}</p>
<p>: {isLoggedIn ? "예" : "아니오"}</p> <p>: {isLoggedIn ? "예" : "아니오"}</p>
<p>: {isAdmin ? "예" : "아니오"}</p> <p>: {isAdmin ? "예" : "아니오"}</p>
{error && <p className="text-red-500">: {error}</p>} {error && <p className="text-destructive">: {error}</p>}
</div> </div>
{user && ( {user && (
<div className="rounded bg-blue-100 p-4"> <div className="rounded bg-primary/10 p-4">
<h2 className="mb-2 font-semibold"> </h2> <h2 className="mb-2 font-semibold"> </h2>
<p>ID: {user.userId}</p> <p>ID: {user.userId}</p>
<p>: {user.userName}</p> <p>: {user.userName}</p>
@ -34,7 +34,7 @@ export default function AdminDebugPage() {
</div> </div>
)} )}
<div className="rounded bg-green-100 p-4"> <div className="rounded bg-emerald-100 p-4">
<h2 className="mb-2 font-semibold"> </h2> <h2 className="mb-2 font-semibold"> </h2>
<p> <p>
localStorage : {typeof window !== "undefined" && localStorage.getItem("authToken") ? "존재" : "없음"} localStorage : {typeof window !== "undefined" && localStorage.getItem("authToken") ? "존재" : "없음"}

View File

@ -220,13 +220,13 @@ export default function LayoutManagementPage() {
}; };
return ( return (
<div className="min-h-screen bg-gray-50"> <div className="min-h-screen bg-muted">
<div className="w-full max-w-none px-4 py-8 space-y-8"> <div className="w-full max-w-none px-4 py-8 space-y-8">
{/* 페이지 제목 */} {/* 페이지 제목 */}
<div className="flex items-center justify-between bg-white rounded-lg shadow-sm border p-6"> <div className="flex items-center justify-between bg-white rounded-lg shadow-sm border p-6">
<div> <div>
<h1 className="text-3xl font-bold text-gray-900"> </h1> <h1 className="text-3xl font-bold text-foreground"> </h1>
<p className="mt-2 text-gray-600"> </p> <p className="mt-2 text-muted-foreground"> </p>
</div> </div>
<Button className="flex items-center gap-2 shadow-sm" onClick={() => setCreateModalOpen(true)}> <Button className="flex items-center gap-2 shadow-sm" onClick={() => setCreateModalOpen(true)}>
<Plus className="h-4 w-4" /> <Plus className="h-4 w-4" />
@ -239,7 +239,7 @@ export default function LayoutManagementPage() {
<div className="flex items-center gap-4"> <div className="flex items-center gap-4">
<div className="flex-1"> <div className="flex-1">
<div className="relative"> <div className="relative">
<Search className="absolute top-1/2 left-3 h-4 w-4 -translate-y-1/2 transform text-gray-400" /> <Search className="absolute top-1/2 left-3 h-4 w-4 -translate-y-1/2 transform text-muted-foreground/70" />
<Input <Input
placeholder="레이아웃 이름 또는 설명으로 검색..." placeholder="레이아웃 이름 또는 설명으로 검색..."
value={searchTerm} value={searchTerm}
@ -276,7 +276,7 @@ export default function LayoutManagementPage() {
{loading ? ( {loading ? (
<div className="py-8 text-center"> ...</div> <div className="py-8 text-center"> ...</div>
) : layouts.length === 0 ? ( ) : layouts.length === 0 ? (
<div className="py-8 text-center text-gray-500"> .</div> <div className="py-8 text-center text-muted-foreground"> .</div>
) : ( ) : (
<> <>
{/* 레이아웃 그리드 */} {/* 레이아웃 그리드 */}
@ -288,7 +288,7 @@ export default function LayoutManagementPage() {
<CardHeader className="pb-3"> <CardHeader className="pb-3">
<div className="flex items-start justify-between"> <div className="flex items-start justify-between">
<div className="flex items-center gap-2"> <div className="flex items-center gap-2">
<CategoryIcon className="h-5 w-5 text-gray-600" /> <CategoryIcon className="h-5 w-5 text-muted-foreground" />
<Badge variant="secondary"> <Badge variant="secondary">
{CATEGORY_NAMES[layout.category as keyof typeof CATEGORY_NAMES]} {CATEGORY_NAMES[layout.category as keyof typeof CATEGORY_NAMES]}
</Badge> </Badge>
@ -312,7 +312,7 @@ export default function LayoutManagementPage() {
<Copy className="mr-2 h-4 w-4" /> <Copy className="mr-2 h-4 w-4" />
</DropdownMenuItem> </DropdownMenuItem>
<DropdownMenuItem onClick={() => handleDelete(layout)} className="text-red-600"> <DropdownMenuItem onClick={() => handleDelete(layout)} className="text-destructive">
<Trash2 className="mr-2 h-4 w-4" /> <Trash2 className="mr-2 h-4 w-4" />
</DropdownMenuItem> </DropdownMenuItem>
@ -321,17 +321,17 @@ export default function LayoutManagementPage() {
</div> </div>
<CardTitle className="text-lg">{layout.layoutName}</CardTitle> <CardTitle className="text-lg">{layout.layoutName}</CardTitle>
{layout.description && ( {layout.description && (
<p className="line-clamp-2 text-sm text-gray-600">{layout.description}</p> <p className="line-clamp-2 text-sm text-muted-foreground">{layout.description}</p>
)} )}
</CardHeader> </CardHeader>
<CardContent> <CardContent>
<div className="space-y-2"> <div className="space-y-2">
<div className="flex items-center justify-between text-sm"> <div className="flex items-center justify-between text-sm">
<span className="text-gray-500">:</span> <span className="text-muted-foreground">:</span>
<Badge variant="outline">{layout.layoutType}</Badge> <Badge variant="outline">{layout.layoutType}</Badge>
</div> </div>
<div className="flex items-center justify-between text-sm"> <div className="flex items-center justify-between text-sm">
<span className="text-gray-500"> :</span> <span className="text-muted-foreground"> :</span>
<span>{layout.zonesConfig.length}</span> <span>{layout.zonesConfig.length}</span>
</div> </div>
{layout.isPublic === "Y" && ( {layout.isPublic === "Y" && (
@ -397,7 +397,7 @@ export default function LayoutManagementPage() {
</AlertDialogHeader> </AlertDialogHeader>
<AlertDialogFooter> <AlertDialogFooter>
<AlertDialogCancel></AlertDialogCancel> <AlertDialogCancel></AlertDialogCancel>
<AlertDialogAction onClick={confirmDelete} className="bg-red-600 hover:bg-red-700"> <AlertDialogAction onClick={confirmDelete} className="bg-destructive hover:bg-red-700">
</AlertDialogAction> </AlertDialogAction>
</AlertDialogFooter> </AlertDialogFooter>

View File

@ -59,25 +59,25 @@ export default function MonitoringPage() {
const getStatusIcon = (status: string) => { const getStatusIcon = (status: string) => {
switch (status) { switch (status) {
case 'completed': case 'completed':
return <CheckCircle className="h-4 w-4 text-green-500" />; return <CheckCircle className="h-4 w-4 text-emerald-500" />;
case 'failed': case 'failed':
return <AlertCircle className="h-4 w-4 text-red-500" />; return <AlertCircle className="h-4 w-4 text-destructive" />;
case 'running': case 'running':
return <Play className="h-4 w-4 text-blue-500" />; return <Play className="h-4 w-4 text-primary" />;
case 'pending': case 'pending':
return <Clock className="h-4 w-4 text-yellow-500" />; return <Clock className="h-4 w-4 text-amber-500" />;
default: default:
return <Clock className="h-4 w-4 text-gray-500" />; return <Clock className="h-4 w-4 text-muted-foreground" />;
} }
}; };
const getStatusBadge = (status: string) => { const getStatusBadge = (status: string) => {
const variants = { const variants = {
completed: "bg-green-100 text-green-800", completed: "bg-emerald-100 text-emerald-800",
failed: "bg-destructive/20 text-red-800", failed: "bg-destructive/20 text-red-800",
running: "bg-primary/20 text-blue-800", running: "bg-primary/20 text-primary",
pending: "bg-yellow-100 text-yellow-800", pending: "bg-amber-100 text-yellow-800",
cancelled: "bg-gray-100 text-gray-800", cancelled: "bg-muted text-foreground",
}; };
const labels = { const labels = {
@ -120,7 +120,7 @@ export default function MonitoringPage() {
} }
return ( return (
<div className="min-h-screen bg-gray-50"> <div className="min-h-screen bg-muted">
<div className="w-full max-w-none px-4 py-8 space-y-8"> <div className="w-full max-w-none px-4 py-8 space-y-8">
{/* 헤더 */} {/* 헤더 */}
<div> <div>
@ -191,7 +191,7 @@ export default function MonitoringPage() {
<div className="text-2xl"></div> <div className="text-2xl"></div>
</CardHeader> </CardHeader>
<CardContent> <CardContent>
<div className="text-2xl font-bold text-green-600">{monitoring.successful_jobs_today}</div> <div className="text-2xl font-bold text-emerald-600">{monitoring.successful_jobs_today}</div>
<p className="text-xs text-muted-foreground"> <p className="text-xs text-muted-foreground">
: {getSuccessRate()}% : {getSuccessRate()}%
</p> </p>

View File

@ -16,7 +16,7 @@ export default function BarcodeLabelDesignerPage() {
return ( return (
<DndProvider backend={HTML5Backend}> <DndProvider backend={HTML5Backend}>
<BarcodeDesignerProvider labelId={labelId}> <BarcodeDesignerProvider labelId={labelId}>
<div className="flex h-screen flex-col overflow-hidden bg-gray-50"> <div className="flex h-screen flex-col overflow-hidden bg-muted">
<BarcodeDesignerToolbar /> <BarcodeDesignerToolbar />
<div className="flex flex-1 overflow-hidden"> <div className="flex flex-1 overflow-hidden">
<BarcodeDesignerLeftPanel /> <BarcodeDesignerLeftPanel />

View File

@ -29,12 +29,12 @@ export default function BarcodeLabelManagementPage() {
}; };
return ( return (
<div className="min-h-screen bg-gray-50"> <div className="min-h-screen bg-muted">
<div className="w-full max-w-none space-y-8 px-4 py-8"> <div className="w-full max-w-none space-y-8 px-4 py-8">
<div className="flex items-center justify-between rounded-lg border bg-white p-6 shadow-sm"> <div className="flex items-center justify-between rounded-lg border bg-white p-6 shadow-sm">
<div> <div>
<h1 className="text-3xl font-bold text-gray-900"> </h1> <h1 className="text-3xl font-bold text-foreground"> </h1>
<p className="mt-2 text-gray-600">ZD421 </p> <p className="mt-2 text-muted-foreground">ZD421 </p>
</div> </div>
<Button onClick={handleCreateNew} className="gap-2"> <Button onClick={handleCreateNew} className="gap-2">
<Plus className="h-4 w-4" /> <Plus className="h-4 w-4" />
@ -43,7 +43,7 @@ export default function BarcodeLabelManagementPage() {
</div> </div>
<Card className="shadow-sm"> <Card className="shadow-sm">
<CardHeader className="bg-gray-50/50"> <CardHeader className="bg-muted/50">
<CardTitle className="flex items-center gap-2"> <CardTitle className="flex items-center gap-2">
<Search className="h-5 w-5" /> <Search className="h-5 w-5" />
@ -75,7 +75,7 @@ export default function BarcodeLabelManagementPage() {
</Card> </Card>
<Card className="shadow-sm"> <Card className="shadow-sm">
<CardHeader className="bg-gray-50/50"> <CardHeader className="bg-muted/50">
<CardTitle className="flex items-center justify-between"> <CardTitle className="flex items-center justify-between">
<span className="flex items-center gap-2"> <span className="flex items-center gap-2">

View File

@ -67,7 +67,7 @@ export default function ReportDesignerPage() {
return ( return (
<DndProvider backend={HTML5Backend}> <DndProvider backend={HTML5Backend}>
<ReportDesignerProvider reportId={reportId}> <ReportDesignerProvider reportId={reportId}>
<div className="flex h-screen flex-col overflow-hidden bg-gray-50"> <div className="flex h-screen flex-col overflow-hidden bg-muted">
{/* 상단 툴바 */} {/* 상단 툴바 */}
<ReportDesignerToolbar /> <ReportDesignerToolbar />

View File

@ -30,13 +30,13 @@ export default function ReportManagementPage() {
}; };
return ( return (
<div className="min-h-screen bg-gray-50"> <div className="min-h-screen bg-muted">
<div className="w-full max-w-none space-y-8 px-4 py-8"> <div className="w-full max-w-none space-y-8 px-4 py-8">
{/* 페이지 제목 */} {/* 페이지 제목 */}
<div className="flex items-center justify-between rounded-lg border bg-white p-6 shadow-sm"> <div className="flex items-center justify-between rounded-lg border bg-white p-6 shadow-sm">
<div> <div>
<h1 className="text-3xl font-bold text-gray-900"> </h1> <h1 className="text-3xl font-bold text-foreground"> </h1>
<p className="mt-2 text-gray-600"> </p> <p className="mt-2 text-muted-foreground"> </p>
</div> </div>
<Button onClick={handleCreateNew} className="gap-2"> <Button onClick={handleCreateNew} className="gap-2">
<Plus className="h-4 w-4" /> <Plus className="h-4 w-4" />
@ -45,7 +45,7 @@ export default function ReportManagementPage() {
{/* 검색 영역 */} {/* 검색 영역 */}
<Card className="shadow-sm"> <Card className="shadow-sm">
<CardHeader className="bg-gray-50/50"> <CardHeader className="bg-muted/50">
<CardTitle className="flex items-center gap-2"> <CardTitle className="flex items-center gap-2">
<Search className="h-5 w-5" /> <Search className="h-5 w-5" />
@ -78,7 +78,7 @@ export default function ReportManagementPage() {
{/* 리포트 목록 */} {/* 리포트 목록 */}
<Card className="shadow-sm"> <Card className="shadow-sm">
<CardHeader className="bg-gray-50/50"> <CardHeader className="bg-muted/50">
<CardTitle className="flex items-center justify-between"> <CardTitle className="flex items-center justify-between">
<span className="flex items-center gap-2"> <span className="flex items-center gap-2">
📋 📋

View File

@ -204,7 +204,7 @@ export default function EditWebTypePage() {
} }
return ( return (
<div className="min-h-screen bg-gray-50"> <div className="min-h-screen bg-muted">
<div className="w-full max-w-none px-4 py-8 space-y-8"> <div className="w-full max-w-none px-4 py-8 space-y-8">
{/* 헤더 */} {/* 헤더 */}
<div className="mb-6 flex items-center gap-4"> <div className="mb-6 flex items-center gap-4">
@ -244,7 +244,7 @@ export default function EditWebTypePage() {
<div className="grid grid-cols-1 gap-4 md:grid-cols-2"> <div className="grid grid-cols-1 gap-4 md:grid-cols-2">
<div className="space-y-2"> <div className="space-y-2">
<Label htmlFor="type_name"> <Label htmlFor="type_name">
<span className="text-red-500">*</span> <span className="text-destructive">*</span>
</Label> </Label>
<Input <Input
id="type_name" id="type_name"
@ -267,7 +267,7 @@ export default function EditWebTypePage() {
{/* 카테고리 */} {/* 카테고리 */}
<div className="space-y-2"> <div className="space-y-2">
<Label htmlFor="category"> <Label htmlFor="category">
<span className="text-red-500">*</span> <span className="text-destructive">*</span>
</Label> </Label>
<Select value={formData.category || ""} onValueChange={(value) => handleInputChange("category", value)}> <Select value={formData.category || ""} onValueChange={(value) => handleInputChange("category", value)}>
<SelectTrigger> <SelectTrigger>
@ -286,7 +286,7 @@ export default function EditWebTypePage() {
{/* 연결된 컴포넌트 */} {/* 연결된 컴포넌트 */}
<div className="space-y-2"> <div className="space-y-2">
<Label htmlFor="component_name"> <Label htmlFor="component_name">
<span className="text-red-500">*</span> <span className="text-destructive">*</span>
</Label> </Label>
<Select <Select
value={formData.component_name || "TextWidget"} value={formData.component_name || "TextWidget"}
@ -338,15 +338,15 @@ export default function EditWebTypePage() {
</SelectContent> </SelectContent>
</Select> </Select>
{formData.config_panel && ( {formData.config_panel && (
<div className="rounded-lg border border-green-200 bg-green-50 p-3"> <div className="rounded-lg border border-emerald-200 bg-emerald-50 p-3">
<div className="flex items-center gap-2"> <div className="flex items-center gap-2">
<div className="h-2 w-2 rounded-full bg-green-500"></div> <div className="h-2 w-2 rounded-full bg-emerald-500"></div>
<span className="text-sm font-medium text-green-700"> <span className="text-sm font-medium text-emerald-700">
: {getConfigPanelInfo(formData.config_panel)?.label || formData.config_panel} : {getConfigPanelInfo(formData.config_panel)?.label || formData.config_panel}
</span> </span>
</div> </div>
{getConfigPanelInfo(formData.config_panel)?.description && ( {getConfigPanelInfo(formData.config_panel)?.description && (
<p className="mt-1 ml-4 text-xs text-green-600"> <p className="mt-1 ml-4 text-xs text-emerald-600">
{getConfigPanelInfo(formData.config_panel)?.description} {getConfigPanelInfo(formData.config_panel)?.description}
</p> </p>
)} )}
@ -427,7 +427,7 @@ export default function EditWebTypePage() {
rows={4} rows={4}
className="font-mono text-xs" className="font-mono text-xs"
/> />
{jsonErrors.default_config && <p className="text-xs text-red-500">{jsonErrors.default_config}</p>} {jsonErrors.default_config && <p className="text-xs text-destructive">{jsonErrors.default_config}</p>}
</div> </div>
{/* 유효성 검사 규칙 */} {/* 유효성 검사 규칙 */}
@ -441,7 +441,7 @@ export default function EditWebTypePage() {
rows={4} rows={4}
className="font-mono text-xs" className="font-mono text-xs"
/> />
{jsonErrors.validation_rules && <p className="text-xs text-red-500">{jsonErrors.validation_rules}</p>} {jsonErrors.validation_rules && <p className="text-xs text-destructive">{jsonErrors.validation_rules}</p>}
</div> </div>
{/* 기본 스타일 */} {/* 기본 스타일 */}
@ -455,7 +455,7 @@ export default function EditWebTypePage() {
rows={4} rows={4}
className="font-mono text-xs" className="font-mono text-xs"
/> />
{jsonErrors.default_style && <p className="text-xs text-red-500">{jsonErrors.default_style}</p>} {jsonErrors.default_style && <p className="text-xs text-destructive">{jsonErrors.default_style}</p>}
</div> </div>
{/* 입력 속성 */} {/* 입력 속성 */}
@ -469,7 +469,7 @@ export default function EditWebTypePage() {
rows={4} rows={4}
className="font-mono text-xs" className="font-mono text-xs"
/> />
{jsonErrors.input_properties && <p className="text-xs text-red-500">{jsonErrors.input_properties}</p>} {jsonErrors.input_properties && <p className="text-xs text-destructive">{jsonErrors.input_properties}</p>}
</div> </div>
</div> </div>
</CardContent> </CardContent>
@ -498,8 +498,8 @@ export default function EditWebTypePage() {
{/* 에러 메시지 */} {/* 에러 메시지 */}
{updateError && ( {updateError && (
<div className="mt-4 rounded-md border border-red-200 bg-red-50 p-4"> <div className="mt-4 rounded-md border border-destructive/20 bg-destructive/10 p-4">
<p className="text-red-600"> <p className="text-destructive">
: {updateError instanceof Error ? updateError.message : "알 수 없는 오류"} : {updateError instanceof Error ? updateError.message : "알 수 없는 오류"}
</p> </p>
</div> </div>

View File

@ -56,7 +56,7 @@ export default function WebTypeDetailPage() {
return ( return (
<div className="flex h-96 items-center justify-center"> <div className="flex h-96 items-center justify-center">
<div className="text-center"> <div className="text-center">
<div className="mb-2 text-lg text-red-600"> .</div> <div className="mb-2 text-lg text-destructive"> .</div>
<Link href="/admin/standards"> <Link href="/admin/standards">
<Button variant="outline"> </Button> <Button variant="outline"> </Button>
</Link> </Link>
@ -80,7 +80,7 @@ export default function WebTypeDetailPage() {
} }
return ( return (
<div className="min-h-screen bg-gray-50"> <div className="min-h-screen bg-muted">
<div className="w-full max-w-none px-4 py-8 space-y-8"> <div className="w-full max-w-none px-4 py-8 space-y-8">
{/* 헤더 */} {/* 헤더 */}
<div className="mb-6 flex items-center justify-between"> <div className="mb-6 flex items-center justify-between">

View File

@ -159,7 +159,7 @@ export default function NewWebTypePage() {
}; };
return ( return (
<div className="min-h-screen bg-gray-50"> <div className="min-h-screen bg-muted">
<div className="w-full max-w-none px-4 py-8 space-y-8"> <div className="w-full max-w-none px-4 py-8 space-y-8">
{/* 헤더 */} {/* 헤더 */}
<div className="mb-6 flex items-center gap-4"> <div className="mb-6 flex items-center gap-4">
@ -186,7 +186,7 @@ export default function NewWebTypePage() {
{/* 웹타입 코드 */} {/* 웹타입 코드 */}
<div className="space-y-2"> <div className="space-y-2">
<Label htmlFor="web_type"> <Label htmlFor="web_type">
<span className="text-red-500">*</span> <span className="text-destructive">*</span>
</Label> </Label>
<Input <Input
id="web_type" id="web_type"
@ -202,7 +202,7 @@ export default function NewWebTypePage() {
<div className="grid grid-cols-1 gap-4 md:grid-cols-2"> <div className="grid grid-cols-1 gap-4 md:grid-cols-2">
<div className="space-y-2"> <div className="space-y-2">
<Label htmlFor="type_name"> <Label htmlFor="type_name">
<span className="text-red-500">*</span> <span className="text-destructive">*</span>
</Label> </Label>
<Input <Input
id="type_name" id="type_name"
@ -225,7 +225,7 @@ export default function NewWebTypePage() {
{/* 카테고리 */} {/* 카테고리 */}
<div className="space-y-2"> <div className="space-y-2">
<Label htmlFor="category"> <Label htmlFor="category">
<span className="text-red-500">*</span> <span className="text-destructive">*</span>
</Label> </Label>
<Select value={formData.category} onValueChange={(value) => handleInputChange("category", value)}> <Select value={formData.category} onValueChange={(value) => handleInputChange("category", value)}>
<SelectTrigger> <SelectTrigger>
@ -244,7 +244,7 @@ export default function NewWebTypePage() {
{/* 연결된 컴포넌트 */} {/* 연결된 컴포넌트 */}
<div className="space-y-2"> <div className="space-y-2">
<Label htmlFor="component_name"> <Label htmlFor="component_name">
<span className="text-red-500">*</span> <span className="text-destructive">*</span>
</Label> </Label>
<Select <Select
value={formData.component_name || "TextWidget"} value={formData.component_name || "TextWidget"}
@ -296,15 +296,15 @@ export default function NewWebTypePage() {
</SelectContent> </SelectContent>
</Select> </Select>
{formData.config_panel && ( {formData.config_panel && (
<div className="rounded-lg border border-green-200 bg-green-50 p-3"> <div className="rounded-lg border border-emerald-200 bg-emerald-50 p-3">
<div className="flex items-center gap-2"> <div className="flex items-center gap-2">
<div className="h-2 w-2 rounded-full bg-green-500"></div> <div className="h-2 w-2 rounded-full bg-emerald-500"></div>
<span className="text-sm font-medium text-green-700"> <span className="text-sm font-medium text-emerald-700">
: {getConfigPanelInfo(formData.config_panel)?.label || formData.config_panel} : {getConfigPanelInfo(formData.config_panel)?.label || formData.config_panel}
</span> </span>
</div> </div>
{getConfigPanelInfo(formData.config_panel)?.description && ( {getConfigPanelInfo(formData.config_panel)?.description && (
<p className="mt-1 ml-4 text-xs text-green-600"> <p className="mt-1 ml-4 text-xs text-emerald-600">
{getConfigPanelInfo(formData.config_panel)?.description} {getConfigPanelInfo(formData.config_panel)?.description}
</p> </p>
)} )}
@ -385,7 +385,7 @@ export default function NewWebTypePage() {
rows={4} rows={4}
className="font-mono text-xs" className="font-mono text-xs"
/> />
{jsonErrors.default_config && <p className="text-xs text-red-500">{jsonErrors.default_config}</p>} {jsonErrors.default_config && <p className="text-xs text-destructive">{jsonErrors.default_config}</p>}
</div> </div>
{/* 유효성 검사 규칙 */} {/* 유효성 검사 규칙 */}
@ -399,7 +399,7 @@ export default function NewWebTypePage() {
rows={4} rows={4}
className="font-mono text-xs" className="font-mono text-xs"
/> />
{jsonErrors.validation_rules && <p className="text-xs text-red-500">{jsonErrors.validation_rules}</p>} {jsonErrors.validation_rules && <p className="text-xs text-destructive">{jsonErrors.validation_rules}</p>}
</div> </div>
{/* 기본 스타일 */} {/* 기본 스타일 */}
@ -413,7 +413,7 @@ export default function NewWebTypePage() {
rows={4} rows={4}
className="font-mono text-xs" className="font-mono text-xs"
/> />
{jsonErrors.default_style && <p className="text-xs text-red-500">{jsonErrors.default_style}</p>} {jsonErrors.default_style && <p className="text-xs text-destructive">{jsonErrors.default_style}</p>}
</div> </div>
{/* 입력 속성 */} {/* 입력 속성 */}
@ -427,7 +427,7 @@ export default function NewWebTypePage() {
rows={4} rows={4}
className="font-mono text-xs" className="font-mono text-xs"
/> />
{jsonErrors.input_properties && <p className="text-xs text-red-500">{jsonErrors.input_properties}</p>} {jsonErrors.input_properties && <p className="text-xs text-destructive">{jsonErrors.input_properties}</p>}
</div> </div>
</div> </div>
</CardContent> </CardContent>
@ -448,8 +448,8 @@ export default function NewWebTypePage() {
{/* 에러 메시지 */} {/* 에러 메시지 */}
{createError && ( {createError && (
<div className="mt-4 rounded-md border border-red-200 bg-red-50 p-4"> <div className="mt-4 rounded-md border border-destructive/20 bg-destructive/10 p-4">
<p className="text-red-600"> <p className="text-destructive">
: {createError instanceof Error ? createError.message : "알 수 없는 오류"} : {createError instanceof Error ? createError.message : "알 수 없는 오류"}
</p> </p>
</div> </div>

View File

@ -142,28 +142,28 @@ export default function CollectionManagementPage() {
const getStatusBadge = (isActive: string) => { const getStatusBadge = (isActive: string) => {
return isActive === "Y" ? ( return isActive === "Y" ? (
<Badge className="bg-green-100 text-green-800"></Badge> <Badge className="bg-emerald-100 text-emerald-800"></Badge>
) : ( ) : (
<Badge className="bg-red-100 text-red-800"></Badge> <Badge className="bg-destructive/10 text-red-800"></Badge>
); );
}; };
const getTypeBadge = (type: string) => { const getTypeBadge = (type: string) => {
const option = collectionTypeOptions.find(opt => opt.value === type); const option = collectionTypeOptions.find(opt => opt.value === type);
const colors = { const colors = {
full: "bg-blue-100 text-blue-800", full: "bg-primary/10 text-primary",
incremental: "bg-purple-100 text-purple-800", incremental: "bg-purple-100 text-purple-800",
delta: "bg-orange-100 text-orange-800", delta: "bg-amber-100 text-orange-800",
}; };
return ( return (
<Badge className={colors[type as keyof typeof colors] || "bg-gray-100 text-gray-800"}> <Badge className={colors[type as keyof typeof colors] || "bg-muted text-foreground"}>
{option?.label || type} {option?.label || type}
</Badge> </Badge>
); );
}; };
return ( return (
<div className="min-h-screen bg-gray-50"> <div className="min-h-screen bg-muted">
<div className="w-full max-w-none px-4 py-8 space-y-8"> <div className="w-full max-w-none px-4 py-8 space-y-8">
{/* 헤더 */} {/* 헤더 */}
<div className="flex items-center justify-between"> <div className="flex items-center justify-between">

View File

@ -51,8 +51,8 @@ export default function DataFlowEditPage() {
return ( return (
<div className="flex h-64 items-center justify-center"> <div className="flex h-64 items-center justify-center">
<div className="text-center"> <div className="text-center">
<div className="mx-auto mb-4 h-8 w-8 animate-spin rounded-full border-b-2 border-blue-600"></div> <div className="mx-auto mb-4 h-8 w-8 animate-spin rounded-full border-b-2 border-primary"></div>
<p className="text-gray-500"> ...</p> <p className="text-muted-foreground"> ...</p>
</div> </div>
</div> </div>
); );
@ -68,16 +68,16 @@ export default function DataFlowEditPage() {
<span></span> <span></span>
</Button> </Button>
<div> <div>
<h1 className="text-2xl font-bold text-gray-900">📊 </h1> <h1 className="text-2xl font-bold text-foreground">📊 </h1>
<p className="mt-1 text-gray-600"> <p className="mt-1 text-muted-foreground">
<span className="font-medium text-blue-600">{diagramName}</span> <span className="font-medium text-primary">{diagramName}</span>
</p> </p>
</div> </div>
</div> </div>
</div> </div>
{/* 데이터플로우 디자이너 */} {/* 데이터플로우 디자이너 */}
<div className="rounded-lg border border-gray-200 bg-white"> <div className="rounded-lg border border-border bg-white">
<DataFlowDesigner <DataFlowDesigner
key={diagramId} key={diagramId}
selectedDiagram={diagramName} selectedDiagram={diagramName}

View File

@ -17,8 +17,8 @@ export default function NodeEditorPage() {
}, [router]); }, [router]);
return ( return (
<div className="flex h-screen items-center justify-center bg-gray-50"> <div className="flex h-screen items-center justify-center bg-muted">
<div className="text-gray-500"> ...</div> <div className="text-muted-foreground"> ...</div>
</div> </div>
); );
} }

View File

@ -528,14 +528,14 @@ export default function I18nPage() {
? "공통" ? "공통"
: companies.find((c) => c.code === row.original.companyCode)?.name || row.original.companyCode; : companies.find((c) => c.code === row.original.companyCode)?.name || row.original.companyCode;
return <span className={row.original.isActive === "N" ? "text-gray-400" : ""}>{companyName}</span>; return <span className={row.original.isActive === "N" ? "text-muted-foreground/70" : ""}>{companyName}</span>;
}, },
}, },
{ {
accessorKey: "menuName", accessorKey: "menuName",
header: "메뉴명", header: "메뉴명",
cell: ({ row }: any) => ( cell: ({ row }: any) => (
<span className={row.original.isActive === "N" ? "text-gray-400" : ""}>{row.original.menuName}</span> <span className={row.original.isActive === "N" ? "text-muted-foreground/70" : ""}>{row.original.menuName}</span>
), ),
}, },
{ {
@ -543,8 +543,8 @@ export default function I18nPage() {
header: "언어 키", header: "언어 키",
cell: ({ row }: any) => ( cell: ({ row }: any) => (
<div <div
className={`cursor-pointer rounded p-1 hover:bg-gray-100 ${ className={`cursor-pointer rounded p-1 hover:bg-muted ${
row.original.isActive === "N" ? "text-gray-400" : "" row.original.isActive === "N" ? "text-muted-foreground/70" : ""
}`} }`}
onDoubleClick={() => handleEditKey(row.original)} onDoubleClick={() => handleEditKey(row.original)}
> >
@ -556,7 +556,7 @@ export default function I18nPage() {
accessorKey: "description", accessorKey: "description",
header: "설명", header: "설명",
cell: ({ row }: any) => ( cell: ({ row }: any) => (
<span className={row.original.isActive === "N" ? "text-gray-400" : ""}>{row.original.description}</span> <span className={row.original.isActive === "N" ? "text-muted-foreground/70" : ""}>{row.original.description}</span>
), ),
}, },
{ {
@ -567,8 +567,8 @@ export default function I18nPage() {
onClick={() => handleToggleStatus(row.original.keyId)} onClick={() => handleToggleStatus(row.original.keyId)}
className={`rounded px-2 py-1 text-xs font-medium transition-colors ${ className={`rounded px-2 py-1 text-xs font-medium transition-colors ${
row.original.isActive === "Y" row.original.isActive === "Y"
? "bg-green-100 text-green-800 hover:bg-green-200" ? "bg-emerald-100 text-emerald-800 hover:bg-emerald-200"
: "bg-gray-100 text-gray-800 hover:bg-gray-200" : "bg-muted text-foreground hover:bg-muted/80"
}`} }`}
> >
{row.original.isActive === "Y" ? "활성" : "비활성"} {row.original.isActive === "Y" ? "활성" : "비활성"}
@ -605,8 +605,8 @@ export default function I18nPage() {
header: "언어 코드", header: "언어 코드",
cell: ({ row }: any) => ( cell: ({ row }: any) => (
<div <div
className={`cursor-pointer rounded p-1 hover:bg-gray-100 ${ className={`cursor-pointer rounded p-1 hover:bg-muted ${
row.original.isActive === "N" ? "text-gray-400" : "" row.original.isActive === "N" ? "text-muted-foreground/70" : ""
}`} }`}
onDoubleClick={() => handleEditLanguage(row.original)} onDoubleClick={() => handleEditLanguage(row.original)}
> >
@ -618,14 +618,14 @@ export default function I18nPage() {
accessorKey: "langName", accessorKey: "langName",
header: "언어명 (영문)", header: "언어명 (영문)",
cell: ({ row }: any) => ( cell: ({ row }: any) => (
<span className={row.original.isActive === "N" ? "text-gray-400" : ""}>{row.original.langName}</span> <span className={row.original.isActive === "N" ? "text-muted-foreground/70" : ""}>{row.original.langName}</span>
), ),
}, },
{ {
accessorKey: "langNative", accessorKey: "langNative",
header: "언어명 (원어)", header: "언어명 (원어)",
cell: ({ row }: any) => ( cell: ({ row }: any) => (
<span className={row.original.isActive === "N" ? "text-gray-400" : ""}>{row.original.langNative}</span> <span className={row.original.isActive === "N" ? "text-muted-foreground/70" : ""}>{row.original.langNative}</span>
), ),
}, },
{ {
@ -636,8 +636,8 @@ export default function I18nPage() {
onClick={() => handleToggleLanguageStatus(row.original.langCode)} onClick={() => handleToggleLanguageStatus(row.original.langCode)}
className={`rounded px-2 py-1 text-xs font-medium transition-colors ${ className={`rounded px-2 py-1 text-xs font-medium transition-colors ${
row.original.isActive === "Y" row.original.isActive === "Y"
? "bg-green-100 text-green-800 hover:bg-green-200" ? "bg-emerald-100 text-emerald-800 hover:bg-emerald-200"
: "bg-gray-100 text-gray-800 hover:bg-gray-200" : "bg-muted text-foreground hover:bg-muted/80"
}`} }`}
> >
{row.original.isActive === "Y" ? "활성" : "비활성"} {row.original.isActive === "Y" ? "활성" : "비활성"}
@ -651,7 +651,7 @@ export default function I18nPage() {
} }
return ( return (
<div className="min-h-screen bg-gray-50"> <div className="min-h-screen bg-muted">
<div className="w-full max-w-none px-4 py-8"> <div className="w-full max-w-none px-4 py-8">
<div className="container mx-auto p-2"> <div className="container mx-auto p-2">
{/* 탭 네비게이션 */} {/* 탭 네비게이션 */}
@ -659,7 +659,7 @@ export default function I18nPage() {
<button <button
onClick={() => setActiveTab("keys")} onClick={() => setActiveTab("keys")}
className={`rounded-t-lg px-3 py-1.5 text-sm font-medium transition-colors ${ className={`rounded-t-lg px-3 py-1.5 text-sm font-medium transition-colors ${
activeTab === "keys" ? "bg-accent0 text-white" : "bg-gray-100 text-gray-700 hover:bg-gray-200" activeTab === "keys" ? "bg-accent0 text-white" : "bg-muted text-foreground hover:bg-muted/80"
}`} }`}
> >
@ -667,7 +667,7 @@ export default function I18nPage() {
<button <button
onClick={() => setActiveTab("languages")} onClick={() => setActiveTab("languages")}
className={`rounded-t-lg px-3 py-1.5 text-sm font-medium transition-colors ${ className={`rounded-t-lg px-3 py-1.5 text-sm font-medium transition-colors ${
activeTab === "languages" ? "bg-accent0 text-white" : "bg-gray-100 text-gray-700 hover:bg-gray-200" activeTab === "languages" ? "bg-accent0 text-white" : "bg-muted text-foreground hover:bg-muted/80"
}`} }`}
> >
@ -854,7 +854,7 @@ export default function I18nPage() {
</div> </div>
</div> </div>
) : ( ) : (
<div className="flex h-64 items-center justify-center text-gray-500"> <div className="flex h-64 items-center justify-center text-muted-foreground">
<div className="text-center"> <div className="text-center">
<div className="mb-2 text-lg font-medium"> </div> <div className="mb-2 text-lg font-medium"> </div>
<div className="text-sm"> </div> <div className="text-sm"> </div>

View File

@ -120,12 +120,12 @@ export default function TemplatesManagePage() {
// 간단한 아이콘 매핑 (실제로는 더 복잡한 시스템 필요) // 간단한 아이콘 매핑 (실제로는 더 복잡한 시스템 필요)
const iconMap: Record<string, JSX.Element> = { const iconMap: Record<string, JSX.Element> = {
table: <div className="h-4 w-4 border border-gray-400" />, table: <div className="h-4 w-4 border border-input" />,
"mouse-pointer": <div className="h-4 w-4 rounded bg-blue-500" />, "mouse-pointer": <div className="h-4 w-4 rounded bg-primary" />,
upload: <div className="h-4 w-4 border-2 border-dashed border-gray-400" />, upload: <div className="h-4 w-4 border-2 border-dashed border-input" />,
}; };
return iconMap[iconName] || <div className="h-4 w-4 rounded bg-gray-300" />; return iconMap[iconName] || <div className="h-4 w-4 rounded bg-muted/60" />;
}; };
if (error) { if (error) {
@ -133,7 +133,7 @@ export default function TemplatesManagePage() {
<div className="w-full max-w-none px-4 py-8"> <div className="w-full max-w-none px-4 py-8">
<Card> <Card>
<CardContent className="flex flex-col items-center justify-center py-8"> <CardContent className="flex flex-col items-center justify-center py-8">
<p className="mb-4 text-red-600">릿 .</p> <p className="mb-4 text-destructive">릿 .</p>
<Button onClick={() => refetch()} variant="outline"> <Button onClick={() => refetch()} variant="outline">
<RefreshCw className="mr-2 h-4 w-4" /> <RefreshCw className="mr-2 h-4 w-4" />
@ -145,13 +145,13 @@ export default function TemplatesManagePage() {
} }
return ( return (
<div className="min-h-screen bg-gray-50"> <div className="min-h-screen bg-muted">
<div className="w-full max-w-none px-4 py-8 space-y-8"> <div className="w-full max-w-none px-4 py-8 space-y-8">
{/* 페이지 제목 */} {/* 페이지 제목 */}
<div className="flex items-center justify-between bg-white rounded-lg shadow-sm border p-6"> <div className="flex items-center justify-between bg-white rounded-lg shadow-sm border p-6">
<div> <div>
<h1 className="text-3xl font-bold text-gray-900">릿 </h1> <h1 className="text-3xl font-bold text-foreground">릿 </h1>
<p className="mt-2 text-gray-600"> 릿 </p> <p className="mt-2 text-muted-foreground"> 릿 </p>
</div> </div>
<div className="flex space-x-2"> <div className="flex space-x-2">
<Button asChild className="shadow-sm"> <Button asChild className="shadow-sm">
@ -164,9 +164,9 @@ export default function TemplatesManagePage() {
{/* 필터 및 검색 */} {/* 필터 및 검색 */}
<Card className="shadow-sm"> <Card className="shadow-sm">
<CardHeader className="bg-gray-50/50"> <CardHeader className="bg-muted/50">
<CardTitle className="flex items-center"> <CardTitle className="flex items-center">
<Filter className="mr-2 h-5 w-5 text-gray-600" /> <Filter className="mr-2 h-5 w-5 text-muted-foreground" />
</CardTitle> </CardTitle>
</CardHeader> </CardHeader>
@ -176,7 +176,7 @@ export default function TemplatesManagePage() {
<div className="space-y-2"> <div className="space-y-2">
<label className="text-sm font-medium"></label> <label className="text-sm font-medium"></label>
<div className="relative"> <div className="relative">
<Search className="absolute top-1/2 left-3 h-4 w-4 -translate-y-1/2 text-gray-400" /> <Search className="absolute top-1/2 left-3 h-4 w-4 -translate-y-1/2 text-muted-foreground/70" />
<Input <Input
placeholder="템플릿명, 설명 검색..." placeholder="템플릿명, 설명 검색..."
value={searchTerm} value={searchTerm}
@ -232,7 +232,7 @@ export default function TemplatesManagePage() {
{/* 템플릿 목록 테이블 */} {/* 템플릿 목록 테이블 */}
<Card className="shadow-sm"> <Card className="shadow-sm">
<CardHeader className="bg-gray-50/50"> <CardHeader className="bg-muted/50">
<CardTitle>릿 ({filteredAndSortedTemplates.length})</CardTitle> <CardTitle>릿 ({filteredAndSortedTemplates.length})</CardTitle>
</CardHeader> </CardHeader>
<CardContent> <CardContent>
@ -293,7 +293,7 @@ export default function TemplatesManagePage() {
</TableRow> </TableRow>
) : filteredAndSortedTemplates.length === 0 ? ( ) : filteredAndSortedTemplates.length === 0 ? (
<TableRow> <TableRow>
<TableCell colSpan={11} className="py-8 text-center text-gray-500"> <TableCell colSpan={11} className="py-8 text-center text-muted-foreground">
릿 . 릿 .
</TableCell> </TableCell>
</TableRow> </TableRow>
@ -357,7 +357,7 @@ export default function TemplatesManagePage() {
</Button> </Button>
<AlertDialog> <AlertDialog>
<AlertDialogTrigger asChild> <AlertDialogTrigger asChild>
<Button size="sm" variant="ghost" className="text-red-600 hover:text-red-700"> <Button size="sm" variant="ghost" className="text-destructive hover:text-destructive">
<Trash2 className="h-4 w-4" /> <Trash2 className="h-4 w-4" />
</Button> </Button>
</AlertDialogTrigger> </AlertDialogTrigger>
@ -373,7 +373,7 @@ export default function TemplatesManagePage() {
<AlertDialogCancel></AlertDialogCancel> <AlertDialogCancel></AlertDialogCancel>
<AlertDialogAction <AlertDialogAction
onClick={() => handleDelete(template.template_code, template.template_name)} onClick={() => handleDelete(template.template_code, template.template_name)}
className="bg-red-600 hover:bg-red-700" className="bg-destructive hover:bg-red-700"
disabled={isDeleting} disabled={isDeleting}
> >

View File

@ -25,27 +25,27 @@ export default function TestPage() {
<h1 className="mb-4 text-2xl font-bold"> </h1> <h1 className="mb-4 text-2xl font-bold"> </h1>
<div className="space-y-4"> <div className="space-y-4">
<div className="rounded bg-green-100 p-4"> <div className="rounded bg-emerald-100 p-4">
<h2 className="mb-2 font-semibold"> </h2> <h2 className="mb-2 font-semibold"> </h2>
<p> : {tokenInfo.hasToken ? "✅ 예" : "❌ 아니오"}</p> <p> : {tokenInfo.hasToken ? "✅ 예" : "❌ 아니오"}</p>
<p> : {tokenInfo.tokenLength}</p> <p> : {tokenInfo.tokenLength}</p>
<p> : {tokenInfo.tokenStart}</p> <p> : {tokenInfo.tokenStart}</p>
</div> </div>
<div className="rounded bg-blue-100 p-4"> <div className="rounded bg-primary/10 p-4">
<h2 className="mb-2 font-semibold"> </h2> <h2 className="mb-2 font-semibold"> </h2>
<p> URL: {tokenInfo.currentUrl}</p> <p> URL: {tokenInfo.currentUrl}</p>
<p>: {tokenInfo.timestamp}</p> <p>: {tokenInfo.timestamp}</p>
</div> </div>
<div className="rounded bg-yellow-100 p-4"> <div className="rounded bg-amber-100 p-4">
<h2 className="mb-2 font-semibold"> </h2> <h2 className="mb-2 font-semibold"> </h2>
<button <button
onClick={() => { onClick={() => {
const token = localStorage.getItem("authToken"); const token = localStorage.getItem("authToken");
alert(`토큰: ${token ? "존재" : "없음"}\n길이: ${token ? token.length : 0}`); alert(`토큰: ${token ? "존재" : "없음"}\n길이: ${token ? token.length : 0}`);
}} }}
className="rounded bg-blue-500 px-4 py-2 text-white hover:bg-blue-600" className="rounded bg-primary px-4 py-2 text-white hover:bg-primary"
> >
</button> </button>

View File

@ -31,12 +31,12 @@ export default function TokenTestPage() {
<h1 className="mb-4 text-2xl font-bold"> </h1> <h1 className="mb-4 text-2xl font-bold"> </h1>
<div className="space-y-4"> <div className="space-y-4">
<div className="rounded bg-gray-100 p-4"> <div className="rounded bg-muted p-4">
<h2 className="mb-2 font-semibold"> </h2> <h2 className="mb-2 font-semibold"> </h2>
<pre className="text-sm">{JSON.stringify(tokenInfo, null, 2)}</pre> <pre className="text-sm">{JSON.stringify(tokenInfo, null, 2)}</pre>
</div> </div>
<div className="rounded bg-blue-100 p-4"> <div className="rounded bg-primary/10 p-4">
<h2 className="mb-2 font-semibold"> </h2> <h2 className="mb-2 font-semibold"> </h2>
<button <button
onClick={() => { onClick={() => {
@ -44,7 +44,7 @@ export default function TokenTestPage() {
console.log("현재 토큰:", token); console.log("현재 토큰:", token);
alert(`토큰: ${token ? "존재" : "없음"}`); alert(`토큰: ${token ? "존재" : "없음"}`);
}} }}
className="rounded bg-blue-500 px-4 py-2 text-white hover:bg-blue-600" className="rounded bg-primary px-4 py-2 text-white hover:bg-primary"
> >
</button> </button>

View File

@ -259,7 +259,7 @@ export default function RoleDetailPage({ params }: { params: Promise<{ id: strin
</div> </div>
<span <span
className={`rounded-full px-3 py-1 text-sm font-medium ${ className={`rounded-full px-3 py-1 text-sm font-medium ${
roleGroup.status === "active" ? "bg-green-100 text-green-800" : "bg-gray-100 text-gray-800" roleGroup.status === "active" ? "bg-emerald-100 text-emerald-800" : "bg-muted text-foreground"
}`} }`}
> >
{roleGroup.status === "active" ? "활성" : "비활성"} {roleGroup.status === "active" ? "활성" : "비활성"}

View File

@ -273,7 +273,7 @@ export default function RolesPage() {
</div> </div>
<span <span
className={`rounded-full px-2 py-1 text-xs font-medium ${ className={`rounded-full px-2 py-1 text-xs font-medium ${
role.status === "active" ? "bg-green-100 text-green-800" : "bg-gray-100 text-gray-800" role.status === "active" ? "bg-emerald-100 text-emerald-800" : "bg-muted text-foreground"
}`} }`}
> >
{role.status === "active" ? "활성" : "비활성"} {role.status === "active" ? "활성" : "비활성"}

View File

@ -388,7 +388,7 @@ export default function ValidationDemoPage() {
<CardDescription> . .</CardDescription> <CardDescription> . .</CardDescription>
</CardHeader> </CardHeader>
<CardContent> <CardContent>
<div className="relative min-h-[400px] rounded-lg border border-dashed border-gray-300 p-4"> <div className="relative min-h-[400px] rounded-lg border border-dashed border-input p-4">
<EnhancedInteractiveScreenViewer <EnhancedInteractiveScreenViewer
component={TEST_COMPONENTS[0]} // container component={TEST_COMPONENTS[0]} // container
allComponents={TEST_COMPONENTS} allComponents={TEST_COMPONENTS}
@ -485,7 +485,7 @@ export default function ValidationDemoPage() {
<div className="space-y-2"> <div className="space-y-2">
<h4 className="font-semibold"> </h4> <h4 className="font-semibold"> </h4>
<pre className="max-h-60 overflow-auto rounded-md bg-gray-100 p-3 text-sm"> <pre className="max-h-60 overflow-auto rounded-md bg-muted p-3 text-sm">
{JSON.stringify(formData, null, 2)} {JSON.stringify(formData, null, 2)}
</pre> </pre>
</div> </div>
@ -493,15 +493,15 @@ export default function ValidationDemoPage() {
<div className="space-y-2"> <div className="space-y-2">
<h4 className="font-semibold"> </h4> <h4 className="font-semibold"> </h4>
<div className="grid grid-cols-2 gap-4"> <div className="grid grid-cols-2 gap-4">
<div className="rounded-md bg-green-50 p-3"> <div className="rounded-md bg-emerald-50 p-3">
<div className="text-lg font-bold text-green-600"> <div className="text-lg font-bold text-emerald-600">
{Object.values(validationState.fieldStates).filter((f) => f.status === "valid").length} {Object.values(validationState.fieldStates).filter((f) => f.status === "valid").length}
</div> </div>
<div className="text-sm text-green-700"> </div> <div className="text-sm text-emerald-700"> </div>
</div> </div>
<div className="rounded-md bg-red-50 p-3"> <div className="rounded-md bg-destructive/10 p-3">
<div className="text-lg font-bold text-red-600">{validationState.errors.length}</div> <div className="text-lg font-bold text-destructive">{validationState.errors.length}</div>
<div className="text-sm text-red-700"> </div> <div className="text-sm text-destructive"> </div>
</div> </div>
</div> </div>
</div> </div>

View File

@ -29,7 +29,7 @@ const statusConfig: Record<string, { label: string; variant: "default" | "second
const lineStatusConfig: Record<string, { label: string; icon: React.ReactNode }> = { const lineStatusConfig: Record<string, { label: string; icon: React.ReactNode }> = {
waiting: { label: "대기", icon: <Clock className="h-3 w-3 text-muted-foreground" /> }, waiting: { label: "대기", icon: <Clock className="h-3 w-3 text-muted-foreground" /> },
pending: { label: "진행 중", icon: <Clock className="h-3 w-3 text-primary" /> }, pending: { label: "진행 중", icon: <Clock className="h-3 w-3 text-primary" /> },
approved: { label: "승인", icon: <CheckCircle2 className="h-3 w-3 text-green-600" /> }, approved: { label: "승인", icon: <CheckCircle2 className="h-3 w-3 text-emerald-600" /> },
rejected: { label: "반려", icon: <XCircle className="h-3 w-3 text-destructive" /> }, rejected: { label: "반려", icon: <XCircle className="h-3 w-3 text-destructive" /> },
skipped: { label: "건너뜀", icon: <Clock className="h-3 w-3 text-muted-foreground" /> }, skipped: { label: "건너뜀", icon: <Clock className="h-3 w-3 text-muted-foreground" /> },
}; };

View File

@ -107,18 +107,18 @@ export default function DashboardViewPage({ params }: DashboardViewPageProps) {
return ( return (
<div className="h-screen"> <div className="h-screen">
{/* 대시보드 헤더 - 보기 모드에서는 숨김 */} {/* 대시보드 헤더 - 보기 모드에서는 숨김 */}
{/* <div className="border-b border-gray-200 bg-white px-6 py-4"> {/* <div className="border-b border-border bg-white px-6 py-4">
<div className="flex items-center justify-between"> <div className="flex items-center justify-between">
<div> <div>
<h1 className="text-2xl font-bold text-gray-800">{dashboard.title}</h1> <h1 className="text-2xl font-bold text-foreground">{dashboard.title}</h1>
{dashboard.description && <p className="mt-1 text-sm text-gray-600">{dashboard.description}</p>} {dashboard.description && <p className="mt-1 text-sm text-muted-foreground">{dashboard.description}</p>}
</div> </div>
<div className="flex items-center gap-3"> <div className="flex items-center gap-3">
{/* *\/} {/* *\/}
<button <button
onClick={loadDashboard} onClick={loadDashboard}
className="rounded-lg border border-gray-300 px-3 py-2 text-gray-600 hover:bg-gray-50 hover:text-gray-800" className="rounded-lg border border-input px-3 py-2 text-muted-foreground hover:bg-muted hover:text-foreground"
title="새로고침" title="새로고침"
> >
🔄 🔄
@ -133,7 +133,7 @@ export default function DashboardViewPage({ params }: DashboardViewPageProps) {
document.documentElement.requestFullscreen(); document.documentElement.requestFullscreen();
} }
}} }}
className="rounded-lg border border-gray-300 px-3 py-2 text-gray-600 hover:bg-gray-50 hover:text-gray-800" className="rounded-lg border border-input px-3 py-2 text-muted-foreground hover:bg-muted hover:text-foreground"
title="전체화면" title="전체화면"
> >
@ -144,7 +144,7 @@ export default function DashboardViewPage({ params }: DashboardViewPageProps) {
onClick={() => { onClick={() => {
router.push(`/admin/screenMng/dashboardList?load=${resolvedParams.dashboardId}`); router.push(`/admin/screenMng/dashboardList?load=${resolvedParams.dashboardId}`);
}} }}
className="rounded-lg bg-blue-500 px-4 py-2 text-white hover:bg-blue-600" className="rounded-lg bg-primary px-4 py-2 text-white hover:bg-primary"
> >
</button> </button>
@ -152,7 +152,7 @@ export default function DashboardViewPage({ params }: DashboardViewPageProps) {
</div> </div>
{/* *\/} {/* *\/}
<div className="mt-2 flex items-center gap-4 text-xs text-gray-500"> <div className="mt-2 flex items-center gap-4 text-xs text-muted-foreground">
<span>: {new Date(dashboard.createdAt).toLocaleString()}</span> <span>: {new Date(dashboard.createdAt).toLocaleString()}</span>
<span>: {new Date(dashboard.updatedAt).toLocaleString()}</span> <span>: {new Date(dashboard.updatedAt).toLocaleString()}</span>
<span>: {dashboard.elements.length}</span> <span>: {dashboard.elements.length}</span>

View File

@ -5,7 +5,7 @@ import { useAuth } from "@/hooks/useAuth";
import { FileCheck, Menu, Users, Bell, FileText, Layout, Server, Shield, Calendar } from "lucide-react"; import { FileCheck, Menu, Users, Bell, FileText, Layout, Server, Shield, Calendar } from "lucide-react";
const quickAccessItems = [ const quickAccessItems = [
{ label: "결재함", icon: FileCheck, href: "/admin/approvalBox", color: "text-blue-600 bg-blue-50" }, { label: "결재함", icon: FileCheck, href: "/admin/approvalBox", color: "text-primary bg-primary/10" },
{ label: "메뉴 관리", icon: Menu, href: "/admin/menu", color: "text-violet-600 bg-violet-50" }, { label: "메뉴 관리", icon: Menu, href: "/admin/menu", color: "text-violet-600 bg-violet-50" },
{ label: "사용자 관리", icon: Users, href: "/admin/userMng", color: "text-emerald-600 bg-emerald-50" }, { label: "사용자 관리", icon: Users, href: "/admin/userMng", color: "text-emerald-600 bg-emerald-50" },
{ label: "공지사항", icon: Bell, href: "/admin/system-notices", color: "text-amber-600 bg-amber-50" }, { label: "공지사항", icon: Bell, href: "/admin/system-notices", color: "text-amber-600 bg-amber-50" },

View File

@ -385,13 +385,13 @@ export default function MultiLangPage() {
</div> </div>
<div className="flex items-end"> <div className="flex items-end">
<div className="text-sm text-gray-600"> : {filteredLangKeys.length}</div> <div className="text-sm text-muted-foreground"> : {filteredLangKeys.length}</div>
</div> </div>
</div> </div>
</div> </div>
<div className="mb-4 flex items-center justify-between"> <div className="mb-4 flex items-center justify-between">
<div className="text-sm text-gray-600">: {filteredLangKeys.length}</div> <div className="text-sm text-muted-foreground">: {filteredLangKeys.length}</div>
</div> </div>
<DataTable <DataTable
@ -417,7 +417,7 @@ export default function MultiLangPage() {
{languages.map((lang) => ( {languages.map((lang) => (
<div key={lang.langCode} className="rounded-lg border p-4"> <div key={lang.langCode} className="rounded-lg border p-4">
<div className="font-semibold">{lang.langName}</div> <div className="font-semibold">{lang.langName}</div>
<div className="text-sm text-gray-600">{lang.langNative}</div> <div className="text-sm text-muted-foreground">{lang.langNative}</div>
<Badge variant={lang.isActive === "Y" ? "default" : "secondary"} className="mt-2"> <Badge variant={lang.isActive === "Y" ? "default" : "secondary"} className="mt-2">
{lang.isActive === "Y" ? "활성" : "비활성"} {lang.isActive === "Y" ? "활성" : "비활성"}
</Badge> </Badge>
@ -440,7 +440,7 @@ export default function MultiLangPage() {
<h3 className="mb-2 font-semibold">{company.name}</h3> <h3 className="mb-2 font-semibold">{company.name}</h3>
<div className="space-y-1"> <div className="space-y-1">
{menus.map((menu) => ( {menus.map((menu) => (
<div key={menu.code} className="text-sm text-gray-600"> <div key={menu.code} className="text-sm text-muted-foreground">
{menu.name} {menu.name}
</div> </div>
))} ))}

View File

@ -5,7 +5,7 @@ import { useAuth } from "@/hooks/useAuth";
import { FileCheck, Menu, Users, Bell, FileText, Layout, Server, Shield, Calendar, ArrowRight } from "lucide-react"; import { FileCheck, Menu, Users, Bell, FileText, Layout, Server, Shield, Calendar, ArrowRight } from "lucide-react";
const quickAccessItems = [ const quickAccessItems = [
{ label: "결재함", icon: FileCheck, href: "/admin/approvalBox", color: "text-blue-600 bg-blue-50" }, { label: "결재함", icon: FileCheck, href: "/admin/approvalBox", color: "text-primary bg-primary/10" },
{ label: "메뉴 관리", icon: Menu, href: "/admin/menu", color: "text-violet-600 bg-violet-50" }, { label: "메뉴 관리", icon: Menu, href: "/admin/menu", color: "text-violet-600 bg-violet-50" },
{ label: "사용자 관리", icon: Users, href: "/admin/userMng", color: "text-emerald-600 bg-emerald-50" }, { label: "사용자 관리", icon: Users, href: "/admin/userMng", color: "text-emerald-600 bg-emerald-50" },
{ label: "공지사항", icon: Bell, href: "/admin/system-notices", color: "text-amber-600 bg-amber-50" }, { label: "공지사항", icon: Bell, href: "/admin/system-notices", color: "text-amber-600 bg-amber-50" },

View File

@ -174,10 +174,10 @@ function PopScreenViewPage() {
if (loading) { if (loading) {
return ( return (
<div className="flex h-screen w-full items-center justify-center bg-gray-100"> <div className="flex h-screen w-full items-center justify-center bg-muted">
<div className="text-center"> <div className="text-center">
<Loader2 className="mx-auto h-10 w-10 animate-spin text-blue-500" /> <Loader2 className="mx-auto h-10 w-10 animate-spin text-primary" />
<p className="mt-4 text-gray-600">POP ...</p> <p className="mt-4 text-muted-foreground">POP ...</p>
</div> </div>
</div> </div>
); );
@ -185,13 +185,13 @@ function PopScreenViewPage() {
if (error || !screen) { if (error || !screen) {
return ( return (
<div className="flex h-screen w-full items-center justify-center bg-gray-100"> <div className="flex h-screen w-full items-center justify-center bg-muted">
<div className="text-center max-w-md p-6"> <div className="text-center max-w-md p-6">
<div className="mx-auto mb-4 flex h-16 w-16 items-center justify-center rounded-full bg-red-100"> <div className="mx-auto mb-4 flex h-16 w-16 items-center justify-center rounded-full bg-destructive/10">
<span className="text-2xl">!</span> <span className="text-2xl">!</span>
</div> </div>
<h2 className="mb-2 text-xl font-bold text-gray-800"> </h2> <h2 className="mb-2 text-xl font-bold text-foreground"> </h2>
<p className="mb-4 text-gray-600">{error || "요청하신 POP 화면이 존재하지 않습니다."}</p> <p className="mb-4 text-muted-foreground">{error || "요청하신 POP 화면이 존재하지 않습니다."}</p>
<Button onClick={() => router.back()} variant="outline"> <Button onClick={() => router.back()} variant="outline">
<ArrowLeft className="mr-2 h-4 w-4" /> <ArrowLeft className="mr-2 h-4 w-4" />
@ -205,7 +205,7 @@ function PopScreenViewPage() {
<ScreenPreviewProvider isPreviewMode={isPreviewMode}> <ScreenPreviewProvider isPreviewMode={isPreviewMode}>
<ActiveTabProvider> <ActiveTabProvider>
<TableOptionsProvider> <TableOptionsProvider>
<div className="h-screen bg-gray-100 flex flex-col"> <div className="h-screen bg-muted flex flex-col">
{/* 상단 툴바 (프리뷰 모드에서만) */} {/* 상단 툴바 (프리뷰 모드에서만) */}
{isPreviewMode && ( {isPreviewMode && (
<div className="sticky top-0 z-50 bg-white border-b shadow-sm"> <div className="sticky top-0 z-50 bg-white border-b shadow-sm">
@ -216,13 +216,13 @@ function PopScreenViewPage() {
</Button> </Button>
<span className="text-sm font-medium">{screen.screenName}</span> <span className="text-sm font-medium">{screen.screenName}</span>
<span className="text-xs text-gray-400"> <span className="text-xs text-muted-foreground/70">
({currentModeKey.replace("_", " ")}) ({currentModeKey.replace("_", " ")})
</span> </span>
</div> </div>
<div className="flex items-center gap-2"> <div className="flex items-center gap-2">
<div className="flex items-center gap-1 bg-gray-100 rounded-lg p-1"> <div className="flex items-center gap-1 bg-muted rounded-lg p-1">
<Button <Button
variant={deviceType === "mobile" ? "default" : "ghost"} variant={deviceType === "mobile" ? "default" : "ghost"}
size="sm" size="sm"
@ -243,7 +243,7 @@ function PopScreenViewPage() {
</Button> </Button>
</div> </div>
<div className="flex items-center gap-1 bg-gray-100 rounded-lg p-1"> <div className="flex items-center gap-1 bg-muted rounded-lg p-1">
<Button <Button
variant={isLandscape ? "default" : "ghost"} variant={isLandscape ? "default" : "ghost"}
size="sm" size="sm"
@ -295,7 +295,7 @@ function PopScreenViewPage() {
)} )}
<div <div
className={`bg-white transition-all duration-300 ${isPreviewMode ? "shadow-2xl rounded-3xl overflow-auto border-8 border-gray-800" : "w-full min-h-full"}`} className={`bg-white transition-all duration-300 ${isPreviewMode ? "shadow-2xl rounded-3xl overflow-auto border-8 border-foreground" : "w-full min-h-full"}`}
style={isPreviewMode ? { style={isPreviewMode ? {
width: currentDevice.width, width: currentDevice.width,
maxHeight: "80vh", maxHeight: "80vh",
@ -333,13 +333,13 @@ function PopScreenViewPage() {
) : ( ) : (
// 빈 화면 // 빈 화면
<div className="flex flex-col items-center justify-center min-h-[400px] p-8 text-center"> <div className="flex flex-col items-center justify-center min-h-[400px] p-8 text-center">
<div className="w-16 h-16 rounded-full bg-gray-100 flex items-center justify-center mb-4"> <div className="w-16 h-16 rounded-full bg-muted flex items-center justify-center mb-4">
<Smartphone className="h-8 w-8 text-gray-400" /> <Smartphone className="h-8 w-8 text-muted-foreground/70" />
</div> </div>
<h3 className="text-lg font-semibold text-gray-800 mb-2"> <h3 className="text-lg font-semibold text-foreground mb-2">
</h3> </h3>
<p className="text-sm text-gray-500 max-w-xs"> <p className="text-sm text-muted-foreground max-w-xs">
POP . POP .
</p> </p>
</div> </div>

View File

@ -227,9 +227,9 @@ export default function SimpleTypeSafetyTest() {
</div> </div>
{passedTests === totalTests && totalTests > 0 && ( {passedTests === totalTests && totalTests > 0 && (
<div className="mt-4 rounded-lg border border-green-200 bg-green-50 p-4"> <div className="mt-4 rounded-lg border border-emerald-200 bg-emerald-50 p-4">
<div className="font-medium text-green-800">🎉 !</div> <div className="font-medium text-emerald-800">🎉 !</div>
<div className="mt-2 text-sm text-green-600"> <div className="mt-2 text-sm text-emerald-600">
, , . , , .
</div> </div>
</div> </div>

View File

@ -138,7 +138,7 @@ export default function StressTestPage() {
<div className="space-y-4 text-center"> <div className="space-y-4 text-center">
<h1 className="text-3xl font-bold">🔥 </h1> <h1 className="text-3xl font-bold">🔥 </h1>
<p className="text-muted-foreground"> </p> <p className="text-muted-foreground"> </p>
<div className="rounded-lg bg-orange-50 p-3 text-sm text-orange-600"> <div className="rounded-lg bg-amber-50 p-3 text-sm text-amber-600">
주의: 주의:
</div> </div>
@ -184,19 +184,19 @@ export default function StressTestPage() {
<CardContent className="space-y-4"> <CardContent className="space-y-4">
<div className="grid grid-cols-2 gap-4 md:grid-cols-4"> <div className="grid grid-cols-2 gap-4 md:grid-cols-4">
<div className="text-center"> <div className="text-center">
<div className="text-2xl font-bold text-green-600">{testResults.passedTests}</div> <div className="text-2xl font-bold text-emerald-600">{testResults.passedTests}</div>
<div className="text-muted-foreground text-sm"></div> <div className="text-muted-foreground text-sm"></div>
</div> </div>
<div className="text-center"> <div className="text-center">
<div className="text-2xl font-bold text-red-600">{testResults.failedTests}</div> <div className="text-2xl font-bold text-destructive">{testResults.failedTests}</div>
<div className="text-muted-foreground text-sm"></div> <div className="text-muted-foreground text-sm"></div>
</div> </div>
<div className="text-center"> <div className="text-center">
<div className="text-2xl font-bold text-yellow-600">{testResults.warningTests}</div> <div className="text-2xl font-bold text-amber-600">{testResults.warningTests}</div>
<div className="text-muted-foreground text-sm"></div> <div className="text-muted-foreground text-sm"></div>
</div> </div>
<div className="text-center"> <div className="text-center">
<div className="text-2xl font-bold text-blue-600">{Math.round(testResults.totalDuration)}ms</div> <div className="text-2xl font-bold text-primary">{Math.round(testResults.totalDuration)}ms</div>
<div className="text-muted-foreground text-sm"> </div> <div className="text-muted-foreground text-sm"> </div>
</div> </div>
</div> </div>
@ -212,16 +212,16 @@ export default function StressTestPage() {
</div> </div>
{testResults.success ? ( {testResults.success ? (
<div className="rounded-lg border border-green-200 bg-green-50 p-4"> <div className="rounded-lg border border-emerald-200 bg-emerald-50 p-4">
<div className="font-medium text-green-800">🎉 !</div> <div className="font-medium text-emerald-800">🎉 !</div>
<div className="mt-2 text-sm text-green-600"> <div className="mt-2 text-sm text-emerald-600">
. .
</div> </div>
</div> </div>
) : ( ) : (
<div className="rounded-lg border border-red-200 bg-red-50 p-4"> <div className="rounded-lg border border-destructive/20 bg-destructive/10 p-4">
<div className="font-medium text-red-800"> </div> <div className="font-medium text-red-800"> </div>
<div className="mt-2 text-sm text-red-600"> <div className="mt-2 text-sm text-destructive">
. . . .
</div> </div>
</div> </div>
@ -253,7 +253,7 @@ export default function StressTestPage() {
{/* 메트릭스 표시 */} {/* 메트릭스 표시 */}
{result.metrics && ( {result.metrics && (
<div className="mt-3 rounded bg-gray-50 p-3 text-xs"> <div className="mt-3 rounded bg-muted p-3 text-xs">
<div className="mb-1 font-medium">📊 :</div> <div className="mb-1 font-medium">📊 :</div>
<div className="grid grid-cols-2 gap-2"> <div className="grid grid-cols-2 gap-2">
{Object.entries(result.metrics).map(([key, value]) => ( {Object.entries(result.metrics).map(([key, value]) => (
@ -286,7 +286,7 @@ export default function StressTestPage() {
<ul className="space-y-2"> <ul className="space-y-2">
{testResults.recommendation.map((rec, index) => ( {testResults.recommendation.map((rec, index) => (
<li key={index} className="flex items-start gap-2"> <li key={index} className="flex items-start gap-2">
<span className="mt-0.5 text-blue-500"></span> <span className="mt-0.5 text-primary"></span>
<span className="text-sm">{rec}</span> <span className="text-sm">{rec}</span>
</li> </li>
))} ))}
@ -304,7 +304,7 @@ export default function StressTestPage() {
<CardTitle className="text-lg">📋 ( 10)</CardTitle> <CardTitle className="text-lg">📋 ( 10)</CardTitle>
</CardHeader> </CardHeader>
<CardContent> <CardContent>
<div className="h-40 overflow-y-auto rounded bg-black p-4 font-mono text-xs text-green-400"> <div className="h-40 overflow-y-auto rounded bg-black p-4 font-mono text-xs text-emerald-400">
{testLogs.slice(-10).map((log, index) => ( {testLogs.slice(-10).map((log, index) => (
<div key={index}>{log}</div> <div key={index}>{log}</div>
))} ))}

View File

@ -66,10 +66,10 @@ export const GlobalFileViewer: React.FC<GlobalFileViewerProps> = ({
return <Video {...iconProps} className="text-purple-600" />; return <Video {...iconProps} className="text-purple-600" />;
} }
if (['mp3', 'wav', 'flac', 'aac', 'ogg'].includes(extension)) { if (['mp3', 'wav', 'flac', 'aac', 'ogg'].includes(extension)) {
return <Music {...iconProps} className="text-green-600" />; return <Music {...iconProps} className="text-emerald-600" />;
} }
if (['zip', 'rar', '7z', 'tar', 'gz'].includes(extension)) { if (['zip', 'rar', '7z', 'tar', 'gz'].includes(extension)) {
return <Archive {...iconProps} className="text-yellow-600" />; return <Archive {...iconProps} className="text-amber-600" />;
} }
if (['txt', 'md', 'doc', 'docx', 'pdf', 'rtf'].includes(extension)) { if (['txt', 'md', 'doc', 'docx', 'pdf', 'rtf'].includes(extension)) {
return <FileText {...iconProps} className="text-destructive" />; return <FileText {...iconProps} className="text-destructive" />;
@ -192,7 +192,7 @@ export const GlobalFileViewer: React.FC<GlobalFileViewerProps> = ({
{showControls && ( {showControls && (
<div className="flex items-center gap-2"> <div className="flex items-center gap-2">
<div className="relative flex-1"> <div className="relative flex-1">
<Search className="absolute left-2 top-1/2 transform -translate-y-1/2 w-4 h-4 text-gray-400" /> <Search className="absolute left-2 top-1/2 transform -translate-y-1/2 w-4 h-4 text-muted-foreground/70" />
<Input <Input
placeholder="파일명으로 검색..." placeholder="파일명으로 검색..."
value={searchQuery} value={searchQuery}
@ -219,7 +219,7 @@ export const GlobalFileViewer: React.FC<GlobalFileViewerProps> = ({
style={{ maxHeight }} style={{ maxHeight }}
> >
{filteredFiles.length === 0 ? ( {filteredFiles.length === 0 ? (
<div className="text-center py-8 text-gray-500"> <div className="text-center py-8 text-muted-foreground">
{searchQuery ? "검색 결과가 없습니다." : "저장된 파일이 없습니다."} {searchQuery ? "검색 결과가 없습니다." : "저장된 파일이 없습니다."}
</div> </div>
) : ( ) : (
@ -232,7 +232,7 @@ export const GlobalFileViewer: React.FC<GlobalFileViewerProps> = ({
<div className="font-medium truncate"> <div className="font-medium truncate">
{file.realFileName || file.savedFileName} {file.realFileName || file.savedFileName}
</div> </div>
<div className="text-sm text-gray-500 flex items-center gap-2"> <div className="text-sm text-muted-foreground flex items-center gap-2">
<span>{formatFileSize(file.fileSize)}</span> <span>{formatFileSize(file.fileSize)}</span>
<div className="flex items-center gap-1"> <div className="flex items-center gap-1">
<Clock className="w-3 h-3" /> <Clock className="w-3 h-3" />
@ -273,7 +273,7 @@ export const GlobalFileViewer: React.FC<GlobalFileViewerProps> = ({
variant="ghost" variant="ghost"
size="sm" size="sm"
onClick={() => handleRemove(file)} onClick={() => handleRemove(file)}
className="flex items-center gap-1 text-destructive hover:text-red-700" className="flex items-center gap-1 text-destructive hover:text-destructive"
> >
<Trash2 className="w-3 h-3" /> <Trash2 className="w-3 h-3" />
</Button> </Button>

View File

@ -225,7 +225,7 @@ export function AddColumnModal({ isOpen, onClose, tableName, onSuccess }: AddCol
<div className="grid grid-cols-1 gap-4 md:grid-cols-2"> <div className="grid grid-cols-1 gap-4 md:grid-cols-2">
<div className="space-y-2"> <div className="space-y-2">
<Label htmlFor="columnName"> <Label htmlFor="columnName">
<span className="text-red-500">*</span> <span className="text-destructive">*</span>
</Label> </Label>
<Input <Input
id="columnName" id="columnName"
@ -233,7 +233,7 @@ export function AddColumnModal({ isOpen, onClose, tableName, onSuccess }: AddCol
onChange={(e) => updateColumn({ name: e.target.value })} onChange={(e) => updateColumn({ name: e.target.value })}
placeholder="column_name" placeholder="column_name"
disabled={loading} disabled={loading}
className={validationErrors.some((e) => e.includes("컬럼명")) ? "border-red-300" : ""} className={validationErrors.some((e) => e.includes("컬럼명")) ? "border-destructive/30" : ""}
/> />
<p className="text-muted-foreground text-xs"> , // </p> <p className="text-muted-foreground text-xs"> , // </p>
</div> </div>
@ -255,7 +255,7 @@ export function AddColumnModal({ isOpen, onClose, tableName, onSuccess }: AddCol
<div className="grid grid-cols-1 gap-4 md:grid-cols-2"> <div className="grid grid-cols-1 gap-4 md:grid-cols-2">
<div className="space-y-2"> <div className="space-y-2">
<Label> <Label>
<span className="text-red-500">*</span> <span className="text-destructive">*</span>
</Label> </Label>
<Select value={column.inputType} onValueChange={handleInputTypeChange} disabled={loading}> <Select value={column.inputType} onValueChange={handleInputTypeChange} disabled={loading}>
<SelectTrigger> <SelectTrigger>
@ -354,7 +354,7 @@ export function AddColumnModal({ isOpen, onClose, tableName, onSuccess }: AddCol
<Button <Button
onClick={handleAddColumn} onClick={handleAddColumn}
disabled={!isFormValid || loading} disabled={!isFormValid || loading}
className="bg-blue-600 hover:bg-blue-700" className="bg-primary hover:bg-primary/90"
> >
{loading ? ( {loading ? (
<> <>

View File

@ -49,7 +49,7 @@ export function AuthenticationConfig({
{/* 인증 타입별 설정 필드 */} {/* 인증 타입별 설정 필드 */}
{authType === "api-key" && ( {authType === "api-key" && (
<div className="space-y-4 rounded-md border bg-gray-50 p-4"> <div className="space-y-4 rounded-md border bg-muted p-4">
<h4 className="text-sm font-medium">API Key </h4> <h4 className="text-sm font-medium">API Key </h4>
{/* 키 위치 */} {/* 키 위치 */}
@ -96,7 +96,7 @@ export function AuthenticationConfig({
)} )}
{authType === "bearer" && ( {authType === "bearer" && (
<div className="space-y-4 rounded-md border bg-gray-50 p-4"> <div className="space-y-4 rounded-md border bg-muted p-4">
<h4 className="text-sm font-medium">Bearer Token </h4> <h4 className="text-sm font-medium">Bearer Token </h4>
{/* 토큰 */} {/* 토큰 */}
@ -111,14 +111,14 @@ export function AuthenticationConfig({
/> />
</div> </div>
<p className="text-xs text-gray-500"> <p className="text-xs text-muted-foreground">
* Authorization &quot;Bearer &#123;token&#125;&quot; . * Authorization &quot;Bearer &#123;token&#125;&quot; .
</p> </p>
</div> </div>
)} )}
{authType === "basic" && ( {authType === "basic" && (
<div className="space-y-4 rounded-md border bg-gray-50 p-4"> <div className="space-y-4 rounded-md border bg-muted p-4">
<h4 className="text-sm font-medium">Basic Auth </h4> <h4 className="text-sm font-medium">Basic Auth </h4>
{/* 사용자명 */} {/* 사용자명 */}
@ -145,12 +145,12 @@ export function AuthenticationConfig({
/> />
</div> </div>
<p className="text-xs text-gray-500">* Authorization Base64 .</p> <p className="text-xs text-muted-foreground">* Authorization Base64 .</p>
</div> </div>
)} )}
{authType === "oauth2" && ( {authType === "oauth2" && (
<div className="space-y-4 rounded-md border bg-gray-50 p-4"> <div className="space-y-4 rounded-md border bg-muted p-4">
<h4 className="text-sm font-medium">OAuth 2.0 </h4> <h4 className="text-sm font-medium">OAuth 2.0 </h4>
{/* Client ID */} {/* Client ID */}
@ -189,12 +189,12 @@ export function AuthenticationConfig({
/> />
</div> </div>
<p className="text-xs text-gray-500">* OAuth 2.0 Client Credentials Grant .</p> <p className="text-xs text-muted-foreground">* OAuth 2.0 Client Credentials Grant .</p>
</div> </div>
)} )}
{authType === "db-token" && ( {authType === "db-token" && (
<div className="space-y-4 rounded-md border bg-gray-50 p-4"> <div className="space-y-4 rounded-md border bg-muted p-4">
<h4 className="text-sm font-medium">DB </h4> <h4 className="text-sm font-medium">DB </h4>
<div className="space-y-2"> <div className="space-y-2">
@ -275,14 +275,14 @@ export function AuthenticationConfig({
/> />
</div> </div>
<p className="text-xs text-gray-500"> <p className="text-xs text-muted-foreground">
company_code는 . company_code는 .
</p> </p>
</div> </div>
)} )}
{authType === "none" && ( {authType === "none" && (
<div className="rounded-md border border-dashed p-4 text-center text-sm text-gray-500"> <div className="rounded-md border border-dashed p-4 text-center text-sm text-muted-foreground">
API입니다. API입니다.
</div> </div>
)} )}

View File

@ -341,14 +341,14 @@ export function CreateTableModal({
<div className="grid grid-cols-1 gap-4 md:grid-cols-2"> <div className="grid grid-cols-1 gap-4 md:grid-cols-2">
<div className="space-y-2"> <div className="space-y-2">
<Label htmlFor="tableName"> <Label htmlFor="tableName">
<span className="text-red-500">*</span> <span className="text-destructive">*</span>
</Label> </Label>
<Input <Input
id="tableName" id="tableName"
value={tableName} value={tableName}
onChange={(e) => handleTableNameChange(e.target.value)} onChange={(e) => handleTableNameChange(e.target.value)}
placeholder="예: customer_info" placeholder="예: customer_info"
className={tableNameError ? "border-red-300" : ""} className={tableNameError ? "border-destructive/30" : ""}
/> />
{tableNameError && <p className="text-destructive text-sm">{tableNameError}</p>} {tableNameError && <p className="text-destructive text-sm">{tableNameError}</p>}
<p className="text-muted-foreground text-xs"> , // </p> <p className="text-muted-foreground text-xs"> , // </p>
@ -369,7 +369,7 @@ export function CreateTableModal({
<div className="space-y-3"> <div className="space-y-3">
<div className="flex items-center justify-between"> <div className="flex items-center justify-between">
<Label> <Label>
<span className="text-red-500">*</span> <span className="text-destructive">*</span>
</Label> </Label>
<Button type="button" variant="outline" size="sm" onClick={addColumn} disabled={loading}> <Button type="button" variant="outline" size="sm" onClick={addColumn} disabled={loading}>
<Plus className="mr-1 h-4 w-4" /> <Plus className="mr-1 h-4 w-4" />
@ -440,7 +440,7 @@ export function CreateTableModal({
<div className="font-medium">:</div> <div className="font-medium">:</div>
<ul className="list-inside list-disc space-y-1"> <ul className="list-inside list-disc space-y-1">
{validationResult.warnings.map((warning: string, index: number) => ( {validationResult.warnings.map((warning: string, index: number) => (
<li key={index} className="text-sm text-orange-600"> <li key={index} className="text-sm text-amber-600">
{warning} {warning}
</li> </li>
))} ))}
@ -471,7 +471,7 @@ export function CreateTableModal({
<Button <Button
onClick={handleCreateTable} onClick={handleCreateTable}
disabled={!isFormValid || loading || (validationResult && !validationResult.isValid)} disabled={!isFormValid || loading || (validationResult && !validationResult.isValid)}
className="bg-green-600 hover:bg-green-700" className="bg-emerald-600 hover:bg-green-700"
> >
{loading ? ( {loading ? (
<> <>

View File

@ -276,11 +276,11 @@ export function DDLLogViewer({ isOpen, onClose }: DDLLogViewerProps) {
<TableCell> <TableCell>
<div className="flex items-center gap-2"> <div className="flex items-center gap-2">
{log.success ? ( {log.success ? (
<CheckCircle2 className="h-4 w-4 text-green-600" /> <CheckCircle2 className="h-4 w-4 text-emerald-600" />
) : ( ) : (
<XCircle className="h-4 w-4 text-destructive" /> <XCircle className="h-4 w-4 text-destructive" />
)} )}
<span className={log.success ? "text-green-600" : "text-destructive"}> <span className={log.success ? "text-emerald-600" : "text-destructive"}>
{log.success ? "성공" : "실패"} {log.success ? "성공" : "실패"}
</span> </span>
</div> </div>
@ -323,7 +323,7 @@ export function DDLLogViewer({ isOpen, onClose }: DDLLogViewerProps) {
<CardTitle className="text-sm font-medium"></CardTitle> <CardTitle className="text-sm font-medium"></CardTitle>
</CardHeader> </CardHeader>
<CardContent> <CardContent>
<div className="text-2xl font-bold text-green-600">{statistics.successfulExecutions}</div> <div className="text-2xl font-bold text-emerald-600">{statistics.successfulExecutions}</div>
</CardContent> </CardContent>
</Card> </Card>

View File

@ -484,7 +484,7 @@ export const ExternalDbConnectionModal: React.FC<ExternalDbConnectionModalProps>
<div <div
className={`rounded-md border p-3 text-sm ${ className={`rounded-md border p-3 text-sm ${
testResult.success testResult.success
? "border-green-200 bg-green-50 text-green-800" ? "border-emerald-200 bg-emerald-50 text-emerald-800"
: "border-destructive/20 bg-destructive/10 text-red-800" : "border-destructive/20 bg-destructive/10 text-red-800"
}`} }`}
> >
@ -527,7 +527,7 @@ export const ExternalDbConnectionModal: React.FC<ExternalDbConnectionModalProps>
</Button> </Button>
{showAdvanced && ( {showAdvanced && (
<div className="space-y-4 border-l-2 border-gray-200 pl-6"> <div className="space-y-4 border-l-2 border-border pl-6">
<div className="grid grid-cols-3 gap-4"> <div className="grid grid-cols-3 gap-4">
<div> <div>
<Label htmlFor="connection_timeout"> ()</Label> <Label htmlFor="connection_timeout"> ()</Label>

View File

@ -116,7 +116,7 @@ export function HeadersManager({ headers, onChange }: HeadersManagerProps) {
variant="ghost" variant="ghost"
size="sm" size="sm"
onClick={() => removeHeader(index)} onClick={() => removeHeader(index)}
className="h-8 w-8 p-0 text-red-600 hover:bg-red-50 hover:text-red-700" className="h-8 w-8 p-0 text-destructive hover:bg-destructive/10 hover:text-destructive"
> >
<Trash2 className="h-4 w-4" /> <Trash2 className="h-4 w-4" />
</Button> </Button>
@ -127,12 +127,12 @@ export function HeadersManager({ headers, onChange }: HeadersManagerProps) {
</Table> </Table>
</div> </div>
) : ( ) : (
<div className="rounded-md border border-dashed p-4 text-center text-sm text-gray-500"> <div className="rounded-md border border-dashed p-4 text-center text-sm text-muted-foreground">
. . . .
</div> </div>
)} )}
<p className="text-xs text-gray-500"> <p className="text-xs text-muted-foreground">
* HTTP . . * HTTP . .
</p> </p>
</div> </div>

View File

@ -239,30 +239,30 @@ export const LayoutFormModal: React.FC<LayoutFormModalProps> = ({ open, onOpenCh
<div className="mb-6 flex items-center justify-center"> <div className="mb-6 flex items-center justify-center">
<div className="flex items-center gap-4"> <div className="flex items-center gap-4">
<div <div
className={`flex items-center gap-2 ${step === "basic" ? "text-primary" : step === "template" || step === "advanced" ? "text-green-600" : "text-gray-400"}`} className={`flex items-center gap-2 ${step === "basic" ? "text-primary" : step === "template" || step === "advanced" ? "text-emerald-600" : "text-muted-foreground/70"}`}
> >
<div <div
className={`flex h-8 w-8 items-center justify-center rounded-full text-sm font-medium ${step === "basic" ? "bg-primary/20 text-primary" : step === "template" || step === "advanced" ? "bg-green-100 text-green-600" : "bg-gray-100"}`} className={`flex h-8 w-8 items-center justify-center rounded-full text-sm font-medium ${step === "basic" ? "bg-primary/20 text-primary" : step === "template" || step === "advanced" ? "bg-emerald-100 text-emerald-600" : "bg-muted"}`}
> >
1 1
</div> </div>
<span className="text-sm font-medium"> </span> <span className="text-sm font-medium"> </span>
</div> </div>
<div className="h-px w-8 bg-gray-300" /> <div className="h-px w-8 bg-muted/60" />
<div <div
className={`flex items-center gap-2 ${step === "template" ? "text-primary" : step === "advanced" ? "text-green-600" : "text-gray-400"}`} className={`flex items-center gap-2 ${step === "template" ? "text-primary" : step === "advanced" ? "text-emerald-600" : "text-muted-foreground/70"}`}
> >
<div <div
className={`flex h-8 w-8 items-center justify-center rounded-full text-sm font-medium ${step === "template" ? "bg-primary/20 text-primary" : step === "advanced" ? "bg-green-100 text-green-600" : "bg-gray-100"}`} className={`flex h-8 w-8 items-center justify-center rounded-full text-sm font-medium ${step === "template" ? "bg-primary/20 text-primary" : step === "advanced" ? "bg-emerald-100 text-emerald-600" : "bg-muted"}`}
> >
2 2
</div> </div>
<span className="text-sm font-medium">릿 </span> <span className="text-sm font-medium">릿 </span>
</div> </div>
<div className="h-px w-8 bg-gray-300" /> <div className="h-px w-8 bg-muted/60" />
<div className={`flex items-center gap-2 ${step === "advanced" ? "text-primary" : "text-gray-400"}`}> <div className={`flex items-center gap-2 ${step === "advanced" ? "text-primary" : "text-muted-foreground/70"}`}>
<div <div
className={`flex h-8 w-8 items-center justify-center rounded-full text-sm font-medium ${step === "advanced" ? "bg-primary/20 text-primary" : "bg-gray-100"}`} className={`flex h-8 w-8 items-center justify-center rounded-full text-sm font-medium ${step === "advanced" ? "bg-primary/20 text-primary" : "bg-muted"}`}
> >
3 3
</div> </div>
@ -305,7 +305,7 @@ export const LayoutFormModal: React.FC<LayoutFormModalProps> = ({ open, onOpenCh
<Card <Card
key={category.id} key={category.id}
className={`cursor-pointer transition-all ${ className={`cursor-pointer transition-all ${
formData.category === category.id ? "bg-accent ring-2 ring-blue-500" : "hover:bg-gray-50" formData.category === category.id ? "bg-accent ring-2 ring-ring" : "hover:bg-muted"
}`} }`}
onClick={() => setFormData((prev) => ({ ...prev, category: category.id }))} onClick={() => setFormData((prev) => ({ ...prev, category: category.id }))}
> >
@ -314,7 +314,7 @@ export const LayoutFormModal: React.FC<LayoutFormModalProps> = ({ open, onOpenCh
<IconComponent className="h-5 w-5 text-muted-foreground" /> <IconComponent className="h-5 w-5 text-muted-foreground" />
<div> <div>
<div className="font-medium">{category.name}</div> <div className="font-medium">{category.name}</div>
<div className="text-xs text-gray-500">{category.description}</div> <div className="text-xs text-muted-foreground">{category.description}</div>
</div> </div>
</div> </div>
</CardContent> </CardContent>
@ -341,13 +341,13 @@ export const LayoutFormModal: React.FC<LayoutFormModalProps> = ({ open, onOpenCh
<div className="space-y-4"> <div className="space-y-4">
<div> <div>
<Label> 릿 *</Label> <Label> 릿 *</Label>
<p className="mb-3 text-sm text-gray-500"> </p> <p className="mb-3 text-sm text-muted-foreground"> </p>
<div className="grid grid-cols-2 gap-3"> <div className="grid grid-cols-2 gap-3">
{LAYOUT_TEMPLATES.map((template) => ( {LAYOUT_TEMPLATES.map((template) => (
<Card <Card
key={template.id} key={template.id}
className={`cursor-pointer transition-all ${ className={`cursor-pointer transition-all ${
formData.template === template.id ? "bg-accent ring-2 ring-blue-500" : "hover:bg-gray-50" formData.template === template.id ? "bg-accent ring-2 ring-ring" : "hover:bg-muted"
}`} }`}
onClick={() => onClick={() =>
setFormData((prev) => ({ setFormData((prev) => ({
@ -364,8 +364,8 @@ export const LayoutFormModal: React.FC<LayoutFormModalProps> = ({ open, onOpenCh
<Badge variant="secondary">{template.zones} </Badge> <Badge variant="secondary">{template.zones} </Badge>
</div> </div>
<div className="text-sm text-muted-foreground">{template.description}</div> <div className="text-sm text-muted-foreground">{template.description}</div>
<div className="text-xs text-gray-500">: {template.example}</div> <div className="text-xs text-muted-foreground">: {template.example}</div>
<div className="rounded bg-gray-100 p-2 text-center font-mono text-xs">{template.icon}</div> <div className="rounded bg-muted p-2 text-center font-mono text-xs">{template.icon}</div>
</div> </div>
</CardContent> </CardContent>
</Card> </Card>
@ -418,7 +418,7 @@ export const LayoutFormModal: React.FC<LayoutFormModalProps> = ({ open, onOpenCh
> >
<Plus className="h-4 w-4" /> <Plus className="h-4 w-4" />
</Button> </Button>
<span className="text-sm text-gray-500"> </span> <span className="text-sm text-muted-foreground"> </span>
</div> </div>
</div> </div>
</div> </div>
@ -428,17 +428,17 @@ export const LayoutFormModal: React.FC<LayoutFormModalProps> = ({ open, onOpenCh
<div className="space-y-4"> <div className="space-y-4">
{generationResult ? ( {generationResult ? (
<Alert <Alert
className={generationResult.success ? "border-green-200 bg-green-50" : "border-destructive/20 bg-destructive/10"} className={generationResult.success ? "border-emerald-200 bg-emerald-50" : "border-destructive/20 bg-destructive/10"}
> >
<Info className="h-4 w-4" /> <Info className="h-4 w-4" />
<AlertDescription className={generationResult.success ? "text-green-800" : "text-red-800"}> <AlertDescription className={generationResult.success ? "text-emerald-800" : "text-red-800"}>
{generationResult.message} {generationResult.message}
{generationResult.success && generationResult.files && ( {generationResult.success && generationResult.files && (
<div className="mt-2"> <div className="mt-2">
<div className="text-sm font-medium"> :</div> <div className="text-sm font-medium"> :</div>
<ul className="mt-1 space-y-1 text-xs"> <ul className="mt-1 space-y-1 text-xs">
{generationResult.files.map((file, index) => ( {generationResult.files.map((file, index) => (
<li key={index} className="text-green-700"> <li key={index} className="text-emerald-700">
{file} {file}
</li> </li>
))} ))}

View File

@ -748,7 +748,7 @@ export const MenuFormModal: React.FC<MenuFormModalProps> = ({
</SelectContent> </SelectContent>
</Select> </Select>
{!isEdit && level !== 1 && ( {!isEdit && level !== 1 && (
<p className="text-xs text-gray-500">{getText(MENU_MANAGEMENT_KEYS.FORM_COMPANY_SUBMENU_NOTE)}</p> <p className="text-xs text-muted-foreground">{getText(MENU_MANAGEMENT_KEYS.FORM_COMPANY_SUBMENU_NOTE)}</p>
)} )}
</div> </div>
@ -817,7 +817,7 @@ export const MenuFormModal: React.FC<MenuFormModalProps> = ({
}} }}
> >
<div className="font-medium">{key.langKey}</div> <div className="font-medium">{key.langKey}</div>
{key.description && <div className="text-xs text-gray-500">{key.description}</div>} {key.description && <div className="text-xs text-muted-foreground">{key.description}</div>}
</div> </div>
))} ))}
</div> </div>
@ -825,7 +825,7 @@ export const MenuFormModal: React.FC<MenuFormModalProps> = ({
)} )}
</div> </div>
{selectedLangKeyInfo && ( {selectedLangKeyInfo && (
<p className="text-xs text-gray-500"> <p className="text-xs text-muted-foreground">
{getText(MENU_MANAGEMENT_KEYS.FORM_LANG_KEY_SELECTED) {getText(MENU_MANAGEMENT_KEYS.FORM_LANG_KEY_SELECTED)
.replace("{key}", selectedLangKeyInfo.langKey) .replace("{key}", selectedLangKeyInfo.langKey)
.replace("{description}", selectedLangKeyInfo.description)} .replace("{description}", selectedLangKeyInfo.description)}
@ -896,7 +896,7 @@ export const MenuFormModal: React.FC<MenuFormModalProps> = ({
{/* 검색 입력 */} {/* 검색 입력 */}
<div className="sticky top-0 border-b bg-white p-2"> <div className="sticky top-0 border-b bg-white p-2">
<div className="relative"> <div className="relative">
<Search className="absolute top-1/2 left-2 h-4 w-4 -translate-y-1/2 text-gray-400" /> <Search className="absolute top-1/2 left-2 h-4 w-4 -translate-y-1/2 text-muted-foreground/70" />
<Input <Input
placeholder="화면 검색..." placeholder="화면 검색..."
value={screenSearchText} value={screenSearchText}
@ -918,14 +918,14 @@ export const MenuFormModal: React.FC<MenuFormModalProps> = ({
<div <div
key={`screen-${screen.screenId || screen.id || index}-${screen.screenCode || index}`} key={`screen-${screen.screenId || screen.id || index}-${screen.screenCode || index}`}
onClick={() => handleScreenSelect(screen)} onClick={() => handleScreenSelect(screen)}
className="cursor-pointer border-b px-3 py-2 last:border-b-0 hover:bg-gray-100" className="cursor-pointer border-b px-3 py-2 last:border-b-0 hover:bg-muted"
> >
<div className="flex items-center justify-between"> <div className="flex items-center justify-between">
<div> <div>
<div className="text-sm font-medium">{screen.screenName}</div> <div className="text-sm font-medium">{screen.screenName}</div>
<div className="text-xs text-gray-500">{screen.screenCode}</div> <div className="text-xs text-muted-foreground">{screen.screenCode}</div>
</div> </div>
<div className="text-xs text-gray-400">ID: {screen.screenId || screen.id || "N/A"}</div> <div className="text-xs text-muted-foreground/70">ID: {screen.screenId || screen.id || "N/A"}</div>
</div> </div>
</div> </div>
))} ))}
@ -933,7 +933,7 @@ export const MenuFormModal: React.FC<MenuFormModalProps> = ({
(screen) => (screen) =>
screen.screenName.toLowerCase().includes(screenSearchText.toLowerCase()) || screen.screenName.toLowerCase().includes(screenSearchText.toLowerCase()) ||
screen.screenCode.toLowerCase().includes(screenSearchText.toLowerCase()), screen.screenCode.toLowerCase().includes(screenSearchText.toLowerCase()),
).length === 0 && <div className="px-3 py-2 text-sm text-gray-500"> .</div>} ).length === 0 && <div className="px-3 py-2 text-sm text-muted-foreground"> .</div>}
</div> </div>
</div> </div>
)} )}
@ -942,7 +942,7 @@ export const MenuFormModal: React.FC<MenuFormModalProps> = ({
{/* 선택된 화면 정보 표시 */} {/* 선택된 화면 정보 표시 */}
{selectedScreen && ( {selectedScreen && (
<div className="bg-accent rounded-md border p-3"> <div className="bg-accent rounded-md border p-3">
<div className="text-sm font-medium text-blue-900">{selectedScreen.screenName}</div> <div className="text-sm font-medium text-primary">{selectedScreen.screenName}</div>
<div className="text-primary text-xs">: {selectedScreen.screenCode}</div> <div className="text-primary text-xs">: {selectedScreen.screenCode}</div>
<div className="text-primary text-xs"> URL: {formData.menuUrl}</div> <div className="text-primary text-xs"> URL: {formData.menuUrl}</div>
</div> </div>
@ -971,7 +971,7 @@ export const MenuFormModal: React.FC<MenuFormModalProps> = ({
{/* 검색창 */} {/* 검색창 */}
<div className="border-b p-2"> <div className="border-b p-2">
<div className="relative"> <div className="relative">
<Search className="absolute top-2.5 left-2 h-4 w-4 text-gray-400" /> <Search className="absolute top-2.5 left-2 h-4 w-4 text-muted-foreground/70" />
<Input <Input
type="text" type="text"
placeholder="대시보드 검색..." placeholder="대시보드 검색..."
@ -995,13 +995,13 @@ export const MenuFormModal: React.FC<MenuFormModalProps> = ({
<div <div
key={dashboard.id} key={dashboard.id}
onClick={() => handleDashboardSelect(dashboard)} onClick={() => handleDashboardSelect(dashboard)}
className="cursor-pointer border-b px-3 py-2 last:border-b-0 hover:bg-gray-100" className="cursor-pointer border-b px-3 py-2 last:border-b-0 hover:bg-muted"
> >
<div className="flex items-center justify-between"> <div className="flex items-center justify-between">
<div> <div>
<div className="text-sm font-medium">{dashboard.title}</div> <div className="text-sm font-medium">{dashboard.title}</div>
{dashboard.description && ( {dashboard.description && (
<div className="text-xs text-gray-500">{dashboard.description}</div> <div className="text-xs text-muted-foreground">{dashboard.description}</div>
)} )}
</div> </div>
</div> </div>
@ -1012,7 +1012,7 @@ export const MenuFormModal: React.FC<MenuFormModalProps> = ({
dashboard.title.toLowerCase().includes(dashboardSearchText.toLowerCase()) || dashboard.title.toLowerCase().includes(dashboardSearchText.toLowerCase()) ||
(dashboard.description && (dashboard.description &&
dashboard.description.toLowerCase().includes(dashboardSearchText.toLowerCase())), dashboard.description.toLowerCase().includes(dashboardSearchText.toLowerCase())),
).length === 0 && <div className="px-3 py-2 text-sm text-gray-500"> .</div>} ).length === 0 && <div className="px-3 py-2 text-sm text-muted-foreground"> .</div>}
</div> </div>
</div> </div>
)} )}
@ -1021,7 +1021,7 @@ export const MenuFormModal: React.FC<MenuFormModalProps> = ({
{/* 선택된 대시보드 정보 표시 */} {/* 선택된 대시보드 정보 표시 */}
{selectedDashboard && ( {selectedDashboard && (
<div className="bg-accent rounded-md border p-3"> <div className="bg-accent rounded-md border p-3">
<div className="text-sm font-medium text-blue-900">{selectedDashboard.title}</div> <div className="text-sm font-medium text-primary">{selectedDashboard.title}</div>
{selectedDashboard.description && ( {selectedDashboard.description && (
<div className="text-primary text-xs">: {selectedDashboard.description}</div> <div className="text-primary text-xs">: {selectedDashboard.description}</div>
)} )}

View File

@ -745,7 +745,7 @@ export function MenuPermissionsTable({ permissions, onPermissionsChange, roleGro
<span> (C)</span> <span> (C)</span>
<Checkbox <Checkbox
onCheckedChange={(checked) => handleSelectAll("createYn", checked as boolean)} onCheckedChange={(checked) => handleSelectAll("createYn", checked as boolean)}
className="data-[state=checked]:bg-green-600" className="data-[state=checked]:bg-emerald-600"
/> />
</div> </div>
</TableHead> </TableHead>
@ -754,7 +754,7 @@ export function MenuPermissionsTable({ permissions, onPermissionsChange, roleGro
<span> (R)</span> <span> (R)</span>
<Checkbox <Checkbox
onCheckedChange={(checked) => handleSelectAll("readYn", checked as boolean)} onCheckedChange={(checked) => handleSelectAll("readYn", checked as boolean)}
className="data-[state=checked]:bg-blue-600" className="data-[state=checked]:bg-primary"
/> />
</div> </div>
</TableHead> </TableHead>
@ -772,7 +772,7 @@ export function MenuPermissionsTable({ permissions, onPermissionsChange, roleGro
<span> (D)</span> <span> (D)</span>
<Checkbox <Checkbox
onCheckedChange={(checked) => handleSelectAll("deleteYn", checked as boolean)} onCheckedChange={(checked) => handleSelectAll("deleteYn", checked as boolean)}
className="data-[state=checked]:bg-red-600" className="data-[state=checked]:bg-destructive"
/> />
</div> </div>
</TableHead> </TableHead>
@ -834,7 +834,7 @@ export function MenuPermissionsTable({ permissions, onPermissionsChange, roleGro
<Checkbox <Checkbox
checked={menu.createYn === "Y"} checked={menu.createYn === "Y"}
onCheckedChange={(checked) => handlePermissionChange(menu.menuObjid, "createYn", checked as boolean)} onCheckedChange={(checked) => handlePermissionChange(menu.menuObjid, "createYn", checked as boolean)}
className="data-[state=checked]:bg-green-600" className="data-[state=checked]:bg-emerald-600"
/> />
</div> </div>
<div className="flex items-center justify-between text-sm"> <div className="flex items-center justify-between text-sm">
@ -842,7 +842,7 @@ export function MenuPermissionsTable({ permissions, onPermissionsChange, roleGro
<Checkbox <Checkbox
checked={menu.readYn === "Y"} checked={menu.readYn === "Y"}
onCheckedChange={(checked) => handlePermissionChange(menu.menuObjid, "readYn", checked as boolean)} onCheckedChange={(checked) => handlePermissionChange(menu.menuObjid, "readYn", checked as boolean)}
className="data-[state=checked]:bg-blue-600" className="data-[state=checked]:bg-primary"
/> />
</div> </div>
<div className="flex items-center justify-between text-sm"> <div className="flex items-center justify-between text-sm">
@ -858,7 +858,7 @@ export function MenuPermissionsTable({ permissions, onPermissionsChange, roleGro
<Checkbox <Checkbox
checked={menu.deleteYn === "Y"} checked={menu.deleteYn === "Y"}
onCheckedChange={(checked) => handlePermissionChange(menu.menuObjid, "deleteYn", checked as boolean)} onCheckedChange={(checked) => handlePermissionChange(menu.menuObjid, "deleteYn", checked as boolean)}
className="data-[state=checked]:bg-red-600" className="data-[state=checked]:bg-destructive"
/> />
</div> </div>
</div> </div>

View File

@ -564,7 +564,7 @@ export function RestApiConnectionModal({ isOpen, onClose, onSave, connection }:
{testResult && ( {testResult && (
<div <div
className={`rounded-md border p-4 ${ className={`rounded-md border p-4 ${
testResult.success ? "border-green-200 bg-green-50" : "border-red-200 bg-red-50" testResult.success ? "border-emerald-200 bg-emerald-50" : "border-destructive/20 bg-destructive/10"
}`} }`}
> >
<div className="mb-2 flex items-center justify-between"> <div className="mb-2 flex items-center justify-between">
@ -572,17 +572,17 @@ export function RestApiConnectionModal({ isOpen, onClose, onSave, connection }:
{testResult.success ? "성공" : "실패"} {testResult.success ? "성공" : "실패"}
</Badge> </Badge>
{testResult.response_time && ( {testResult.response_time && (
<span className="text-sm text-gray-600"> : {testResult.response_time}ms</span> <span className="text-sm text-muted-foreground"> : {testResult.response_time}ms</span>
)} )}
</div> </div>
<p className="text-sm">{testResult.message}</p> <p className="text-sm">{testResult.message}</p>
{testResult.status_code && ( {testResult.status_code && (
<p className="mt-1 text-xs text-gray-500"> : {testResult.status_code}</p> <p className="mt-1 text-xs text-muted-foreground"> : {testResult.status_code}</p>
)} )}
{testResult.error_details && <p className="mt-2 text-xs text-red-600">{testResult.error_details}</p>} {testResult.error_details && <p className="mt-2 text-xs text-destructive">{testResult.error_details}</p>}
</div> </div>
)} )}
</div> </div>

View File

@ -79,9 +79,9 @@ export function RoleDeleteModal({ isOpen, onClose, onSuccess, role }: RoleDelete
<div className="space-y-4"> <div className="space-y-4">
{/* 경고 메시지 */} {/* 경고 메시지 */}
<div className="rounded-lg border border-orange-300 bg-orange-50 p-4"> <div className="rounded-lg border border-orange-300 bg-amber-50 p-4">
<div className="flex items-start gap-3"> <div className="flex items-start gap-3">
<AlertTriangle className="mt-0.5 h-5 w-5 flex-shrink-0 text-orange-600" /> <AlertTriangle className="mt-0.5 h-5 w-5 flex-shrink-0 text-amber-600" />
<div className="space-y-2"> <div className="space-y-2">
<p className="text-sm font-semibold text-orange-900"> ?</p> <p className="text-sm font-semibold text-orange-900"> ?</p>
<p className="text-xs text-orange-800"> <p className="text-xs text-orange-800">
@ -124,7 +124,7 @@ export function RoleDeleteModal({ isOpen, onClose, onSuccess, role }: RoleDelete
<div <div
className={`rounded-lg border p-3 text-sm ${ className={`rounded-lg border p-3 text-sm ${
alertType === "success" alertType === "success"
? "border-green-300 bg-green-50 text-green-800" ? "border-green-300 bg-emerald-50 text-emerald-800"
: "border-destructive/50 bg-destructive/10 text-destructive" : "border-destructive/50 bg-destructive/10 text-destructive"
}`} }`}
> >

View File

@ -194,7 +194,7 @@ export function RoleFormModal({ isOpen, onClose, onSuccess, editingRole }: RoleF
{/* 권한 그룹명 */} {/* 권한 그룹명 */}
<div> <div>
<Label htmlFor="authName" className="text-xs sm:text-sm"> <Label htmlFor="authName" className="text-xs sm:text-sm">
<span className="text-red-500">*</span> <span className="text-destructive">*</span>
</Label> </Label>
<Input <Input
id="authName" id="authName"
@ -210,7 +210,7 @@ export function RoleFormModal({ isOpen, onClose, onSuccess, editingRole }: RoleF
{/* 권한 코드 */} {/* 권한 코드 */}
<div> <div>
<Label htmlFor="authCode" className="text-xs sm:text-sm"> <Label htmlFor="authCode" className="text-xs sm:text-sm">
<span className="text-red-500">*</span> <span className="text-destructive">*</span>
</Label> </Label>
<Input <Input
id="authCode" id="authCode"
@ -243,7 +243,7 @@ export function RoleFormModal({ isOpen, onClose, onSuccess, editingRole }: RoleF
) : ( ) : (
<div> <div>
<Label htmlFor="companyCode" className="text-xs sm:text-sm"> <Label htmlFor="companyCode" className="text-xs sm:text-sm">
<span className="text-red-500">*</span> <span className="text-destructive">*</span>
</Label> </Label>
{isSuperAdmin ? ( {isSuperAdmin ? (
<> <>
@ -345,10 +345,10 @@ export function RoleFormModal({ isOpen, onClose, onSuccess, editingRole }: RoleF
<div <div
className={`rounded-lg border p-3 text-sm ${ className={`rounded-lg border p-3 text-sm ${
alertType === "success" alertType === "success"
? "border-green-300 bg-green-50 text-green-800" ? "border-green-300 bg-emerald-50 text-emerald-800"
: alertType === "error" : alertType === "error"
? "border-destructive/50 bg-destructive/10 text-destructive" ? "border-destructive/50 bg-destructive/10 text-destructive"
: "border-blue-300 bg-blue-50 text-blue-800" : "border-primary/40 bg-primary/10 text-primary"
}`} }`}
> >
<div className="flex items-start gap-2"> <div className="flex items-start gap-2">

View File

@ -223,7 +223,7 @@ export const ScreenAssignmentTab: React.FC<ScreenAssignmentTabProps> = ({ menus
</div> </div>
{selectedMenu && ( {selectedMenu && (
<div className="rounded-lg border bg-gray-50 p-4"> <div className="rounded-lg border bg-muted p-4">
<div className="flex items-center justify-between"> <div className="flex items-center justify-between">
<div> <div>
<h3 className="font-medium"> <h3 className="font-medium">
@ -267,7 +267,7 @@ export const ScreenAssignmentTab: React.FC<ScreenAssignmentTabProps> = ({ menus
<Monitor className="h-5 w-5" /> <Monitor className="h-5 w-5" />
({assignedScreens.length}) ({assignedScreens.length})
</CardTitle> </CardTitle>
<Button onClick={openAssignDialog} className="bg-blue-600 hover:bg-blue-700"> <Button onClick={openAssignDialog} className="bg-primary hover:bg-primary/90">
<Plus className="mr-2 h-4 w-4" /> <Plus className="mr-2 h-4 w-4" />
</Button> </Button>
@ -275,15 +275,15 @@ export const ScreenAssignmentTab: React.FC<ScreenAssignmentTabProps> = ({ menus
</CardHeader> </CardHeader>
<CardContent> <CardContent>
{loading ? ( {loading ? (
<div className="py-8 text-center text-gray-500"> ...</div> <div className="py-8 text-center text-muted-foreground"> ...</div>
) : assignedScreens.length === 0 ? ( ) : assignedScreens.length === 0 ? (
<div className="py-8 text-center text-gray-500"> . .</div> <div className="py-8 text-center text-muted-foreground"> . .</div>
) : ( ) : (
<div className="space-y-3"> <div className="space-y-3">
{assignedScreens.map((screen) => ( {assignedScreens.map((screen) => (
<div <div
key={screen.screenId} key={screen.screenId}
className="flex items-center justify-between rounded-lg border p-4 hover:bg-gray-50" className="flex items-center justify-between rounded-lg border p-4 hover:bg-muted"
> >
<div className="flex-1"> <div className="flex-1">
<div className="flex items-center gap-3"> <div className="flex items-center gap-3">
@ -298,7 +298,7 @@ export const ScreenAssignmentTab: React.FC<ScreenAssignmentTabProps> = ({ menus
<p className="mt-1 text-sm text-muted-foreground"> <p className="mt-1 text-sm text-muted-foreground">
: {screen.tableName} | : {screen.createdDate.toLocaleDateString()} : {screen.tableName} | : {screen.createdDate.toLocaleDateString()}
</p> </p>
{screen.description && <p className="mt-1 text-sm text-gray-500">{screen.description}</p>} {screen.description && <p className="mt-1 text-sm text-muted-foreground">{screen.description}</p>}
</div> </div>
<Button <Button
variant="outline" variant="outline"
@ -307,7 +307,7 @@ export const ScreenAssignmentTab: React.FC<ScreenAssignmentTabProps> = ({ menus
setSelectedScreen(screen); setSelectedScreen(screen);
setShowUnassignDialog(true); setShowUnassignDialog(true);
}} }}
className="text-destructive hover:text-red-700" className="text-destructive hover:text-destructive"
> >
<X className="h-4 w-4" /> <X className="h-4 w-4" />
</Button> </Button>
@ -330,7 +330,7 @@ export const ScreenAssignmentTab: React.FC<ScreenAssignmentTabProps> = ({ menus
<div className="space-y-4"> <div className="space-y-4">
{/* 검색 */} {/* 검색 */}
<div className="relative"> <div className="relative">
<Search className="absolute top-1/2 left-3 h-4 w-4 -translate-y-1/2 transform text-gray-400" /> <Search className="absolute top-1/2 left-3 h-4 w-4 -translate-y-1/2 transform text-muted-foreground/70" />
<Input <Input
placeholder="화면명 또는 코드로 검색..." placeholder="화면명 또는 코드로 검색..."
value={searchTerm} value={searchTerm}
@ -342,13 +342,13 @@ export const ScreenAssignmentTab: React.FC<ScreenAssignmentTabProps> = ({ menus
{/* 화면 목록 */} {/* 화면 목록 */}
<div className="max-h-64 space-y-2 overflow-y-auto"> <div className="max-h-64 space-y-2 overflow-y-auto">
{filteredAvailableScreens.length === 0 ? ( {filteredAvailableScreens.length === 0 ? (
<div className="py-4 text-center text-gray-500"> .</div> <div className="py-4 text-center text-muted-foreground"> .</div>
) : ( ) : (
filteredAvailableScreens.map((screen) => ( filteredAvailableScreens.map((screen) => (
<div <div
key={screen.screenId} key={screen.screenId}
className={`cursor-pointer rounded-lg border p-3 transition-colors ${ className={`cursor-pointer rounded-lg border p-3 transition-colors ${
selectedScreen?.screenId === screen.screenId ? "border-primary bg-accent" : "hover:bg-gray-50" selectedScreen?.screenId === screen.screenId ? "border-primary bg-accent" : "hover:bg-muted"
}`} }`}
onClick={() => setSelectedScreen(screen)} onClick={() => setSelectedScreen(screen)}
> >
@ -385,7 +385,7 @@ export const ScreenAssignmentTab: React.FC<ScreenAssignmentTabProps> = ({ menus
</AlertDialogHeader> </AlertDialogHeader>
<AlertDialogFooter> <AlertDialogFooter>
<AlertDialogCancel onClick={() => setSelectedScreen(null)}></AlertDialogCancel> <AlertDialogCancel onClick={() => setSelectedScreen(null)}></AlertDialogCancel>
<AlertDialogAction onClick={handleUnassignScreen} className="bg-red-600 hover:bg-red-700"> <AlertDialogAction onClick={handleUnassignScreen} className="bg-destructive hover:bg-red-700">
</AlertDialogAction> </AlertDialogAction>
</AlertDialogFooter> </AlertDialogFooter>

View File

@ -116,7 +116,7 @@ export function SortableCodeItem({
}} }}
onPointerDown={(e) => e.stopPropagation()} onPointerDown={(e) => e.stopPropagation()}
onMouseDown={(e) => e.stopPropagation()} onMouseDown={(e) => e.stopPropagation()}
className="text-muted-foreground hover:text-foreground -ml-1 flex h-5 w-5 items-center justify-center rounded transition-colors hover:bg-gray-100" className="text-muted-foreground hover:text-foreground -ml-1 flex h-5 w-5 items-center justify-center rounded transition-colors hover:bg-muted"
title={isExpanded ? "접기" : "펼치기"} title={isExpanded ? "접기" : "펼치기"}
> >
{isExpanded ? <ChevronDown className="h-4 w-4" /> : <ChevronRight className="h-4 w-4" />} {isExpanded ? <ChevronDown className="h-4 w-4" /> : <ChevronRight className="h-4 w-4" />}
@ -135,12 +135,12 @@ export function SortableCodeItem({
</Badge> </Badge>
)} )}
{depth === 2 && ( {depth === 2 && (
<Badge variant="outline" className="bg-blue-50 px-1.5 py-0 text-[10px] text-blue-600"> <Badge variant="outline" className="bg-primary/10 px-1.5 py-0 text-[10px] text-primary">
</Badge> </Badge>
)} )}
{depth === 3 && ( {depth === 3 && (
<Badge variant="outline" className="bg-green-50 px-1.5 py-0 text-[10px] text-green-600"> <Badge variant="outline" className="bg-emerald-50 px-1.5 py-0 text-[10px] text-emerald-600">
</Badge> </Badge>
)} )}
@ -196,7 +196,7 @@ export function SortableCodeItem({
onAddChild(); onAddChild();
}} }}
title="하위 코드 추가" title="하위 코드 추가"
className="text-blue-600 hover:bg-blue-50 hover:text-blue-700" className="text-primary hover:bg-primary/10 hover:text-primary"
> >
<Plus className="h-3 w-3" /> <Plus className="h-3 w-3" />
</Button> </Button>

View File

@ -102,11 +102,11 @@ export function TableLogViewer({ tableName, open, onOpenChange }: TableLogViewer
const getOperationBadge = (type: string) => { const getOperationBadge = (type: string) => {
switch (type) { switch (type) {
case "INSERT": case "INSERT":
return <Badge className="bg-green-500"></Badge>; return <Badge className="bg-emerald-500"></Badge>;
case "UPDATE": case "UPDATE":
return <Badge className="bg-blue-500"></Badge>; return <Badge className="bg-primary"></Badge>;
case "DELETE": case "DELETE":
return <Badge className="bg-red-500"></Badge>; return <Badge className="bg-destructive"></Badge>;
default: default:
return <Badge>{type}</Badge>; return <Badge>{type}</Badge>;
} }
@ -150,7 +150,7 @@ export function TableLogViewer({ tableName, open, onOpenChange }: TableLogViewer
<div className="grid grid-cols-2 gap-3 md:grid-cols-3"> <div className="grid grid-cols-2 gap-3 md:grid-cols-3">
<div> <div>
<label className="mb-1 block text-sm text-gray-600"> </label> <label className="mb-1 block text-sm text-muted-foreground"> </label>
{/* Radix UI Select v2.x: 빈 문자열 value="" 금지 → "__all__" 사용 */} {/* Radix UI Select v2.x: 빈 문자열 value="" 금지 → "__all__" 사용 */}
<Select <Select
value={operationType || "__all__"} value={operationType || "__all__"}
@ -169,22 +169,22 @@ export function TableLogViewer({ tableName, open, onOpenChange }: TableLogViewer
</div> </div>
<div> <div>
<label className="mb-1 block text-sm text-gray-600"> </label> <label className="mb-1 block text-sm text-muted-foreground"> </label>
<Input type="date" value={startDate} onChange={(e) => setStartDate(e.target.value)} /> <Input type="date" value={startDate} onChange={(e) => setStartDate(e.target.value)} />
</div> </div>
<div> <div>
<label className="mb-1 block text-sm text-gray-600"> </label> <label className="mb-1 block text-sm text-muted-foreground"> </label>
<Input type="date" value={endDate} onChange={(e) => setEndDate(e.target.value)} /> <Input type="date" value={endDate} onChange={(e) => setEndDate(e.target.value)} />
</div> </div>
<div> <div>
<label className="mb-1 block text-sm text-gray-600"></label> <label className="mb-1 block text-sm text-muted-foreground"></label>
<Input placeholder="사용자 ID" value={changedBy} onChange={(e) => setChangedBy(e.target.value)} /> <Input placeholder="사용자 ID" value={changedBy} onChange={(e) => setChangedBy(e.target.value)} />
</div> </div>
<div> <div>
<label className="mb-1 block text-sm text-gray-600"> ID</label> <label className="mb-1 block text-sm text-muted-foreground"> ID</label>
<Input placeholder="레코드 ID" value={originalId} onChange={(e) => setOriginalId(e.target.value)} /> <Input placeholder="레코드 ID" value={originalId} onChange={(e) => setOriginalId(e.target.value)} />
</div> </div>
@ -204,7 +204,7 @@ export function TableLogViewer({ tableName, open, onOpenChange }: TableLogViewer
<LoadingSpinner /> <LoadingSpinner />
</div> </div>
) : logs.length === 0 ? ( ) : logs.length === 0 ? (
<div className="flex h-64 items-center justify-center text-gray-500"> .</div> <div className="flex h-64 items-center justify-center text-muted-foreground"> .</div>
) : ( ) : (
<Table> <Table>
<TableHeader> <TableHeader>
@ -243,7 +243,7 @@ export function TableLogViewer({ tableName, open, onOpenChange }: TableLogViewer
{/* 페이지네이션 */} {/* 페이지네이션 */}
<div className="flex items-center justify-between border-t pt-4"> <div className="flex items-center justify-between border-t pt-4">
<div className="text-sm text-gray-600"> <div className="text-sm text-muted-foreground">
{total} ( {page} / {totalPages}) {total} ( {page} / {totalPages})
</div> </div>
<div className="flex gap-2"> <div className="flex gap-2">

View File

@ -157,12 +157,12 @@ export function TemplateImportExport({ onTemplateImported }: TemplateImportExpor
</CardHeader> </CardHeader>
<CardContent className="space-y-4"> <CardContent className="space-y-4">
<div <div
className="cursor-pointer rounded-lg border-2 border-dashed border-gray-300 p-8 text-center transition-colors hover:border-gray-400" className="cursor-pointer rounded-lg border-2 border-dashed border-input p-8 text-center transition-colors hover:border-input"
onClick={triggerFileSelect} onClick={triggerFileSelect}
> >
<Upload className="mx-auto mb-4 h-12 w-12 text-gray-400" /> <Upload className="mx-auto mb-4 h-12 w-12 text-muted-foreground/70" />
<p className="mb-2 text-lg font-medium text-gray-900">릿 JSON </p> <p className="mb-2 text-lg font-medium text-foreground">릿 JSON </p>
<p className="text-sm text-gray-500"> JSON </p> <p className="text-sm text-muted-foreground"> JSON </p>
</div> </div>
<input ref={fileInputRef} type="file" accept=".json" onChange={handleFileUpload} className="hidden" /> <input ref={fileInputRef} type="file" accept=".json" onChange={handleFileUpload} className="hidden" />
</CardContent> </CardContent>
@ -196,7 +196,7 @@ export function TemplateImportExport({ onTemplateImported }: TemplateImportExpor
<Card> <Card>
<CardHeader> <CardHeader>
<CardTitle className="flex items-center text-lg"> <CardTitle className="flex items-center text-lg">
<CheckCircle className="mr-2 h-5 w-5 text-green-600" /> <CheckCircle className="mr-2 h-5 w-5 text-emerald-600" />
3. 릿 3. 릿
</CardTitle> </CardTitle>
</CardHeader> </CardHeader>

View File

@ -54,28 +54,28 @@ export function UserAuthEditModal({ isOpen, onClose, onSuccess, user }: UserAuth
label: "회사 관리자", label: "회사 관리자",
description: "자기 회사 데이터 및 사용자 관리 가능", description: "자기 회사 데이터 및 사용자 관리 가능",
icon: <Building2 className="h-4 w-4" />, icon: <Building2 className="h-4 w-4" />,
color: "text-blue-600", color: "text-primary",
}, },
{ {
value: "USER", value: "USER",
label: "일반 사용자", label: "일반 사용자",
description: "자기 회사 데이터 조회/수정만 가능", description: "자기 회사 데이터 조회/수정만 가능",
icon: <User className="h-4 w-4" />, icon: <User className="h-4 w-4" />,
color: "text-gray-600", color: "text-muted-foreground",
}, },
{ {
value: "GUEST", value: "GUEST",
label: "게스트", label: "게스트",
description: "제한된 조회 권한", description: "제한된 조회 권한",
icon: <Users className="h-4 w-4" />, icon: <Users className="h-4 w-4" />,
color: "text-green-600", color: "text-emerald-600",
}, },
{ {
value: "PARTNER", value: "PARTNER",
label: "협력업체", label: "협력업체",
description: "협력업체 전용 권한", description: "협력업체 전용 권한",
icon: <Shield className="h-4 w-4" />, icon: <Shield className="h-4 w-4" />,
color: "text-orange-600", color: "text-amber-600",
}, },
]; ];
@ -158,7 +158,7 @@ export function UserAuthEditModal({ isOpen, onClose, onSuccess, user }: UserAuth
{/* 권한 선택 */} {/* 권한 선택 */}
<div className="space-y-2"> <div className="space-y-2">
<Label htmlFor="userType" className="text-sm font-medium"> <Label htmlFor="userType" className="text-sm font-medium">
<span className="text-red-500">*</span> <span className="text-destructive">*</span>
</Label> </Label>
<Select value={selectedUserType} onValueChange={setSelectedUserType}> <Select value={selectedUserType} onValueChange={setSelectedUserType}>
<SelectTrigger className="h-10"> <SelectTrigger className="h-10">
@ -180,9 +180,9 @@ export function UserAuthEditModal({ isOpen, onClose, onSuccess, user }: UserAuth
{/* SUPER_ADMIN 경고 */} {/* SUPER_ADMIN 경고 */}
{showConfirmation && selectedUserType === "SUPER_ADMIN" && ( {showConfirmation && selectedUserType === "SUPER_ADMIN" && (
<div className="rounded-lg border border-orange-300 bg-orange-50 p-4"> <div className="rounded-lg border border-orange-300 bg-amber-50 p-4">
<div className="flex items-start gap-3"> <div className="flex items-start gap-3">
<AlertTriangle className="mt-0.5 h-5 w-5 flex-shrink-0 text-orange-600" /> <AlertTriangle className="mt-0.5 h-5 w-5 flex-shrink-0 text-amber-600" />
<div className="space-y-2"> <div className="space-y-2">
<p className="text-sm font-semibold text-orange-900"> </p> <p className="text-sm font-semibold text-orange-900"> </p>
<p className="text-xs text-orange-800"> <p className="text-xs text-orange-800">

View File

@ -23,7 +23,7 @@ function AlertModal({ isOpen, onClose, title, message, type = "info" }: AlertMod
const getTypeColor = () => { const getTypeColor = () => {
switch (type) { switch (type) {
case "success": case "success":
return "text-green-600"; return "text-emerald-600";
case "error": case "error":
return "text-destructive"; return "text-destructive";
default: default:
@ -446,7 +446,7 @@ export function UserFormModal({ isOpen, onClose, onSuccess, editingUser }: UserF
<div className="grid grid-cols-2 gap-4"> <div className="grid grid-cols-2 gap-4">
<div className="space-y-2"> <div className="space-y-2">
<Label htmlFor="userId" className="text-sm font-medium"> <Label htmlFor="userId" className="text-sm font-medium">
ID <span className="text-red-500">*</span> ID <span className="text-destructive">*</span>
</Label> </Label>
{isEditMode ? ( {isEditMode ? (
<Input id="userId" value={formData.userId} disabled className="bg-muted cursor-not-allowed" /> <Input id="userId" value={formData.userId} disabled className="bg-muted cursor-not-allowed" />
@ -474,7 +474,7 @@ export function UserFormModal({ isOpen, onClose, onSuccess, editingUser }: UserF
{/* 중복확인 결과 메시지 */} {/* 중복확인 결과 메시지 */}
{duplicateCheckMessage && ( {duplicateCheckMessage && (
<div <div
className={`mt-1 text-sm ${duplicateCheckType === "success" ? "text-green-600" : "text-destructive"}`} className={`mt-1 text-sm ${duplicateCheckType === "success" ? "text-emerald-600" : "text-destructive"}`}
> >
{duplicateCheckMessage} {duplicateCheckMessage}
</div> </div>
@ -485,7 +485,7 @@ export function UserFormModal({ isOpen, onClose, onSuccess, editingUser }: UserF
<div className="space-y-2"> <div className="space-y-2">
<Label htmlFor="userName" className="text-sm font-medium"> <Label htmlFor="userName" className="text-sm font-medium">
<span className="text-red-500">*</span> <span className="text-destructive">*</span>
</Label> </Label>
<Input <Input
id="userName" id="userName"
@ -501,7 +501,7 @@ export function UserFormModal({ isOpen, onClose, onSuccess, editingUser }: UserF
{!isEditMode && ( {!isEditMode && (
<div className="space-y-2"> <div className="space-y-2">
<Label htmlFor="userPassword" className="text-sm font-medium"> <Label htmlFor="userPassword" className="text-sm font-medium">
<span className="text-red-500">*</span> <span className="text-destructive">*</span>
</Label> </Label>
<div className="relative"> <div className="relative">
<Input <Input
@ -532,7 +532,7 @@ export function UserFormModal({ isOpen, onClose, onSuccess, editingUser }: UserF
{/* 회사 선택 */} {/* 회사 선택 */}
<div className="space-y-2"> <div className="space-y-2">
<Label htmlFor="companyCode" className="text-sm font-medium"> <Label htmlFor="companyCode" className="text-sm font-medium">
<span className="text-red-500">*</span> <span className="text-destructive">*</span>
</Label> </Label>
{isSuperAdmin ? ( {isSuperAdmin ? (
<> <>

View File

@ -136,7 +136,7 @@ export function UserPasswordResetModal({ isOpen, onClose, userId, userName, onSu
<div className="space-y-4" onKeyDown={handleKeyDown}> <div className="space-y-4" onKeyDown={handleKeyDown}>
{/* 대상 사용자 정보 */} {/* 대상 사용자 정보 */}
<div> <div>
<Label className="text-sm font-medium text-gray-700"> <Label className="text-sm font-medium text-foreground">
:{" "} :{" "}
<span className="font-semibold"> <span className="font-semibold">
{userName} ({userId}) {userName} ({userId})
@ -197,10 +197,10 @@ export function UserPasswordResetModal({ isOpen, onClose, userId, userName, onSu
{/* 비밀번호 일치 여부 표시 */} {/* 비밀번호 일치 여부 표시 */}
{showMismatchError && <p className="text-sm text-destructive"> .</p>} {showMismatchError && <p className="text-sm text-destructive"> .</p>}
{isPasswordMatch && <p className="text-sm text-green-600"> .</p>} {isPasswordMatch && <p className="text-sm text-emerald-600"> .</p>}
</div> </div>
<div className="text-xs text-gray-500">* , , .</div> <div className="text-xs text-muted-foreground">* , , .</div>
</div> </div>
<div className="mt-6 flex justify-end space-x-2"> <div className="mt-6 flex justify-end space-x-2">

View File

@ -59,7 +59,7 @@ export function DriverListView({ drivers, config, isCompact = false }: DriverLis
return ( return (
<div className="h-full w-full overflow-auto"> <div className="h-full w-full overflow-auto">
<table className="min-w-full divide-y divide-gray-200"> <table className="min-w-full divide-y divide-border">
<thead className="sticky top-0 z-10 bg-muted"> <thead className="sticky top-0 z-10 bg-muted">
<tr> <tr>
{visibleColumns.includes("status") && ( {visibleColumns.includes("status") && (
@ -96,7 +96,7 @@ export function DriverListView({ drivers, config, isCompact = false }: DriverLis
)} )}
</tr> </tr>
</thead> </thead>
<tbody className="divide-y divide-gray-200 bg-background"> <tbody className="divide-y divide-border bg-background">
{drivers.map((driver) => { {drivers.map((driver) => {
const statusColors = getStatusColor(driver.status); const statusColors = getStatusColor(driver.status);
return ( return (

View File

@ -180,16 +180,16 @@ export function ListWidget({ element, onConfigUpdate }: ListWidgetProps) {
const renderIcon = (icon?: string, color?: string) => { const renderIcon = (icon?: string, color?: string) => {
const colorClass = const colorClass =
color === "blue" color === "blue"
? "text-blue-600" ? "text-primary"
: color === "orange" : color === "orange"
? "text-orange-600" ? "text-amber-600"
: color === "green" : color === "green"
? "text-green-600" ? "text-emerald-600"
: color === "red" : color === "red"
? "text-red-600" ? "text-destructive"
: color === "purple" : color === "purple"
? "text-purple-600" ? "text-purple-600"
: "text-gray-600"; : "text-muted-foreground";
switch (icon) { switch (icon) {
case "truck": case "truck":
@ -209,16 +209,16 @@ export function ListWidget({ element, onConfigUpdate }: ListWidgetProps) {
const renderFieldGroup = (group: FieldGroup, data: Record<string, any>) => { const renderFieldGroup = (group: FieldGroup, data: Record<string, any>) => {
const colorClass = const colorClass =
group.color === "blue" group.color === "blue"
? "text-blue-600" ? "text-primary"
: group.color === "orange" : group.color === "orange"
? "text-orange-600" ? "text-amber-600"
: group.color === "green" : group.color === "green"
? "text-green-600" ? "text-emerald-600"
: group.color === "red" : group.color === "red"
? "text-red-600" ? "text-destructive"
: group.color === "purple" : group.color === "purple"
? "text-purple-600" ? "text-purple-600"
: "text-gray-600"; : "text-muted-foreground";
return ( return (
<div key={group.id} className="rounded-lg border p-4"> <div key={group.id} className="rounded-lg border p-4">

View File

@ -1592,11 +1592,11 @@ export default function DigitalTwinEditor({ layoutId, layoutName, onBack }: Digi
<div className="bg-muted flex items-center justify-center gap-2 border-b p-4"> <div className="bg-muted flex items-center justify-center gap-2 border-b p-4">
<span className="text-muted-foreground text-sm font-medium">:</span> <span className="text-muted-foreground text-sm font-medium">:</span>
{[ {[
{ type: "area" as ToolType, label: "영역", icon: Grid3x3, color: "text-blue-500" }, { type: "area" as ToolType, label: "영역", icon: Grid3x3, color: "text-primary" },
{ type: "location-bed" as ToolType, label: "베드", icon: Package, color: "text-blue-600" }, { type: "location-bed" as ToolType, label: "베드", icon: Package, color: "text-primary" },
{ type: "location-stp" as ToolType, label: "정차", icon: Move, color: "text-gray-500" }, { type: "location-stp" as ToolType, label: "정차", icon: Move, color: "text-muted-foreground" },
// { type: "crane-gantry" as ToolType, label: "겐트리", icon: Combine, color: "text-green-500" }, // { type: "crane-gantry" as ToolType, label: "겐트리", icon: Combine, color: "text-emerald-500" },
{ type: "crane-mobile" as ToolType, label: "크레인", icon: Truck, color: "text-yellow-500" }, { type: "crane-mobile" as ToolType, label: "크레인", icon: Truck, color: "text-amber-500" },
{ type: "rack" as ToolType, label: "랙", icon: Box, color: "text-purple-500" }, { type: "rack" as ToolType, label: "랙", icon: Box, color: "text-purple-500" },
].map((tool) => { ].map((tool) => {
const Icon = tool.icon; const Icon = tool.icon;
@ -1919,7 +1919,7 @@ export default function DigitalTwinEditor({ layoutId, layoutName, onBack }: Digi
</div> </div>
</div> </div>
{isLocationPlaced ? ( {isLocationPlaced ? (
<Check className="h-4 w-4 text-green-500" /> <Check className="h-4 w-4 text-emerald-500" />
) : locationType === "location-stp" ? ( ) : locationType === "location-stp" ? (
<ParkingCircle className="text-muted-foreground h-4 w-4" /> <ParkingCircle className="text-muted-foreground h-4 w-4" />
) : ( ) : (
@ -2052,7 +2052,7 @@ export default function DigitalTwinEditor({ layoutId, layoutName, onBack }: Digi
</div> </div>
{/* 중앙: 3D 캔버스 */} {/* 중앙: 3D 캔버스 */}
<div className="relative h-full flex-1 bg-gray-100"> <div className="relative h-full flex-1 bg-muted">
{isLoading ? ( {isLoading ? (
<div className="flex h-full items-center justify-center"> <div className="flex h-full items-center justify-center">
<Loader2 className="text-muted-foreground h-8 w-8 animate-spin" /> <Loader2 className="text-muted-foreground h-8 w-8 animate-spin" />

View File

@ -716,7 +716,7 @@ export default function DigitalTwinViewer({ layoutId }: DigitalTwinViewerProps)
</p> </p>
)} )}
{obj.materialCount !== undefined && obj.materialCount > 0 && ( {obj.materialCount !== undefined && obj.materialCount > 0 && (
<p className="text-xs text-yellow-600"> <p className="text-xs text-amber-600">
: <span className="font-semibold">{obj.materialCount}</span> : <span className="font-semibold">{obj.materialCount}</span>
</p> </p>
)} )}
@ -802,7 +802,7 @@ export default function DigitalTwinViewer({ layoutId }: DigitalTwinViewerProps)
</p> </p>
)} )}
{locationObj.materialCount !== undefined && locationObj.materialCount > 0 && ( {locationObj.materialCount !== undefined && locationObj.materialCount > 0 && (
<p className="mt-0.5 text-[10px] text-yellow-600"> <p className="mt-0.5 text-[10px] text-amber-600">
: <span className="font-semibold">{locationObj.materialCount}</span> : <span className="font-semibold">{locationObj.materialCount}</span>
</p> </p>
)} )}

View File

@ -1239,7 +1239,7 @@ export default function Yard3DCanvas({
}; };
return ( return (
<div className="h-full w-full bg-gray-100" onClick={handleCanvasClick}> <div className="h-full w-full bg-muted" onClick={handleCanvasClick}>
<Canvas <Canvas
camera={{ camera={{
position: [50, 30, 50], position: [50, 30, 50],

View File

@ -15,16 +15,16 @@ export const OBJECT_COLORS: Record<string, string> = {
// Tailwind 색상 클래스 매핑 (아이콘용) // Tailwind 색상 클래스 매핑 (아이콘용)
export const OBJECT_COLOR_CLASSES: Record<string, string> = { export const OBJECT_COLOR_CLASSES: Record<string, string> = {
area: "text-blue-500", area: "text-primary",
"location-bed": "text-blue-600", "location-bed": "text-primary",
"location-stp": "text-gray-500", "location-stp": "text-muted-foreground",
"location-temp": "text-orange-500", "location-temp": "text-amber-500",
"location-dest": "text-emerald-500", "location-dest": "text-emerald-500",
"crane-mobile": "text-purple-500", "crane-mobile": "text-purple-500",
rack: "text-red-500", rack: "text-destructive",
}; };
// 기본 색상 // 기본 색상
export const DEFAULT_COLOR = "#3b82f6"; export const DEFAULT_COLOR = "#3b82f6";
export const DEFAULT_COLOR_CLASS = "text-blue-500"; export const DEFAULT_COLOR_CLASS = "text-primary";

View File

@ -71,7 +71,7 @@ function CategoryNode({
<Folder className="h-4 w-4 shrink-0 text-amber-500" /> <Folder className="h-4 w-4 shrink-0 text-amber-500" />
) )
) : ( ) : (
<Tag className="h-4 w-4 shrink-0 text-blue-500" /> <Tag className="h-4 w-4 shrink-0 text-primary" />
)} )}
{/* 카테고리 이름 */} {/* 카테고리 이름 */}

View File

@ -309,8 +309,8 @@ export function KeyGenerateModal({
preview?.exists preview?.exists
? "border-destructive bg-destructive/10" ? "border-destructive bg-destructive/10"
: preview?.isOverride : preview?.isOverride
? "border-blue-500 bg-blue-500/10" ? "border-primary bg-primary/10"
: "border-green-500 bg-green-500/10" : "border-emerald-500 bg-emerald-500/10"
)}> )}>
<div className="flex items-center gap-2"> <div className="flex items-center gap-2">
{previewLoading ? ( {previewLoading ? (
@ -318,9 +318,9 @@ export function KeyGenerateModal({
) : preview?.exists ? ( ) : preview?.exists ? (
<AlertCircle className="h-4 w-4 text-destructive" /> <AlertCircle className="h-4 w-4 text-destructive" />
) : preview?.isOverride ? ( ) : preview?.isOverride ? (
<Info className="h-4 w-4 text-blue-500" /> <Info className="h-4 w-4 text-primary" />
) : ( ) : (
<CheckCircle2 className="h-4 w-4 text-green-500" /> <CheckCircle2 className="h-4 w-4 text-emerald-500" />
)} )}
<code className="text-xs font-mono sm:text-sm"> <code className="text-xs font-mono sm:text-sm">
{generatedKeyPreview} {generatedKeyPreview}
@ -332,7 +332,7 @@ export function KeyGenerateModal({
</p> </p>
)} )}
{preview?.isOverride && !preview?.exists && ( {preview?.isOverride && !preview?.exists && (
<p className="mt-1 text-xs text-blue-600"> <p className="mt-1 text-xs text-primary">
. . . .
</p> </p>
)} )}

View File

@ -9,6 +9,6 @@ export function ErrorMessage({ message }: ErrorMessageProps) {
if (!message) return null; if (!message) return null;
return ( return (
<div className="my-4 rounded-lg border border-destructive/20 bg-destructive/10 px-4 py-3 text-sm text-red-700">{message}</div> <div className="my-4 rounded-lg border border-destructive/20 bg-destructive/10 px-4 py-3 text-sm text-destructive">{message}</div>
); );
} }

View File

@ -61,7 +61,7 @@ function DraggableItem({
return ( return (
<div <div
ref={drag} ref={drag}
className={`flex cursor-move items-center gap-2 rounded border p-2 text-sm hover:border-blue-500 hover:bg-blue-50 ${ className={`flex cursor-move items-center gap-2 rounded border p-2 text-sm hover:border-primary hover:bg-primary/10 ${
isDragging ? "opacity-50" : "" isDragging ? "opacity-50" : ""
}`} }`}
> >

View File

@ -51,7 +51,7 @@ export function BarcodeDesignerCanvas() {
}), [widthPx, heightPx, components.length, addComponent, snapValueToGrid]); }), [widthPx, heightPx, components.length, addComponent, snapValueToGrid]);
return ( return (
<div className="flex flex-1 items-center justify-center overflow-auto bg-gray-100 p-6"> <div className="flex flex-1 items-center justify-center overflow-auto bg-muted p-6">
<div <div
key={`canvas-${widthMm}-${heightMm}`} key={`canvas-${widthMm}-${heightMm}`}
ref={(r) => { ref={(r) => {

View File

@ -202,7 +202,7 @@ export function BarcodeLabelCanvasComponent({ component }: Props) {
}} }}
/> />
) : ( ) : (
<div className="flex h-full w-full items-center justify-center bg-gray-100 text-xs text-gray-400"> <div className="flex h-full w-full items-center justify-center bg-muted text-xs text-muted-foreground/70">
</div> </div>
); );
@ -244,7 +244,7 @@ export function BarcodeLabelCanvasComponent({ component }: Props) {
{selected && component.type !== "line" && ( {selected && component.type !== "line" && (
<div <div
data-resize-handle data-resize-handle
className="absolute bottom-0 right-0 h-2 w-2 cursor-se-resize bg-blue-500" className="absolute bottom-0 right-0 h-2 w-2 cursor-se-resize bg-primary"
onMouseDown={(e) => { onMouseDown={(e) => {
e.stopPropagation(); e.stopPropagation();
setIsResizing(true); setIsResizing(true);

View File

@ -151,7 +151,7 @@ export function BarcodePrintPreviewModal({
</p> </p>
{/* 미리보기 캔버스 (축소) */} {/* 미리보기 캔버스 (축소) */}
<div className="flex justify-center rounded border bg-gray-100 p-4"> <div className="flex justify-center rounded border bg-muted p-4">
<div <div
className="relative bg-white shadow" className="relative bg-white shadow"
style={{ style={{

View File

@ -27,26 +27,26 @@ interface AlertModalProps {
const alertConfig = { const alertConfig = {
success: { success: {
icon: CheckCircle, icon: CheckCircle,
iconColor: "text-green-500", iconColor: "text-emerald-500",
titleColor: "text-green-700", titleColor: "text-emerald-700",
buttonVariant: "default" as const, buttonVariant: "default" as const,
}, },
error: { error: {
icon: XCircle, icon: XCircle,
iconColor: "text-red-500", iconColor: "text-destructive",
titleColor: "text-red-700", titleColor: "text-destructive",
buttonVariant: "destructive" as const, buttonVariant: "destructive" as const,
}, },
warning: { warning: {
icon: AlertTriangle, icon: AlertTriangle,
iconColor: "text-yellow-500", iconColor: "text-amber-500",
titleColor: "text-yellow-700", titleColor: "text-yellow-700",
buttonVariant: "default" as const, buttonVariant: "default" as const,
}, },
info: { info: {
icon: Info, icon: Info,
iconColor: "text-blue-500", iconColor: "text-primary",
titleColor: "text-blue-700", titleColor: "text-primary",
buttonVariant: "default" as const, buttonVariant: "default" as const,
}, },
}; };

View File

@ -1131,7 +1131,7 @@ export const ExcelUploadModal: React.FC<ExcelUploadModalProps> = ({
<FileSpreadsheet className="h-5 w-5" /> <FileSpreadsheet className="h-5 w-5" />
{isMasterDetail && ( {isMasterDetail && (
<span className="ml-2 rounded bg-blue-100 px-2 py-0.5 text-xs font-normal text-blue-700"> <span className="ml-2 rounded bg-primary/10 px-2 py-0.5 text-xs font-normal text-primary">
- -
</span> </span>
)} )}
@ -1322,15 +1322,15 @@ export const ExcelUploadModal: React.FC<ExcelUploadModalProps> = ({
isDragOver isDragOver
? "border-primary bg-primary/5" ? "border-primary bg-primary/5"
: file : file
? "border-green-500 bg-green-50" ? "border-emerald-500 bg-emerald-50"
: "border-muted-foreground/25 hover:border-primary hover:bg-muted/50" : "border-muted-foreground/25 hover:border-primary hover:bg-muted/50"
)} )}
> >
{file ? ( {file ? (
<div className="flex items-center gap-3"> <div className="flex items-center gap-3">
<FileSpreadsheet className="h-8 w-8 text-green-600" /> <FileSpreadsheet className="h-8 w-8 text-emerald-600" />
<div> <div>
<p className="text-sm font-medium text-green-700">{file.name}</p> <p className="text-sm font-medium text-emerald-700">{file.name}</p>
<p className="text-xs text-muted-foreground"> <p className="text-xs text-muted-foreground">
</p> </p>
@ -1561,11 +1561,11 @@ export const ExcelUploadModal: React.FC<ExcelUploadModalProps> = ({
{/* 중복 체크 안내 */} {/* 중복 체크 안내 */}
{duplicateCheckCount > 0 ? ( {duplicateCheckCount > 0 ? (
<div className="rounded-md border border-blue-200 bg-blue-50 p-3"> <div className="rounded-md border border-primary/20 bg-primary/10 p-3">
<div className="flex items-start justify-between gap-4"> <div className="flex items-start justify-between gap-4">
<div className="flex items-start gap-2"> <div className="flex items-start gap-2">
<Copy className="mt-0.5 h-4 w-4 text-blue-600" /> <Copy className="mt-0.5 h-4 w-4 text-primary" />
<div className="text-[10px] text-blue-700 sm:text-xs"> <div className="text-[10px] text-primary sm:text-xs">
<p className="font-medium"> <p className="font-medium">
: {columnMappings : {columnMappings
.filter((m) => m.checkDuplicate && m.systemColumn) .filter((m) => m.checkDuplicate && m.systemColumn)
@ -1581,12 +1581,12 @@ export const ExcelUploadModal: React.FC<ExcelUploadModalProps> = ({
</div> </div>
</div> </div>
<div className="flex items-center gap-2 shrink-0"> <div className="flex items-center gap-2 shrink-0">
<span className="text-[10px] text-blue-700 sm:text-xs"> :</span> <span className="text-[10px] text-primary sm:text-xs"> :</span>
<Select <Select
value={duplicateAction} value={duplicateAction}
onValueChange={(value) => setDuplicateAction(value as "overwrite" | "skip")} onValueChange={(value) => setDuplicateAction(value as "overwrite" | "skip")}
> >
<SelectTrigger className="h-7 w-[100px] text-[10px] sm:text-xs border-blue-300 bg-white"> <SelectTrigger className="h-7 w-[100px] text-[10px] sm:text-xs border-primary/40 bg-white">
<SelectValue /> <SelectValue />
</SelectTrigger> </SelectTrigger>
<SelectContent> <SelectContent>
@ -1686,7 +1686,7 @@ export const ExcelUploadModal: React.FC<ExcelUploadModalProps> = ({
<span className="font-medium">{mapping.excelColumn}</span> {" "} <span className="font-medium">{mapping.excelColumn}</span> {" "}
{col?.label || mapping.systemColumn} {col?.label || mapping.systemColumn}
{mapping.checkDuplicate && ( {mapping.checkDuplicate && (
<span className="ml-2 text-blue-600"> <span className="ml-2 text-primary">
( : {mapping.duplicateAction === "overwrite" ? "덮어쓰기" : "건너뛰기"}) ( : {mapping.duplicateAction === "overwrite" ? "덮어쓰기" : "건너뛰기"})
</span> </span>
)} )}
@ -1701,9 +1701,9 @@ export const ExcelUploadModal: React.FC<ExcelUploadModalProps> = ({
{/* 중복 체크 요약 */} {/* 중복 체크 요약 */}
{duplicateCheckCount > 0 && ( {duplicateCheckCount > 0 && (
<div className="rounded-md border border-blue-200 bg-blue-50 p-4"> <div className="rounded-md border border-primary/20 bg-primary/10 p-4">
<h3 className="text-sm font-medium text-blue-800 sm:text-base"> </h3> <h3 className="text-sm font-medium text-primary sm:text-base"> </h3>
<div className="mt-2 space-y-1 text-[10px] text-blue-700 sm:text-xs"> <div className="mt-2 space-y-1 text-[10px] text-primary sm:text-xs">
<p> <p>
<span className="font-medium"> :</span>{" "} <span className="font-medium"> :</span>{" "}
{columnMappings {columnMappings

View File

@ -140,9 +140,9 @@ const CompactValidationIndicator: React.FC<{
<span className="text-destructive">{validationState.errors.length} </span> <span className="text-destructive">{validationState.errors.length} </span>
)} )}
{validationState.warnings.length > 0 && ( {validationState.warnings.length > 0 && (
<span className="ml-2 text-orange-600">{validationState.warnings.length} </span> <span className="ml-2 text-amber-600">{validationState.warnings.length} </span>
)} )}
{validationState.isValid && <span className="text-green-600"> </span>} {validationState.isValid && <span className="text-emerald-600"> </span>}
</div> </div>
<Button size="sm" onClick={onSave} disabled={!canSave || saveState.status === "saving"} className="h-8"> <Button size="sm" onClick={onSave} disabled={!canSave || saveState.status === "saving"} className="h-8">
@ -176,7 +176,7 @@ const ValidationStatusBadge: React.FC<{ status: FormValidationState["status"] }>
variant: "default" as const, variant: "default" as const,
icon: CheckCircle, icon: CheckCircle,
text: "유효함", text: "유효함",
className: "bg-green-500 hover:bg-green-600", className: "bg-emerald-500 hover:bg-emerald-600",
}; };
case "invalid": case "invalid":
return { return {
@ -238,14 +238,14 @@ const ValidationSummary: React.FC<{ validationState: FormValidationState }> = ({
)} )}
{validationState.warnings.length > 0 && ( {validationState.warnings.length > 0 && (
<div className="flex items-center gap-1 text-orange-600"> <div className="flex items-center gap-1 text-amber-600">
<AlertTriangle className="h-4 w-4" /> <AlertTriangle className="h-4 w-4" />
<span>{validationState.warnings.length} </span> <span>{validationState.warnings.length} </span>
</div> </div>
)} )}
{validationState.isValid && validationState.errors.length === 0 && validationState.warnings.length === 0 && ( {validationState.isValid && validationState.errors.length === 0 && validationState.warnings.length === 0 && (
<div className="flex items-center gap-1 text-green-600"> <div className="flex items-center gap-1 text-emerald-600">
<CheckCircle className="h-4 w-4" /> <CheckCircle className="h-4 w-4" />
<span> </span> <span> </span>
</div> </div>
@ -307,8 +307,8 @@ const ValidationErrorItem: React.FC<{ error: ValidationError }> = ({ error }) =>
*/ */
const ValidationWarningItem: React.FC<{ warning: ValidationWarning }> = ({ warning }) => { const ValidationWarningItem: React.FC<{ warning: ValidationWarning }> = ({ warning }) => {
return ( return (
<Alert className="border-orange-200 bg-orange-50 py-2"> <Alert className="border-orange-200 bg-amber-50 py-2">
<AlertTriangle className="h-4 w-4 text-orange-600" /> <AlertTriangle className="h-4 w-4 text-amber-600" />
<AlertDescription className="text-sm"> <AlertDescription className="text-sm">
<span className="font-medium">{warning.field}:</span> {warning.message} <span className="font-medium">{warning.field}:</span> {warning.message}
{warning.suggestion && <span className="mt-1 block text-xs text-orange-700">💡 {warning.suggestion}</span>} {warning.suggestion && <span className="mt-1 block text-xs text-orange-700">💡 {warning.suggestion}</span>}
@ -364,15 +364,15 @@ export const FieldValidationIndicator: React.FC<{
{showIcon && ( {showIcon && (
<> <>
{status === "validating" && <Clock className="text-muted-foreground h-3 w-3 animate-spin" />} {status === "validating" && <Clock className="text-muted-foreground h-3 w-3 animate-spin" />}
{status === "valid" && !error && <CheckCircle className="h-3 w-3 text-green-600" />} {status === "valid" && !error && <CheckCircle className="h-3 w-3 text-emerald-600" />}
{error && <AlertCircle className="text-destructive h-3 w-3" />} {error && <AlertCircle className="text-destructive h-3 w-3" />}
{warning && !error && <AlertTriangle className="h-3 w-3 text-orange-600" />} {warning && !error && <AlertTriangle className="h-3 w-3 text-amber-600" />}
</> </>
)} )}
{error && <span className="text-destructive">{error.message}</span>} {error && <span className="text-destructive">{error.message}</span>}
{warning && !error && <span className="text-orange-600">{warning.message}</span>} {warning && !error && <span className="text-amber-600">{warning.message}</span>}
</div> </div>
); );
}; };

View File

@ -1028,7 +1028,7 @@ export const ScreenModal: React.FC<ScreenModalProps> = ({ className }) => {
{loading ? ( {loading ? (
<div className="flex h-full items-center justify-center"> <div className="flex h-full items-center justify-center">
<div className="text-center"> <div className="text-center">
<div className="mx-auto mb-4 h-8 w-8 animate-spin rounded-full border-b-2 border-blue-600"></div> <div className="mx-auto mb-4 h-8 w-8 animate-spin rounded-full border-b-2 border-primary"></div>
<p className="text-muted-foreground"> ...</p> <p className="text-muted-foreground"> ...</p>
</div> </div>
</div> </div>

View File

@ -102,26 +102,26 @@ export function TableHistoryModal({
const getOperationIcon = (type: string) => { const getOperationIcon = (type: string) => {
switch (type) { switch (type) {
case "INSERT": case "INSERT":
return <Plus className="h-4 w-4 text-green-600" />; return <Plus className="h-4 w-4 text-emerald-600" />;
case "UPDATE": case "UPDATE":
return <FileEdit className="h-4 w-4 text-blue-600" />; return <FileEdit className="h-4 w-4 text-primary" />;
case "DELETE": case "DELETE":
return <Trash2 className="h-4 w-4 text-red-600" />; return <Trash2 className="h-4 w-4 text-destructive" />;
default: default:
return <Clock className="h-4 w-4 text-gray-600" />; return <Clock className="h-4 w-4 text-muted-foreground" />;
} }
}; };
const getOperationBadge = (type: string) => { const getOperationBadge = (type: string) => {
switch (type) { switch (type) {
case "INSERT": case "INSERT":
return <span className="text-sm font-medium text-green-600"></span>; return <span className="text-sm font-medium text-emerald-600"></span>;
case "UPDATE": case "UPDATE":
return <span className="text-sm font-medium text-blue-600"></span>; return <span className="text-sm font-medium text-primary"></span>;
case "DELETE": case "DELETE":
return <span className="text-sm font-medium text-red-600"></span>; return <span className="text-sm font-medium text-destructive"></span>;
default: default:
return <span className="text-sm font-medium text-gray-600">{type}</span>; return <span className="text-sm font-medium text-muted-foreground">{type}</span>;
} }
}; };
@ -301,8 +301,8 @@ export function TableHistoryModal({
) : ( ) : (
<div className="space-y-6"> <div className="space-y-6">
{timeline.map((event, index) => ( {timeline.map((event, index) => (
<div key={index} className="relative border-l-2 border-gray-200 pb-6 pl-8 last:border-l-0"> <div key={index} className="relative border-l-2 border-border pb-6 pl-8 last:border-l-0">
<div className="absolute top-0 -left-3 rounded-full border-2 border-gray-200 bg-white p-1"> <div className="absolute top-0 -left-3 rounded-full border-2 border-border bg-white p-1">
{getOperationIcon(event.operation_type)} {getOperationIcon(event.operation_type)}
</div> </div>
@ -325,12 +325,12 @@ export function TableHistoryModal({
<p className="text-muted-foreground text-xs font-medium"> :</p> <p className="text-muted-foreground text-xs font-medium"> :</p>
<div className="space-y-1"> <div className="space-y-1">
{event.changes.map((change, idx) => ( {event.changes.map((change, idx) => (
<div key={idx} className="rounded bg-gray-50 p-2 text-xs"> <div key={idx} className="rounded bg-muted p-2 text-xs">
<span className="font-mono font-medium">{change.column}</span> <span className="font-mono font-medium">{change.column}</span>
<div className="mt-1 flex items-center gap-2"> <div className="mt-1 flex items-center gap-2">
<span className="text-red-600 line-through">{change.oldValue || "(없음)"}</span> <span className="text-destructive line-through">{change.oldValue || "(없음)"}</span>
<span></span> <span></span>
<span className="font-medium text-green-600">{change.newValue || "(없음)"}</span> <span className="font-medium text-emerald-600">{change.newValue || "(없음)"}</span>
</div> </div>
</div> </div>
))} ))}
@ -355,7 +355,7 @@ export function TableHistoryModal({
</div> </div>
) : ( ) : (
<table className="w-full text-xs"> <table className="w-full text-xs">
<thead className="sticky top-0 border-b bg-gray-50"> <thead className="sticky top-0 border-b bg-muted">
<tr> <tr>
{!recordId && <th className="p-2 text-left font-medium"></th>} {!recordId && <th className="p-2 text-left font-medium"></th>}
<th className="p-2 text-left font-medium"></th> <th className="p-2 text-left font-medium"></th>
@ -370,11 +370,11 @@ export function TableHistoryModal({
{detailRecords.map((record) => { {detailRecords.map((record) => {
const displayValue = getDisplayValue(record); const displayValue = getDisplayValue(record);
return ( return (
<tr key={record.log_id} className="border-b hover:bg-gray-50"> <tr key={record.log_id} className="border-b hover:bg-muted">
{!recordId && ( {!recordId && (
<td className="p-2"> <td className="p-2">
{displayValue ? ( {displayValue ? (
<span className="font-medium text-gray-900">{displayValue}</span> <span className="font-medium text-foreground">{displayValue}</span>
) : ( ) : (
<span className="text-muted-foreground text-xs">-</span> <span className="text-muted-foreground text-xs">-</span>
)} )}
@ -382,8 +382,8 @@ export function TableHistoryModal({
)} )}
<td className="p-2">{getOperationBadge(record.operation_type)}</td> <td className="p-2">{getOperationBadge(record.operation_type)}</td>
<td className="p-2 font-mono">{record.changed_column}</td> <td className="p-2 font-mono">{record.changed_column}</td>
<td className="max-w-[200px] truncate p-2 text-red-600">{record.old_value || "-"}</td> <td className="max-w-[200px] truncate p-2 text-destructive">{record.old_value || "-"}</td>
<td className="max-w-[200px] truncate p-2 text-green-600">{record.new_value || "-"}</td> <td className="max-w-[200px] truncate p-2 text-emerald-600">{record.new_value || "-"}</td>
<td className="p-2">{record.changed_by}</td> <td className="p-2">{record.changed_by}</td>
<td className="text-muted-foreground p-2">{formatDate(record.changed_at)}</td> <td className="text-muted-foreground p-2">{formatDate(record.changed_at)}</td>
</tr> </tr>

View File

@ -10,7 +10,7 @@ interface ValidationMessageProps {
export function ValidationMessage({ message, isValid, isLoading, className }: ValidationMessageProps) { export function ValidationMessage({ message, isValid, isLoading, className }: ValidationMessageProps) {
if (isLoading) { if (isLoading) {
return <p className={cn("text-sm text-gray-500", className)}> ...</p>; return <p className={cn("text-sm text-muted-foreground", className)}> ...</p>;
} }
if (!message) { if (!message) {
@ -18,6 +18,6 @@ export function ValidationMessage({ message, isValid, isLoading, className }: Va
} }
return ( return (
<p className={cn("text-sm transition-colors", isValid ? "text-green-600" : "text-destructive", className)}>{message}</p> <p className={cn("text-sm transition-colors", isValid ? "text-emerald-600" : "text-destructive", className)}>{message}</p>
); );
} }

View File

@ -212,16 +212,16 @@ export function ListTestWidget({ element }: ListTestWidgetProps) {
const renderIcon = (icon?: string, color?: string) => { const renderIcon = (icon?: string, color?: string) => {
const colorClass = const colorClass =
color === "blue" color === "blue"
? "text-blue-600" ? "text-primary"
: color === "orange" : color === "orange"
? "text-orange-600" ? "text-amber-600"
: color === "green" : color === "green"
? "text-green-600" ? "text-emerald-600"
: color === "red" : color === "red"
? "text-red-600" ? "text-destructive"
: color === "purple" : color === "purple"
? "text-purple-600" ? "text-purple-600"
: "text-gray-600"; : "text-muted-foreground";
switch (icon) { switch (icon) {
case "truck": case "truck":
@ -241,16 +241,16 @@ export function ListTestWidget({ element }: ListTestWidgetProps) {
const renderFieldGroup = (group: FieldGroup, groupData: Record<string, any>) => { const renderFieldGroup = (group: FieldGroup, groupData: Record<string, any>) => {
const colorClass = const colorClass =
group.color === "blue" group.color === "blue"
? "text-blue-600" ? "text-primary"
: group.color === "orange" : group.color === "orange"
? "text-orange-600" ? "text-amber-600"
: group.color === "green" : group.color === "green"
? "text-green-600" ? "text-emerald-600"
: group.color === "red" : group.color === "red"
? "text-red-600" ? "text-destructive"
: group.color === "purple" : group.color === "purple"
? "text-purple-600" ? "text-purple-600"
: "text-gray-600"; : "text-muted-foreground";
return ( return (
<div key={group.id} className="rounded-lg border p-4"> <div key={group.id} className="rounded-lg border p-4">

View File

@ -1352,8 +1352,8 @@ export default function MapTestWidgetV2({ element }: MapTestWidgetV2Props) {
{/* 이동경로 날짜 선택 */} {/* 이동경로 날짜 선택 */}
{selectedUserId && ( {selectedUserId && (
<div className="flex items-center gap-1 rounded border bg-blue-50 px-2 py-1"> <div className="flex items-center gap-1 rounded border bg-primary/10 px-2 py-1">
<span className="text-xs text-blue-600">🛣</span> <span className="text-xs text-primary">🛣</span>
<input <input
type="date" type="date"
value={routeDate} value={routeDate}
@ -1363,10 +1363,10 @@ export default function MapTestWidgetV2({ element }: MapTestWidgetV2Props) {
loadRoute(selectedUserId, e.target.value); loadRoute(selectedUserId, e.target.value);
} }
}} }}
className="h-6 rounded border-none bg-transparent px-1 text-xs text-blue-600 focus:outline-none" className="h-6 rounded border-none bg-transparent px-1 text-xs text-primary focus:outline-none"
/> />
<span className="text-xs text-blue-600">({routePoints.length})</span> <span className="text-xs text-primary">({routePoints.length})</span>
<button onClick={clearRoute} className="ml-1 text-xs text-blue-400 hover:text-blue-600"> <button onClick={clearRoute} className="ml-1 text-xs text-primary/80 hover:text-primary">
</button> </button>
</div> </div>
@ -1494,15 +1494,15 @@ export default function MapTestWidgetV2({ element }: MapTestWidgetV2Props) {
const parsed = JSON.parse(matchingPolygon.description); const parsed = JSON.parse(matchingPolygon.description);
popupContent = ` popupContent = `
<div class="min-w-[200px]"> <div class="min-w-[200px]">
${matchingPolygon.source ? `<div class="mb-2 border-b pb-2"><div class="text-gray-500 text-xs">📡 ${matchingPolygon.source}</div></div>` : ""} ${matchingPolygon.source ? `<div class="mb-2 border-b pb-2"><div class="text-muted-foreground text-xs">📡 ${matchingPolygon.source}</div></div>` : ""}
<div class="bg-gray-100 rounded p-2"> <div class="bg-muted rounded p-2">
<div class="text-gray-900 mb-1 text-xs font-semibold"> </div> <div class="text-foreground mb-1 text-xs font-semibold"> </div>
<div class="space-y-2"> <div class="space-y-2">
${popupFields ${popupFields
.map((field) => { .map((field) => {
const value = parsed[field.fieldName]; const value = parsed[field.fieldName];
if (value === undefined || value === null) return ""; if (value === undefined || value === null) return "";
return `<div class="text-xs"><span class="text-gray-600 font-medium">${field.label}:</span> <span class="text-gray-900">${value}</span></div>`; return `<div class="text-xs"><span class="text-muted-foreground font-medium">${field.label}:</span> <span class="text-foreground">${value}</span></div>`;
}) })
.join("")} .join("")}
</div> </div>
@ -1980,7 +1980,7 @@ export default function MapTestWidgetV2({ element }: MapTestWidgetV2Props) {
<button <button
onClick={() => loadTripInfo(identifier)} onClick={() => loadTripInfo(identifier)}
disabled={tripInfoLoading === identifier} disabled={tripInfoLoading === identifier}
className="w-full rounded bg-gray-100 px-2 py-1.5 text-xs text-gray-700 hover:bg-gray-200 disabled:opacity-50" className="w-full rounded bg-muted px-2 py-1.5 text-xs text-foreground hover:bg-muted/80 disabled:opacity-50"
> >
{tripInfoLoading === identifier ? "로딩 중..." : "📊 운행/공차 정보 보기"} {tripInfoLoading === identifier ? "로딩 중..." : "📊 운행/공차 정보 보기"}
</button> </button>
@ -1996,10 +1996,10 @@ export default function MapTestWidgetV2({ element }: MapTestWidgetV2Props) {
{/* 운행 정보 */} {/* 운행 정보 */}
{hasTripInfo && ( {hasTripInfo && (
<div className="mb-2"> <div className="mb-2">
<div className="text-xs font-semibold text-blue-600 mb-1">🚛 </div> <div className="text-xs font-semibold text-primary mb-1">🚛 </div>
<div className="bg-blue-50 rounded p-2 space-y-1"> <div className="bg-primary/10 rounded p-2 space-y-1">
{(info.last_trip_start || info.last_trip_end) && ( {(info.last_trip_start || info.last_trip_end) && (
<div className="text-[10px] text-gray-600"> <div className="text-[10px] text-muted-foreground">
<span className="font-medium">:</span>{" "} <span className="font-medium">:</span>{" "}
{formatDateTime(info.last_trip_start)} ~ {formatDateTime(info.last_trip_end)} {formatDateTime(info.last_trip_start)} ~ {formatDateTime(info.last_trip_end)}
</div> </div>
@ -2007,20 +2007,20 @@ export default function MapTestWidgetV2({ element }: MapTestWidgetV2Props) {
<div className="flex gap-3 text-[10px]"> <div className="flex gap-3 text-[10px]">
{info.last_trip_distance !== undefined && info.last_trip_distance !== null && ( {info.last_trip_distance !== undefined && info.last_trip_distance !== null && (
<span> <span>
<span className="font-medium text-gray-600">:</span>{" "} <span className="font-medium text-muted-foreground">:</span>{" "}
<span className="text-blue-700 font-semibold">{formatDistance(info.last_trip_distance)}</span> <span className="text-primary font-semibold">{formatDistance(info.last_trip_distance)}</span>
</span> </span>
)} )}
{info.last_trip_time !== undefined && info.last_trip_time !== null && ( {info.last_trip_time !== undefined && info.last_trip_time !== null && (
<span> <span>
<span className="font-medium text-gray-600">:</span>{" "} <span className="font-medium text-muted-foreground">:</span>{" "}
<span className="text-blue-700 font-semibold">{formatTime(info.last_trip_time)}</span> <span className="text-primary font-semibold">{formatTime(info.last_trip_time)}</span>
</span> </span>
)} )}
</div> </div>
{/* 출발지/도착지 */} {/* 출발지/도착지 */}
{(info.departure || info.arrival) && ( {(info.departure || info.arrival) && (
<div className="text-[10px] text-gray-600 pt-1 border-t border-blue-100"> <div className="text-[10px] text-muted-foreground pt-1 border-t border-primary/10">
{info.departure && <span>: {info.departure}</span>} {info.departure && <span>: {info.departure}</span>}
{info.departure && info.arrival && " → "} {info.departure && info.arrival && " → "}
{info.arrival && <span>: {info.arrival}</span>} {info.arrival && <span>: {info.arrival}</span>}
@ -2033,10 +2033,10 @@ export default function MapTestWidgetV2({ element }: MapTestWidgetV2Props) {
{/* 공차 정보 */} {/* 공차 정보 */}
{hasEmptyTripInfo && ( {hasEmptyTripInfo && (
<div> <div>
<div className="text-xs font-semibold text-orange-600 mb-1">📦 </div> <div className="text-xs font-semibold text-amber-600 mb-1">📦 </div>
<div className="bg-orange-50 rounded p-2 space-y-1"> <div className="bg-amber-50 rounded p-2 space-y-1">
{(info.last_empty_start || info.last_empty_end) && ( {(info.last_empty_start || info.last_empty_end) && (
<div className="text-[10px] text-gray-600"> <div className="text-[10px] text-muted-foreground">
<span className="font-medium">:</span>{" "} <span className="font-medium">:</span>{" "}
{formatDateTime(info.last_empty_start)} ~ {formatDateTime(info.last_empty_end)} {formatDateTime(info.last_empty_start)} ~ {formatDateTime(info.last_empty_end)}
</div> </div>
@ -2044,13 +2044,13 @@ export default function MapTestWidgetV2({ element }: MapTestWidgetV2Props) {
<div className="flex gap-3 text-[10px]"> <div className="flex gap-3 text-[10px]">
{info.last_empty_distance !== undefined && info.last_empty_distance !== null && ( {info.last_empty_distance !== undefined && info.last_empty_distance !== null && (
<span> <span>
<span className="font-medium text-gray-600">:</span>{" "} <span className="font-medium text-muted-foreground">:</span>{" "}
<span className="text-orange-700 font-semibold">{formatDistance(info.last_empty_distance)}</span> <span className="text-orange-700 font-semibold">{formatDistance(info.last_empty_distance)}</span>
</span> </span>
)} )}
{info.last_empty_time !== undefined && info.last_empty_time !== null && ( {info.last_empty_time !== undefined && info.last_empty_time !== null && (
<span> <span>
<span className="font-medium text-gray-600">:</span>{" "} <span className="font-medium text-muted-foreground">:</span>{" "}
<span className="text-orange-700 font-semibold">{formatTime(info.last_empty_time)}</span> <span className="text-orange-700 font-semibold">{formatTime(info.last_empty_time)}</span>
</span> </span>
)} )}
@ -2085,7 +2085,7 @@ export default function MapTestWidgetV2({ element }: MapTestWidgetV2Props) {
<button <button
onClick={() => loadRoute(visibleUserId)} onClick={() => loadRoute(visibleUserId)}
disabled={routeLoading} disabled={routeLoading}
className="w-full rounded bg-blue-500 px-2 py-1 text-xs text-white hover:bg-blue-600 disabled:opacity-50" className="w-full rounded bg-primary px-2 py-1 text-xs text-white hover:bg-primary disabled:opacity-50"
> >
{routeLoading && selectedUserId === visibleUserId ? "로딩 중..." : "🛣️ 이동경로 보기"} {routeLoading && selectedUserId === visibleUserId ? "로딩 중..." : "🛣️ 이동경로 보기"}
</button> </button>

View File

@ -357,7 +357,7 @@ export default function VehicleMapOnlyWidget({ element, refreshInterval = 30000
<button <button
onClick={() => loadRoute(vehicle)} onClick={() => loadRoute(vehicle)}
disabled={isRouteLoading} disabled={isRouteLoading}
className="w-full rounded bg-blue-500 px-2 py-1 text-xs text-white hover:bg-blue-600 disabled:opacity-50" className="w-full rounded bg-primary px-2 py-1 text-xs text-white hover:bg-primary disabled:opacity-50"
> >
{isRouteLoading && selectedVehicle?.id === vehicle.id {isRouteLoading && selectedVehicle?.id === vehicle.id
? "로딩 중..." ? "로딩 중..."
@ -391,7 +391,7 @@ export default function VehicleMapOnlyWidget({ element, refreshInterval = 30000
{/* 이동경로 정보 표시 - 상단으로 이동하여 주석 처리 */} {/* 이동경로 정보 표시 - 상단으로 이동하여 주석 처리 */}
{/* {selectedVehicle && routePoints.length > 0 && ( {/* {selectedVehicle && routePoints.length > 0 && (
<div className="absolute bottom-2 right-2 z-[1000] rounded-lg bg-blue-500/90 p-2 shadow-lg backdrop-blur-sm"> <div className="absolute bottom-2 right-2 z-[1000] rounded-lg bg-primary/90 p-2 shadow-lg backdrop-blur-sm">
<div className="flex items-center gap-2"> <div className="flex items-center gap-2">
<div className="text-xs text-white"> <div className="text-xs text-white">
<div className="font-semibold">🛣 {selectedVehicle.name} </div> <div className="font-semibold">🛣 {selectedVehicle.name} </div>

View File

@ -733,13 +733,13 @@ export const ConnectionSetupModal: React.FC<ConnectionSetupModalProps> = ({
<AlertDialogContent> <AlertDialogContent>
<AlertDialogHeader> <AlertDialogHeader>
<AlertDialogTitle className="flex items-center gap-2"> <AlertDialogTitle className="flex items-center gap-2">
<CheckCircle className="h-5 w-5 text-green-500" /> <CheckCircle className="h-5 w-5 text-emerald-500" />
</AlertDialogTitle> </AlertDialogTitle>
<AlertDialogDescription className="text-base"> <AlertDialogDescription className="text-base">
<span className="font-medium text-green-600">{createdConnectionName}</span> . <span className="font-medium text-emerald-600">{createdConnectionName}</span> .
<br /> <br />
<span className="mt-2 block text-sm text-gray-500"> <span className="mt-2 block text-sm text-muted-foreground">
. .
</span> </span>
</AlertDialogDescription> </AlertDialogDescription>

View File

@ -800,7 +800,7 @@ export const DataFlowDesigner: React.FC<DataFlowDesignerProps> = ({
]); ]);
return ( return (
<div className="data-flow-designer h-screen bg-gray-100"> <div className="data-flow-designer h-screen bg-muted">
<div className="flex h-full"> <div className="flex h-full">
{/* 사이드바 */} {/* 사이드바 */}
<DataFlowSidebar <DataFlowSidebar
@ -892,12 +892,12 @@ export const DataFlowDesigner: React.FC<DataFlowDesignerProps> = ({
{/* 안내 메시지 */} {/* 안내 메시지 */}
{nodes.length === 0 && ( {nodes.length === 0 && (
<div className="pointer-events-none absolute inset-0 flex items-center justify-center"> <div className="pointer-events-none absolute inset-0 flex items-center justify-center">
<div className="text-center text-gray-500"> <div className="text-center text-muted-foreground">
<div className="mb-2 text-2xl">📊</div> <div className="mb-2 text-2xl">📊</div>
<div className="mb-1 text-lg font-medium"> </div> <div className="mb-1 text-lg font-medium"> </div>
<div className="text-sm"> <div className="text-sm">
<div> </div> <div> </div>
<div className="mt-1 text-xs text-gray-400"> Del </div> <div className="mt-1 text-xs text-muted-foreground/70"> Del </div>
</div> </div>
</div> </div>
</div> </div>

View File

@ -35,9 +35,9 @@ export const DataFlowSidebar: React.FC<DataFlowSidebarProps> = ({
getSelectedTableNames, getSelectedTableNames,
}) => { }) => {
return ( return (
<div className="w-80 border-r border-gray-200 bg-white shadow-lg"> <div className="w-80 border-r border-border bg-white shadow-lg">
<div className="p-6"> <div className="p-6">
<h2 className="mb-6 text-xl font-bold text-gray-800"> </h2> <h2 className="mb-6 text-xl font-bold text-foreground"> </h2>
{/* 테이블 선택기 */} {/* 테이블 선택기 */}
<TableSelector companyCode={companyCode} onTableAdd={onTableAdd} selectedTables={getSelectedTableNames()} /> <TableSelector companyCode={companyCode} onTableAdd={onTableAdd} selectedTables={getSelectedTableNames()} />
@ -46,7 +46,7 @@ export const DataFlowSidebar: React.FC<DataFlowSidebarProps> = ({
<div className="space-y-3"> <div className="space-y-3">
<button <button
onClick={onRemoveOrphanedNodes} onClick={onRemoveOrphanedNodes}
className="w-full rounded-lg bg-orange-500 p-3 font-medium text-white transition-colors hover:bg-orange-600" className="w-full rounded-lg bg-amber-500 p-3 font-medium text-white transition-colors hover:bg-orange-600"
disabled={nodes.length === 0} disabled={nodes.length === 0}
> >
🧹 🧹
@ -54,14 +54,14 @@ export const DataFlowSidebar: React.FC<DataFlowSidebarProps> = ({
<button <button
onClick={onClearAll} onClick={onClearAll}
className="w-full rounded-lg bg-destructive/100 p-3 font-medium text-white transition-colors hover:bg-red-600" className="w-full rounded-lg bg-destructive/100 p-3 font-medium text-white transition-colors hover:bg-destructive"
> >
🗑 🗑
</button> </button>
<button <button
onClick={onOpenSaveModal} onClick={onOpenSaveModal}
className={`w-full rounded-lg bg-green-500 p-3 font-medium text-white transition-colors hover:bg-green-600 ${ className={`w-full rounded-lg bg-emerald-500 p-3 font-medium text-white transition-colors hover:bg-emerald-600 ${
hasUnsavedChanges ? "animate-pulse" : "" hasUnsavedChanges ? "animate-pulse" : ""
}`} }`}
> >
@ -70,8 +70,8 @@ export const DataFlowSidebar: React.FC<DataFlowSidebarProps> = ({
</div> </div>
{/* 통계 정보 */} {/* 통계 정보 */}
<div className="mt-6 rounded-lg bg-gray-50 p-4"> <div className="mt-6 rounded-lg bg-muted p-4">
<div className="mb-2 text-sm font-semibold text-gray-700"></div> <div className="mb-2 text-sm font-semibold text-foreground"></div>
<div className="space-y-1 text-sm text-muted-foreground"> <div className="space-y-1 text-sm text-muted-foreground">
<div className="flex justify-between"> <div className="flex justify-between">
<span> :</span> <span> :</span>
@ -83,7 +83,7 @@ export const DataFlowSidebar: React.FC<DataFlowSidebarProps> = ({
</div> </div>
<div className="flex justify-between"> <div className="flex justify-between">
<span> :</span> <span> :</span>
<span className="font-medium text-orange-600">{tempRelationships.length}</span> <span className="font-medium text-amber-600">{tempRelationships.length}</span>
</div> </div>
<div className="flex justify-between"> <div className="flex justify-between">
<span> ID:</span> <span> ID:</span>
@ -98,7 +98,7 @@ export const DataFlowSidebar: React.FC<DataFlowSidebarProps> = ({
</span> </span>
</div> </div>
{hasUnsavedChanges && ( {hasUnsavedChanges && (
<div className="mt-2 text-xs font-medium text-orange-600"> </div> <div className="mt-2 text-xs font-medium text-amber-600"> </div>
)} )}
</div> </div>
</div> </div>

View File

@ -24,7 +24,7 @@ export const EdgeInfoPanel: React.FC<EdgeInfoPanelProps> = ({
return ( return (
<div <div
className="fixed z-50 rounded-xl border border-gray-200 bg-white shadow-2xl" className="fixed z-50 rounded-xl border border-border bg-white shadow-2xl"
style={{ style={{
left: position.x - 160, left: position.x - 160,
top: position.y - 100, top: position.y - 100,
@ -33,7 +33,7 @@ export const EdgeInfoPanel: React.FC<EdgeInfoPanelProps> = ({
}} }}
> >
{/* 헤더 */} {/* 헤더 */}
<div className="flex items-center justify-between rounded-t-xl border-b border-gray-200 bg-blue-600 p-4"> <div className="flex items-center justify-between rounded-t-xl border-b border-border bg-primary p-4">
<div className="flex items-center gap-3"> <div className="flex items-center gap-3">
<div className="flex h-8 w-8 items-center justify-center rounded-full bg-white/20 backdrop-blur-sm"> <div className="flex h-8 w-8 items-center justify-center rounded-full bg-white/20 backdrop-blur-sm">
<span className="text-sm text-white">🔗</span> <span className="text-sm text-white">🔗</span>
@ -52,10 +52,10 @@ export const EdgeInfoPanel: React.FC<EdgeInfoPanelProps> = ({
</div> </div>
{/* 관계 정보 요약 */} {/* 관계 정보 요약 */}
<div className="border-b border-gray-100 bg-gray-50 p-3"> <div className="border-b border-border bg-muted p-3">
<div className="flex items-center justify-center gap-4"> <div className="flex items-center justify-center gap-4">
<div className="text-center"> <div className="text-center">
<div className="text-xs font-medium tracking-wide text-gray-500 uppercase"> </div> <div className="text-xs font-medium tracking-wide text-muted-foreground uppercase"> </div>
<div className="mt-1 inline-flex items-center rounded-full bg-indigo-100 px-2 py-0.5 text-xs font-semibold text-indigo-800"> <div className="mt-1 inline-flex items-center rounded-full bg-indigo-100 px-2 py-0.5 text-xs font-semibold text-indigo-800">
{edgeInfo.connectionType} {edgeInfo.connectionType}
</div> </div>
@ -68,7 +68,7 @@ export const EdgeInfoPanel: React.FC<EdgeInfoPanelProps> = ({
{/* From 테이블 */} {/* From 테이블 */}
<div className="rounded-lg border-l-4 border-emerald-400 bg-emerald-50 p-3"> <div className="rounded-lg border-l-4 border-emerald-400 bg-emerald-50 p-3">
<div className="mb-2 text-xs font-bold tracking-wide text-emerald-700 uppercase">FROM</div> <div className="mb-2 text-xs font-bold tracking-wide text-emerald-700 uppercase">FROM</div>
<div className="mb-2 text-base font-bold text-gray-800">{edgeInfo.fromTable}</div> <div className="mb-2 text-base font-bold text-foreground">{edgeInfo.fromTable}</div>
<div className="space-y-1"> <div className="space-y-1">
<div className="flex flex-wrap gap-2"> <div className="flex flex-wrap gap-2">
{edgeInfo.fromColumns.map((column, index) => ( {edgeInfo.fromColumns.map((column, index) => (
@ -89,15 +89,15 @@ export const EdgeInfoPanel: React.FC<EdgeInfoPanelProps> = ({
</div> </div>
{/* To 테이블 */} {/* To 테이블 */}
<div className="rounded-lg border-l-4 border-blue-400 bg-accent p-3"> <div className="rounded-lg border-l-4 border-primary/60 bg-accent p-3">
<div className="mb-2 text-xs font-bold tracking-wide text-blue-700 uppercase">TO</div> <div className="mb-2 text-xs font-bold tracking-wide text-primary uppercase">TO</div>
<div className="mb-2 text-base font-bold text-gray-800">{edgeInfo.toTable}</div> <div className="mb-2 text-base font-bold text-foreground">{edgeInfo.toTable}</div>
<div className="space-y-1"> <div className="space-y-1">
<div className="flex flex-wrap gap-2"> <div className="flex flex-wrap gap-2">
{edgeInfo.toColumns.map((column, index) => ( {edgeInfo.toColumns.map((column, index) => (
<span <span
key={index} key={index}
className="inline-flex items-center rounded-md bg-primary/20 px-2.5 py-0.5 text-xs font-medium text-blue-800 ring-1 ring-blue-200" className="inline-flex items-center rounded-md bg-primary/20 px-2.5 py-0.5 text-xs font-medium text-primary ring-1 ring-primary/20"
> >
{column} {column}
</span> </span>
@ -108,16 +108,16 @@ export const EdgeInfoPanel: React.FC<EdgeInfoPanelProps> = ({
</div> </div>
{/* 액션 버튼 */} {/* 액션 버튼 */}
<div className="flex gap-2 border-t border-gray-200 bg-gray-50 p-3"> <div className="flex gap-2 border-t border-border bg-muted p-3">
<button <button
onClick={onEdit} onClick={onEdit}
className="flex flex-1 items-center justify-center gap-1 rounded-lg bg-blue-600 px-3 py-2 text-xs font-semibold text-white shadow-sm transition-all hover:bg-blue-700 hover:shadow-md" className="flex flex-1 items-center justify-center gap-1 rounded-lg bg-primary px-3 py-2 text-xs font-semibold text-white shadow-sm transition-all hover:bg-primary/90 hover:shadow-md"
> >
<span></span> <span></span>
</button> </button>
<button <button
onClick={onDelete} onClick={onDelete}
className="flex flex-1 items-center justify-center gap-1 rounded-lg bg-red-600 px-3 py-2 text-xs font-semibold text-white shadow-sm transition-all hover:bg-red-700 hover:shadow-md" className="flex flex-1 items-center justify-center gap-1 rounded-lg bg-destructive px-3 py-2 text-xs font-semibold text-white shadow-sm transition-all hover:bg-red-700 hover:shadow-md"
> >
<span></span> <span></span>
</button> </button>

View File

@ -136,16 +136,16 @@ export const RelationshipListModal: React.FC<RelationshipListModalProps> = ({
return ( return (
<div className="pointer-events-auto absolute top-4 right-4 z-40 w-80 rounded-xl border border-primary/20 bg-white shadow-lg"> <div className="pointer-events-auto absolute top-4 right-4 z-40 w-80 rounded-xl border border-primary/20 bg-white shadow-lg">
{/* 헤더 */} {/* 헤더 */}
<div className="flex items-center justify-between rounded-t-xl border-b border-blue-100 bg-gradient-to-r from-blue-50 to-indigo-50 p-3"> <div className="flex items-center justify-between rounded-t-xl border-b border-primary/10 bg-gradient-to-r from-primary/5 to-indigo-50 p-3">
<div className="flex items-center gap-2"> <div className="flex items-center gap-2">
<div className="rounded-full bg-primary/20 p-1"> <div className="rounded-full bg-primary/20 p-1">
<span className="text-sm text-primary">🔗</span> <span className="text-sm text-primary">🔗</span>
</div> </div>
<div className="text-sm font-semibold text-gray-800"> </div> <div className="text-sm font-semibold text-foreground"> </div>
</div> </div>
<button <button
onClick={onClose} onClick={onClose}
className="flex h-6 w-6 items-center justify-center rounded-full text-gray-400 transition-colors hover:bg-gray-100 hover:text-muted-foreground" className="flex h-6 w-6 items-center justify-center rounded-full text-muted-foreground/70 transition-colors hover:bg-muted hover:text-muted-foreground"
> >
<svg className="h-4 w-4" fill="none" viewBox="0 0 24 24" stroke="currentColor"> <svg className="h-4 w-4" fill="none" viewBox="0 0 24 24" stroke="currentColor">
<path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} d="M6 18L18 6M6 6l12 12" /> <path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} d="M6 18L18 6M6 6l12 12" />
@ -159,10 +159,10 @@ export const RelationshipListModal: React.FC<RelationshipListModalProps> = ({
{relationships.map((relationship) => ( {relationships.map((relationship) => (
<div <div
key={relationship.id} key={relationship.id}
className="rounded-lg border border-gray-200 p-3 transition-all hover:border-blue-300 hover:bg-accent" className="rounded-lg border border-border p-3 transition-all hover:border-primary/40 hover:bg-accent"
> >
<div className="mb-1 flex items-center justify-between"> <div className="mb-1 flex items-center justify-between">
<h4 className="text-sm font-medium text-gray-900"> <h4 className="text-sm font-medium text-foreground">
{relationship.relationshipName || `${relationship.fromTable}${relationship.toTable}`} {relationship.relationshipName || `${relationship.fromTable}${relationship.toTable}`}
</h4> </h4>
<div className="flex items-center gap-1"> <div className="flex items-center gap-1">
@ -172,7 +172,7 @@ export const RelationshipListModal: React.FC<RelationshipListModalProps> = ({
e.stopPropagation(); e.stopPropagation();
handleEdit(relationship); handleEdit(relationship);
}} }}
className="flex h-6 w-6 items-center justify-center rounded text-gray-400 hover:bg-primary/20 hover:text-primary" className="flex h-6 w-6 items-center justify-center rounded text-muted-foreground/70 hover:bg-primary/20 hover:text-primary"
title="관계 편집" title="관계 편집"
> >
<svg className="h-3 w-3" fill="none" viewBox="0 0 24 24" stroke="currentColor"> <svg className="h-3 w-3" fill="none" viewBox="0 0 24 24" stroke="currentColor">
@ -190,7 +190,7 @@ export const RelationshipListModal: React.FC<RelationshipListModalProps> = ({
e.stopPropagation(); e.stopPropagation();
handleDelete(relationship); handleDelete(relationship);
}} }}
className="flex h-6 w-6 items-center justify-center rounded text-gray-400 hover:bg-destructive/20 hover:text-destructive" className="flex h-6 w-6 items-center justify-center rounded text-muted-foreground/70 hover:bg-destructive/20 hover:text-destructive"
title="관계 삭제" title="관계 삭제"
> >
<svg className="h-3 w-3" fill="none" viewBox="0 0 24 24" stroke="currentColor"> <svg className="h-3 w-3" fill="none" viewBox="0 0 24 24" stroke="currentColor">

View File

@ -161,13 +161,13 @@ const SaveDiagramModal: React.FC<SaveDiagramModalProps> = ({
</div> </div>
{/* 관계 요약 정보 */} {/* 관계 요약 정보 */}
<div className="grid grid-cols-3 gap-4 rounded-lg bg-gray-50 p-4"> <div className="grid grid-cols-3 gap-4 rounded-lg bg-muted p-4">
<div className="text-center"> <div className="text-center">
<div className="text-2xl font-bold text-primary">{relationships.length}</div> <div className="text-2xl font-bold text-primary">{relationships.length}</div>
<div className="text-sm text-muted-foreground"> </div> <div className="text-sm text-muted-foreground"> </div>
</div> </div>
<div className="text-center"> <div className="text-center">
<div className="text-2xl font-bold text-green-600">{connectedTables.length}</div> <div className="text-2xl font-bold text-emerald-600">{connectedTables.length}</div>
<div className="text-sm text-muted-foreground"> </div> <div className="text-sm text-muted-foreground"> </div>
</div> </div>
<div className="text-center"> <div className="text-center">
@ -207,7 +207,7 @@ const SaveDiagramModal: React.FC<SaveDiagramModalProps> = ({
{relationships.map((relationship, index) => ( {relationships.map((relationship, index) => (
<div <div
key={relationship.id || index} key={relationship.id || index}
className="flex items-center justify-between rounded-lg border bg-white p-3 hover:bg-gray-50" className="flex items-center justify-between rounded-lg border bg-white p-3 hover:bg-muted"
> >
<div className="flex-1"> <div className="flex-1">
<div className="flex items-center gap-2 text-sm"> <div className="flex items-center gap-2 text-sm">
@ -234,10 +234,10 @@ const SaveDiagramModal: React.FC<SaveDiagramModalProps> = ({
{/* 관계가 없는 경우 안내 */} {/* 관계가 없는 경우 안내 */}
{relationships.length === 0 && ( {relationships.length === 0 && (
<div className="py-8 text-center text-gray-500"> <div className="py-8 text-center text-muted-foreground">
<div className="mb-2 text-4xl">📭</div> <div className="mb-2 text-4xl">📭</div>
<div className="text-sm"> .</div> <div className="text-sm"> .</div>
<div className="mt-1 text-xs text-gray-400"> .</div> <div className="mt-1 text-xs text-muted-foreground/70"> .</div>
</div> </div>
)} )}
</div> </div>
@ -249,7 +249,7 @@ const SaveDiagramModal: React.FC<SaveDiagramModalProps> = ({
<Button <Button
onClick={handleSave} onClick={handleSave}
disabled={isLoading || relationships.length === 0} disabled={isLoading || relationships.length === 0}
className="bg-blue-600 hover:bg-blue-700" className="bg-primary hover:bg-primary/90"
> >
{isLoading ? ( {isLoading ? (
<div className="flex items-center gap-2"> <div className="flex items-center gap-2">
@ -269,13 +269,13 @@ const SaveDiagramModal: React.FC<SaveDiagramModalProps> = ({
<AlertDialogContent> <AlertDialogContent>
<AlertDialogHeader> <AlertDialogHeader>
<AlertDialogTitle className="flex items-center gap-2"> <AlertDialogTitle className="flex items-center gap-2">
<CheckCircle className="h-5 w-5 text-green-500" /> <CheckCircle className="h-5 w-5 text-emerald-500" />
</AlertDialogTitle> </AlertDialogTitle>
<AlertDialogDescription className="text-base"> <AlertDialogDescription className="text-base">
<span className="font-medium text-green-600">{savedDiagramName}</span> . <span className="font-medium text-emerald-600">{savedDiagramName}</span> .
<br /> <br />
<span className="mt-2 block text-sm text-gray-500"> <span className="mt-2 block text-sm text-muted-foreground">
. .
</span> </span>
</AlertDialogDescription> </AlertDialogDescription>

View File

@ -26,14 +26,14 @@ export const SelectedTablesPanel: React.FC<SelectedTablesPanelProps> = ({
return ( return (
<div className="pointer-events-auto absolute top-4 left-4 z-40 w-80 rounded-xl border border-primary/20 bg-white shadow-lg"> <div className="pointer-events-auto absolute top-4 left-4 z-40 w-80 rounded-xl border border-primary/20 bg-white shadow-lg">
{/* 헤더 */} {/* 헤더 */}
<div className="flex items-center justify-between rounded-t-xl border-b border-blue-100 bg-gradient-to-r from-blue-50 to-indigo-50 p-3"> <div className="flex items-center justify-between rounded-t-xl border-b border-primary/10 bg-gradient-to-r from-primary/5 to-indigo-50 p-3">
<div className="flex items-center gap-2"> <div className="flex items-center gap-2">
<div className="flex h-6 w-6 items-center justify-center rounded-full bg-primary/20"> <div className="flex h-6 w-6 items-center justify-center rounded-full bg-primary/20">
<span className="text-sm text-primary">📋</span> <span className="text-sm text-primary">📋</span>
</div> </div>
<div> <div>
<div className="text-sm font-semibold text-gray-800"> </div> <div className="text-sm font-semibold text-foreground"> </div>
<div className="text-xs text-gray-500"> <div className="text-xs text-muted-foreground">
{selectedNodes.length === 1 {selectedNodes.length === 1
? "FROM 테이블 선택됨" ? "FROM 테이블 선택됨"
: selectedNodes.length === 2 : selectedNodes.length === 2
@ -44,7 +44,7 @@ export const SelectedTablesPanel: React.FC<SelectedTablesPanelProps> = ({
</div> </div>
<button <button
onClick={onClose} onClick={onClose}
className="flex h-5 w-5 items-center justify-center rounded-full text-gray-400 hover:bg-gray-100 hover:text-muted-foreground" className="flex h-5 w-5 items-center justify-center rounded-full text-muted-foreground/70 hover:bg-muted hover:text-muted-foreground"
> >
</button> </button>
@ -66,14 +66,14 @@ export const SelectedTablesPanel: React.FC<SelectedTablesPanelProps> = ({
index === 0 index === 0
? "border-l-4 border-emerald-400 bg-emerald-50" ? "border-l-4 border-emerald-400 bg-emerald-50"
: index === 1 : index === 1
? "border-l-4 border-blue-400 bg-accent" ? "border-l-4 border-primary/60 bg-accent"
: "bg-muted" : "bg-muted"
}`} }`}
> >
<div className="mb-1 flex items-center justify-between"> <div className="mb-1 flex items-center justify-between">
<div <div
className={`text-xs font-medium ${ className={`text-xs font-medium ${
index === 0 ? "text-emerald-700" : index === 1 ? "text-blue-700" : "text-gray-700" index === 0 ? "text-emerald-700" : index === 1 ? "text-primary" : "text-foreground"
}`} }`}
> >
{displayName} {displayName}
@ -81,7 +81,7 @@ export const SelectedTablesPanel: React.FC<SelectedTablesPanelProps> = ({
{selectedNodes.length === 2 && ( {selectedNodes.length === 2 && (
<div <div
className={`rounded-full px-2 py-0.5 text-xs font-bold ${ className={`rounded-full px-2 py-0.5 text-xs font-bold ${
index === 0 ? "bg-emerald-200 text-emerald-800" : "bg-blue-200 text-blue-800" index === 0 ? "bg-emerald-200 text-emerald-800" : "bg-blue-200 text-primary"
}`} }`}
> >
{index === 0 ? "FROM" : "TO"} {index === 0 ? "FROM" : "TO"}
@ -94,7 +94,7 @@ export const SelectedTablesPanel: React.FC<SelectedTablesPanelProps> = ({
{/* 연결 화살표 (마지막이 아닌 경우) */} {/* 연결 화살표 (마지막이 아닌 경우) */}
{index < selectedNodes.length - 1 && ( {index < selectedNodes.length - 1 && (
<div className="flex justify-center py-1"> <div className="flex justify-center py-1">
<div className="text-gray-400"></div> <div className="text-muted-foreground/70"></div>
</div> </div>
)} )}
</div> </div>
@ -104,14 +104,14 @@ export const SelectedTablesPanel: React.FC<SelectedTablesPanelProps> = ({
</div> </div>
{/* 액션 버튼 */} {/* 액션 버튼 */}
<div className="flex gap-2 border-t border-blue-100 p-3"> <div className="flex gap-2 border-t border-primary/10 p-3">
<button <button
onClick={onOpenConnectionModal} onClick={onOpenConnectionModal}
disabled={!canCreateConnection} disabled={!canCreateConnection}
className={`flex flex-1 items-center justify-center gap-1 rounded-lg px-3 py-2 text-xs font-medium transition-colors ${ className={`flex flex-1 items-center justify-center gap-1 rounded-lg px-3 py-2 text-xs font-medium transition-colors ${
canCreateConnection canCreateConnection
? "bg-accent0 text-white hover:bg-blue-600" ? "bg-accent0 text-white hover:bg-primary"
: "cursor-not-allowed bg-gray-300 text-gray-500" : "cursor-not-allowed bg-muted/60 text-muted-foreground"
}`} }`}
> >
<span>🔗</span> <span>🔗</span>
@ -119,7 +119,7 @@ export const SelectedTablesPanel: React.FC<SelectedTablesPanelProps> = ({
</button> </button>
<button <button
onClick={onClear} onClick={onClear}
className="flex flex-1 items-center justify-center gap-1 rounded-lg bg-gray-200 px-3 py-2 text-xs font-medium text-muted-foreground hover:bg-gray-300" className="flex flex-1 items-center justify-center gap-1 rounded-lg bg-muted/80 px-3 py-2 text-xs font-medium text-muted-foreground hover:bg-muted/60"
> >
<span>🗑</span> <span>🗑</span>
<span></span> <span></span>

View File

@ -32,13 +32,13 @@ export const TableNode: React.FC<{ data: TableNodeData }> = ({ data }) => {
const { table, onColumnClick, onScrollAreaEnter, onScrollAreaLeave, selectedColumns = [] } = data; const { table, onColumnClick, onScrollAreaEnter, onScrollAreaLeave, selectedColumns = [] } = data;
return ( return (
<div className="relative flex min-w-[280px] cursor-pointer flex-col overflow-hidden rounded-lg border-2 border-gray-300 bg-white shadow-lg transition-all hover:shadow-xl"> <div className="relative flex min-w-[280px] cursor-pointer flex-col overflow-hidden rounded-lg border-2 border-input bg-white shadow-lg transition-all hover:shadow-xl">
{/* React Flow Handles - 숨김 처리 */} {/* React Flow Handles - 숨김 처리 */}
<Handle type="target" position={Position.Left} id="left" className="!invisible !h-1 !w-1" /> <Handle type="target" position={Position.Left} id="left" className="!invisible !h-1 !w-1" />
<Handle type="source" position={Position.Right} id="right" className="!invisible !h-1 !w-1" /> <Handle type="source" position={Position.Right} id="right" className="!invisible !h-1 !w-1" />
{/* 테이블 헤더 - 통일된 디자인 */} {/* 테이블 헤더 - 통일된 디자인 */}
<div className="bg-blue-600 p-3 text-white"> <div className="bg-primary p-3 text-white">
<h3 className="truncate text-sm font-semibold">{table.displayName}</h3> <h3 className="truncate text-sm font-semibold">{table.displayName}</h3>
{table.description && <p className="mt-1 truncate text-xs opacity-75">{table.description}</p>} {table.description && <p className="mt-1 truncate text-xs opacity-75">{table.description}</p>}
</div> </div>
@ -56,7 +56,7 @@ export const TableNode: React.FC<{ data: TableNodeData }> = ({ data }) => {
<div <div
key={columnKey} key={columnKey}
className={`relative cursor-pointer rounded px-2 py-1 text-xs transition-colors ${ className={`relative cursor-pointer rounded px-2 py-1 text-xs transition-colors ${
isSelected ? "bg-primary/20 text-blue-800 ring-2 ring-blue-500" : "text-gray-700 hover:bg-gray-100" isSelected ? "bg-primary/20 text-primary ring-2 ring-ring" : "text-foreground hover:bg-muted"
}`} }`}
onClick={() => onColumnClick(table.tableName, columnKey)} onClick={() => onColumnClick(table.tableName, columnKey)}
> >
@ -64,9 +64,9 @@ export const TableNode: React.FC<{ data: TableNodeData }> = ({ data }) => {
<div className="flex items-center justify-between"> <div className="flex items-center justify-between">
<span className="font-mono font-medium">{columnDisplayName}</span> <span className="font-mono font-medium">{columnDisplayName}</span>
<span className="text-gray-500">{columnType}</span> <span className="text-muted-foreground">{columnType}</span>
</div> </div>
{column.description && <div className="mt-0.5 text-gray-500">{column.description}</div>} {column.description && <div className="mt-0.5 text-muted-foreground">{column.description}</div>}
</div> </div>
); );
})} })}

View File

@ -74,7 +74,7 @@ export const TableSelector: React.FC<TableSelectorProps> = ({ companyCode, onTab
return ( return (
<div className="space-y-4"> <div className="space-y-4">
<div className="flex items-center justify-between"> <div className="flex items-center justify-between">
<h3 className="text-lg font-semibold text-gray-800"> </h3> <h3 className="text-lg font-semibold text-foreground"> </h3>
<Button onClick={loadTables} variant="outline" size="sm" disabled={loading}> <Button onClick={loadTables} variant="outline" size="sm" disabled={loading}>
{loading ? "로딩중..." : "새로고침"} {loading ? "로딩중..." : "새로고침"}
</Button> </Button>
@ -82,7 +82,7 @@ export const TableSelector: React.FC<TableSelectorProps> = ({ companyCode, onTab
{/* 검색 입력 */} {/* 검색 입력 */}
<div className="relative"> <div className="relative">
<Search className="absolute top-1/2 left-3 h-4 w-4 -translate-y-1/2 text-gray-400" /> <Search className="absolute top-1/2 left-3 h-4 w-4 -translate-y-1/2 text-muted-foreground/70" />
<Input <Input
placeholder="테이블명으로 검색..." placeholder="테이블명으로 검색..."
value={searchTerm} value={searchTerm}
@ -99,11 +99,11 @@ export const TableSelector: React.FC<TableSelectorProps> = ({ companyCode, onTab
<div className="max-h-96 space-y-2 overflow-y-auto"> <div className="max-h-96 space-y-2 overflow-y-auto">
{loading ? ( {loading ? (
<div className="flex items-center justify-center py-8"> <div className="flex items-center justify-center py-8">
<div className="text-sm text-gray-500"> ...</div> <div className="text-sm text-muted-foreground"> ...</div>
</div> </div>
) : filteredTables.length === 0 ? ( ) : filteredTables.length === 0 ? (
<div className="flex items-center justify-center py-8"> <div className="flex items-center justify-center py-8">
<div className="text-center text-sm text-gray-500"> <div className="text-center text-sm text-muted-foreground">
{searchTerm ? "검색 결과가 없습니다." : "등록된 테이블이 없습니다."} {searchTerm ? "검색 결과가 없습니다." : "등록된 테이블이 없습니다."}
</div> </div>
</div> </div>
@ -114,14 +114,14 @@ export const TableSelector: React.FC<TableSelectorProps> = ({ companyCode, onTab
<Card <Card
key={table.tableName} key={table.tableName}
className={`cursor-pointer transition-all hover:shadow-md ${ className={`cursor-pointer transition-all hover:shadow-md ${
isSelected ? "cursor-not-allowed border-primary bg-accent opacity-60" : "hover:border-gray-300" isSelected ? "cursor-not-allowed border-primary bg-accent opacity-60" : "hover:border-input"
}`} }`}
onDoubleClick={() => !isSelected && handleAddTable(table)} onDoubleClick={() => !isSelected && handleAddTable(table)}
> >
<CardHeader className="pb-2"> <CardHeader className="pb-2">
<div> <div>
<CardTitle className="text-sm font-medium">{table.displayName}</CardTitle> <CardTitle className="text-sm font-medium">{table.displayName}</CardTitle>
<div className="mt-1 text-xs text-gray-500">{table.columnCount} </div> <div className="mt-1 text-xs text-muted-foreground">{table.columnCount} </div>
</div> </div>
</CardHeader> </CardHeader>
<CardContent className="pt-0"> <CardContent className="pt-0">
@ -132,7 +132,7 @@ export const TableSelector: React.FC<TableSelectorProps> = ({ companyCode, onTab
{isSelected && <span className="font-medium text-primary">()</span>} {isSelected && <span className="font-medium text-primary">()</span>}
</div> </div>
{table.description && <p className="line-clamp-2 text-xs text-gray-500">{table.description}</p>} {table.description && <p className="line-clamp-2 text-xs text-muted-foreground">{table.description}</p>}
</div> </div>
</CardContent> </CardContent>
</Card> </Card>
@ -142,7 +142,7 @@ export const TableSelector: React.FC<TableSelectorProps> = ({ companyCode, onTab
</div> </div>
{/* 통계 정보 */} {/* 통계 정보 */}
<div className="rounded-lg bg-gray-50 p-3 text-xs text-muted-foreground"> <div className="rounded-lg bg-muted p-3 text-xs text-muted-foreground">
<div className="flex items-center justify-between"> <div className="flex items-center justify-between">
<span> : {tables.length}</span> <span> : {tables.length}</span>
{searchTerm && <span> : {filteredTables.length}</span>} {searchTerm && <span> : {filteredTables.length}</span>}

View File

@ -63,7 +63,7 @@ export const ConditionRenderer: React.FC<ConditionRendererProps> = ({
return ( return (
<div className="space-y-2"> <div className="space-y-2">
{conditions.length === 0 ? ( {conditions.length === 0 ? (
<div className="rounded-lg border border-dashed p-3 text-center text-xs text-gray-500"> <div className="rounded-lg border border-dashed p-3 text-center text-xs text-muted-foreground">
. .
<br /> <br />
. .
@ -92,7 +92,7 @@ export const ConditionRenderer: React.FC<ConditionRendererProps> = ({
)} )}
{/* 그룹 레벨에 따른 들여쓰기 */} {/* 그룹 레벨에 따른 들여쓰기 */}
<div <div
className="flex items-center gap-2 rounded border-2 border-dashed border-blue-300 bg-accent/50 p-2" className="flex items-center gap-2 rounded border-2 border-dashed border-primary/40 bg-accent/50 p-2"
style={{ marginLeft: `${(condition.groupLevel || 0) * 20}px` }} style={{ marginLeft: `${(condition.groupLevel || 0) * 20}px` }}
> >
<span className="font-mono text-sm text-primary">(</span> <span className="font-mono text-sm text-primary">(</span>
@ -110,7 +110,7 @@ export const ConditionRenderer: React.FC<ConditionRendererProps> = ({
return ( return (
<div key={condition.id} className="flex items-center gap-2"> <div key={condition.id} className="flex items-center gap-2">
<div <div
className="flex items-center gap-2 rounded border-2 border-dashed border-blue-300 bg-accent/50 p-2" className="flex items-center gap-2 rounded border-2 border-dashed border-primary/40 bg-accent/50 p-2"
style={{ marginLeft: `${(condition.groupLevel || 0) * 20}px` }} style={{ marginLeft: `${(condition.groupLevel || 0) * 20}px` }}
> >
<span className="font-mono text-sm text-primary">)</span> <span className="font-mono text-sm text-primary">)</span>

View File

@ -114,7 +114,7 @@ export const ActionConditionRenderer: React.FC<ActionConditionRendererProps> = (
value={settings.actions[actionIndex].conditions![condIndex - 1]?.logicalOperator || "AND"} value={settings.actions[actionIndex].conditions![condIndex - 1]?.logicalOperator || "AND"}
onValueChange={(value: "AND" | "OR") => updateLogicalOperator(condIndex - 1, value)} onValueChange={(value: "AND" | "OR") => updateLogicalOperator(condIndex - 1, value)}
> >
<SelectTrigger className="h-6 w-24 border-green-200 bg-green-50 text-xs"> <SelectTrigger className="h-6 w-24 border-emerald-200 bg-emerald-50 text-xs">
<SelectValue /> <SelectValue />
</SelectTrigger> </SelectTrigger>
<SelectContent> <SelectContent>
@ -124,11 +124,11 @@ export const ActionConditionRenderer: React.FC<ActionConditionRendererProps> = (
</Select> </Select>
)} )}
<div <div
className="flex items-center gap-2 rounded border-2 border-dashed border-green-300 bg-green-50/50 p-1" className="flex items-center gap-2 rounded border-2 border-dashed border-green-300 bg-emerald-50/50 p-1"
style={{ marginLeft: `${(condition.groupLevel || 0) * 15}px` }} style={{ marginLeft: `${(condition.groupLevel || 0) * 15}px` }}
> >
<span className="font-mono text-xs text-green-600">(</span> <span className="font-mono text-xs text-emerald-600">(</span>
<span className="text-xs text-green-600"> </span> <span className="text-xs text-emerald-600"> </span>
<Button <Button
size="sm" size="sm"
variant="ghost" variant="ghost"
@ -147,11 +147,11 @@ export const ActionConditionRenderer: React.FC<ActionConditionRendererProps> = (
return ( return (
<div className="flex items-center gap-2"> <div className="flex items-center gap-2">
<div <div
className="flex items-center gap-2 rounded border-2 border-dashed border-green-300 bg-green-50/50 p-1" className="flex items-center gap-2 rounded border-2 border-dashed border-green-300 bg-emerald-50/50 p-1"
style={{ marginLeft: `${(condition.groupLevel || 0) * 15}px` }} style={{ marginLeft: `${(condition.groupLevel || 0) * 15}px` }}
> >
<span className="font-mono text-xs text-green-600">)</span> <span className="font-mono text-xs text-emerald-600">)</span>
<span className="text-xs text-green-600"> </span> <span className="text-xs text-emerald-600"> </span>
<Button <Button
size="sm" size="sm"
variant="ghost" variant="ghost"
@ -174,7 +174,7 @@ export const ActionConditionRenderer: React.FC<ActionConditionRendererProps> = (
value={settings.actions[actionIndex].conditions![condIndex - 1]?.logicalOperator || "AND"} value={settings.actions[actionIndex].conditions![condIndex - 1]?.logicalOperator || "AND"}
onValueChange={(value: "AND" | "OR") => updateLogicalOperator(condIndex - 1, value)} onValueChange={(value: "AND" | "OR") => updateLogicalOperator(condIndex - 1, value)}
> >
<SelectTrigger className="h-6 w-24 border-green-200 bg-green-50 text-xs"> <SelectTrigger className="h-6 w-24 border-emerald-200 bg-emerald-50 text-xs">
<SelectValue /> <SelectValue />
</SelectTrigger> </SelectTrigger>
<SelectContent> <SelectContent>

View File

@ -82,21 +82,21 @@ export const ActionConditionsSection: React.FC<ActionConditionsSectionProps> = (
<div className="mt-3"> <div className="mt-3">
<details className="group"> <details className="group">
<summary <summary
className={`flex cursor-pointer items-center justify-between rounded border p-2 text-xs font-medium hover:bg-gray-50 hover:text-gray-900 ${ className={`flex cursor-pointer items-center justify-between rounded border p-2 text-xs font-medium hover:bg-muted hover:text-foreground ${
isConditionRequired && !hasValidConditions isConditionRequired && !hasValidConditions
? "border-red-300 bg-destructive/10 text-red-700" ? "border-destructive/30 bg-destructive/10 text-destructive"
: "border-gray-200 text-gray-700" : "border-border text-foreground"
}`} }`}
> >
<div className="flex items-center gap-2"> <div className="flex items-center gap-2">
🔍 🔍
{isConditionRequired ? ( {isConditionRequired ? (
<span className="rounded bg-destructive/20 px-1 py-0.5 text-xs font-semibold text-red-700"></span> <span className="rounded bg-destructive/20 px-1 py-0.5 text-xs font-semibold text-destructive"></span>
) : ( ) : (
<span className="text-gray-500">()</span> <span className="text-muted-foreground">()</span>
)} )}
{action.conditions && action.conditions.length > 0 && ( {action.conditions && action.conditions.length > 0 && (
<span className="rounded-full bg-primary/20 px-2 py-0.5 text-xs text-blue-700"> <span className="rounded-full bg-primary/20 px-2 py-0.5 text-xs text-primary">
{action.conditions.length} {action.conditions.length}
</span> </span>
)} )}
@ -113,14 +113,14 @@ export const ActionConditionsSection: React.FC<ActionConditionsSectionProps> = (
e.stopPropagation(); e.stopPropagation();
clearAllConditions(); clearAllConditions();
}} }}
className="h-5 w-5 p-0 text-red-500 hover:text-red-700" className="h-5 w-5 p-0 text-destructive hover:text-destructive"
title="조건 모두 삭제" title="조건 모두 삭제"
> >
<Trash2 className="h-3 w-3" /> <Trash2 className="h-3 w-3" />
</Button> </Button>
)} )}
</summary> </summary>
<div className="mt-2 space-y-2 border-l-2 border-gray-100 pl-4"> <div className="mt-2 space-y-2 border-l-2 border-border pl-4">
<div className="mb-2 flex items-center justify-between"> <div className="mb-2 flex items-center justify-between">
<div className="flex gap-1"> <div className="flex gap-1">
<Button size="sm" variant="outline" onClick={addActionCondition} className="h-6 text-xs"> <Button size="sm" variant="outline" onClick={addActionCondition} className="h-6 text-xs">
@ -151,13 +151,13 @@ export const ActionConditionsSection: React.FC<ActionConditionsSectionProps> = (
<div <div
className={`rounded border p-3 text-xs ${ className={`rounded border p-3 text-xs ${
isConditionRequired isConditionRequired
? "border-destructive/20 bg-destructive/10 text-red-700" ? "border-destructive/20 bg-destructive/10 text-destructive"
: "border-gray-200 bg-gray-50 text-muted-foreground" : "border-border bg-muted text-muted-foreground"
}`} }`}
> >
{isConditionRequired ? ( {isConditionRequired ? (
<div className="flex items-start gap-2"> <div className="flex items-start gap-2">
<span className="text-red-500"></span> <span className="text-destructive"></span>
<div> <div>
<div className="font-medium"> </div> <div className="font-medium"> </div>
<div className="mt-1"> <div className="mt-1">

Some files were not shown because too many files have changed in this diff Show More