diff --git a/DESIGN.md b/DESIGN.md new file mode 100644 index 0000000..6634998 --- /dev/null +++ b/DESIGN.md @@ -0,0 +1,373 @@ +# Design System: Notion + +## 1. Visual Theme & Atmosphere + +Notion's website embodies the philosophy of the tool itself: a blank canvas that gets out of your way. The design system is built on warm neutrals rather than cold grays, creating a distinctly approachable minimalism that feels like quality paper rather than sterile glass. The page canvas is pure white (`#ffffff`) but the text isn't pure black -- it's a warm near-black (`rgba(0,0,0,0.95)`) that softens the reading experience imperceptibly. The warm gray scale (`#f6f5f4`, `#31302e`, `#615d59`, `#a39e98`) carries subtle yellow-brown undertones, giving the interface a tactile, almost analog warmth. + +The custom NotionInter font (a modified Inter) is the backbone of the system. At display sizes (64px), it uses aggressive negative letter-spacing (-2.125px), creating headlines that feel compressed and precise. The weight range is broader than typical systems: 400 for body, 500 for UI elements, 600 for semi-bold labels, and 700 for display headings. OpenType features `"lnum"` (lining numerals) and `"locl"` (localized forms) are enabled on larger text, adding typographic sophistication that rewards close reading. + +What makes Notion's visual language distinctive is its border philosophy. Rather than heavy borders or shadows, Notion uses ultra-thin `1px solid rgba(0,0,0,0.1)` borders -- borders that exist as whispers, barely perceptible division lines that create structure without weight. The shadow system is equally restrained: multi-layer stacks with cumulative opacity never exceeding 0.05, creating depth that's felt rather than seen. + +**Key Characteristics:** +- NotionInter (modified Inter) with negative letter-spacing at display sizes (-2.125px at 64px) +- Warm neutral palette: grays carry yellow-brown undertones (`#f6f5f4` warm white, `#31302e` warm dark) +- Near-black text via `rgba(0,0,0,0.95)` -- not pure black, creating micro-warmth +- Ultra-thin borders: `1px solid rgba(0,0,0,0.1)` throughout -- whisper-weight division +- Multi-layer shadow stacks with sub-0.05 opacity for barely-there depth +- Notion Blue (`#0075de`) as the singular accent color for CTAs and interactive elements +- Pill badges (9999px radius) with tinted blue backgrounds for status indicators +- 8px base spacing unit with an organic, non-rigid scale + +## 2. Color Palette & Roles + +### Primary +- **Notion Black** (`rgba(0,0,0,0.95)` / `#000000f2`): Primary text, headings, body copy. The 95% opacity softens pure black without sacrificing readability. +- **Pure White** (`#ffffff`): Page background, card surfaces, button text on blue. +- **Notion Blue** (`#0075de`): Primary CTA, link color, interactive accent -- the only saturated color in the core UI chrome. + +### Brand Secondary +- **Deep Navy** (`#213183`): Secondary brand color, used sparingly for emphasis and dark feature sections. +- **Active Blue** (`#005bab`): Button active/pressed state -- darker variant of Notion Blue. + +### Warm Neutral Scale +- **Warm White** (`#f6f5f4`): Background surface tint, section alternation, subtle card fill. The yellow undertone is key. +- **Warm Dark** (`#31302e`): Dark surface background, dark section text. Warmer than standard grays. +- **Warm Gray 500** (`#615d59`): Secondary text, descriptions, muted labels. +- **Warm Gray 300** (`#a39e98`): Placeholder text, disabled states, caption text. + +### Semantic Accent Colors +- **Teal** (`#2a9d99`): Success states, positive indicators. +- **Green** (`#1aae39`): Confirmation, completion badges. +- **Orange** (`#dd5b00`): Warning states, attention indicators. +- **Pink** (`#ff64c8`): Decorative accent, feature highlights. +- **Purple** (`#391c57`): Premium features, deep accents. +- **Brown** (`#523410`): Earthy accent, warm feature sections. + +### Interactive +- **Link Blue** (`#0075de`): Primary link color with underline-on-hover. +- **Link Light Blue** (`#62aef0`): Lighter link variant for dark backgrounds. +- **Focus Blue** (`#097fe8`): Focus ring on interactive elements. +- **Badge Blue Bg** (`#f2f9ff`): Pill badge background, tinted blue surface. +- **Badge Blue Text** (`#097fe8`): Pill badge text, darker blue for readability. + +### Shadows & Depth +- **Card Shadow** (`rgba(0,0,0,0.04) 0px 4px 18px, rgba(0,0,0,0.027) 0px 2.025px 7.84688px, rgba(0,0,0,0.02) 0px 0.8px 2.925px, rgba(0,0,0,0.01) 0px 0.175px 1.04062px`): Multi-layer card elevation. +- **Deep Shadow** (`rgba(0,0,0,0.01) 0px 1px 3px, rgba(0,0,0,0.02) 0px 3px 7px, rgba(0,0,0,0.02) 0px 7px 15px, rgba(0,0,0,0.04) 0px 14px 28px, rgba(0,0,0,0.05) 0px 23px 52px`): Five-layer deep elevation for modals and featured content. +- **Whisper Border** (`1px solid rgba(0,0,0,0.1)`): Standard division border -- cards, dividers, sections. + +## 3. Typography Rules + +### Font Family +- **Primary**: `NotionInter`, with fallbacks: `Inter, -apple-system, system-ui, Segoe UI, Helvetica, Apple Color Emoji, Arial, Segoe UI Emoji, Segoe UI Symbol` +- **OpenType Features**: `"lnum"` (lining numerals) and `"locl"` (localized forms) enabled on display and heading text. + +### Hierarchy + +| Role | Font | Size | Weight | Line Height | Letter Spacing | Notes | +|------|------|------|--------|-------------|----------------|-------| +| Display Hero | NotionInter | 64px (4.00rem) | 700 | 1.00 (tight) | -2.125px | Maximum compression, billboard headlines | +| Display Secondary | NotionInter | 54px (3.38rem) | 700 | 1.04 (tight) | -1.875px | Secondary hero, feature headlines | +| Section Heading | NotionInter | 48px (3.00rem) | 700 | 1.00 (tight) | -1.5px | Feature section titles, with `"lnum"` | +| Sub-heading Large | NotionInter | 40px (2.50rem) | 700 | 1.50 | normal | Card headings, feature sub-sections | +| Sub-heading | NotionInter | 26px (1.63rem) | 700 | 1.23 (tight) | -0.625px | Section sub-titles, content headers | +| Card Title | NotionInter | 22px (1.38rem) | 700 | 1.27 (tight) | -0.25px | Feature cards, list titles | +| Body Large | NotionInter | 20px (1.25rem) | 600 | 1.40 | -0.125px | Introductions, feature descriptions | +| Body | NotionInter | 16px (1.00rem) | 400 | 1.50 | normal | Standard reading text | +| Body Medium | NotionInter | 16px (1.00rem) | 500 | 1.50 | normal | Navigation, emphasized UI text | +| Body Semibold | NotionInter | 16px (1.00rem) | 600 | 1.50 | normal | Strong labels, active states | +| Body Bold | NotionInter | 16px (1.00rem) | 700 | 1.50 | normal | Headlines at body size | +| Nav / Button | NotionInter | 15px (0.94rem) | 600 | 1.33 | normal | Navigation links, button text | +| Caption | NotionInter | 14px (0.88rem) | 500 | 1.43 | normal | Metadata, secondary labels | +| Caption Light | NotionInter | 14px (0.88rem) | 400 | 1.43 | normal | Body captions, descriptions | +| Badge | NotionInter | 12px (0.75rem) | 600 | 1.33 | 0.125px | Pill badges, tags, status labels | +| Micro Label | NotionInter | 12px (0.75rem) | 400 | 1.33 | 0.125px | Small metadata, timestamps | + +### Principles +- **Compression at scale**: NotionInter at display sizes uses -2.125px letter-spacing at 64px, progressively relaxing to -0.625px at 26px and normal at 16px. The compression creates density at headlines while maintaining readability at body sizes. +- **Four-weight system**: 400 (body/reading), 500 (UI/interactive), 600 (emphasis/navigation), 700 (headings/display). The broader weight range compared to most systems allows nuanced hierarchy. +- **Warm scaling**: Line height tightens as size increases -- 1.50 at body (16px), 1.23-1.27 at sub-headings, 1.00-1.04 at display. This creates denser, more impactful headlines. +- **Badge micro-tracking**: The 12px badge text uses positive letter-spacing (0.125px) -- the only positive tracking in the system, creating wider, more legible small text. + +## 4. Component Stylings + +### Buttons + +**Primary Blue** +- Background: `#0075de` (Notion Blue) +- Text: `#ffffff` +- Padding: 8px 16px +- Radius: 4px (subtle) +- Border: `1px solid transparent` +- Hover: background darkens to `#005bab` +- Active: scale(0.9) transform +- Focus: `2px solid` focus outline, `var(--shadow-level-200)` shadow +- Use: Primary CTA ("Get Notion free", "Try it") + +**Secondary / Tertiary** +- Background: `rgba(0,0,0,0.05)` (translucent warm gray) +- Text: `#000000` (near-black) +- Padding: 8px 16px +- Radius: 4px +- Hover: text color shifts, scale(1.05) +- Active: scale(0.9) transform +- Use: Secondary actions, form submissions + +**Ghost / Link Button** +- Background: transparent +- Text: `rgba(0,0,0,0.95)` +- Decoration: underline on hover +- Use: Tertiary actions, inline links + +**Pill Badge Button** +- Background: `#f2f9ff` (tinted blue) +- Text: `#097fe8` +- Padding: 4px 8px +- Radius: 9999px (full pill) +- Font: 12px weight 600 +- Use: Status badges, feature labels, "New" tags + +### Cards & Containers +- Background: `#ffffff` +- Border: `1px solid rgba(0,0,0,0.1)` (whisper border) +- Radius: 12px (standard cards), 16px (featured/hero cards) +- Shadow: `rgba(0,0,0,0.04) 0px 4px 18px, rgba(0,0,0,0.027) 0px 2.025px 7.84688px, rgba(0,0,0,0.02) 0px 0.8px 2.925px, rgba(0,0,0,0.01) 0px 0.175px 1.04062px` +- Hover: subtle shadow intensification +- Image cards: 12px top radius, image fills top half + +### Inputs & Forms +- Background: `#ffffff` +- Text: `rgba(0,0,0,0.9)` +- Border: `1px solid #dddddd` +- Padding: 6px +- Radius: 4px +- Focus: blue outline ring +- Placeholder: warm gray `#a39e98` + +### Navigation +- Clean horizontal nav on white, not sticky +- Brand logo left-aligned (33x34px icon + wordmark) +- Links: NotionInter 15px weight 500-600, near-black text +- Hover: color shift to `var(--color-link-primary-text-hover)` +- CTA: blue pill button ("Get Notion free") right-aligned +- Mobile: hamburger menu collapse +- Product dropdowns with multi-level categorized menus + +### Image Treatment +- Product screenshots with `1px solid rgba(0,0,0,0.1)` border +- Top-rounded images: `12px 12px 0px 0px` radius +- Dashboard/workspace preview screenshots dominate feature sections +- Warm gradient backgrounds behind hero illustrations (decorative character illustrations) + +### Distinctive Components + +**Feature Cards with Illustrations** +- Large illustrative headers (The Great Wave, product UI screenshots) +- 12px radius card with whisper border +- Title at 22px weight 700, description at 16px weight 400 +- Warm white (`#f6f5f4`) background variant for alternating sections + +**Trust Bar / Logo Grid** +- Company logos (trusted teams section) in their brand colors +- Horizontal scroll or grid layout with team counts +- Metric display: large number + description pattern + +**Metric Cards** +- Large number display (e.g., "$4,200 ROI") +- NotionInter 40px+ weight 700 for the metric +- Description below in warm gray body text +- Whisper-bordered card container + +## 5. Layout Principles + +### Spacing System +- Base unit: 8px +- Scale: 2px, 3px, 4px, 5px, 6px, 7px, 8px, 11px, 12px, 14px, 16px, 24px, 32px +- Non-rigid organic scale with fractional values (5.6px, 6.4px) for micro-adjustments + +### Grid & Container +- Max content width: approximately 1200px +- Hero: centered single-column with generous top padding (80-120px) +- Feature sections: 2-3 column grids for cards +- Full-width warm white (`#f6f5f4`) section backgrounds for alternation +- Code/dashboard screenshots as contained with whisper border + +### Whitespace Philosophy +- **Generous vertical rhythm**: 64-120px between major sections. Notion lets content breathe with vast vertical padding. +- **Warm alternation**: White sections alternate with warm white (`#f6f5f4`) sections, creating gentle visual rhythm without harsh color breaks. +- **Content-first density**: Body text blocks are compact (line-height 1.50) but surrounded by ample margin, creating islands of readable content in a sea of white space. + +### Border Radius Scale +- Micro (4px): Buttons, inputs, functional interactive elements +- Subtle (5px): Links, list items, menu items +- Standard (8px): Small cards, containers, inline elements +- Comfortable (12px): Standard cards, feature containers, image tops +- Large (16px): Hero cards, featured content, promotional blocks +- Full Pill (9999px): Badges, pills, status indicators +- Circle (100%): Tab indicators, avatars + +## 6. Depth & Elevation + +| Level | Treatment | Use | +|-------|-----------|-----| +| Flat (Level 0) | No shadow, no border | Page background, text blocks | +| Whisper (Level 1) | `1px solid rgba(0,0,0,0.1)` | Standard borders, card outlines, dividers | +| Soft Card (Level 2) | 4-layer shadow stack (max opacity 0.04) | Content cards, feature blocks | +| Deep Card (Level 3) | 5-layer shadow stack (max opacity 0.05, 52px blur) | Modals, featured panels, hero elements | +| Focus (Accessibility) | `2px solid var(--focus-color)` outline | Keyboard focus on all interactive elements | + +**Shadow Philosophy**: Notion's shadow system uses multiple layers with extremely low individual opacity (0.01 to 0.05) that accumulate into soft, natural-looking elevation. The 4-layer card shadow spans from 1.04px to 18px blur, creating a gradient of depth rather than a single hard shadow. The 5-layer deep shadow extends to 52px blur at 0.05 opacity, producing ambient occlusion that feels like natural light rather than computer-generated depth. This layered approach makes elements feel embedded in the page rather than floating above it. + +### Decorative Depth +- Hero section: decorative character illustrations (playful, hand-drawn style) +- Section alternation: white to warm white (`#f6f5f4`) background shifts +- No hard section borders -- separation comes from background color changes and spacing + +## 7. Responsive Behavior + +### Breakpoints + +| Name | Width | Key Changes | +|------|-------|-------------| +| Mobile Small | <400px | Tight single column, minimal padding | +| Mobile | 400-600px | Standard mobile, stacked layout | +| Tablet Small | 600-768px | 2-column grids begin | +| Tablet | 768-1080px | Full card grids, expanded padding | +| Desktop Small | 1080-1200px | Standard desktop layout | +| Desktop | 1200-1440px | Full layout, maximum content width | +| Large Desktop | >1440px | Centered, generous margins | + +### Touch Targets +- Buttons use comfortable padding (8px-16px vertical) +- Navigation links at 15px with adequate spacing +- Pill badges have 8px horizontal padding for tap targets +- Mobile menu toggle uses standard hamburger button + +### Collapsing Strategy +- Hero: 64px display -> scales to 40px -> 26px on mobile, maintains proportional letter-spacing +- Navigation: horizontal links + blue CTA -> hamburger menu +- Feature cards: 3-column -> 2-column -> single column stacked +- Product screenshots: maintain aspect ratio with responsive images +- Trust bar logos: grid -> horizontal scroll on mobile +- Footer: multi-column -> stacked single column +- Section spacing: 80px+ -> 48px on mobile + +### Image Behavior +- Workspace screenshots maintain whisper border at all sizes +- Hero illustrations scale proportionally +- Product screenshots use responsive images with consistent border radius +- Full-width warm white sections maintain edge-to-edge treatment + +## 8. Accessibility & States + +### Focus System +- All interactive elements receive visible focus indicators +- Focus outline: `2px solid` with focus color + shadow level 200 +- Tab navigation supported throughout all interactive components +- High contrast text: near-black on white exceeds WCAG AAA (>14:1 ratio) + +### Interactive States +- **Default**: Standard appearance with whisper borders +- **Hover**: Color shift on text, scale(1.05) on buttons, underline on links +- **Active/Pressed**: scale(0.9) transform, darker background variant +- **Focus**: Blue outline ring with shadow reinforcement +- **Disabled**: Warm gray (`#a39e98`) text, reduced opacity + +### Color Contrast +- Primary text (rgba(0,0,0,0.95)) on white: ~18:1 ratio +- Secondary text (#615d59) on white: ~5.5:1 ratio (WCAG AA) +- Blue CTA (#0075de) on white: ~4.6:1 ratio (WCAG AA for large text) +- Badge text (#097fe8) on badge bg (#f2f9ff): ~4.5:1 ratio (WCAG AA for large text) + +## 9. Agent Prompt Guide + +### Quick Color Reference +- Primary CTA: Notion Blue (`#0075de`) +- Background: Pure White (`#ffffff`) +- Alt Background: Warm White (`#f6f5f4`) +- Heading text: Near-Black (`rgba(0,0,0,0.95)`) +- Body text: Near-Black (`rgba(0,0,0,0.95)`) +- Secondary text: Warm Gray 500 (`#615d59`) +- Muted text: Warm Gray 300 (`#a39e98`) +- Border: `1px solid rgba(0,0,0,0.1)` +- Link: Notion Blue (`#0075de`) +- Focus ring: Focus Blue (`#097fe8`) + +### Example Component Prompts +- "Create a hero section on white background. Headline at 64px NotionInter weight 700, line-height 1.00, letter-spacing -2.125px, color rgba(0,0,0,0.95). Subtitle at 20px weight 600, line-height 1.40, color #615d59. Blue CTA button (#0075de, 4px radius, 8px 16px padding, white text) and ghost button (transparent bg, near-black text, underline on hover)." +- "Design a card: white background, 1px solid rgba(0,0,0,0.1) border, 12px radius. Use shadow stack: rgba(0,0,0,0.04) 0px 4px 18px, rgba(0,0,0,0.027) 0px 2.025px 7.85px, rgba(0,0,0,0.02) 0px 0.8px 2.93px, rgba(0,0,0,0.01) 0px 0.175px 1.04px. Title at 22px NotionInter weight 700, letter-spacing -0.25px. Body at 16px weight 400, color #615d59." +- "Build a pill badge: #f2f9ff background, #097fe8 text, 9999px radius, 4px 8px padding, 12px NotionInter weight 600, letter-spacing 0.125px." +- "Create navigation: white header. NotionInter 15px weight 600 for links, near-black text. Blue pill CTA 'Get Notion free' right-aligned (#0075de bg, white text, 4px radius)." +- "Design an alternating section layout: white sections alternate with warm white (#f6f5f4) sections. Each section has 64-80px vertical padding, max-width 1200px centered. Section heading at 48px weight 700, line-height 1.00, letter-spacing -1.5px." + +### Iteration Guide +1. Always use warm neutrals -- Notion's grays have yellow-brown undertones (#f6f5f4, #31302e, #615d59, #a39e98), never blue-gray +2. Letter-spacing scales with font size: -2.125px at 64px, -1.875px at 54px, -0.625px at 26px, normal at 16px +3. Four weights: 400 (read), 500 (interact), 600 (emphasize), 700 (announce) +4. Borders are whispers: 1px solid rgba(0,0,0,0.1) -- never heavier +5. Shadows use 4-5 layers with individual opacity never exceeding 0.05 +6. The warm white (#f6f5f4) section background is essential for visual rhythm +7. Pill badges (9999px) for status/tags, 4px radius for buttons and inputs +8. Notion Blue (#0075de) is the only saturated color in core UI -- use it sparingly for CTAs and links + +## 10. ERP-Specific Adaptations + +> This section adapts the Notion design system for our universal business ERP platform. +> We use Ant Design 5 components but override their theme tokens to match Notion's warm minimalism. + +### Color Token Mapping (Ant Design Theme) +- `colorPrimary`: `#0075de` (Notion Blue) +- `colorBgContainer`: `#ffffff` +- `colorBgLayout`: `#f6f5f4` (Warm White) +- `colorBorder`: `rgba(0,0,0,0.1)` +- `colorBorderSecondary`: `rgba(0,0,0,0.06)` +- `colorText`: `rgba(0,0,0,0.95)` +- `colorTextSecondary`: `#615d59` +- `colorTextTertiary`: `#a39e98` +- `colorTextQuaternary`: `#d4d0cc` +- `colorSuccess`: `#1aae39` +- `colorWarning`: `#dd5b00` +- `colorError`: `#e5534b` +- `colorInfo`: `#0075de` +- `borderRadius`: 4 +- `borderRadiusLG`: 8 +- `borderRadiusSM`: 2 +- `fontFamily`: `'Inter', -apple-system, system-ui, 'Segoe UI', Helvetica, Arial, sans-serif` + +### Sidebar Navigation Adaptation +- Sidebar background: `#ffffff` (white, not dark) +- Sidebar item text: `rgba(0,0,0,0.95)` +- Sidebar item hover: `#f6f5f4` background +- Sidebar item active: `#f2f9ff` background + `#0075de` left indicator +- Sidebar group headers: `#615d59` uppercase 11px weight 600 letter-spacing 0.5px +- Sidebar collapsed: icon-only with tooltip + +### Table/List Adaptation +- Table header: `#f6f5f4` background, `#615d59` text, 13px weight 600 +- Table row hover: `#fafaf9` (slightly warmer than pure white) +- Table row selected: `#f2f9ff` background +- Table border: `1px solid rgba(0,0,0,0.06)` +- Striped rows: optional, `#fafaf9` on even rows + +### Form Adaptation +- Input border: `1px solid rgba(0,0,0,0.15)` +- Input focus: `#0075de` ring + soft shadow +- Input background: `#ffffff` +- Label: `rgba(0,0,0,0.95)` 14px weight 500 +- Help text: `#a39e98` 12px weight 400 +- Error state: `#e5534b` border + text + +### Dashboard Widget Adaptation +- Stat cards: white bg, whisper border, 12px radius, multi-layer shadow +- Metric number: Inter 32px weight 700 +- Metric label: `#615d59` 13px weight 500 +- Action list items: `#f6f5f4` bg on hover, whisper border between items +- Status badges: pill (9999px) with semantic color tints + +### Dark Mode Adaptation +- Background: `#191918` (warm dark, slightly lighter than pure black) +- Surface: `#232322` (cards, modals) +- Text primary: `rgba(255,255,255,0.95)` +- Text secondary: `#a39e98` +- Border: `rgba(255,255,255,0.08)` +- Blue accent: `#62aef0` (lighter for dark backgrounds) +- Sidebar: `#1e1e1d` diff --git a/apps/web/src/App.tsx b/apps/web/src/App.tsx index 82e8da7..ddd7c09 100644 --- a/apps/web/src/App.tsx +++ b/apps/web/src/App.tsx @@ -31,52 +31,52 @@ function PrivateRoute({ children }: { children: React.ReactNode }) { const themeConfig = { token: { - colorPrimary: '#4F46E5', - colorSuccess: '#059669', - colorWarning: '#D97706', - colorError: '#DC2626', - colorInfo: '#2563EB', - colorBgLayout: '#F1F5F9', - colorBgContainer: '#FFFFFF', - colorBgElevated: '#FFFFFF', - colorBorder: '#E2E8F0', - colorBorderSecondary: '#F1F5F9', - borderRadius: 8, - borderRadiusLG: 12, - borderRadiusSM: 6, - fontFamily: "-apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, 'PingFang SC', 'Microsoft YaHei', 'Hiragino Sans GB', 'Helvetica Neue', Helvetica, Arial, sans-serif", + colorPrimary: '#0075de', + colorSuccess: '#1aae39', + colorWarning: '#dd5b00', + colorError: '#e5534b', + colorInfo: '#0075de', + colorBgLayout: '#f6f5f4', + colorBgContainer: '#ffffff', + colorBgElevated: '#ffffff', + colorBorder: 'rgba(0, 0, 0, 0.1)', + colorBorderSecondary: 'rgba(0, 0, 0, 0.06)', + borderRadius: 4, + borderRadiusLG: 8, + borderRadiusSM: 2, + fontFamily: "'Inter', -apple-system, system-ui, 'Segoe UI', 'PingFang SC', 'Microsoft YaHei', 'Hiragino Sans GB', Helvetica, Arial, sans-serif", fontSize: 14, fontSizeHeading4: 20, controlHeight: 36, controlHeightLG: 40, controlHeightSM: 28, - boxShadow: '0 1px 3px 0 rgba(0, 0, 0, 0.06), 0 1px 2px -1px rgba(0, 0, 0, 0.06)', - boxShadowSecondary: '0 4px 6px -1px rgba(0, 0, 0, 0.07), 0 2px 4px -2px rgba(0, 0, 0, 0.07)', + boxShadow: 'none', + boxShadowSecondary: 'rgba(0,0,0,0.04) 0px 4px 18px, rgba(0,0,0,0.027) 0px 2.025px 7.85px', }, components: { Button: { - primaryShadow: '0 1px 2px 0 rgba(79, 70, 229, 0.3)', + primaryShadow: 'none', fontWeight: 500, }, Card: { paddingLG: 20, }, Table: { - headerBg: '#F8FAFC', - headerColor: '#475569', - rowHoverBg: '#F5F3FF', + headerBg: '#fafaf9', + headerColor: '#615d59', + rowHoverBg: '#f2f9ff', fontSize: 14, }, Menu: { - itemBorderRadius: 8, + itemBorderRadius: 4, itemMarginInline: 8, - itemHeight: 40, + itemHeight: 36, }, Modal: { - borderRadiusLG: 16, + borderRadiusLG: 12, }, Tag: { - borderRadiusSM: 6, + borderRadiusSM: 4, }, }, }; @@ -85,20 +85,20 @@ const darkThemeConfig = { ...themeConfig, token: { ...themeConfig.token, - colorBgLayout: '#0B0F1A', - colorBgContainer: '#111827', - colorBgElevated: '#1E293B', - colorBorder: '#1E293B', - colorBorderSecondary: '#1E293B', - boxShadow: '0 1px 3px 0 rgba(0, 0, 0, 0.3)', - boxShadowSecondary: '0 4px 6px -1px rgba(0, 0, 0, 0.4)', + colorBgLayout: '#191918', + colorBgContainer: '#232322', + colorBgElevated: '#2a2a29', + colorBorder: 'rgba(255, 255, 255, 0.08)', + colorBorderSecondary: 'rgba(255, 255, 255, 0.05)', + boxShadow: 'none', + boxShadowSecondary: 'rgba(0,0,0,0.2) 0px 4px 18px, rgba(0,0,0,0.15) 0px 2px 8px', }, components: { ...themeConfig.components, Table: { - headerBg: '#1E293B', - headerColor: '#94A3B8', - rowHoverBg: '#1E293B', + headerBg: '#2a2a29', + headerColor: '#a39e98', + rowHoverBg: '#2a2a29', }, }, }; diff --git a/apps/web/src/components/NotificationPanel.tsx b/apps/web/src/components/NotificationPanel.tsx index 9077e1d..5dbedd0 100644 --- a/apps/web/src/components/NotificationPanel.tsx +++ b/apps/web/src/components/NotificationPanel.tsx @@ -50,7 +50,7 @@ export default function NotificationPanel() { @@ -166,7 +166,7 @@ export default function NotificationPanel() { position: 'relative', }} onMouseEnter={(e) => { - e.currentTarget.style.background = isDark ? '#1E293B' : '#F1F5F9'; + e.currentTarget.style.background = isDark ? '#1e1e1d' : '#f6f5f4'; }} onMouseLeave={(e) => { e.currentTarget.style.background = 'transparent'; @@ -175,7 +175,7 @@ export default function NotificationPanel() { diff --git a/apps/web/src/index.css b/apps/web/src/index.css index ec1c1b7..9d6080d 100644 --- a/apps/web/src/index.css +++ b/apps/web/src/index.css @@ -2,65 +2,65 @@ /* ==================================================================== * ERP Platform — Design System Tokens & Global Styles - * Inspired by Linear, Feishu, SAP Fiori modern design language + * Inspired by Notion: warm minimalism, whisper borders, soft elevation * ==================================================================== */ /* --- Design Tokens (CSS Custom Properties) --- */ :root { - /* Primary Palette */ - --erp-primary: #4F46E5; - --erp-primary-hover: #4338CA; - --erp-primary-active: #3730A3; - --erp-primary-light: #EEF2FF; - --erp-primary-light-hover: #E0E7FF; - --erp-primary-bg-subtle: #F5F3FF; + /* Primary Palette — Notion Blue */ + --erp-primary: #0075de; + --erp-primary-hover: #005bab; + --erp-primary-active: #004a8c; + --erp-primary-light: #f2f9ff; + --erp-primary-light-hover: #e4f1ff; + --erp-primary-bg-subtle: #f2f9ff; - /* Semantic Colors */ - --erp-success: #059669; - --erp-success-bg: #ECFDF5; - --erp-warning: #D97706; - --erp-warning-bg: #FFFBEB; - --erp-error: #DC2626; - --erp-error-bg: #FEF2F2; - --erp-info: #2563EB; - --erp-info-bg: #EFF6FF; + /* Semantic Colors — Notion warm tones */ + --erp-success: #1aae39; + --erp-success-bg: #ecfdf5; + --erp-warning: #dd5b00; + --erp-warning-bg: #fff7ed; + --erp-error: #e5534b; + --erp-error-bg: #fef2f2; + --erp-info: #0075de; + --erp-info-bg: #f2f9ff; - /* Neutral Palette */ - --erp-bg-page: #F1F5F9; - --erp-bg-container: #FFFFFF; - --erp-bg-elevated: #FFFFFF; - --erp-bg-spotlight: #F8FAFC; - --erp-bg-sidebar: #0F172A; - --erp-bg-sidebar-hover: #1E293B; - --erp-bg-sidebar-active: rgba(79, 70, 229, 0.15); + /* Neutral Palette — Warm neutrals with yellow-brown undertones */ + --erp-bg-page: #f6f5f4; + --erp-bg-container: #ffffff; + --erp-bg-elevated: #ffffff; + --erp-bg-spotlight: #fafaf9; + --erp-bg-sidebar: #ffffff; + --erp-bg-sidebar-hover: #f6f5f4; + --erp-bg-sidebar-active: #f2f9ff; - /* Text Colors */ - --erp-text-primary: #0F172A; - --erp-text-secondary: #475569; - --erp-text-tertiary: #94A3B8; - --erp-text-inverse: #F8FAFC; - --erp-text-sidebar: #CBD5E1; - --erp-text-sidebar-active: #FFFFFF; + /* Text Colors — Warm near-black */ + --erp-text-primary: rgba(0, 0, 0, 0.95); + --erp-text-secondary: #615d59; + --erp-text-tertiary: #a39e98; + --erp-text-inverse: #ffffff; + --erp-text-sidebar: #615d59; + --erp-text-sidebar-active: #0075de; - /* Border Colors */ - --erp-border: #E2E8F0; - --erp-border-light: #F1F5F9; - --erp-border-dark: #334155; + /* Border Colors — Whisper borders */ + --erp-border: rgba(0, 0, 0, 0.1); + --erp-border-light: rgba(0, 0, 0, 0.06); + --erp-border-dark: rgba(0, 0, 0, 0.15); - /* Shadows */ - --erp-shadow-xs: 0 1px 2px 0 rgba(0, 0, 0, 0.03); - --erp-shadow-sm: 0 1px 3px 0 rgba(0, 0, 0, 0.06), 0 1px 2px -1px rgba(0, 0, 0, 0.06); - --erp-shadow-md: 0 4px 6px -1px rgba(0, 0, 0, 0.07), 0 2px 4px -2px rgba(0, 0, 0, 0.07); - --erp-shadow-lg: 0 10px 15px -3px rgba(0, 0, 0, 0.08), 0 4px 6px -4px rgba(0, 0, 0, 0.08); - --erp-shadow-xl: 0 20px 25px -5px rgba(0, 0, 0, 0.08), 0 8px 10px -6px rgba(0, 0, 0, 0.08); + /* Shadows — Multi-layer, ultra-subtle */ + --erp-shadow-xs: 0 1px 2px rgba(0, 0, 0, 0.01), 0 0.175px 1.04px rgba(0, 0, 0, 0.01); + --erp-shadow-sm: rgba(0, 0, 0, 0.04) 0px 4px 18px, rgba(0, 0, 0, 0.027) 0px 2.025px 7.85px, rgba(0, 0, 0, 0.02) 0px 0.8px 2.93px, rgba(0, 0, 0, 0.01) 0px 0.175px 1.04px; + --erp-shadow-md: rgba(0, 0, 0, 0.01) 0px 1px 3px, rgba(0, 0, 0, 0.02) 0px 3px 7px, rgba(0, 0, 0, 0.02) 0px 7px 15px, rgba(0, 0, 0, 0.04) 0px 14px 28px, rgba(0, 0, 0, 0.05) 0px 23px 52px; + --erp-shadow-lg: rgba(0, 0, 0, 0.01) 0px 2px 4px, rgba(0, 0, 0, 0.02) 0px 5px 12px, rgba(0, 0, 0, 0.03) 0px 10px 24px, rgba(0, 0, 0, 0.04) 0px 18px 40px, rgba(0, 0, 0, 0.05) 0px 30px 64px; + --erp-shadow-xl: rgba(0, 0, 0, 0.01) 0px 3px 6px, rgba(0, 0, 0, 0.02) 0px 8px 16px, rgba(0, 0, 0, 0.03) 0px 14px 30px, rgba(0, 0, 0, 0.04) 0px 22px 48px, rgba(0, 0, 0, 0.06) 0px 36px 80px; - /* Radius */ - --erp-radius-sm: 6px; - --erp-radius-md: 8px; - --erp-radius-lg: 12px; - --erp-radius-xl: 16px; + /* Radius — Notion subtle roundness */ + --erp-radius-sm: 2px; + --erp-radius-md: 4px; + --erp-radius-lg: 8px; + --erp-radius-xl: 12px; - /* Spacing */ + /* Spacing — 8px base unit */ --erp-space-xs: 4px; --erp-space-sm: 8px; --erp-space-md: 16px; @@ -68,9 +68,9 @@ --erp-space-xl: 32px; --erp-space-2xl: 48px; - /* Typography */ - --erp-font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, 'PingFang SC', - 'Microsoft YaHei', 'Hiragino Sans GB', 'Helvetica Neue', Helvetica, Arial, sans-serif; + /* Typography — Inter as primary */ + --erp-font-family: 'Inter', -apple-system, system-ui, 'Segoe UI', 'PingFang SC', + 'Microsoft YaHei', 'Hiragino Sans GB', Helvetica, Arial, sans-serif; --erp-font-mono: 'SF Mono', 'Fira Code', 'Fira Mono', 'Roboto Mono', Menlo, Monaco, Consolas, 'Liberation Mono', 'Courier New', monospace; --erp-font-size-xs: 12px; @@ -86,10 +86,10 @@ --erp-transition-base: 0.2s cubic-bezier(0.4, 0, 0.2, 1); --erp-transition-slow: 0.3s cubic-bezier(0.4, 0, 0.2, 1); - /* Trend Colors */ - --erp-trend-up: #059669; - --erp-trend-down: #DC2626; - --erp-trend-neutral: #64748B; + /* Trend Colors — Warm */ + --erp-trend-up: #1aae39; + --erp-trend-down: #e5534b; + --erp-trend-neutral: #615d59; /* Line Height */ --erp-line-height-tight: 1.25; @@ -102,36 +102,48 @@ --erp-header-height: 56px; } -/* --- Dark Mode Tokens --- */ +/* --- Dark Mode Tokens — Warm dark, Notion-inspired --- */ [data-theme='dark'] { - --erp-bg-page: #0B0F1A; - --erp-bg-container: #111827; - --erp-bg-elevated: #1E293B; - --erp-bg-spotlight: #1E293B; - --erp-bg-sidebar: #070B14; - --erp-bg-sidebar-hover: #111827; + --erp-primary-light: rgba(0, 117, 222, 0.15); + --erp-primary-light-hover: rgba(0, 117, 222, 0.22); + --erp-primary-bg-subtle: rgba(0, 117, 222, 0.1); - --erp-text-primary: #F1F5F9; - --erp-text-secondary: #94A3B8; - --erp-text-tertiary: #64748B; + --erp-bg-page: #191918; + --erp-bg-container: #232322; + --erp-bg-elevated: #2a2a29; + --erp-bg-spotlight: #2a2a29; + --erp-bg-sidebar: #1e1e1d; + --erp-bg-sidebar-hover: #2a2a29; - --erp-border: #1E293B; - --erp-border-light: #1E293B; + --erp-text-primary: rgba(255, 255, 255, 0.95); + --erp-text-secondary: #a39e98; + --erp-text-tertiary: #6d6862; + --erp-text-sidebar: #a39e98; + --erp-text-sidebar-active: #62aef0; - --erp-shadow-xs: 0 1px 2px 0 rgba(0, 0, 0, 0.3); - --erp-shadow-sm: 0 1px 3px 0 rgba(0, 0, 0, 0.4), 0 1px 2px -1px rgba(0, 0, 0, 0.3); - --erp-shadow-md: 0 4px 6px -1px rgba(0, 0, 0, 0.4), 0 2px 4px -2px rgba(0, 0, 0, 0.3); - --erp-shadow-lg: 0 10px 15px -3px rgba(0, 0, 0, 0.5), 0 4px 6px -4px rgba(0, 0, 0, 0.3); + --erp-border: rgba(255, 255, 255, 0.08); + --erp-border-light: rgba(255, 255, 255, 0.05); + --erp-border-dark: rgba(255, 255, 255, 0.12); - --erp-trend-up: #34D399; - --erp-trend-down: #F87171; - --erp-trend-neutral: #94A3B8; + --erp-shadow-xs: 0 1px 2px rgba(0, 0, 0, 0.3); + --erp-shadow-sm: rgba(0, 0, 0, 0.2) 0px 4px 18px, rgba(0, 0, 0, 0.15) 0px 2px 8px; + --erp-shadow-md: rgba(0, 0, 0, 0.15) 0px 1px 3px, rgba(0, 0, 0, 0.2) 0px 5px 12px, rgba(0, 0, 0, 0.2) 0px 10px 24px; + --erp-shadow-lg: rgba(0, 0, 0, 0.2) 0px 2px 6px, rgba(0, 0, 0, 0.25) 0px 8px 20px, rgba(0, 0, 0, 0.3) 0px 16px 40px; + + --erp-trend-up: #2a9d99; + --erp-trend-down: #e5534b; + --erp-trend-neutral: #a39e98; + + --erp-success-bg: rgba(26, 174, 57, 0.15); + --erp-warning-bg: rgba(221, 91, 0, 0.15); + --erp-error-bg: rgba(229, 83, 75, 0.15); + --erp-info-bg: rgba(0, 117, 222, 0.15); } -[data-theme='dark'] .erp-stat-card-trend-up { color: #34D399; } -[data-theme='dark'] .erp-stat-card-trend-down { color: #FCA5A5; } -[data-theme='dark'] .erp-stat-card-trend-neutral { color: #94A3B8; } -[data-theme='dark'] .erp-stat-card-trend-label { color: #94A3B8; } +[data-theme='dark'] .erp-stat-card-trend-up { color: #2a9d99; } +[data-theme='dark'] .erp-stat-card-trend-down { color: #e5534b; } +[data-theme='dark'] .erp-stat-card-trend-neutral { color: #a39e98; } +[data-theme='dark'] .erp-stat-card-trend-label { color: #a39e98; } /* --- Global Reset & Base --- */ body { @@ -170,20 +182,20 @@ body { /* --- Selection --- */ ::selection { - background-color: var(--erp-primary-light); - color: var(--erp-primary); + background-color: rgba(0, 117, 222, 0.15); + color: var(--erp-text-primary); } /* ==================================================================== * Component Overrides — Ant Design Enhancement * ==================================================================== */ -/* --- Card --- */ +/* --- Card — Whisper border, soft shadow --- */ .ant-card { border-radius: var(--erp-radius-lg) !important; - border: 1px solid var(--erp-border-light) !important; - box-shadow: var(--erp-shadow-xs) !important; - transition: box-shadow var(--erp-transition-base), transform var(--erp-transition-base) !important; + border: 1px solid var(--erp-border) !important; + box-shadow: none !important; + transition: box-shadow var(--erp-transition-base) !important; } .ant-card:hover { @@ -209,15 +221,14 @@ body { /* --- Statistic Cards --- */ .stat-card { border-radius: var(--erp-radius-lg) !important; - border: none !important; + border: 1px solid var(--erp-border) !important; overflow: hidden; position: relative; - transition: all var(--erp-transition-base) !important; + transition: box-shadow var(--erp-transition-base) !important; } .stat-card:hover { - transform: translateY(-2px) !important; - box-shadow: var(--erp-shadow-md) !important; + box-shadow: var(--erp-shadow-sm) !important; } .stat-card .ant-statistic-title { @@ -259,30 +270,29 @@ body { border-bottom: 1px solid var(--erp-border-light) !important; } -/* --- Button --- */ +/* --- Button — Subtle 4px radius, no heavy shadow --- */ .ant-btn-primary { - border-radius: var(--erp-radius-md) !important; + border-radius: 4px !important; font-weight: 500 !important; - box-shadow: 0 1px 2px 0 rgba(79, 70, 229, 0.3) !important; + box-shadow: none !important; transition: all var(--erp-transition-fast) !important; } .ant-btn-primary:hover { - box-shadow: 0 2px 4px 0 rgba(79, 70, 229, 0.4) !important; - transform: translateY(-1px); + opacity: 0.9; } .ant-btn-default { - border-radius: var(--erp-radius-md) !important; + border-radius: 4px !important; font-weight: 500 !important; } -/* --- Input --- */ +/* --- Input — 4px radius, whisper border --- */ .ant-input, .ant-input-affix-wrapper, .ant-select-selector, .ant-picker { - border-radius: var(--erp-radius-md) !important; + border-radius: 4px !important; transition: all var(--erp-transition-fast) !important; } @@ -297,7 +307,7 @@ body { .ant-select-focused .ant-select-selector, .ant-picker-focused { border-color: var(--erp-primary) !important; - box-shadow: 0 0 0 2px rgba(79, 70, 229, 0.12) !important; + box-shadow: 0 0 0 2px rgba(0, 117, 222, 0.12) !important; } /* --- Modal --- */ @@ -426,12 +436,12 @@ body { border-radius: var(--erp-radius-lg) var(--erp-radius-lg) 0 0; } -.erp-gradient-card.indigo::before { background: linear-gradient(90deg, #4F46E5, #818CF8); } -.erp-gradient-card.emerald::before { background: linear-gradient(90deg, #059669, #34D399); } -.erp-gradient-card.amber::before { background: linear-gradient(90deg, #D97706, #FBBF24); } -.erp-gradient-card.rose::before { background: linear-gradient(90deg, #E11D48, #FB7185); } -.erp-gradient-card.sky::before { background: linear-gradient(90deg, #0284C7, #38BDF8); } -.erp-gradient-card.violet::before { background: linear-gradient(90deg, #7C3AED, #A78BFA); } +.erp-gradient-card.indigo::before { background: linear-gradient(90deg, #0075de, #62aef0); } +.erp-gradient-card.emerald::before { background: linear-gradient(90deg, #1aae39, #4ade80); } +.erp-gradient-card.amber::before { background: linear-gradient(90deg, #dd5b00, #fbbf24); } +.erp-gradient-card.rose::before { background: linear-gradient(90deg, #e5534b, #f87171); } +.erp-gradient-card.sky::before { background: linear-gradient(90deg, #0075de, #38bdf8); } +.erp-gradient-card.violet::before { background: linear-gradient(90deg, #391c57, #a78bfa); } /* --- Fade-in Animation --- */ @keyframes erp-fade-in { @@ -529,23 +539,23 @@ body { * ==================================================================== */ .erp-sidebar-menu .ant-menu-item { - margin: 2px 8px !important; - border-radius: var(--erp-radius-md) !important; - height: 40px !important; - line-height: 40px !important; + margin: 1px 8px !important; + border-radius: 4px !important; + height: 36px !important; + line-height: 36px !important; } .erp-sidebar-menu .ant-menu-item-selected { - background: var(--erp-primary) !important; - color: #fff !important; + background: #f2f9ff !important; + color: #0075de !important; } .erp-sidebar-menu .ant-menu-item-selected .anticon { - color: #fff !important; + color: #0075de !important; } .erp-sidebar-menu .ant-menu-item:not(.ant-menu-item-selected):hover { - background: var(--erp-bg-sidebar-hover) !important; + background: #f6f5f4 !important; } /* Sidebar group label */ @@ -555,17 +565,17 @@ body { font-weight: 600; text-transform: uppercase; letter-spacing: 0.8px; - color: #94A3B8; + color: #a39e98; } /* ==================================================================== * MainLayout — CSS classes replacing inline styles * ==================================================================== */ -/* Sider */ +/* Sider — White sidebar, Notion style */ .erp-sider-dark { - background: #0F172A !important; - border-right: none !important; + background: #ffffff !important; + border-right: 1px solid rgba(0, 0, 0, 0.06) !important; position: fixed !important; left: 0; top: 0; @@ -575,57 +585,66 @@ body { } [data-theme='dark'] .erp-sider-dark { - background: #070B14 !important; + background: #1e1e1d !important; + border-right: 1px solid rgba(255, 255, 255, 0.05) !important; } -/* Logo */ +/* Logo — Warm neutral, Notion style */ .erp-sidebar-logo { height: 56px; display: flex; align-items: center; padding: 0 20px; - border-bottom: 1px solid rgba(255, 255, 255, 0.06); + border-bottom: 1px solid rgba(0, 0, 0, 0.06); cursor: pointer; } +[data-theme='dark'] .erp-sidebar-logo { + border-bottom: 1px solid rgba(255, 255, 255, 0.05); +} + .ant-layout-sider-collapsed .erp-sidebar-logo { justify-content: center; padding: 0; } .erp-sidebar-logo-icon { - width: 32px; - height: 32px; - border-radius: 8px; - background: linear-gradient(135deg, #4F46E5, #818CF8); + width: 28px; + height: 28px; + border-radius: 4px; + background: #0075de; display: flex; align-items: center; justify-content: center; flex-shrink: 0; - font-size: 14px; + font-size: 13px; font-weight: 800; color: #fff; } .erp-sidebar-logo-text { - margin-left: 12px; - color: #F8FAFC; - font-size: 16px; + margin-left: 10px; + color: rgba(0, 0, 0, 0.95); + font-size: 15px; font-weight: 700; letter-spacing: -0.3px; white-space: nowrap; } -/* Sidebar menu item */ +[data-theme='dark'] .erp-sidebar-logo-text { + color: rgba(255, 255, 255, 0.95); +} + +/* Sidebar menu item — White sidebar, warm text */ .erp-sidebar-item { display: flex; align-items: center; - height: 40px; - margin: 2px 8px; - padding: 0 16px; - border-radius: 8px; + height: 36px; + margin: 1px 8px; + padding: 0 12px; + border-radius: 4px; cursor: pointer; - color: #94A3B8; + color: #615d59; font-size: 14px; font-weight: 400; transition: all 0.15s cubic-bezier(0.4, 0, 0.2, 1); @@ -638,14 +657,28 @@ body { } .erp-sidebar-item:hover:not(.erp-sidebar-item-active) { - background: rgba(255, 255, 255, 0.06); - color: #E2E8F0; + background: #f6f5f4; + color: rgba(0, 0, 0, 0.95); +} + +[data-theme='dark'] .erp-sidebar-item { + color: #a39e98; +} + +[data-theme='dark'] .erp-sidebar-item:hover:not(.erp-sidebar-item-active) { + background: #2a2a29; + color: rgba(255, 255, 255, 0.95); } .erp-sidebar-item-active { - background: linear-gradient(135deg, #4F46E5, #6366F1); - color: #fff; - font-weight: 600; + background: #f2f9ff; + color: #0075de; + font-weight: 500; +} + +[data-theme='dark'] .erp-sidebar-item-active { + background: rgba(0, 117, 222, 0.15); + color: #62aef0; } .erp-sidebar-item-icon { @@ -658,17 +691,17 @@ body { margin-left: 12px; } -/* Sidebar sub-menu (plugin group) */ +/* Sidebar sub-menu (plugin group) — Warm gray group headers */ .erp-sidebar-submenu-title { display: flex; align-items: center; height: 32px; margin: 6px 8px 2px 8px; padding: 0 12px; - border-radius: 6px; + border-radius: 4px; cursor: pointer; - color: #94A3B8; - font-size: 12px; + color: #a39e98; + font-size: 11px; font-weight: 600; text-transform: uppercase; letter-spacing: 0.5px; @@ -677,12 +710,25 @@ body { } .erp-sidebar-submenu-title:hover { - background: rgba(255, 255, 255, 0.06); - color: #E2E8F0; + background: #f6f5f4; + color: #615d59; +} + +[data-theme='dark'] .erp-sidebar-submenu-title { + color: #6d6862; +} + +[data-theme='dark'] .erp-sidebar-submenu-title:hover { + background: #2a2a29; + color: #a39e98; } .erp-sidebar-submenu-title-active { - color: #A5B4FC; + color: #0075de; +} + +[data-theme='dark'] .erp-sidebar-submenu-title-active { + color: #62aef0; } .erp-sidebar-submenu-arrow { @@ -707,10 +753,10 @@ body { transition: margin-left 0.2s cubic-bezier(0.4, 0, 0.2, 1); } -.erp-main-layout-light { background: #F1F5F9; } -.erp-main-layout-dark { background: #0B0F1A; } +.erp-main-layout-light { background: #f6f5f4; } +.erp-main-layout-dark { background: #191918; } -/* Header */ +/* Header — Clean white, whisper border bottom */ .erp-header { height: 56px !important; padding: 0 24px !important; @@ -724,44 +770,44 @@ body { } .erp-header-light { - background: #FFFFFF !important; - border-bottom: 1px solid #F1F5F9; - box-shadow: 0 1px 3px rgba(0, 0, 0, 0.04); + background: #ffffff !important; + border-bottom: 1px solid rgba(0, 0, 0, 0.06); + box-shadow: none; } .erp-header-dark { - background: #111827 !important; - border-bottom: 1px solid #1E293B; + background: #232322 !important; + border-bottom: 1px solid rgba(255, 255, 255, 0.05); box-shadow: none; } .erp-header-btn { - width: 36px; - height: 36px; - border-radius: 8px; + width: 32px; + height: 32px; + border-radius: 4px; display: flex; align-items: center; justify-content: center; cursor: pointer; transition: all 0.15s ease; - color: #94A3B8; + color: #615d59; will-change: background; } -.erp-header-light .erp-header-btn { color: #64748B; } -.erp-header-dark .erp-header-btn { color: #94A3B8; } -.erp-header-btn:hover { background: #F1F5F9; } -.erp-header-dark .erp-header-btn:hover { background: #1E293B; } +.erp-header-light .erp-header-btn { color: #615d59; } +.erp-header-dark .erp-header-btn { color: #a39e98; } +.erp-header-btn:hover { background: #f6f5f4; } +.erp-header-dark .erp-header-btn:hover { background: #2a2a29; } .erp-header-title { font-size: 15px; font-weight: 600; } -.erp-text-light { color: #0F172A; } -.erp-text-dark { color: #F1F5F9; } -.erp-text-light-secondary { color: #334155; } -.erp-text-dark-secondary { color: #E2E8F0; } +.erp-text-light { color: rgba(0, 0, 0, 0.95); } +.erp-text-dark { color: rgba(255, 255, 255, 0.95); } +.erp-text-light-secondary { color: #615d59; } +.erp-text-dark-secondary { color: #a39e98; } .erp-header-divider { width: 1px; height: 24px; margin: 0 8px; } -.erp-header-divider-light { background: #E2E8F0; } -.erp-header-divider-dark { background: #1E293B; } +.erp-header-divider-light { background: rgba(0, 0, 0, 0.06); } +.erp-header-divider-dark { background: rgba(255, 255, 255, 0.05); } /* User avatar */ .erp-header-user { @@ -774,11 +820,11 @@ body { transition: all 0.15s ease; } -.erp-header-user:hover { background: #F1F5F9; } -.erp-header-dark .erp-header-user:hover { background: #1E293B; } +.erp-header-user:hover { background: #f6f5f4; } +.erp-header-dark .erp-header-user:hover { background: #2a2a29; } .erp-user-avatar { - background: linear-gradient(135deg, #4F46E5, #818CF8) !important; + background: #0075de !important; font-size: 13px !important; } @@ -786,8 +832,8 @@ body { /* Footer */ .erp-footer { text-align: center; padding: 12px 24px !important; background: transparent !important; font-size: 12px; } -.erp-footer-light { color: #475569; } -.erp-footer-dark { color: #94A3B8; } +.erp-footer-light { color: #a39e98; } +.erp-footer-dark { color: #6d6862; } /* ==================================================================== * Dashboard — Stat Cards & Quick Actions (replacing inline styles) @@ -818,7 +864,7 @@ body { left: 0; right: 0; height: 3px; - background: var(--card-gradient, linear-gradient(135deg, #4F46E5, #6366F1)); + background: var(--card-gradient, linear-gradient(135deg, #0075de, #62aef0)); } .erp-stat-card-body { @@ -849,7 +895,7 @@ body { width: 48px; height: 48px; border-radius: var(--erp-radius-lg); - background: var(--card-icon-bg, rgba(79, 70, 229, 0.12)); + background: var(--card-icon-bg, rgba(0, 117, 222, 0.08)); display: flex; align-items: center; justify-content: center; @@ -867,7 +913,7 @@ body { .erp-section-icon { font-size: 16px; - color: #4F46E5; + color: #0075de; } .erp-section-title { @@ -890,17 +936,17 @@ body { } .erp-quick-action:hover { - background: #EEF2FF; - border-color: var(--action-color, #4F46E5); + background: #f2f9ff; + border-color: var(--action-color, #0075de); } [data-theme='dark'] .erp-quick-action { - background: #0B0F1A; + background: #191918; } [data-theme='dark'] .erp-quick-action:hover { - background: #1E293B; - border-color: var(--action-color, #4F46E5); + background: #2a2a29; + border-color: var(--action-color, #0075de); } .erp-quick-action-icon { @@ -910,8 +956,8 @@ body { display: flex; align-items: center; justify-content: center; - background: color-mix(in srgb, var(--action-color, #4F46E5) 10%, transparent); - color: var(--action-color, #4F46E5); + background: color-mix(in srgb, var(--action-color, #0075de) 8%, transparent); + color: var(--action-color, #0075de); font-size: 16px; flex-shrink: 0; } @@ -1000,8 +1046,8 @@ body { display: flex; align-items: center; justify-content: center; - background: color-mix(in srgb, var(--action-color, #4F46E5) 10%, transparent); - color: var(--action-color, #4F46E5); + background: color-mix(in srgb, var(--action-color, #0075de) 8%, transparent); + color: var(--action-color, #0075de); font-size: 18px; flex-shrink: 0; transition: transform 0.15s ease; @@ -1029,7 +1075,7 @@ body { padding: 12px 16px; border-radius: var(--erp-radius-md); background: var(--erp-bg-spotlight); - border-left: 3px solid var(--task-color, #4F46E5); + border-left: 3px solid var(--task-color, #0075de); cursor: pointer; transition: all 0.15s ease; } @@ -1046,8 +1092,8 @@ body { display: flex; align-items: center; justify-content: center; - background: color-mix(in srgb, var(--task-color, #4F46E5) 12%, transparent); - color: var(--task-color, #4F46E5); + background: color-mix(in srgb, var(--task-color, #0075de) 8%, transparent); + color: var(--task-color, #0075de); font-size: 14px; flex-shrink: 0; } @@ -1069,7 +1115,7 @@ body { gap: 12px; margin-top: 2px; font-size: var(--erp-font-size-xs); - color: #64748B; + color: #a39e98; } .erp-task-priority { @@ -1081,13 +1127,13 @@ body { font-weight: 600; } -.erp-task-priority-high { background: #FEF2F2; color: #B91C1C; } -.erp-task-priority-medium { background: #FFFBEB; color: #92400E; } -.erp-task-priority-low { background: #ECFDF5; color: #047857; } +.erp-task-priority-high { background: #fef2f2; color: #e5534b; } +.erp-task-priority-medium { background: #fff7ed; color: #dd5b00; } +.erp-task-priority-low { background: #ecfdf5; color: #1aae39; } -[data-theme='dark'] .erp-task-priority-high { background: rgba(185, 28, 28, 0.15); color: #FCA5A5; } -[data-theme='dark'] .erp-task-priority-medium { background: rgba(146, 64, 14, 0.15); color: #FCD34D; } -[data-theme='dark'] .erp-task-priority-low { background: rgba(4, 120, 87, 0.15); color: #6EE7B7; } +[data-theme='dark'] .erp-task-priority-high { background: rgba(229, 83, 75, 0.15); color: #e5534b; } +[data-theme='dark'] .erp-task-priority-medium { background: rgba(221, 91, 0, 0.15); color: #dd5b00; } +[data-theme='dark'] .erp-task-priority-low { background: rgba(26, 174, 57, 0.15); color: #1aae39; } /* Activity Timeline */ .erp-activity-list { @@ -1143,12 +1189,12 @@ body { .erp-activity-time { font-size: 11px; - color: #64748B; + color: #a39e98; margin-top: 2px; } [data-theme='dark'] .erp-activity-time { - color: #94A3B8; + color: #6d6862; } /* Empty State */ diff --git a/apps/web/src/pages/Home.tsx b/apps/web/src/pages/Home.tsx index 274edc7..674fa48 100644 --- a/apps/web/src/pages/Home.tsx +++ b/apps/web/src/pages/Home.tsx @@ -167,7 +167,7 @@ export default function Home() { title: '用户总数', value: stats.userCount, icon: , - gradient: 'linear-gradient(135deg, #4F46E5, #6366F1)', + gradient: 'linear-gradient(135deg, #0075de, #62aef0)', iconBg: 'rgba(79, 70, 229, 0.12)', delay: 'erp-fade-in erp-fade-in-delay-1', trend: { value: '+2', direction: 'up', label: '较上周' }, @@ -179,7 +179,7 @@ export default function Home() { title: '角色数量', value: stats.roleCount, icon: , - gradient: 'linear-gradient(135deg, #059669, #10B981)', + gradient: 'linear-gradient(135deg, #1aae39, #10B981)', iconBg: 'rgba(5, 150, 105, 0.12)', delay: 'erp-fade-in erp-fade-in-delay-2', trend: { value: '+1', direction: 'up', label: '较上月' }, @@ -191,7 +191,7 @@ export default function Home() { title: '流程实例', value: stats.processInstanceCount, icon: , - gradient: 'linear-gradient(135deg, #D97706, #F59E0B)', + gradient: 'linear-gradient(135deg, #dd5b00, #F59E0B)', iconBg: 'rgba(217, 119, 6, 0.12)', delay: 'erp-fade-in erp-fade-in-delay-3', trend: { value: '0', direction: 'neutral', label: '较昨日' }, @@ -213,18 +213,18 @@ export default function Home() { ]; const quickActions = [ - { icon: , label: '用户管理', path: '/users', color: '#4F46E5' }, - { icon: , label: '权限管理', path: '/roles', color: '#059669' }, - { icon: , label: '组织架构', path: '/organizations', color: '#D97706' }, + { icon: , label: '用户管理', path: '/users', color: '#0075de' }, + { icon: , label: '权限管理', path: '/roles', color: '#1aae39' }, + { icon: , label: '组织架构', path: '/organizations', color: '#dd5b00' }, { icon: , label: '工作流', path: '/workflow', color: '#7C3AED' }, { icon: , label: '消息中心', path: '/messages', color: '#E11D48' }, - { icon: , label: '系统设置', path: '/settings', color: '#64748B' }, + { icon: , label: '系统设置', path: '/settings', color: '#615d59' }, ]; const pendingTasks: TaskItem[] = [ - { id: '1', title: '审核新用户注册申请', priority: 'high', assignee: '系统', dueText: '待处理', color: '#DC2626', icon: , path: '/users' }, - { id: '2', title: '配置工作流审批节点', priority: 'medium', assignee: '管理员', dueText: '进行中', color: '#D97706', icon: , path: '/workflow' }, - { id: '3', title: '更新角色权限策略', priority: 'low', assignee: '管理员', dueText: '计划中', color: '#059669', icon: , path: '/roles' }, + { id: '1', title: '审核新用户注册申请', priority: 'high', assignee: '系统', dueText: '待处理', color: '#e5534b', icon: , path: '/users' }, + { id: '2', title: '配置工作流审批节点', priority: 'medium', assignee: '管理员', dueText: '进行中', color: '#dd5b00', icon: , path: '/workflow' }, + { id: '3', title: '更新角色权限策略', priority: 'low', assignee: '管理员', dueText: '计划中', color: '#1aae39', icon: , path: '/roles' }, ]; const recentActivities: ActivityItem[] = [ @@ -243,13 +243,13 @@ export default function Home() {

