feat: add floating AI chat widget to all pages

- Add AiChatWidget component with floating button in bottom-right
- Routes to /api/ai/chat/message for chat, ticket, form, cover letter intents
- Quick action buttons for common tasks
- Appears on all public pages via app.tsx root layout
- Appears on all dashboard pages via DashboardShell
This commit is contained in:
Tracewebstudio Dev 2026-04-15 20:03:47 +02:00
parent 6f550458e7
commit fe81ce54c7
3 changed files with 602 additions and 175 deletions

View file

@ -1,9 +1,10 @@
import { MetaProvider, Title } from '@solidjs/meta'; import { MetaProvider, Title } from "@solidjs/meta";
import { Router } from '@solidjs/router'; import { Router } from "@solidjs/router";
import { FileRoutes } from '@solidjs/start/router'; import { FileRoutes } from "@solidjs/start/router";
import { ErrorBoundary, Suspense } from 'solid-js'; import { ErrorBoundary, Suspense } from "solid-js";
import { AuthProvider } from '~/lib/auth'; import { AuthProvider } from "~/lib/auth";
import './app.css'; import { AiChatWidget } from "~/components/AiChatWidget";
import "./app.css";
export default function App() { export default function App() {
return ( return (
@ -14,16 +15,34 @@ export default function App() {
<AuthProvider> <AuthProvider>
<ErrorBoundary <ErrorBoundary
fallback={(err) => ( fallback={(err) => (
<main style={{ padding: '24px', 'font-family': 'Inter, system-ui, sans-serif', color: '#111827', background: '#fff' }}> <main
<h1 style={{ margin: 0, 'font-size': '20px' }}>Frontend Error</h1> style={{
<p style={{ 'margin-top': '8px' }}>A runtime error occurred while rendering this page.</p> padding: "24px",
<pre style={{ 'margin-top': '12px', padding: '12px', background: '#f3f4f6', 'border-radius': '8px', 'white-space': 'pre-wrap' }}> "font-family": "Inter, system-ui, sans-serif",
color: "#111827",
background: "#fff",
}}
>
<h1 style={{ margin: 0, "font-size": "20px" }}>Frontend Error</h1>
<p style={{ "margin-top": "8px" }}>
A runtime error occurred while rendering this page.
</p>
<pre
style={{
"margin-top": "12px",
padding: "12px",
background: "#f3f4f6",
"border-radius": "8px",
"white-space": "pre-wrap",
}}
>
{String((err as any)?.message || err)} {String((err as any)?.message || err)}
</pre> </pre>
</main> </main>
)} )}
> >
<Suspense>{props.children}</Suspense> <Suspense>{props.children}</Suspense>
<AiChatWidget />
</ErrorBoundary> </ErrorBoundary>
</AuthProvider> </AuthProvider>
</MetaProvider> </MetaProvider>

View file

@ -0,0 +1,333 @@
import { createSignal, Show, For, onMount } from "solid-js";
import { MessageCircle, X, Send, Bot, User, Loader } from "lucide-solid";
const API = "/api/gateway";
interface ChatMessage {
role: "user" | "assistant";
content: string;
intent?: string;
}
interface ChatResponse {
message: string;
conversation_id: string;
intent: string;
confidence: number;
}
export function AiChatWidget() {
const [isOpen, setIsOpen] = createSignal(false);
const [messages, setMessages] = createSignal<ChatMessage[]>([
{
role: "assistant",
content:
"Hi! I'm your AI assistant. I can help you create support tickets, fill out forms, generate job descriptions, or write cover letters. What can I help you with?",
},
]);
const [input, setInput] = createSignal("");
const [isLoading, setIsLoading] = createSignal(false);
const [conversationId, setConversationId] = createSignal("");
const toggleChat = () => setIsOpen((v) => !v);
const sendMessage = async () => {
const text = input().trim();
if (!text || isLoading()) return;
setIsLoading(true);
const userMessage: ChatMessage = { role: "user", content: text };
setMessages((prev) => [...prev, userMessage]);
setInput("");
try {
const res = await fetch(`${API}/api/ai/chat/message`, {
method: "POST",
headers: { "Content-Type": "application/json" },
body: JSON.stringify({
message: text,
conversation_id: conversationId() || undefined,
}),
});
if (!res.ok) throw new Error("AI request failed");
const data: ChatResponse = await res.json();
if (data.conversation_id && !conversationId()) {
setConversationId(data.conversation_id);
}
const assistantMessage: ChatMessage = {
role: "assistant",
content: data.message,
intent: data.intent,
};
setMessages((prev) => [...prev, assistantMessage]);
} catch (err) {
setMessages((prev) => [
...prev,
{
role: "assistant",
content:
"I'm having trouble connecting right now. Please try again or contact support@nxtgauge.com.",
},
]);
} finally {
setIsLoading(false);
}
};
const handleKeyDown = (e: KeyboardEvent) => {
if (e.key === "Enter" && !e.shiftKey) {
e.preventDefault();
sendMessage();
}
};
return (
<>
{/* Floating button */}
<button
onClick={toggleChat}
style={{
position: "fixed",
bottom: "24px",
right: "24px",
width: "56px",
height: "56px",
"border-radius": "50%",
background: "#FF5E13",
border: "none",
cursor: "pointer",
display: "flex",
"align-items": "center",
"justify-content": "center",
"box-shadow": "0 4px 16px rgba(255, 90, 19, 0.35)",
"z-index": "9999",
transition: "transform 0.2s",
}}
title="AI Assistant"
>
<Show when={isOpen()} fallback={<MessageCircle size={24} color="#fff" />}>
<X size={24} color="#fff" />
</Show>
</button>
{/* Chat window */}
<Show when={isOpen()}>
<div
style={{
position: "fixed",
bottom: "96px",
right: "24px",
width: "380px",
height: "520px",
background: "#fff",
"border-radius": "16px",
"box-shadow": "0 8px 40px rgba(0,0,0,0.15)",
display: "flex",
"flex-direction": "column",
"z-index": "9998",
overflow: "hidden",
}}
>
{/* Header */}
<div
style={{
background: "linear-gradient(135deg, #FF5E13 0%, #E5470F 100%)",
padding: "16px 20px",
display: "flex",
"align-items": "center",
"justify-content": "space-between",
}}
>
<div style={{ display: "flex", "align-items": "center", gap: "10px" }}>
<Bot size={22} color="#fff" />
<div>
<p style={{ margin: 0, color: "#fff", "font-weight": "700", "font-size": "15px" }}>
AI Assistant
</p>
<p style={{ margin: 0, color: "rgba(255,255,255,0.8)", "font-size": "11px" }}>
Powered by gemma3
</p>
</div>
</div>
<button
onClick={toggleChat}
style={{
background: "none",
border: "none",
cursor: "pointer",
padding: "4px",
}}
>
<X size={20} color="#fff" />
</button>
</div>
{/* Quick actions */}
<div
style={{
padding: "10px 16px",
"border-bottom": "1px solid #E5E7EB",
display: "flex",
gap: "8px",
"flex-wrap": "wrap",
}}
>
{["Create Ticket", "Job Description", "Cover Letter", "Fill Form"].map((label) => (
<button
onClick={() => {
setInput(`${label.toLowerCase()}: `);
}}
style={{
padding: "4px 10px",
"border-radius": "20px",
border: "1px solid #E5E7EB",
background: "#F9FAFB",
"font-size": "11px",
cursor: "pointer",
color: "#374151",
}}
>
{label}
</button>
))}
</div>
{/* Messages */}
<div
style={{
flex: 1,
overflow: "auto",
padding: "16px",
display: "flex",
"flex-direction": "column",
gap: "12px",
}}
>
<For each={messages()}>
{(msg) => (
<div
style={{
display: "flex",
"align-items": "flex-start",
gap: "8px",
"flex-direction": msg.role === "user" ? "row-reverse" : "row",
}}
>
<div
style={{
width: "28px",
height: "28px",
"border-radius": "50%",
background: msg.role === "user" ? "#FF5E13" : "#E5E7EB",
display: "flex",
"align-items": "center",
"justify-content": "center",
"flex-shrink": 0,
}}
>
<Show when={msg.role === "user"} fallback={<Bot size={14} color="#6B7280" />}>
<User size={14} color="#fff" />
</Show>
</div>
<div
style={{
"max-width": "75%",
padding: "10px 14px",
"border-radius": "14px",
background: msg.role === "user" ? "#FF5E13" : "#F3F4F6",
color: msg.role === "user" ? "#fff" : "#111827",
"font-size": "13px",
"line-height": "1.5",
}}
>
<p style={{ margin: 0, "white-space": "pre-wrap" }}>{msg.content}</p>
<Show when={msg.intent && msg.role === "assistant"}>
<p
style={{
margin: "4px 0 0",
"font-size": "10px",
color: "#9CA3AF",
"font-style": "italic",
}}
>
Intent: {msg.intent}
</p>
</Show>
</div>
</div>
)}
</For>
<Show when={isLoading()}>
<div
style={{
display: "flex",
"align-items": "center",
gap: "8px",
color: "#9CA3AF",
"font-size": "13px",
}}
>
<Loader size={14} style={{ animation: "spin 1s linear infinite" }} />
Thinking...
</div>
</Show>
</div>
{/* Input */}
<div
style={{
padding: "12px 16px",
"border-top": "1px solid #E5E7EB",
display: "flex",
gap: "8px",
}}
>
<input
type="text"
value={input()}
onInput={(e) => setInput(e.currentTarget.value)}
onKeyDown={handleKeyDown}
placeholder="Ask me anything..."
style={{
flex: 1,
height: "40px",
"border-radius": "20px",
border: "1px solid #E5E7EB",
padding: "0 16px",
"font-size": "13px",
outline: "none",
}}
/>
<button
onClick={sendMessage}
disabled={isLoading() || !input().trim()}
style={{
width: "40px",
height: "40px",
"border-radius": "50%",
background: isLoading() ? "#E5E7EB" : "#FF5E13",
border: "none",
cursor: isLoading() ? "default" : "pointer",
display: "flex",
"align-items": "center",
"justify-content": "center",
}}
>
<Send size={16} color="#fff" />
</button>
</div>
</div>
</Show>
<style>{`
@keyframes spin {
from { transform: rotate(0deg); }
to { transform: rotate(360deg); }
}
`}</style>
</>
);
}

View file

@ -3,36 +3,49 @@
* Used for pages that need actual backend connectivity * Used for pages that need actual backend connectivity
* (My Profile, My Portfolio, Verification) instead of the preview mock. * (My Profile, My Portfolio, Verification) instead of the preview mock.
*/ */
import { For, JSX, Show, createMemo, createSignal, onMount } from 'solid-js'; import { For, JSX, Show, createMemo, createSignal, onMount } from "solid-js";
import { AiChatWidget } from "./AiChatWidget";
import { import {
User, Briefcase, LayoutDashboard, FolderOpen, MapPin, Star, User,
CreditCard, Globe, ShieldCheck, HelpCircle, Settings, Briefcase,
RefreshCw, LogOut, Bell, ChevronRight, LayoutDashboard,
} from 'lucide-solid'; FolderOpen,
MapPin,
Star,
CreditCard,
Globe,
ShieldCheck,
HelpCircle,
Settings,
RefreshCw,
LogOut,
Bell,
ChevronRight,
} from "lucide-solid";
// ── Icon map (matches DashboardDesignPreview sidebar keys) ──────────────────── // ── Icon map (matches DashboardDesignPreview sidebar keys) ────────────────────
const ICON_MAP: Record<string, any> = { const ICON_MAP: Record<string, any> = {
'my dashboard': LayoutDashboard, "my dashboard": LayoutDashboard,
'my profile': User, "my profile": User,
'my portfolio': FolderOpen, "my portfolio": FolderOpen,
'leads': MapPin, leads: MapPin,
'my responses': Star, "my responses": Star,
'credits': CreditCard, credits: CreditCard,
'explore nxtgauge': Globe, "explore nxtgauge": Globe,
'verification': ShieldCheck, verification: ShieldCheck,
'help center': HelpCircle, "help center": HelpCircle,
'settings': Settings, settings: Settings,
'switch services': RefreshCw, "switch services": RefreshCw,
'jobs': Briefcase, jobs: Briefcase,
'applications': Briefcase, applications: Briefcase,
'shortlisted candidates': User, "shortlisted candidates": User,
'my applications': FolderOpen, "my applications": FolderOpen,
'saved jobs': Star, "saved jobs": Star,
'my requirements': FolderOpen, "my requirements": FolderOpen,
'received responses': Bell, "received responses": Bell,
'shortlisted responses': Star, "shortlisted responses": Star,
'logout': LogOut, logout: LogOut,
}; };
function SidebarIcon(props: { label: string }) { function SidebarIcon(props: { label: string }) {
@ -42,9 +55,9 @@ function SidebarIcon(props: { label: string }) {
} }
function titleCase(value: string) { function titleCase(value: string) {
return String(value || '') return String(value || "")
.toLowerCase() .toLowerCase()
.replace(/_/g, ' ') .replace(/_/g, " ")
.replace(/\b\w/g, (c) => c.toUpperCase()); .replace(/\b\w/g, (c) => c.toUpperCase());
} }
@ -61,14 +74,14 @@ interface Props {
// ── Brand colours ───────────────────────────────────────────────────────────── // ── Brand colours ─────────────────────────────────────────────────────────────
const ORANGE = '#FF5E13'; const ORANGE = "#FF5E13";
const NAVY = '#0D0D2A'; const NAVY = "#0D0D2A";
// ── Component ───────────────────────────────────────────────────────────────── // ── Component ─────────────────────────────────────────────────────────────────
export default function DashboardShell(props: Props) { export default function DashboardShell(props: Props) {
const roleLabel = createMemo(() => { const roleLabel = createMemo(() => {
const k = String(props.roleKey || '').replace(/_/g, ' '); const k = String(props.roleKey || "").replace(/_/g, " ");
return k.charAt(0).toUpperCase() + k.slice(1).toLowerCase(); return k.charAt(0).toUpperCase() + k.slice(1).toLowerCase();
}); });
@ -77,18 +90,21 @@ export default function DashboardShell(props: Props) {
// Fetch unread notification count // Fetch unread notification count
const fetchUnreadCount = async () => { const fetchUnreadCount = async () => {
try { try {
const token = typeof window !== 'undefined' ? window.sessionStorage.getItem('nxtgauge_access_token') || '' : ''; const token =
typeof window !== "undefined"
? window.sessionStorage.getItem("nxtgauge_access_token") || ""
: "";
if (!token) return; if (!token) return;
const res = await fetch('/api/me/notifications/unread-count', { const res = await fetch("/api/me/notifications/unread-count", {
headers: { Authorization: `Bearer ${token}` }, headers: { Authorization: `Bearer ${token}` },
credentials: 'include', credentials: "include",
}); });
if (res.ok) { if (res.ok) {
const data = await res.json(); const data = await res.json();
setUnreadCount(data.unread_count || 0); setUnreadCount(data.unread_count || 0);
} }
} catch (e) { } catch (e) {
console.error('Failed to fetch unread count:', e); console.error("Failed to fetch unread count:", e);
} }
}; };
@ -100,68 +116,94 @@ export default function DashboardShell(props: Props) {
}); });
return ( return (
<div style={{ <div
display: 'flex', style={{
'min-height': '100vh', display: "flex",
background: '#F8FAFC', "min-height": "100vh",
'font-family': "'Exo 2', sans-serif", background: "#F8FAFC",
}}> "font-family": "'Exo 2', sans-serif",
}}
>
{/* ── Sidebar ──────────────────────────────────────────────────────── */} {/* ── Sidebar ──────────────────────────────────────────────────────── */}
<aside style={{ <aside
width: '220px', style={{
'flex-shrink': '0', width: "220px",
background: '#FFFFFF', "flex-shrink": "0",
display: 'flex', background: "#FFFFFF",
'flex-direction': 'column', display: "flex",
'padding': '0', "flex-direction": "column",
'min-height': '100vh', padding: "0",
position: 'sticky', "min-height": "100vh",
top: '0', position: "sticky",
height: '100vh', top: "0",
'overflow-y': 'auto', height: "100vh",
}}> "overflow-y": "auto",
}}
>
{/* Logo */} {/* Logo */}
<div style={{ padding: '20px 16px 12px', 'border-bottom': '1px solid #E5E7EB' }}> <div style={{ padding: "20px 16px 12px", "border-bottom": "1px solid #E5E7EB" }}>
<img src="/nxtgauge-logo.png" alt="Nxtgauge" style={{ height: '40px', 'object-fit': 'contain', 'max-width': '170px' }} /> <img
src="/nxtgauge-logo.png"
alt="Nxtgauge"
style={{ height: "40px", "object-fit": "contain", "max-width": "170px" }}
/>
</div> </div>
{/* Role badge */} {/* Role badge */}
<div style={{ padding: '10px 16px', 'border-bottom': '1px solid #E5E7EB' }}> <div style={{ padding: "10px 16px", "border-bottom": "1px solid #E5E7EB" }}>
<p style={{ margin: '0', 'font-size': '10px', 'letter-spacing': '0.08em', 'text-transform': 'uppercase', color: '#6B7280' }}>Active Role</p> <p
<p style={{ margin: '2px 0 0', 'font-size': '12px', 'font-weight': '700', color: '#111827' }}>{roleLabel()}</p> style={{
margin: "0",
"font-size": "10px",
"letter-spacing": "0.08em",
"text-transform": "uppercase",
color: "#6B7280",
}}
>
Active Role
</p>
<p
style={{
margin: "2px 0 0",
"font-size": "12px",
"font-weight": "700",
color: "#111827",
}}
>
{roleLabel()}
</p>
</div> </div>
{/* Nav items */} {/* Nav items */}
<nav style={{ flex: '1', padding: '8px 8px' }}> <nav style={{ flex: "1", padding: "8px 8px" }}>
<For each={props.sidebarItems}> <For each={props.sidebarItems}>
{(item) => { {(item) => {
const isActive = () => item.toLowerCase() === props.activeSidebar.toLowerCase(); const isActive = () => item.toLowerCase() === props.activeSidebar.toLowerCase();
const isLogout = item.toLowerCase() === 'logout'; const isLogout = item.toLowerCase() === "logout";
return ( return (
<button <button
type="button" type="button"
onClick={() => props.onSidebarSelect(item)} onClick={() => props.onSidebarSelect(item)}
style={{ style={{
display: 'flex', display: "flex",
'align-items': 'center', "align-items": "center",
gap: '9px', gap: "9px",
width: '100%', width: "100%",
'text-align': 'left', "text-align": "left",
height: '34px', height: "34px",
padding: '0 10px', padding: "0 10px",
'border-radius': '8px', "border-radius": "8px",
border: 'none', border: "none",
cursor: 'pointer', cursor: "pointer",
'font-size': '12px', "font-size": "12px",
'font-weight': '600', "font-weight": "600",
'margin-bottom': '4px', "margin-bottom": "4px",
background: isActive() ? '#FFF3EE' : 'transparent', background: isActive() ? "#FFF3EE" : "transparent",
color: isActive() ? ORANGE : isLogout ? '#DC2626' : '#6B7280', color: isActive() ? ORANGE : isLogout ? "#DC2626" : "#6B7280",
transition: 'background 0.15s, color 0.15s', transition: "background 0.15s, color 0.15s",
}} }}
> >
<span style={{ 'flex-shrink': '0', color: isActive() ? ORANGE : '#9CA3AF' }}> <span style={{ "flex-shrink": "0", color: isActive() ? ORANGE : "#9CA3AF" }}>
<SidebarIcon label={item} /> <SidebarIcon label={item} />
</span> </span>
{titleCase(item)} {titleCase(item)}
@ -172,61 +214,94 @@ export default function DashboardShell(props: Props) {
</nav> </nav>
{/* User footer */} {/* User footer */}
<div style={{ padding: '12px 16px', 'border-top': '1px solid #E5E7EB' }}> <div style={{ padding: "12px 16px", "border-top": "1px solid #E5E7EB" }}>
<p style={{ margin: '0', 'font-size': '12px', 'font-weight': '600', color: '#374151', overflow: 'hidden', 'text-overflow': 'ellipsis', 'white-space': 'nowrap' }}> <p
{props.userName || 'User'} style={{
margin: "0",
"font-size": "12px",
"font-weight": "600",
color: "#374151",
overflow: "hidden",
"text-overflow": "ellipsis",
"white-space": "nowrap",
}}
>
{props.userName || "User"}
</p> </p>
</div> </div>
</aside> </aside>
{/* ── Main content ─────────────────────────────────────────────────── */} {/* ── Main content ─────────────────────────────────────────────────── */}
<div style={{ flex: '1', display: 'flex', 'flex-direction': 'column', 'min-width': '0' }}> <div style={{ flex: "1", display: "flex", "flex-direction": "column", "min-width": "0" }}>
{/* Top bar */} {/* Top bar */}
<header style={{ <header
height: '56px', style={{
background: '#fff', height: "56px",
'border-bottom': '1px solid #E5E7EB', background: "#fff",
display: 'flex', "border-bottom": "1px solid #E5E7EB",
'align-items': 'center', display: "flex",
'justify-content': 'space-between', "align-items": "center",
padding: '0 24px', "justify-content": "space-between",
'flex-shrink': '0', padding: "0 24px",
}}> "flex-shrink": "0",
<p style={{ margin: '0', 'font-size': '15px', 'font-weight': '700', color: NAVY }}> }}
>
<p style={{ margin: "0", "font-size": "15px", "font-weight": "700", color: NAVY }}>
{titleCase(props.activeSidebar)} {titleCase(props.activeSidebar)}
</p> </p>
<div style={{ display: 'flex', 'align-items': 'center', gap: '12px' }}> <div style={{ display: "flex", "align-items": "center", gap: "12px" }}>
<button type="button" style={{ position: 'relative', border: 'none', background: 'transparent', cursor: 'pointer', display: 'flex', 'align-items': 'center', 'justify-content': 'center', padding: 0 }}> <button
<Bell size={18} style={{ color: '#9CA3AF' }} /> type="button"
<Show when={unreadCount() > 0}> style={{
<span style={{ position: "relative",
position: 'absolute', border: "none",
top: '-2px', background: "transparent",
right: '-2px', cursor: "pointer",
width: '8px', display: "flex",
height: '8px', "align-items": "center",
background: '#FF5E13', "justify-content": "center",
'border-radius': '50%', padding: 0,
border: '1px solid white' }}
}}></span> >
</Show> <Bell size={18} style={{ color: "#9CA3AF" }} />
</button> <Show when={unreadCount() > 0}>
<div style={{ <span
width: '32px', height: '32px', 'border-radius': '999px', style={{
background: ORANGE, color: '#fff', display: 'flex', position: "absolute",
'align-items': 'center', 'justify-content': 'center', top: "-2px",
'font-size': '13px', 'font-weight': '700', right: "-2px",
}}> width: "8px",
{(props.userName || 'U').charAt(0).toUpperCase()} height: "8px",
</div> background: "#FF5E13",
</div> "border-radius": "50%",
border: "1px solid white",
}}
></span>
</Show>
</button>
<div
style={{
width: "32px",
height: "32px",
"border-radius": "999px",
background: ORANGE,
color: "#fff",
display: "flex",
"align-items": "center",
"justify-content": "center",
"font-size": "13px",
"font-weight": "700",
}}
>
{(props.userName || "U").charAt(0).toUpperCase()}
</div>
</div>
</header> </header>
{/* Page content */} {/* Page content */}
<main style={{ flex: '1', padding: '24px', 'overflow-y': 'auto' }}> <main style={{ flex: "1", padding: "24px", "overflow-y": "auto" }}>{props.children}</main>
{props.children}
</main>
</div> </div>
<AiChatWidget />
</div> </div>
); );
} }
@ -234,65 +309,65 @@ export default function DashboardShell(props: Props) {
// ── Shared UI primitives ────────────────────────────────────────────────────── // ── Shared UI primitives ──────────────────────────────────────────────────────
export const CARD = { export const CARD = {
background: '#fff', background: "#fff",
border: '1px solid #E5E7EB', border: "1px solid #E5E7EB",
'border-radius': '14px', "border-radius": "14px",
padding: '20px', padding: "20px",
'box-shadow': '0 1px 4px rgba(0,0,0,0.06)', "box-shadow": "0 1px 4px rgba(0,0,0,0.06)",
} as const; } as const;
export const BTN_PRIMARY = { export const BTN_PRIMARY = {
height: '38px', height: "38px",
'border-radius': '10px', "border-radius": "10px",
border: 'none', border: "none",
background: NAVY, background: NAVY,
color: '#fff', color: "#fff",
padding: '0 18px', padding: "0 18px",
'font-size': '13px', "font-size": "13px",
'font-weight': '700', "font-weight": "700",
cursor: 'pointer', cursor: "pointer",
} as const; } as const;
export const BTN_ORANGE = { export const BTN_ORANGE = {
height: '38px', height: "38px",
'border-radius': '10px', "border-radius": "10px",
border: 'none', border: "none",
background: ORANGE, background: ORANGE,
color: '#fff', color: "#fff",
padding: '0 18px', padding: "0 18px",
'font-size': '13px', "font-size": "13px",
'font-weight': '700', "font-weight": "700",
cursor: 'pointer', cursor: "pointer",
} as const; } as const;
export const BTN_GHOST = { export const BTN_GHOST = {
height: '38px', height: "38px",
'border-radius': '10px', "border-radius": "10px",
border: '1px solid #E5E7EB', border: "1px solid #E5E7EB",
background: '#fff', background: "#fff",
color: '#374151', color: "#374151",
padding: '0 18px', padding: "0 18px",
'font-size': '13px', "font-size": "13px",
'font-weight': '600', "font-weight": "600",
cursor: 'pointer', cursor: "pointer",
} as const; } as const;
export const INPUT = { export const INPUT = {
height: '40px', height: "40px",
width: '100%', width: "100%",
'border-radius': '8px', "border-radius": "8px",
border: '1px solid #E5E7EB', border: "1px solid #E5E7EB",
padding: '0 12px', padding: "0 12px",
'font-size': '14px', "font-size": "14px",
color: '#111827', color: "#111827",
background: '#fff', background: "#fff",
'box-sizing': 'border-box', "box-sizing": "border-box",
} as const; } as const;
export const LABEL = { export const LABEL = {
display: 'block', display: "block",
'font-size': '12px', "font-size": "12px",
'font-weight': '600', "font-weight": "600",
color: '#374151', color: "#374151",
'margin-bottom': '6px', "margin-bottom": "6px",
} as const; } as const;