2026-04-06 17:20:48 +02:00
|
|
|
/**
|
|
|
|
|
* DashboardShell — real sidebar + header layout wrapper.
|
|
|
|
|
* Used for pages that need actual backend connectivity
|
|
|
|
|
* (My Profile, My Portfolio, Verification) instead of the preview mock.
|
|
|
|
|
*/
|
2026-07-06 01:58:29 +05:30
|
|
|
import { For, JSX, Show, createMemo } from "solid-js";
|
2026-04-15 20:03:47 +02:00
|
|
|
import { AiChatWidget } from "./AiChatWidget";
|
2026-04-29 11:51:37 +02:00
|
|
|
import NotificationBell from "./NotificationBell";
|
2026-04-06 17:20:48 +02:00
|
|
|
import {
|
2026-04-15 20:03:47 +02:00
|
|
|
User,
|
|
|
|
|
Briefcase,
|
|
|
|
|
LayoutDashboard,
|
|
|
|
|
FolderOpen,
|
|
|
|
|
MapPin,
|
|
|
|
|
Star,
|
|
|
|
|
CreditCard,
|
|
|
|
|
Globe,
|
|
|
|
|
ShieldCheck,
|
|
|
|
|
HelpCircle,
|
|
|
|
|
Settings,
|
|
|
|
|
RefreshCw,
|
|
|
|
|
LogOut,
|
|
|
|
|
Bell,
|
|
|
|
|
ChevronRight,
|
2026-07-06 01:58:29 +05:30
|
|
|
Lock,
|
2026-04-15 20:03:47 +02:00
|
|
|
} from "lucide-solid";
|
2026-04-06 17:20:48 +02:00
|
|
|
|
|
|
|
|
const ICON_MAP: Record<string, any> = {
|
2026-04-26 23:58:43 +02:00
|
|
|
my_dashboard: LayoutDashboard,
|
|
|
|
|
my_profile: User,
|
|
|
|
|
my_portfolio: FolderOpen,
|
|
|
|
|
portfolio: FolderOpen,
|
2026-04-15 20:03:47 +02:00
|
|
|
leads: MapPin,
|
2026-04-26 23:58:43 +02:00
|
|
|
my_responses: Star,
|
|
|
|
|
responses: Star,
|
2026-04-15 20:03:47 +02:00
|
|
|
credits: CreditCard,
|
2026-04-26 23:58:43 +02:00
|
|
|
explore_nxtgauge: Globe,
|
|
|
|
|
explore: Globe,
|
2026-04-15 20:03:47 +02:00
|
|
|
verification: ShieldCheck,
|
2026-04-26 23:58:43 +02:00
|
|
|
verification_status: ShieldCheck,
|
|
|
|
|
help_center: HelpCircle,
|
|
|
|
|
help: HelpCircle,
|
|
|
|
|
support: HelpCircle,
|
2026-04-15 20:03:47 +02:00
|
|
|
settings: Settings,
|
2026-04-26 23:58:43 +02:00
|
|
|
switch_services: RefreshCw,
|
|
|
|
|
switch_service: RefreshCw,
|
|
|
|
|
logout: LogOut,
|
2026-04-15 20:03:47 +02:00
|
|
|
jobs: Briefcase,
|
2026-04-26 23:58:43 +02:00
|
|
|
job_postings: Briefcase,
|
2026-04-15 20:03:47 +02:00
|
|
|
applications: Briefcase,
|
2026-04-26 23:58:43 +02:00
|
|
|
my_applications: FolderOpen,
|
|
|
|
|
shortlisted_candidates: User,
|
|
|
|
|
my_requirements: FolderOpen,
|
|
|
|
|
requirements: FolderOpen,
|
|
|
|
|
received_responses: Bell,
|
|
|
|
|
shortlisted_responses: Star,
|
|
|
|
|
saved_jobs: Star,
|
2026-04-06 17:20:48 +02:00
|
|
|
};
|
|
|
|
|
|
2026-04-26 23:58:43 +02:00
|
|
|
function normalizeSidebarIconKey(value: string): string {
|
|
|
|
|
return String(value || "")
|
|
|
|
|
.trim()
|
|
|
|
|
.toLowerCase()
|
|
|
|
|
.replace(/\s+/g, "_")
|
|
|
|
|
.replace(/-/g, "_");
|
|
|
|
|
}
|
|
|
|
|
|
2026-04-06 17:20:48 +02:00
|
|
|
function SidebarIcon(props: { label: string }) {
|
2026-04-26 23:58:43 +02:00
|
|
|
const key = normalizeSidebarIconKey(props.label);
|
2026-04-06 17:20:48 +02:00
|
|
|
const Icon = ICON_MAP[key] || ChevronRight;
|
|
|
|
|
return <Icon size={16} />;
|
|
|
|
|
}
|
|
|
|
|
|
2026-04-10 01:21:36 +02:00
|
|
|
function titleCase(value: string) {
|
2026-04-15 20:03:47 +02:00
|
|
|
return String(value || "")
|
2026-04-10 01:21:36 +02:00
|
|
|
.toLowerCase()
|
2026-04-15 20:03:47 +02:00
|
|
|
.replace(/_/g, " ")
|
2026-04-10 01:21:36 +02:00
|
|
|
.replace(/\b\w/g, (c) => c.toUpperCase());
|
|
|
|
|
}
|
|
|
|
|
|
2026-04-06 17:20:48 +02:00
|
|
|
// ── Types ─────────────────────────────────────────────────────────────────────
|
|
|
|
|
|
|
|
|
|
interface Props {
|
|
|
|
|
sidebarItems: string[];
|
|
|
|
|
activeSidebar: string;
|
|
|
|
|
onSidebarSelect: (item: string) => void;
|
|
|
|
|
roleKey: string;
|
|
|
|
|
userName?: string;
|
2026-07-06 01:58:29 +05:30
|
|
|
isAdmin?: boolean;
|
2026-07-28 20:10:41 +05:30
|
|
|
/** Shows a checkmark on the Verification nav item once the profile is approved. */
|
|
|
|
|
verificationApproved?: boolean;
|
2026-04-06 17:20:48 +02:00
|
|
|
children: JSX.Element;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// ── Brand colours ─────────────────────────────────────────────────────────────
|
|
|
|
|
|
2026-04-26 23:58:43 +02:00
|
|
|
export const ORANGE = "#FF5E13";
|
|
|
|
|
export const NAVY = "#0D0D2A";
|
2026-04-22 01:26:36 +02:00
|
|
|
const DARK_INK = "#03004E";
|
2026-04-06 17:20:48 +02:00
|
|
|
|
|
|
|
|
// ── Component ─────────────────────────────────────────────────────────────────
|
|
|
|
|
|
|
|
|
|
export default function DashboardShell(props: Props) {
|
|
|
|
|
const roleLabel = createMemo(() => {
|
2026-04-15 20:03:47 +02:00
|
|
|
const k = String(props.roleKey || "").replace(/_/g, " ");
|
2026-04-06 17:20:48 +02:00
|
|
|
return k.charAt(0).toUpperCase() + k.slice(1).toLowerCase();
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
return (
|
2026-04-15 20:03:47 +02:00
|
|
|
<div
|
|
|
|
|
style={{
|
|
|
|
|
display: "flex",
|
fix: ESLint, SolidJS reactivity bugs, role workflow bug fixes
ESLint:
- Downgrade eslint v10 → v8.57.1 (compatible with @typescript-eslint v7 + eslint-plugin-solid)
- Fix .eslintrc.cjs: remove invalid require() calls, update to valid rule set
- Add npm run lint script
SolidJS solid/prefer-for (18 fixes across 3 files):
- OpportunityGraph.tsx: EDGES.map() → <For> with reactive visible/drawing signals
- PortfolioPage.tsx: services.map() and experience.map() → <For>
- DashboardDesignPreview.tsx: 15 .map() calls → <For> (stats, packages, timeline,
testimonials, quick actions, step tabs, pills, form fields, status lists, buttons,
counters, filter tabs)
Role workflow bug fixes (from full role audit):
- cover_letter → cover_note in job applications (field name canonical fix)
- applicant_user_id field name fix in shortlisted candidates
- CompanyApplicationsPage: GET → POST for contact unlock endpoint
- CreditsPage: /payments/history → /payments/invoices; response key data.data
- CreditsPage: holds response key data.data (not data.holds)
- ProfessionalResponsesPage: requirement_id → lead_id, decision_at → resolved_at
- PortfolioPage: data.items → data.data; fix vacuous-truth in form completion check;
saveProfessionalForm: check res.ok before showing success
- CustomerBrowseProfessionalsPage: professional_role_code → profession_key
- MyDashboardPage: professional prefix split('_')[0] not replace('_','')
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-08-12 13:38:38 +02:00
|
|
|
// height:100vh (not min-height) keeps the layout bounded so the sidebar
|
|
|
|
|
// stays sticky and <main> scrolls independently — FE-004 fix.
|
|
|
|
|
height: "100vh",
|
|
|
|
|
overflow: "hidden",
|
2026-04-15 20:03:47 +02:00
|
|
|
background: "#F8FAFC",
|
|
|
|
|
"font-family": "'Exo 2', sans-serif",
|
|
|
|
|
}}
|
|
|
|
|
>
|
2026-04-06 17:20:48 +02:00
|
|
|
{/* ── Sidebar ──────────────────────────────────────────────────────── */}
|
2026-04-15 20:03:47 +02:00
|
|
|
<aside
|
|
|
|
|
style={{
|
|
|
|
|
width: "220px",
|
|
|
|
|
"flex-shrink": "0",
|
|
|
|
|
background: "#FFFFFF",
|
|
|
|
|
display: "flex",
|
|
|
|
|
"flex-direction": "column",
|
|
|
|
|
padding: "0",
|
|
|
|
|
"min-height": "100vh",
|
|
|
|
|
position: "sticky",
|
|
|
|
|
top: "0",
|
|
|
|
|
height: "100vh",
|
|
|
|
|
"overflow-y": "auto",
|
|
|
|
|
}}
|
|
|
|
|
>
|
2026-04-06 17:20:48 +02:00
|
|
|
{/* Logo */}
|
2026-04-15 20:03:47 +02:00
|
|
|
<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" }}
|
|
|
|
|
/>
|
2026-04-06 17:20:48 +02:00
|
|
|
</div>
|
|
|
|
|
|
|
|
|
|
{/* Role badge */}
|
2026-04-15 20:03:47 +02:00
|
|
|
<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
|
|
|
|
|
style={{
|
|
|
|
|
margin: "2px 0 0",
|
|
|
|
|
"font-size": "12px",
|
|
|
|
|
"font-weight": "700",
|
|
|
|
|
color: "#111827",
|
|
|
|
|
}}
|
|
|
|
|
>
|
|
|
|
|
{roleLabel()}
|
|
|
|
|
</p>
|
2026-04-06 17:20:48 +02:00
|
|
|
</div>
|
|
|
|
|
|
|
|
|
|
{/* Nav items */}
|
2026-04-15 20:03:47 +02:00
|
|
|
<nav style={{ flex: "1", padding: "8px 8px" }}>
|
2026-04-06 17:20:48 +02:00
|
|
|
<For each={props.sidebarItems}>
|
|
|
|
|
{(item) => {
|
|
|
|
|
const isActive = () => item.toLowerCase() === props.activeSidebar.toLowerCase();
|
2026-04-15 20:03:47 +02:00
|
|
|
const isLogout = item.toLowerCase() === "logout";
|
2026-04-06 17:20:48 +02:00
|
|
|
return (
|
|
|
|
|
<button
|
|
|
|
|
type="button"
|
|
|
|
|
onClick={() => props.onSidebarSelect(item)}
|
|
|
|
|
style={{
|
2026-04-15 20:03:47 +02:00
|
|
|
display: "flex",
|
|
|
|
|
"align-items": "center",
|
|
|
|
|
gap: "9px",
|
|
|
|
|
width: "100%",
|
|
|
|
|
"text-align": "left",
|
|
|
|
|
height: "34px",
|
|
|
|
|
padding: "0 10px",
|
|
|
|
|
"border-radius": "8px",
|
|
|
|
|
border: "none",
|
|
|
|
|
cursor: "pointer",
|
|
|
|
|
"font-size": "12px",
|
|
|
|
|
"font-weight": "600",
|
|
|
|
|
"margin-bottom": "4px",
|
|
|
|
|
background: isActive() ? "#FFF3EE" : "transparent",
|
|
|
|
|
color: isActive() ? ORANGE : isLogout ? "#DC2626" : "#6B7280",
|
|
|
|
|
transition: "background 0.15s, color 0.15s",
|
2026-04-06 17:20:48 +02:00
|
|
|
}}
|
|
|
|
|
>
|
2026-04-15 20:03:47 +02:00
|
|
|
<span style={{ "flex-shrink": "0", color: isActive() ? ORANGE : "#9CA3AF" }}>
|
2026-04-06 17:20:48 +02:00
|
|
|
<SidebarIcon label={item} />
|
|
|
|
|
</span>
|
2026-04-10 01:21:36 +02:00
|
|
|
{titleCase(item)}
|
2026-07-28 20:10:41 +05:30
|
|
|
<Show when={props.verificationApproved && item.toLowerCase() === "verification"}>
|
|
|
|
|
<span style={{ "margin-left": "auto", color: "#10B981", "font-weight": "700" }}>✓</span>
|
|
|
|
|
</Show>
|
2026-04-06 17:20:48 +02:00
|
|
|
</button>
|
|
|
|
|
);
|
|
|
|
|
}}
|
|
|
|
|
</For>
|
|
|
|
|
</nav>
|
|
|
|
|
|
2026-07-06 01:58:29 +05:30
|
|
|
{/* Admin section (only for admins) */}
|
|
|
|
|
<Show when={props.isAdmin}>
|
|
|
|
|
<div style={{ padding: "8px", "border-top": "1px solid #E5E7EB", "margin-top": "auto" }}>
|
|
|
|
|
<p style={{ margin: "4px 8px", "font-size": "10px", "letter-spacing": "0.08em", "text-transform": "uppercase", color: "#9CA3AF" }}>
|
|
|
|
|
Admin Tools
|
|
|
|
|
</p>
|
|
|
|
|
<a
|
|
|
|
|
href="/admin/ai-credits"
|
|
|
|
|
style={{
|
|
|
|
|
display: "flex",
|
|
|
|
|
"align-items": "center",
|
|
|
|
|
gap: "9px",
|
|
|
|
|
width: "100%",
|
|
|
|
|
"text-align": "left",
|
|
|
|
|
height: "34px",
|
|
|
|
|
padding: "0 10px",
|
|
|
|
|
"border-radius": "8px",
|
|
|
|
|
border: "none",
|
|
|
|
|
cursor: "pointer",
|
|
|
|
|
"font-size": "12px",
|
|
|
|
|
"font-weight": "600",
|
|
|
|
|
"margin-top": "4px",
|
|
|
|
|
background: "transparent",
|
|
|
|
|
color: "#6B7280",
|
|
|
|
|
"text-decoration": "none",
|
|
|
|
|
}}
|
|
|
|
|
>
|
|
|
|
|
<span style={{ "flex-shrink": "0", color: "#9CA3AF" }}>
|
|
|
|
|
<Lock size={16} />
|
|
|
|
|
</span>
|
|
|
|
|
AI Credits Admin
|
|
|
|
|
</a>
|
|
|
|
|
</div>
|
|
|
|
|
</Show>
|
|
|
|
|
|
2026-04-06 17:20:48 +02:00
|
|
|
{/* User footer */}
|
2026-04-15 20:03:47 +02:00
|
|
|
<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",
|
|
|
|
|
}}
|
|
|
|
|
>
|
|
|
|
|
{props.userName || "User"}
|
2026-04-06 17:20:48 +02:00
|
|
|
</p>
|
|
|
|
|
</div>
|
|
|
|
|
</aside>
|
|
|
|
|
|
|
|
|
|
{/* ── Main content ─────────────────────────────────────────────────── */}
|
fix: ESLint, SolidJS reactivity bugs, role workflow bug fixes
ESLint:
- Downgrade eslint v10 → v8.57.1 (compatible with @typescript-eslint v7 + eslint-plugin-solid)
- Fix .eslintrc.cjs: remove invalid require() calls, update to valid rule set
- Add npm run lint script
SolidJS solid/prefer-for (18 fixes across 3 files):
- OpportunityGraph.tsx: EDGES.map() → <For> with reactive visible/drawing signals
- PortfolioPage.tsx: services.map() and experience.map() → <For>
- DashboardDesignPreview.tsx: 15 .map() calls → <For> (stats, packages, timeline,
testimonials, quick actions, step tabs, pills, form fields, status lists, buttons,
counters, filter tabs)
Role workflow bug fixes (from full role audit):
- cover_letter → cover_note in job applications (field name canonical fix)
- applicant_user_id field name fix in shortlisted candidates
- CompanyApplicationsPage: GET → POST for contact unlock endpoint
- CreditsPage: /payments/history → /payments/invoices; response key data.data
- CreditsPage: holds response key data.data (not data.holds)
- ProfessionalResponsesPage: requirement_id → lead_id, decision_at → resolved_at
- PortfolioPage: data.items → data.data; fix vacuous-truth in form completion check;
saveProfessionalForm: check res.ok before showing success
- CustomerBrowseProfessionalsPage: professional_role_code → profession_key
- MyDashboardPage: professional prefix split('_')[0] not replace('_','')
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-08-12 13:38:38 +02:00
|
|
|
<div style={{ flex: "1", display: "flex", "flex-direction": "column", "min-width": "0", overflow: "hidden" }}>
|
2026-04-06 17:20:48 +02:00
|
|
|
{/* Top bar */}
|
2026-04-15 20:03:47 +02:00
|
|
|
<header
|
|
|
|
|
style={{
|
|
|
|
|
height: "56px",
|
|
|
|
|
background: "#fff",
|
|
|
|
|
"border-bottom": "1px solid #E5E7EB",
|
|
|
|
|
display: "flex",
|
|
|
|
|
"align-items": "center",
|
|
|
|
|
"justify-content": "space-between",
|
|
|
|
|
padding: "0 24px",
|
|
|
|
|
"flex-shrink": "0",
|
|
|
|
|
}}
|
|
|
|
|
>
|
2026-04-22 01:26:36 +02:00
|
|
|
<p style={{ margin: "0", "font-size": "18px", "font-weight": "700", color: NAVY }}>
|
2026-04-10 01:21:36 +02:00
|
|
|
{titleCase(props.activeSidebar)}
|
2026-04-06 17:20:48 +02:00
|
|
|
</p>
|
2026-04-15 20:03:47 +02:00
|
|
|
<div style={{ display: "flex", "align-items": "center", gap: "12px" }}>
|
2026-04-29 11:51:37 +02:00
|
|
|
<NotificationBell />
|
2026-04-15 20:03:47 +02:00
|
|
|
<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>
|
2026-04-06 17:20:48 +02:00
|
|
|
</header>
|
|
|
|
|
|
|
|
|
|
{/* Page content */}
|
2026-04-15 20:03:47 +02:00
|
|
|
<main style={{ flex: "1", padding: "24px", "overflow-y": "auto" }}>{props.children}</main>
|
2026-04-06 17:20:48 +02:00
|
|
|
</div>
|
2026-04-15 20:03:47 +02:00
|
|
|
<AiChatWidget />
|
2026-04-06 17:20:48 +02:00
|
|
|
</div>
|
|
|
|
|
);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// ── Shared UI primitives ──────────────────────────────────────────────────────
|
|
|
|
|
|
|
|
|
|
export const CARD = {
|
2026-04-15 20:03:47 +02:00
|
|
|
background: "#fff",
|
|
|
|
|
border: "1px solid #E5E7EB",
|
2026-04-22 01:26:36 +02:00
|
|
|
"border-radius": "16px",
|
|
|
|
|
padding: "14px",
|
2026-04-15 20:03:47 +02:00
|
|
|
"box-shadow": "0 1px 4px rgba(0,0,0,0.06)",
|
2026-04-06 17:20:48 +02:00
|
|
|
} as const;
|
|
|
|
|
|
|
|
|
|
export const BTN_PRIMARY = {
|
2026-04-22 01:26:36 +02:00
|
|
|
height: "34px",
|
|
|
|
|
"border-radius": "8px",
|
2026-04-15 20:03:47 +02:00
|
|
|
border: "none",
|
2026-04-06 17:20:48 +02:00
|
|
|
background: NAVY,
|
2026-04-15 20:03:47 +02:00
|
|
|
color: "#fff",
|
2026-04-22 01:26:36 +02:00
|
|
|
padding: "0 14px",
|
|
|
|
|
"font-size": "12px",
|
2026-04-15 20:03:47 +02:00
|
|
|
"font-weight": "700",
|
|
|
|
|
cursor: "pointer",
|
2026-04-06 17:20:48 +02:00
|
|
|
} as const;
|
|
|
|
|
|
|
|
|
|
export const BTN_ORANGE = {
|
2026-04-22 01:26:36 +02:00
|
|
|
height: "34px",
|
|
|
|
|
"border-radius": "8px",
|
2026-04-15 20:03:47 +02:00
|
|
|
border: "none",
|
2026-04-06 17:20:48 +02:00
|
|
|
background: ORANGE,
|
2026-04-15 20:03:47 +02:00
|
|
|
color: "#fff",
|
2026-04-22 01:26:36 +02:00
|
|
|
padding: "0 14px",
|
|
|
|
|
"font-size": "12px",
|
2026-04-15 20:03:47 +02:00
|
|
|
"font-weight": "700",
|
|
|
|
|
cursor: "pointer",
|
2026-04-06 17:20:48 +02:00
|
|
|
} as const;
|
|
|
|
|
|
|
|
|
|
export const BTN_GHOST = {
|
2026-04-22 01:26:36 +02:00
|
|
|
height: "34px",
|
|
|
|
|
"border-radius": "8px",
|
2026-04-15 20:03:47 +02:00
|
|
|
border: "1px solid #E5E7EB",
|
|
|
|
|
background: "#fff",
|
|
|
|
|
color: "#374151",
|
2026-04-22 01:26:36 +02:00
|
|
|
padding: "0 14px",
|
|
|
|
|
"font-size": "12px",
|
|
|
|
|
"font-weight": "700",
|
2026-04-15 20:03:47 +02:00
|
|
|
cursor: "pointer",
|
2026-04-06 17:20:48 +02:00
|
|
|
} as const;
|
|
|
|
|
|
|
|
|
|
export const INPUT = {
|
2026-04-22 01:26:36 +02:00
|
|
|
height: "36px",
|
2026-04-15 20:03:47 +02:00
|
|
|
width: "100%",
|
|
|
|
|
"border-radius": "8px",
|
|
|
|
|
border: "1px solid #E5E7EB",
|
2026-04-22 01:26:36 +02:00
|
|
|
padding: "0 10px",
|
|
|
|
|
"font-size": "12px",
|
2026-04-15 20:03:47 +02:00
|
|
|
color: "#111827",
|
|
|
|
|
background: "#fff",
|
|
|
|
|
"box-sizing": "border-box",
|
2026-04-22 01:26:36 +02:00
|
|
|
outline: "none",
|
2026-04-06 17:20:48 +02:00
|
|
|
} as const;
|
|
|
|
|
|
|
|
|
|
export const LABEL = {
|
2026-04-15 20:03:47 +02:00
|
|
|
display: "block",
|
2026-04-22 01:26:36 +02:00
|
|
|
"font-size": "11px",
|
|
|
|
|
"font-weight": "700",
|
|
|
|
|
color: "#6B7280",
|
2026-04-15 20:03:47 +02:00
|
|
|
"margin-bottom": "6px",
|
2026-04-22 01:26:36 +02:00
|
|
|
"letter-spacing": "0.01em",
|
|
|
|
|
"text-transform": "none",
|
2026-04-06 17:20:48 +02:00
|
|
|
} as const;
|
2026-04-26 23:58:43 +02:00
|
|
|
|
|
|
|
|
export const PKG_CARD = {
|
|
|
|
|
WHITE: "#ffffff",
|
|
|
|
|
COIN_BG: "#FFFBF8",
|
|
|
|
|
COIN_AVATAR_BG: "linear-gradient(135deg, #FEF3C7 0%, #FDE68A 100%)",
|
|
|
|
|
BEST_VALUE_BG: `linear-gradient(135deg, ${ORANGE} 0%, #FF8A5C 100%)`,
|
|
|
|
|
BORDER_DEFAULT: "#E5E7EB",
|
|
|
|
|
TEXT_SECONDARY: "#6B7280",
|
|
|
|
|
TEXT_PRIMARY: "#111827",
|
|
|
|
|
TEXT_ACCENT: ORANGE,
|
|
|
|
|
TEXT_MUTED: "#9CA3AF",
|
|
|
|
|
TEXT_SUCCESS: "#16A34A",
|
|
|
|
|
SHADOW_DEFAULT: "0 1px 3px rgba(0, 0, 0, 0.08)",
|
|
|
|
|
SHADOW_ACCENT: "0 4px 20px rgba(255, 94, 19, 0.15)",
|
|
|
|
|
} as const;
|