工作台

-

+

欢迎回来,这是您的系统概览

@@ -313,7 +313,7 @@ export default function Home() { {pendingTasks.length} 项待处理 @@ -340,7 +340,7 @@ export default function Home() { {priorityLabel[task.priority]} - + ))} @@ -351,7 +351,7 @@ export default function Home() {
- + 最近动态
@@ -400,7 +400,7 @@ export default function Home() {
- + 系统信息
diff --git a/apps/web/src/pages/Login.tsx b/apps/web/src/pages/Login.tsx index b356b17..d1e2391 100644 --- a/apps/web/src/pages/Login.tsx +++ b/apps/web/src/pages/Login.tsx @@ -30,7 +30,7 @@ export default function Login() {
欢迎回来 -

+

请登录您的账户以继续

@@ -163,7 +163,7 @@ export default function Login() { rules={[{ required: true, message: '请输入用户名' }]} > } + prefix={} placeholder="用户名" style={{ height: 44, borderRadius: 10 }} /> @@ -173,7 +173,7 @@ export default function Login() { rules={[{ required: true, message: '请输入密码' }]} > } + prefix={} placeholder="密码" style={{ height: 44, borderRadius: 10 }} /> @@ -197,7 +197,7 @@ export default function Login() {
-

+

ERP Platform v0.1.0 · Powered by Rust + React

diff --git a/apps/web/src/pages/Organizations.tsx b/apps/web/src/pages/Organizations.tsx index 0f1d8b4..d2dbf02 100644 --- a/apps/web/src/pages/Organizations.tsx +++ b/apps/web/src/pages/Organizations.tsx @@ -44,7 +44,7 @@ export default function Organizations() { const cardStyle = { background: isDark ? '#111827' : '#FFFFFF', borderRadius: 12, - border: `1px solid ${isDark ? '#1E293B' : '#F1F5F9'}`, + border: `1px solid ${isDark ? '#1e1e1d' : '#f6f5f4'}`, }; // --- Org tree state --- @@ -264,9 +264,9 @@ export default function Organizations() { {item.name}{' '} {item.code && {item.code}} @@ -282,9 +282,9 @@ export default function Organizations() { {item.name}{' '} {item.code && {item.code}} @@ -343,7 +343,7 @@ export default function Organizations() {

- + 组织架构管理

管理组织、部门和岗位的层级结构
@@ -356,7 +356,7 @@ export default function Organizations() {
= { - uploaded: { color: '#64748B', label: '已上传' }, + uploaded: { color: '#615d59', label: '已上传' }, installed: { color: '#2563EB', label: '已安装' }, - enabled: { color: '#059669', label: '已启用' }, - running: { color: '#059669', label: '运行中' }, - disabled: { color: '#DC2626', label: '已禁用' }, + enabled: { color: '#1aae39', label: '已启用' }, + running: { color: '#1aae39', label: '运行中' }, + disabled: { color: '#e5534b', label: '已禁用' }, uninstalled: { color: '#9333EA', label: '已卸载' }, }; @@ -215,7 +215,7 @@ export default function PluginAdmin() { key: 'status', width: 100, render: (status: PluginStatus) => { - const cfg = STATUS_CONFIG[status] || { color: '#64748B', label: status }; + const cfg = STATUS_CONFIG[status] || { color: '#615d59', label: status }; return {cfg.label}; }, }, diff --git a/apps/web/src/pages/PluginDashboardPage.tsx b/apps/web/src/pages/PluginDashboardPage.tsx index c776a7f..87c76f6 100644 --- a/apps/web/src/pages/PluginDashboardPage.tsx +++ b/apps/web/src/pages/PluginDashboardPage.tsx @@ -299,7 +299,7 @@ export function PluginDashboardPage() { style={{ fontSize: 24, fontWeight: 700, - color: isDark ? '#F1F5F9' : '#0F172A', + color: isDark ? '#f6f5f4' : 'rgba(0,0,0,0.95)', margin: '0 0 4px', letterSpacing: '-0.5px', }} @@ -309,7 +309,7 @@ export function PluginDashboardPage() {

@@ -352,7 +352,7 @@ export function PluginDashboardPage() {

图表分析
@@ -389,7 +389,7 @@ export function PluginDashboardPage() {
{currentEntity?.display_name || selectedEntity} 数据分布 diff --git a/apps/web/src/pages/PluginGraphPage.tsx b/apps/web/src/pages/PluginGraphPage.tsx index 7830acc..4fe6f51 100644 --- a/apps/web/src/pages/PluginGraphPage.tsx +++ b/apps/web/src/pages/PluginGraphPage.tsx @@ -313,8 +313,8 @@ export function PluginGraphPage() { const r = degreeToRadius(degree, isCenter); // Determine node color from its most common edge type, or default palette - let nodeColorBase = '#4F46E5'; - let nodeColorLight = '#818CF8'; + let nodeColorBase = '#0075de'; + let nodeColorLight = '#62aef0'; let nodeColorGlow = 'rgba(79,70,229,0.3)'; if (isCenter) { diff --git a/apps/web/src/pages/PluginMarket.tsx b/apps/web/src/pages/PluginMarket.tsx index 3a107e3..2e8eebc 100644 --- a/apps/web/src/pages/PluginMarket.tsx +++ b/apps/web/src/pages/PluginMarket.tsx @@ -37,12 +37,12 @@ import { const { Title, Text, Paragraph } = Typography; const CATEGORY_COLORS: Record = { - '财务': '#059669', + '财务': '#1aae39', 'CRM': '#2563EB', '进销存': '#9333EA', - '生产': '#DC2626', - '人力资源': '#D97706', - '基础': '#64748B', + '生产': '#e5534b', + '人力资源': '#dd5b00', + '基础': '#615d59', }; export default function PluginMarket() { @@ -190,7 +190,7 @@ export default function PluginMarket() {
{plugin.name} {plugin.category} @@ -244,7 +244,7 @@ export default function PluginMarket() {
- + {selectedPlugin.category} v{selectedPlugin.version} diff --git a/apps/web/src/pages/Roles.tsx b/apps/web/src/pages/Roles.tsx index 9de5e24..028f477 100644 --- a/apps/web/src/pages/Roles.tsx +++ b/apps/web/src/pages/Roles.tsx @@ -153,12 +153,12 @@ export default function Roles() { height: 32, borderRadius: 8, background: record.is_system - ? 'linear-gradient(135deg, #4F46E5, #818CF8)' - : isDark ? '#1E293B' : '#F1F5F9', + ? 'linear-gradient(135deg, #0075de, #62aef0)' + : isDark ? '#1e1e1d' : '#f6f5f4', display: 'flex', alignItems: 'center', justifyContent: 'center', - color: record.is_system ? '#fff' : isDark ? '#94A3B8' : '#64748B', + color: record.is_system ? '#fff' : isDark ? '#a39e98' : '#615d59', fontSize: 14, }} > @@ -174,9 +174,9 @@ export default function Roles() { key: 'code', render: (v: string) => ( @@ -190,7 +190,7 @@ export default function Roles() { key: 'description', ellipsis: true, render: (v: string | undefined) => ( - {v || '-'} + {v || '-'} ), }, { @@ -201,8 +201,8 @@ export default function Roles() { render: (v: boolean) => ( } onClick={() => openPermModal(record)} - style={{ color: '#4F46E5' }} + style={{ color: '#0075de' }} > 权限 @@ -233,7 +233,7 @@ export default function Roles() { type="text" icon={} onClick={() => openEditModal(record)} - style={{ color: isDark ? '#94A3B8' : '#64748B' }} + style={{ color: isDark ? '#a39e98' : '#615d59' }} />
= { - active: '#059669', - disabled: '#DC2626', - locked: '#D97706', + active: '#1aae39', + disabled: '#e5534b', + locked: '#dd5b00', }; const STATUS_BG_MAP: Record = { @@ -219,7 +219,7 @@ export default function Users() { width: 32, height: 32, borderRadius: 8, - background: 'linear-gradient(135deg, #4F46E5, #818CF8)', + background: 'linear-gradient(135deg, #0075de, #62aef0)', display: 'flex', alignItems: 'center', justifyContent: 'center', @@ -233,7 +233,7 @@ export default function Users() {
{v}
{record.display_name && ( -
+
{record.display_name}
)} @@ -261,8 +261,8 @@ export default function Users() { render: (status: string) => ( 0 ? roles.map((r) => ( {r.name} )) - : -, + : -, }, { title: '操作', @@ -299,14 +299,14 @@ export default function Users() { type="text" icon={} onClick={() => openEditModal(record)} - style={{ color: isDark ? '#94A3B8' : '#64748B' }} + style={{ color: isDark ? '#a39e98' : '#615d59' }} />
- } disabled={!!editUser} /> + } disabled={!!editUser} /> {!editUser && ( {r.name} - + {r.code} diff --git a/apps/web/src/pages/dashboard/DashboardWidgets.tsx b/apps/web/src/pages/dashboard/DashboardWidgets.tsx index 51cb67f..97fda98 100644 --- a/apps/web/src/pages/dashboard/DashboardWidgets.tsx +++ b/apps/web/src/pages/dashboard/DashboardWidgets.tsx @@ -46,7 +46,7 @@ function prepareChartData(data: WidgetData['data'], dimensionOrder?: string[]) { const TAG_COLOR_MAP: Record = { blue: '#3B82F6', green: '#10B981', orange: '#F59E0B', red: '#EF4444', purple: '#8B5CF6', cyan: '#06B6D4', magenta: '#EC4899', gold: '#EAB308', - lime: '#84CC16', geekblue: '#6366F1', volcano: '#F97316', + lime: '#84CC16', geekblue: '#62aef0', volcano: '#F97316', }; function tagStrokeColor(color: string): string { @@ -204,7 +204,7 @@ export function SkeletonBreakdownCard({ index }: { index: number }) { function StatWidgetCard({ widgetData }: { widgetData: WidgetData }) { const { widget, count } = widgetData; const animatedValue = useCountUp(count ?? 0); - const color = widget.color || '#4F46E5'; + const color = widget.color || '#0075de'; return (
@@ -229,7 +229,7 @@ function StatWidgetCard({ widgetData }: { widgetData: WidgetData }) { function BarWidgetCard({ widgetData, isDark }: { widgetData: WidgetData; isDark: boolean }) { const { widget, data } = widgetData; const chartData = prepareChartData(data, widget.dimension_order); - const axisLabelStyle = { fill: isDark ? '#94A3B8' : '#475569' }; + const axisLabelStyle = { fill: isDark ? '#a39e98' : '#615d59' }; return ( {chartData.length > 0 ? ( @@ -275,7 +275,7 @@ function FunnelWidgetCard({ widgetData }: { widgetData: WidgetData }) { function LineWidgetCard({ widgetData, isDark }: { widgetData: WidgetData; isDark: boolean }) { const { widget, data } = widgetData; const chartData = prepareChartData(data, widget.dimension_order); - const axisLabelStyle = { fill: isDark ? '#94A3B8' : '#475569' }; + const axisLabelStyle = { fill: isDark ? '#a39e98' : '#615d59' }; return ( {chartData.length > 0 ? ( @@ -315,7 +315,7 @@ function StatCardsWidget({ widgetData }: { widgetData: WidgetData }) { {statCards.map((sc, i) => (
diff --git a/apps/web/src/pages/dashboard/dashboardConstants.tsx b/apps/web/src/pages/dashboard/dashboardConstants.tsx index 2e2de11..955b46d 100644 --- a/apps/web/src/pages/dashboard/dashboardConstants.tsx +++ b/apps/web/src/pages/dashboard/dashboardConstants.tsx @@ -19,9 +19,9 @@ import { // ── 通用调色板 ── const UNIVERSAL_COLORS = [ - { gradient: 'linear-gradient(135deg, #4F46E5, #6366F1)', iconBg: 'rgba(79, 70, 229, 0.12)', tagColor: 'purple' }, - { gradient: 'linear-gradient(135deg, #059669, #10B981)', iconBg: 'rgba(5, 150, 105, 0.12)', tagColor: 'green' }, - { gradient: 'linear-gradient(135deg, #D97706, #F59E0B)', iconBg: 'rgba(217, 119, 6, 0.12)', tagColor: 'orange' }, + { gradient: 'linear-gradient(135deg, #0075de, #62aef0)', iconBg: 'rgba(79, 70, 229, 0.12)', tagColor: 'purple' }, + { gradient: 'linear-gradient(135deg, #1aae39, #10B981)', iconBg: 'rgba(5, 150, 105, 0.12)', tagColor: 'green' }, + { gradient: 'linear-gradient(135deg, #dd5b00, #F59E0B)', iconBg: 'rgba(217, 119, 6, 0.12)', tagColor: 'orange' }, { gradient: 'linear-gradient(135deg, #7C3AED, #A78BFA)', iconBg: 'rgba(124, 58, 237, 0.12)', tagColor: 'volcano' }, { gradient: 'linear-gradient(135deg, #E11D48, #F43F5E)', iconBg: 'rgba(225, 29, 72, 0.12)', tagColor: 'red' }, { gradient: 'linear-gradient(135deg, #0891B2, #06B6D4)', iconBg: 'rgba(8, 145, 178, 0.12)', tagColor: 'cyan' }, diff --git a/apps/web/src/pages/graph/graphRenderer.ts b/apps/web/src/pages/graph/graphRenderer.ts index 46fea51..97df3c2 100644 --- a/apps/web/src/pages/graph/graphRenderer.ts +++ b/apps/web/src/pages/graph/graphRenderer.ts @@ -11,11 +11,11 @@ import type { GraphEdge } from './graphTypes'; /** 关系类型对应的色板 (base / light / glow) — 通用调色板自动分配 */ const EDGE_PALETTE: Array<{ base: string; light: string; glow: string }> = [ - { base: '#4F46E5', light: '#818CF8', glow: 'rgba(79,70,229,0.3)' }, - { base: '#059669', light: '#34D399', glow: 'rgba(5,150,105,0.3)' }, - { base: '#D97706', light: '#FBBF24', glow: 'rgba(217,119,6,0.3)' }, + { base: '#0075de', light: '#62aef0', glow: 'rgba(79,70,229,0.3)' }, + { base: '#1aae39', light: '#34D399', glow: 'rgba(5,150,105,0.3)' }, + { base: '#dd5b00', light: '#FBBF24', glow: 'rgba(217,119,6,0.3)' }, { base: '#0891B2', light: '#22D3EE', glow: 'rgba(8,145,178,0.3)' }, - { base: '#DC2626', light: '#F87171', glow: 'rgba(220,38,38,0.3)' }, + { base: '#e5534b', light: '#F87171', glow: 'rgba(220,38,38,0.3)' }, { base: '#7C3AED', light: '#A78BFA', glow: 'rgba(124,58,237,0.3)' }, { base: '#EA580C', light: '#FB923C', glow: 'rgba(234,88,12,0.3)' }, { base: '#DB2777', light: '#F472B6', glow: 'rgba(219,39,119,0.3)' }, diff --git a/apps/web/src/pages/messages/MessageTemplates.tsx b/apps/web/src/pages/messages/MessageTemplates.tsx index eabd628..4722092 100644 --- a/apps/web/src/pages/messages/MessageTemplates.tsx +++ b/apps/web/src/pages/messages/MessageTemplates.tsx @@ -5,9 +5,9 @@ import type { ColumnsType } from 'antd/es/table'; import { listTemplates, createTemplate, type MessageTemplateInfo } from '../../api/messageTemplates'; const channelMap: Record = { - in_app: { label: '站内', color: '#4F46E5' }, - email: { label: '邮件', color: '#059669' }, - sms: { label: '短信', color: '#D97706' }, + in_app: { label: '站内', color: '#0075de' }, + email: { label: '邮件', color: '#1aae39' }, + sms: { label: '短信', color: '#dd5b00' }, wechat: { label: '微信', color: '#7C3AED' }, }; @@ -64,9 +64,9 @@ export default function MessageTemplates() { key: 'code', render: (v: string) => ( @@ -80,7 +80,7 @@ export default function MessageTemplates() { key: 'channel', width: 90, render: (c: string) => { - const info = channelMap[c] || { label: c, color: '#64748B' }; + const info = channelMap[c] || { label: c, color: '#615d59' }; return ( ( - {v} + {v} ), }, ]; @@ -124,7 +124,7 @@ export default function MessageTemplates() { alignItems: 'center', marginBottom: 16, }}> - + 共 {total} 个模板
= { - urgent: { bg: '#FEF2F2', color: '#DC2626', text: '紧急' }, - important: { bg: '#FFFBEB', color: '#D97706', text: '重要' }, - normal: { bg: '#EEF2FF', color: '#4F46E5', text: '普通' }, + urgent: { bg: '#FEF2F2', color: '#e5534b', text: '紧急' }, + important: { bg: '#FFFBEB', color: '#dd5b00', text: '重要' }, + normal: { bg: '#f2f9ff', color: '#0075de', text: '普通' }, }; export default function NotificationList({ queryFilter }: Props) { @@ -83,7 +83,7 @@ export default function NotificationList({ queryFilter }: Props) { content: (
{record.body} -
+
{record.created_at}
@@ -104,7 +104,7 @@ export default function NotificationList({ queryFilter }: Props) { style={{ fontWeight: record.is_read ? 400 : 600, cursor: 'pointer', - color: record.is_read ? (isDark ? '#94A3B8' : '#64748B') : 'inherit', + color: record.is_read ? (isDark ? '#a39e98' : '#615d59') : 'inherit', }} onClick={() => showDetail(record)} > @@ -114,7 +114,7 @@ export default function NotificationList({ queryFilter }: Props) { width: 6, height: 6, borderRadius: '50%', - background: '#4F46E5', + background: '#0075de', marginRight: 8, }} /> )} @@ -128,7 +128,7 @@ export default function NotificationList({ queryFilter }: Props) { key: 'priority', width: 90, render: (p: string) => { - const info = priorityStyles[p] || { bg: '#F1F5F9', color: '#64748B', text: p }; + const info = priorityStyles[p] || { bg: '#f6f5f4', color: '#615d59', text: p }; return ( {s === 'system' ? '系统' : '用户'}, + render: (s: string) => {s === 'system' ? '系统' : '用户'}, }, { title: '状态', @@ -155,9 +155,9 @@ export default function NotificationList({ queryFilter }: Props) { width: 80, render: (r: boolean) => ( {r ? '已读' : '未读'} @@ -170,7 +170,7 @@ export default function NotificationList({ queryFilter }: Props) { key: 'created_at', width: 180, render: (v: string) => ( - {v} + {v} ), }, { @@ -185,7 +185,7 @@ export default function NotificationList({ queryFilter }: Props) { size="small" icon={} onClick={() => handleMarkRead(record.id)} - style={{ color: '#4F46E5' }} + style={{ color: '#0075de' }} /> )}
- + 通知偏好设置
diff --git a/apps/web/src/pages/plugins/graph/graphConstants.ts b/apps/web/src/pages/plugins/graph/graphConstants.ts index 75aca49..13ce9ca 100644 --- a/apps/web/src/pages/plugins/graph/graphConstants.ts +++ b/apps/web/src/pages/plugins/graph/graphConstants.ts @@ -5,11 +5,11 @@ // 通用边调色板 const EDGE_PALETTE: Array<{ base: string; light: string; glow: string }> = [ - { base: '#4F46E5', light: '#818CF8', glow: 'rgba(79,70,229,0.3)' }, - { base: '#059669', light: '#34D399', glow: 'rgba(5,150,105,0.3)' }, - { base: '#D97706', light: '#FBBF24', glow: 'rgba(217,119,6,0.3)' }, + { base: '#0075de', light: '#62aef0', glow: 'rgba(79,70,229,0.3)' }, + { base: '#1aae39', light: '#34D399', glow: 'rgba(5,150,105,0.3)' }, + { base: '#dd5b00', light: '#FBBF24', glow: 'rgba(217,119,6,0.3)' }, { base: '#0891B2', light: '#22D3EE', glow: 'rgba(8,145,178,0.3)' }, - { base: '#DC2626', light: '#F87171', glow: 'rgba(220,38,38,0.3)' }, + { base: '#e5534b', light: '#F87171', glow: 'rgba(220,38,38,0.3)' }, { base: '#7C3AED', light: '#A78BFA', glow: 'rgba(124,58,237,0.3)' }, { base: '#EA580C', light: '#FB923C', glow: 'rgba(234,88,12,0.3)' }, { base: '#DB2777', light: '#F472B6', glow: 'rgba(219,39,119,0.3)' }, diff --git a/apps/web/src/pages/plugins/graph/graphRenderer.ts b/apps/web/src/pages/plugins/graph/graphRenderer.ts index 4124e8c..5832058 100644 --- a/apps/web/src/pages/plugins/graph/graphRenderer.ts +++ b/apps/web/src/pages/plugins/graph/graphRenderer.ts @@ -295,8 +295,8 @@ export function drawFullGraph( const degree = degreeMap.get(node.id) || 0; const r = degreeToRadius(degree, isCenter); - let nodeColorBase = '#4F46E5'; - let nodeColorLight = '#818CF8'; + let nodeColorBase = '#0075de'; + let nodeColorLight = '#62aef0'; let nodeColorGlow = 'rgba(79,70,229,0.3)'; if (isCenter) { diff --git a/apps/web/src/pages/settings/AuditLogViewer.tsx b/apps/web/src/pages/settings/AuditLogViewer.tsx index 00a116b..9265575 100644 --- a/apps/web/src/pages/settings/AuditLogViewer.tsx +++ b/apps/web/src/pages/settings/AuditLogViewer.tsx @@ -17,9 +17,9 @@ const RESOURCE_TYPE_OPTIONS = [ ]; const ACTION_STYLES: Record = { - create: { bg: '#ECFDF5', color: '#059669', text: '创建' }, - update: { bg: '#EEF2FF', color: '#4F46E5', text: '更新' }, - delete: { bg: '#FEF2F2', color: '#DC2626', text: '删除' }, + create: { bg: '#ECFDF5', color: '#1aae39', text: '创建' }, + update: { bg: '#f2f9ff', color: '#0075de', text: '更新' }, + delete: { bg: '#FEF2F2', color: '#e5534b', text: '删除' }, }; function formatDateTime(value: string): string { @@ -80,7 +80,7 @@ export default function AuditLogViewer() { key: 'action', width: 100, render: (action: string) => { - const info = ACTION_STYLES[action] || { bg: '#F1F5F9', color: '#64748B', text: action }; + const info = ACTION_STYLES[action] || { bg: '#f6f5f4', color: '#615d59', text: action }; return ( ( {v} @@ -115,7 +115,7 @@ export default function AuditLogViewer() { width: 200, ellipsis: true, render: (v: string) => ( - + {v} ), @@ -127,7 +127,7 @@ export default function AuditLogViewer() { width: 200, ellipsis: true, render: (v: string) => ( - + {v} ), @@ -138,7 +138,7 @@ export default function AuditLogViewer() { key: 'created_at', width: 180, render: (value: string) => ( - + {formatDateTime(value)} ), @@ -156,7 +156,7 @@ export default function AuditLogViewer() { padding: 12, background: isDark ? '#111827' : '#FFFFFF', borderRadius: 10, - border: `1px solid ${isDark ? '#1E293B' : '#F1F5F9'}`, + border: `1px solid ${isDark ? '#1e1e1d' : '#f6f5f4'}`, }}>
( @@ -162,7 +162,7 @@ export default function SystemSettings() { type="text" icon={} onClick={() => openEdit(record)} - style={{ color: isDark ? '#94A3B8' : '#64748B' }} + style={{ color: isDark ? '#a39e98' : '#615d59' }} /> } + prefix={} value={searchKey} onChange={(e) => setSearchKey(e.target.value)} onPressEnter={handleSearch} @@ -207,7 +207,7 @@ export default function SystemSettings() {
= { - approved: { bg: '#ECFDF5', color: '#059669', text: '同意' }, - rejected: { bg: '#FEF2F2', color: '#DC2626', text: '拒绝' }, - delegated: { bg: '#EEF2FF', color: '#4F46E5', text: '已委派' }, + approved: { bg: '#ECFDF5', color: '#1aae39', text: '同意' }, + rejected: { bg: '#FEF2F2', color: '#e5534b', text: '拒绝' }, + delegated: { bg: '#f2f9ff', color: '#0075de', text: '已委派' }, }; export default function CompletedTasks() { @@ -50,7 +50,7 @@ export default function CompletedTasks() { key: 'outcome', width: 100, render: (o: string) => { - const info = outcomeStyles[o] || { bg: '#F1F5F9', color: '#64748B', text: o }; + const info = outcomeStyles[o] || { bg: '#f6f5f4', color: '#615d59', text: o }; return ( ( - + {v ? new Date(v).toLocaleString() : '-'} ), @@ -80,7 +80,7 @@ export default function CompletedTasks() {
= { - running: { bg: '#EEF2FF', color: '#4F46E5', text: '运行中' }, - suspended: { bg: '#FFFBEB', color: '#D97706', text: '已挂起' }, - completed: { bg: '#ECFDF5', color: '#059669', text: '已完成' }, - terminated: { bg: '#FEF2F2', color: '#DC2626', text: '已终止' }, + running: { bg: '#f2f9ff', color: '#0075de', text: '运行中' }, + suspended: { bg: '#FFFBEB', color: '#dd5b00', text: '已挂起' }, + completed: { bg: '#ECFDF5', color: '#1aae39', text: '已完成' }, + terminated: { bg: '#FEF2F2', color: '#e5534b', text: '已终止' }, }; export default function InstanceMonitor() { @@ -129,7 +129,7 @@ export default function InstanceMonitor() { key: 'status', width: 100, render: (s: string) => { - const info = statusStyles[s] || { bg: '#F1F5F9', color: '#64748B', text: s }; + const info = statusStyles[s] || { bg: '#f6f5f4', color: '#615d59', text: s }; return ( ( - + {new Date(v).toLocaleString()} ), @@ -214,7 +214,7 @@ export default function InstanceMonitor() {
v ? ( @@ -93,9 +93,9 @@ export default function PendingTasks() { width: 100, render: (s: string) => ( {s} @@ -108,7 +108,7 @@ export default function PendingTasks() { key: 'created_at', width: 180, render: (v: string) => ( - + {new Date(v).toLocaleString()} ), @@ -145,7 +145,7 @@ export default function PendingTasks() {
= { - draft: { bg: '#F1F5F9', color: '#64748B', text: '草稿' }, - published: { bg: '#ECFDF5', color: '#059669', text: '已发布' }, - deprecated: { bg: '#FEF2F2', color: '#DC2626', text: '已弃用' }, + draft: { bg: '#f6f5f4', color: '#615d59', text: '草稿' }, + published: { bg: '#ecfdf5', color: '#1aae39', text: '已发布' }, + deprecated: { bg: '#fef2f2', color: '#e5534b', text: '已弃用' }, }; export default function ProcessDefinitions() { @@ -92,9 +92,9 @@ export default function ProcessDefinitions() { key: 'key', render: (v: string) => ( @@ -110,7 +110,7 @@ export default function ProcessDefinitions() { key: 'status', width: 100, render: (s: string) => { - const info = statusColors[s] || { bg: '#F1F5F9', color: '#64748B', text: s }; + const info = statusColors[s] || { bg: '#f6f5f4', color: '#615d59', text: s }; return ( - + 共 {total} 个流程定义