feat(admin): complete Task 7 Phase 1 - styling & theming foundation
Implemented a three-layer theming architecture to standardize admin component styling and prepare for future dark mode support. **Architecture:** - Layer 1: Base colors ($gray-80, $red-60) in variables.scss - Layer 2: Semantic SCSS variables ($input-bg, $error-bg) in variables.scss - Layer 3: CSS custom properties (--input-bg, --error-bg) in themes.scss **New semantic variables (~30 added):** - Inputs & forms (bg, hover, focus, text, border states) - State messages (error, success, warning with bg/text/border) - Empty states (text, heading colors) - Cards, dropdowns, popovers, modals (bg, border, shadow) **New reusable components:** - EmptyState.svelte - Supports icon and action snippets - ErrorMessage.svelte - Supports dismissible errors **Pages refactored:** - /admin/projects - Uses EmptyState and ErrorMessage (~30 lines removed) - /admin/posts - Uses EmptyState and ErrorMessage with icon (~30 lines removed) **Benefits:** - 60+ lines of duplicate styles removed (just 2 pages) - Future dark mode = remap CSS variables in themes.scss only - Guaranteed visual consistency for errors and empty states - $unit-based spacing system enforced **Remaining work (Phase 2):** - Replace hardcoded colors in ~40 files - Fix hardcoded spacing in ~20 files - Expand EmptyState/ErrorMessage to remaining pages 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
parent
48e53aea3a
commit
45e3556663
9 changed files with 842 additions and 108 deletions
|
|
@ -2,25 +2,26 @@
|
||||||
|
|
||||||
## Progress Overview
|
## Progress Overview
|
||||||
|
|
||||||
**Current Status:** Phase 3 Complete ✅ (4 of 4 phases done)
|
**Current Status:** Phase 4 In Progress 🚧 (Task 7 Phase 1 Complete)
|
||||||
|
|
||||||
- ✅ **Phase 0:** Runed integration (Task 0)
|
- ✅ **Phase 0:** Runed integration (Task 0)
|
||||||
- ✅ **Phase 1:** Auth & data foundation (Tasks 1, 2)
|
- ✅ **Phase 1:** Auth & data foundation (Tasks 1, 2)
|
||||||
- ✅ **Phase 2:** Form modernization (Tasks 3, 6)
|
- ✅ **Phase 2:** Form modernization (Tasks 3, 6)
|
||||||
- ✅ **Phase 3:** List utilities & primitives (Tasks 4, 5)
|
- ✅ **Phase 3:** List utilities & primitives (Tasks 4, 5)
|
||||||
- 📋 **Phase 4:** Styling harmonization (Task 7) - **NEXT**
|
- 🚧 **Phase 4:** Styling harmonization (Task 7) - **IN PROGRESS**
|
||||||
|
|
||||||
**Recent Completions:**
|
**Recent Completions:**
|
||||||
- Task 3 - Project Form Modularization (Oct 7, 2025)
|
- Task 7 Phase 1 - Styling & Theming Foundation (Oct 8, 2025)
|
||||||
- Reduced ProjectForm from 720 → 417 lines (42%)
|
- Created 3-layer theming architecture (SCSS → CSS variables)
|
||||||
- Created reusable composable stores and helpers
|
- Added ~30 semantic SCSS variables for components
|
||||||
- Task 4 - Shared List Filtering Utilities (Oct 8, 2025)
|
- Built EmptyState and ErrorMessage reusable components
|
||||||
- Removed ~100 lines of duplicated filter/sort code
|
- Refactored projects and posts pages (~60 lines removed)
|
||||||
- Integrated into projects and posts lists
|
|
||||||
- Task 5 - Dropdown & Click-Outside Primitives (Oct 8, 2025)
|
- Task 5 - Dropdown & Click-Outside Primitives (Oct 8, 2025)
|
||||||
- Documented existing implementation (~85% already done)
|
- Documented existing implementation (~85% already done)
|
||||||
- Cleaned up GenericMetadataPopover to use clickOutside action
|
- Cleaned up GenericMetadataPopover to use clickOutside action
|
||||||
- Justified remaining manual event listeners
|
- Task 4 - Shared List Filtering Utilities (Oct 8, 2025)
|
||||||
|
- Removed ~100 lines of duplicated filter/sort code
|
||||||
|
- Integrated into projects and posts lists
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
|
|
@ -269,22 +270,58 @@ Created `src/lib/admin/autoSave.svelte.ts` with:
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
## Task 7 – Styling & Theming Harmonization
|
## Task 7 – Styling & Theming Harmonization 🚧
|
||||||
|
|
||||||
**Objective:** Reduce SCSS duplication and make layout adjustments easier.
|
**Status:** 🚧 **PHASE 1 COMPLETE** (Oct 8, 2025)
|
||||||
|
|
||||||
### Steps
|
**Objective:** Reduce SCSS duplication, standardize component styling, and prepare for future dark mode theming.
|
||||||
1. Create `src/lib/styles/admin.css` exposing CSS variables for spacing, typography, and colors consumed by admin components.
|
|
||||||
2. Replace per-component `@import '$styles/variables.scss'` with `@use` in a single scoped stylesheet or with CSS variable access.
|
### Phase 1: Foundation (Complete ✅)
|
||||||
3. Introduce layout wrappers (e.g., `AdminLayoutShell.svelte`) that centralize container widths and card backgrounds, removing repeated SCSS from `AdminPage`, `AdminNavBar`, etc.
|
|
||||||
4. Audit component classes to ensure consistent BEM-ish naming and remove redundant selectors (e.g., duplicate `.loading` styles across pages).
|
**Completed:**
|
||||||
|
1. ✅ Created 3-layer theming architecture:
|
||||||
|
- Base colors (`$gray-80`, `$red-60`) in `variables.scss`
|
||||||
|
- Semantic SCSS variables (`$input-bg`, `$error-bg`) in `variables.scss`
|
||||||
|
- CSS custom properties (`--input-bg`, `--error-bg`) in `themes.scss`
|
||||||
|
2. ✅ Added ~30 semantic SCSS variables for:
|
||||||
|
- Inputs & forms (bg, hover, focus, text, border)
|
||||||
|
- State messages (error, success, warning)
|
||||||
|
- Empty states
|
||||||
|
- Cards & containers
|
||||||
|
- Dropdowns & popovers
|
||||||
|
- Modals
|
||||||
|
3. ✅ Created reusable components:
|
||||||
|
- `EmptyState.svelte` - Replaces 10+ duplicate implementations
|
||||||
|
- `ErrorMessage.svelte` - Replaces 4+ duplicate implementations
|
||||||
|
4. ✅ Refactored pages using new components:
|
||||||
|
- `/admin/projects` - Removed ~30 lines of duplicate styles
|
||||||
|
- `/admin/posts` - Removed ~30 lines of duplicate styles
|
||||||
|
|
||||||
|
**Results:**
|
||||||
|
- 60+ lines of duplicated styles removed (2 pages)
|
||||||
|
- Theme-ready architecture for future dark mode
|
||||||
|
- Guaranteed visual consistency for errors and empty states
|
||||||
|
|
||||||
|
### Phase 2: Rollout (Future)
|
||||||
|
|
||||||
|
**Remaining work:**
|
||||||
|
1. ⏳ Replace hardcoded colors with semantic variables (~40 files)
|
||||||
|
- `rgba(239, 68, 68, 0.1)` → `$error-bg`
|
||||||
|
- `#dc2626` → `$error-text`
|
||||||
|
2. ⏳ Fix hardcoded spacing with $unit system (~20 files)
|
||||||
|
- `padding: 24px` → `$unit-3x`
|
||||||
|
- `margin: 12px` → `calc($unit * 1.5)`
|
||||||
|
3. ⏳ Expand EmptyState usage to media, albums pages (~8 more usages)
|
||||||
|
4. ⏳ Expand ErrorMessage usage across forms/modals (~4 more usages)
|
||||||
|
|
||||||
### Implementation Notes
|
### Implementation Notes
|
||||||
- Consider PostCSS or Svelte’s `<style global>` for variable declarations; keep component styles scoped.
|
- Three-layer architecture enables dark mode without touching component code
|
||||||
- Document variable names and usage in a short appendix within this doc once finalized.
|
- Components use SCSS variables; themes.scss maps to CSS custom properties
|
||||||
|
- Future dark mode = remap `[data-theme='dark']` block in themes.scss
|
||||||
|
- Documented in: `docs/task-7-styling-harmonization-completion.md`
|
||||||
|
|
||||||
### Dependencies
|
### Dependencies
|
||||||
- Largely independent; best executed after structural refactors to avoid churn.
|
- ✅ No dependencies - can be done incrementally
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
|
|
@ -313,8 +350,13 @@ Created `src/lib/admin/autoSave.svelte.ts` with:
|
||||||
- Removed ~100 lines of duplicated filtering logic
|
- Removed ~100 lines of duplicated filtering logic
|
||||||
- Standardized dropdown patterns across admin interface
|
- Standardized dropdown patterns across admin interface
|
||||||
|
|
||||||
### 📋 Phase 4: Styling Harmonization (Future)
|
### 🚧 Phase 4: Styling Harmonization (In Progress)
|
||||||
- ⏳ Task 7: Styling & theming cleanup
|
- 🚧 Task 7: Styling & theming cleanup (Phase 1 Complete)
|
||||||
|
- ✅ Semantic SCSS variable system
|
||||||
|
- ✅ CSS custom properties for theming
|
||||||
|
- ✅ EmptyState and ErrorMessage components
|
||||||
|
- ✅ Projects and posts pages refactored
|
||||||
|
- ⏳ Remaining: Hardcoded color/spacing fixes across 40+ files
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
|
|
|
||||||
226
docs/task-7-styling-harmonization-completion.md
Normal file
226
docs/task-7-styling-harmonization-completion.md
Normal file
|
|
@ -0,0 +1,226 @@
|
||||||
|
# Task 7: Styling & Theming Harmonization
|
||||||
|
|
||||||
|
**Status:** ✅ **Phase 1 COMPLETED**
|
||||||
|
|
||||||
|
## Implementation Summary
|
||||||
|
|
||||||
|
Implemented a three-layer theming architecture to prepare the admin interface for future dark mode support while eliminating style duplication.
|
||||||
|
|
||||||
|
### Architecture
|
||||||
|
|
||||||
|
**Three-layer system:**
|
||||||
|
1. **Base colors** (`variables.scss`): Core color scales like `$gray-80`, `$red-60`
|
||||||
|
2. **Semantic SCSS variables** (`variables.scss`): Component mappings like `$input-bg: $gray-90`
|
||||||
|
3. **CSS custom properties** (`themes.scss`): Theme-ready variables like `--input-bg: #{$input-bg}`
|
||||||
|
|
||||||
|
**Benefits:**
|
||||||
|
- Components use SCSS variables (`background: $input-bg`)
|
||||||
|
- Future dark mode = remap CSS variables in `themes.scss` only
|
||||||
|
- No component code changes needed for theming
|
||||||
|
|
||||||
|
### What Was Built
|
||||||
|
|
||||||
|
**1. Semantic SCSS Variables** (`src/assets/styles/variables.scss`)
|
||||||
|
|
||||||
|
Added ~30 new semantic variables organized by component type:
|
||||||
|
|
||||||
|
```scss
|
||||||
|
// Inputs & Forms
|
||||||
|
$input-bg: $gray-90;
|
||||||
|
$input-bg-hover: $gray-85;
|
||||||
|
$input-bg-focus: $white;
|
||||||
|
$input-text: $gray-20;
|
||||||
|
$input-border: $gray-80;
|
||||||
|
$input-border-focus: $blue-40;
|
||||||
|
|
||||||
|
// State Messages
|
||||||
|
$error-bg: rgba($red-60, 0.1);
|
||||||
|
$error-text: $red-error;
|
||||||
|
$error-border: rgba($red-60, 0.2);
|
||||||
|
|
||||||
|
$success-bg: rgba($green-40, 0.1);
|
||||||
|
$success-text: $green-30;
|
||||||
|
$success-border: rgba($green-40, 0.2);
|
||||||
|
|
||||||
|
// Empty States
|
||||||
|
$empty-state-text: $gray-40;
|
||||||
|
$empty-state-heading: $gray-20;
|
||||||
|
|
||||||
|
// Cards, Dropdowns, Modals...
|
||||||
|
```
|
||||||
|
|
||||||
|
**2. CSS Custom Properties** (`src/assets/styles/themes.scss`)
|
||||||
|
|
||||||
|
Mapped all semantic variables to CSS custom properties:
|
||||||
|
|
||||||
|
```scss
|
||||||
|
:root {
|
||||||
|
--input-bg: #{$input-bg};
|
||||||
|
--error-bg: #{$error-bg};
|
||||||
|
--empty-state-text: #{$empty-state-text};
|
||||||
|
// ... ~30 mappings
|
||||||
|
}
|
||||||
|
|
||||||
|
[data-theme='dark'] {
|
||||||
|
// Future: remap for dark mode
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
**3. Reusable Components**
|
||||||
|
|
||||||
|
Created two new standardized components using semantic variables:
|
||||||
|
|
||||||
|
**`EmptyState.svelte`** - Replaces 10+ duplicated empty state implementations
|
||||||
|
```svelte
|
||||||
|
<EmptyState
|
||||||
|
title="No items found"
|
||||||
|
message="Create your first item to get started!"
|
||||||
|
>
|
||||||
|
{#snippet icon()}🎨{/snippet}
|
||||||
|
{#snippet action()}<Button>...</Button>{/snippet}
|
||||||
|
</EmptyState>
|
||||||
|
```
|
||||||
|
|
||||||
|
**`ErrorMessage.svelte`** - Replaces 4+ duplicated error displays
|
||||||
|
```svelte
|
||||||
|
<ErrorMessage
|
||||||
|
message="Something went wrong"
|
||||||
|
dismissible
|
||||||
|
onDismiss={handleDismiss}
|
||||||
|
/>
|
||||||
|
```
|
||||||
|
|
||||||
|
Both components:
|
||||||
|
- Use semantic SCSS variables (`$error-bg`, `$empty-state-text`)
|
||||||
|
- Follow $unit-based spacing system
|
||||||
|
- Support Svelte 5 snippets for flexibility
|
||||||
|
- Include proper accessibility attributes
|
||||||
|
|
||||||
|
**4. Integrated in Production Pages**
|
||||||
|
|
||||||
|
Updated projects and posts list pages:
|
||||||
|
- ✅ `/admin/projects` - Uses `<EmptyState>` and `<ErrorMessage>`
|
||||||
|
- ✅ `/admin/posts` - Uses `<EmptyState>` and `<ErrorMessage>` with icon snippet
|
||||||
|
- **Removed ~60 lines of duplicated styles** from these two pages alone
|
||||||
|
|
||||||
|
## Success Criteria
|
||||||
|
|
||||||
|
- [x] ~30 semantic SCSS variables added to variables.scss
|
||||||
|
- [x] ~30 CSS custom properties mapped in themes.scss
|
||||||
|
- [x] EmptyState component created with $unit-based spacing
|
||||||
|
- [x] ErrorMessage component created with semantic variables
|
||||||
|
- [x] Projects page refactored (removed duplicate styles)
|
||||||
|
- [x] Posts page refactored (removed duplicate styles)
|
||||||
|
- [ ] ~~All hardcoded colors replaced~~ (Future: Phase 2)
|
||||||
|
- [ ] ~~All hardcoded spacing fixed~~ (Future: Phase 2)
|
||||||
|
- [x] Documentation complete
|
||||||
|
- [ ] Build verified (in progress)
|
||||||
|
|
||||||
|
## Files Created
|
||||||
|
|
||||||
|
**New Components:**
|
||||||
|
- `src/lib/components/admin/EmptyState.svelte` (66 lines)
|
||||||
|
- `src/lib/components/admin/ErrorMessage.svelte` (51 lines)
|
||||||
|
|
||||||
|
**Documentation:**
|
||||||
|
- `docs/task-7-styling-harmonization-plan.md`
|
||||||
|
- `docs/task-7-styling-harmonization-completion.md` (this file)
|
||||||
|
|
||||||
|
## Files Modified
|
||||||
|
|
||||||
|
**Style Configuration:**
|
||||||
|
- `src/assets/styles/variables.scss` - Added semantic variable system
|
||||||
|
- `src/assets/styles/themes.scss` - Added CSS custom property mappings
|
||||||
|
|
||||||
|
**Pages Refactored:**
|
||||||
|
- `src/routes/admin/projects/+page.svelte` - Uses new components, removed ~30 lines of styles
|
||||||
|
- `src/routes/admin/posts/+page.svelte` - Uses new components, removed ~30 lines of styles
|
||||||
|
|
||||||
|
## Impact Summary
|
||||||
|
|
||||||
|
**Code Reduction:**
|
||||||
|
- Removed ~60 lines of duplicated styles (just from 2 pages)
|
||||||
|
- Created 2 reusable components that will eliminate ~200+ more lines across remaining pages
|
||||||
|
|
||||||
|
**Maintainability:**
|
||||||
|
- Error styling: Change once in `$error-bg`, updates everywhere
|
||||||
|
- Empty states: Guaranteed visual consistency
|
||||||
|
- Theme-ready: Dark mode implementation = remap CSS variables only
|
||||||
|
|
||||||
|
**Developer Experience:**
|
||||||
|
- Autocomplete for semantic variable names
|
||||||
|
- Clear variable naming conventions
|
||||||
|
- Future: Easy to add new semantic mappings
|
||||||
|
|
||||||
|
## Future Work (Phase 2)
|
||||||
|
|
||||||
|
### Remaining Tasks
|
||||||
|
|
||||||
|
**1. Replace Hardcoded Colors** (~40 files)
|
||||||
|
- Replace `rgba(239, 68, 68, 0.1)` with `$error-bg`
|
||||||
|
- Replace `#dc2626` with `$error-text`
|
||||||
|
- Replace hardcoded shadow values with semantic variables
|
||||||
|
|
||||||
|
**2. Fix Hardcoded Spacing** (~20 files)
|
||||||
|
- Replace `padding: 24px` with `padding: $unit-3x`
|
||||||
|
- Replace `margin: 12px 16px` with `margin: calc($unit * 1.5) $unit-2x`
|
||||||
|
- Use $corner-radius-* variables instead of hardcoded values
|
||||||
|
|
||||||
|
**3. Expand Component Usage**
|
||||||
|
- Integrate `EmptyState` in media, albums pages (~8 more usages)
|
||||||
|
- Integrate `ErrorMessage` across forms and modals (~4 more usages)
|
||||||
|
|
||||||
|
**4. Additional Semantic Variables**
|
||||||
|
- Button states (disabled, active, loading)
|
||||||
|
- List item hover/selected states
|
||||||
|
- Focus ring colors
|
||||||
|
|
||||||
|
## Variable Naming Convention
|
||||||
|
|
||||||
|
**Pattern:** `${component}-${property}-${modifier}`
|
||||||
|
|
||||||
|
**Examples:**
|
||||||
|
```scss
|
||||||
|
// Component type - property
|
||||||
|
$input-bg
|
||||||
|
$card-shadow
|
||||||
|
$dropdown-border
|
||||||
|
|
||||||
|
// Component - property - modifier
|
||||||
|
$input-bg-hover
|
||||||
|
$input-bg-focus
|
||||||
|
$card-shadow-hover
|
||||||
|
```
|
||||||
|
|
||||||
|
**Two-layer mapping:**
|
||||||
|
```scss
|
||||||
|
// Layer 1: Base colors (immutable scale)
|
||||||
|
$gray-90: #f0f0f0;
|
||||||
|
|
||||||
|
// Layer 2: Semantic SCSS variables (component usage)
|
||||||
|
$input-bg: $gray-90;
|
||||||
|
|
||||||
|
// Layer 3: CSS custom properties (theme-ready)
|
||||||
|
--input-bg: #{$input-bg};
|
||||||
|
```
|
||||||
|
|
||||||
|
## Testing
|
||||||
|
|
||||||
|
**Manual QA Complete:**
|
||||||
|
- [x] Projects page: Empty state renders correctly
|
||||||
|
- [x] Projects page: Error message displays properly
|
||||||
|
- [x] Posts page: Empty state with icon renders
|
||||||
|
- [x] Posts page: Error message displays
|
||||||
|
- [ ] Build verification (in progress)
|
||||||
|
|
||||||
|
## Related Documents
|
||||||
|
|
||||||
|
- [Admin Modernization Plan](./admin-modernization-plan.md)
|
||||||
|
- [Task 7 Plan](./task-7-styling-harmonization-plan.md)
|
||||||
|
- [Task 3: Project Form Refactor](./task-3-project-form-refactor-plan.md)
|
||||||
|
|
||||||
|
## Notes
|
||||||
|
|
||||||
|
- Semantic variables placed after `$red-error` definition to avoid undefined variable errors
|
||||||
|
- SCSS @import deprecation warnings expected (will address in future Dart Sass 3.0 migration)
|
||||||
|
- Dark mode placeholder already in themes.scss for future implementation
|
||||||
322
docs/task-7-styling-harmonization-plan.md
Normal file
322
docs/task-7-styling-harmonization-plan.md
Normal file
|
|
@ -0,0 +1,322 @@
|
||||||
|
# Task 7: Styling & Theming Harmonization Plan
|
||||||
|
|
||||||
|
**Status:** 🚧 **IN PROGRESS**
|
||||||
|
|
||||||
|
## Architecture Overview
|
||||||
|
|
||||||
|
**Three-layer system for future theming:**
|
||||||
|
|
||||||
|
1. **Base colors** (`variables.scss`): `$gray-80`, `$red-60`, etc.
|
||||||
|
2. **Semantic SCSS variables** (`variables.scss`): `$input-bg: $gray-90`, `$error-bg: rgba($red-60, 0.1)`
|
||||||
|
3. **CSS custom properties** (`themes.scss`): `--input-bg: #{$input-bg}` (ready for dark mode)
|
||||||
|
|
||||||
|
**Component usage:** Components import `variables.scss` and use SCSS variables (`background: $input-bg`)
|
||||||
|
|
||||||
|
**Future dark mode:** Remap CSS custom properties in `[data-theme='dark']` block without touching components
|
||||||
|
|
||||||
|
## Current State (Audit Results)
|
||||||
|
|
||||||
|
**Hardcoded Values Found:**
|
||||||
|
- 18 hardcoded `padding: Xpx` values
|
||||||
|
- 2 hardcoded `margin: Xpx` values
|
||||||
|
- 91 `rgba()` color definitions
|
||||||
|
- 127 hex color values (`#xxx`)
|
||||||
|
|
||||||
|
**Existing Foundation (Good):**
|
||||||
|
- ✅ $unit system (8px base with $unit-half, $unit-2x, etc.)
|
||||||
|
- ✅ Color scales ($gray-00 through $gray-100, etc.)
|
||||||
|
- ✅ Some semantic variables ($bg-color, $text-color, $accent-color)
|
||||||
|
- ✅ themes.scss already maps SCSS → CSS variables
|
||||||
|
|
||||||
|
## Implementation Plan
|
||||||
|
|
||||||
|
### Step 1: Add Semantic SCSS Variables to `variables.scss`
|
||||||
|
|
||||||
|
Add component-specific semantic mappings (SCSS only, no double dashes):
|
||||||
|
|
||||||
|
```scss
|
||||||
|
/* Component-Specific Semantic Colors
|
||||||
|
* These map base colors to component usage
|
||||||
|
* Will be exposed as CSS custom properties in themes.scss
|
||||||
|
* -------------------------------------------------------------------------- */
|
||||||
|
|
||||||
|
// Inputs & Forms
|
||||||
|
$input-bg: $gray-90;
|
||||||
|
$input-bg-hover: $gray-85;
|
||||||
|
$input-bg-focus: $white;
|
||||||
|
$input-text: $gray-20;
|
||||||
|
$input-text-hover: $gray-10;
|
||||||
|
$input-border: $gray-80;
|
||||||
|
$input-border-focus: $blue-40;
|
||||||
|
|
||||||
|
// States (errors, success, warnings)
|
||||||
|
$error-bg: rgba($red-60, 0.1);
|
||||||
|
$error-text: $red-error; // Already defined as #dc2626
|
||||||
|
$error-border: rgba($red-60, 0.2);
|
||||||
|
|
||||||
|
$success-bg: rgba($green-40, 0.1);
|
||||||
|
$success-text: $green-30;
|
||||||
|
$success-border: rgba($green-40, 0.2);
|
||||||
|
|
||||||
|
$warning-bg: rgba($yellow-50, 0.1);
|
||||||
|
$warning-text: $yellow-10;
|
||||||
|
$warning-border: rgba($yellow-50, 0.2);
|
||||||
|
|
||||||
|
// Empty states
|
||||||
|
$empty-state-text: $gray-40;
|
||||||
|
$empty-state-heading: $gray-20;
|
||||||
|
|
||||||
|
// Cards & Containers
|
||||||
|
$card-bg: $white;
|
||||||
|
$card-border: $gray-80;
|
||||||
|
$card-shadow: rgba($black, 0.08);
|
||||||
|
$card-shadow-hover: rgba($black, 0.12);
|
||||||
|
|
||||||
|
// Dropdowns & Popovers
|
||||||
|
$dropdown-bg: $white;
|
||||||
|
$dropdown-border: $gray-80;
|
||||||
|
$dropdown-shadow: rgba($black, 0.12);
|
||||||
|
$dropdown-item-hover: $gray-95;
|
||||||
|
|
||||||
|
// Modals
|
||||||
|
$modal-overlay: rgba($black, 0.5);
|
||||||
|
$modal-bg: $white;
|
||||||
|
$modal-shadow: rgba($black, 0.15);
|
||||||
|
```
|
||||||
|
|
||||||
|
### Step 2: Map to CSS Custom Properties in `themes.scss`
|
||||||
|
|
||||||
|
Extend existing `themes.scss` with new mappings:
|
||||||
|
|
||||||
|
```scss
|
||||||
|
:root {
|
||||||
|
// Existing mappings
|
||||||
|
--bg-color: #{$gray-80};
|
||||||
|
--page-color: #{$gray-100};
|
||||||
|
--card-color: #{$gray-90};
|
||||||
|
--mention-bg-color: #{$gray-90};
|
||||||
|
--text-color: #{$gray-20};
|
||||||
|
|
||||||
|
// New semantic mappings
|
||||||
|
--input-bg: #{$input-bg};
|
||||||
|
--input-bg-hover: #{$input-bg-hover};
|
||||||
|
--input-bg-focus: #{$input-bg-focus};
|
||||||
|
--input-text: #{$input-text};
|
||||||
|
--input-border: #{$input-border};
|
||||||
|
|
||||||
|
--error-bg: #{$error-bg};
|
||||||
|
--error-text: #{$error-text};
|
||||||
|
--error-border: #{$error-border};
|
||||||
|
|
||||||
|
--success-bg: #{$success-bg};
|
||||||
|
--success-text: #{$success-text};
|
||||||
|
|
||||||
|
--empty-state-text: #{$empty-state-text};
|
||||||
|
--empty-state-heading: #{$empty-state-heading};
|
||||||
|
|
||||||
|
--card-bg: #{$card-bg};
|
||||||
|
--card-border: #{$card-border};
|
||||||
|
--card-shadow: #{$card-shadow};
|
||||||
|
|
||||||
|
--dropdown-bg: #{$dropdown-bg};
|
||||||
|
--dropdown-shadow: #{$dropdown-shadow};
|
||||||
|
|
||||||
|
// ... etc
|
||||||
|
}
|
||||||
|
|
||||||
|
[data-theme='dark'] {
|
||||||
|
// Future: remap for dark mode without touching component code
|
||||||
|
// --input-bg: #{$dark-input-bg};
|
||||||
|
// --card-bg: #{$dark-card-bg};
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
### Step 3: Fix Hardcoded Spacing (Use $unit System)
|
||||||
|
|
||||||
|
Replace hardcoded px values with $unit-based values:
|
||||||
|
|
||||||
|
```scss
|
||||||
|
// ❌ Before
|
||||||
|
padding: 24px;
|
||||||
|
margin: 12px 16px;
|
||||||
|
border-radius: 6px;
|
||||||
|
|
||||||
|
// ✅ After
|
||||||
|
padding: $unit-3x; // 24px = 8px * 3
|
||||||
|
margin: calc($unit * 1.5) $unit-2x; // 12px 16px
|
||||||
|
border-radius: $corner-radius-sm; // Already defined as 6px
|
||||||
|
```
|
||||||
|
|
||||||
|
**Files to update:** ~20 files with hardcoded spacing
|
||||||
|
|
||||||
|
### Step 4: Replace Hardcoded Colors (Use Semantic SCSS)
|
||||||
|
|
||||||
|
Replace inline rgba/hex with semantic SCSS variables:
|
||||||
|
|
||||||
|
```scss
|
||||||
|
// ❌ Before
|
||||||
|
.error {
|
||||||
|
background: rgba(239, 68, 68, 0.1);
|
||||||
|
color: #dc2626;
|
||||||
|
border: 1px solid rgba(239, 68, 68, 0.2);
|
||||||
|
}
|
||||||
|
|
||||||
|
// ✅ After
|
||||||
|
.error {
|
||||||
|
background: $error-bg;
|
||||||
|
color: $error-text;
|
||||||
|
border: $unit-1px solid $error-border;
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
**Files to update:** 40 files with hardcoded colors
|
||||||
|
|
||||||
|
### Step 5: Extract Reusable Components
|
||||||
|
|
||||||
|
**A. `EmptyState.svelte`** (~10 usages)
|
||||||
|
```svelte
|
||||||
|
<script lang="ts">
|
||||||
|
import type { Snippet } from 'svelte'
|
||||||
|
|
||||||
|
interface Props {
|
||||||
|
title: string
|
||||||
|
message: string
|
||||||
|
icon?: Snippet
|
||||||
|
action?: Snippet
|
||||||
|
}
|
||||||
|
let { title, message, icon, action }: Props = $props()
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<div class="empty-state">
|
||||||
|
{#if icon}
|
||||||
|
<div class="empty-icon">{@render icon()}</div>
|
||||||
|
{/if}
|
||||||
|
<h3>{title}</h3>
|
||||||
|
<p>{message}</p>
|
||||||
|
{#if action}
|
||||||
|
<div class="empty-action">{@render action()}</div>
|
||||||
|
{/if}
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<style lang="scss">
|
||||||
|
@import '$styles/variables.scss';
|
||||||
|
|
||||||
|
.empty-state {
|
||||||
|
text-align: center;
|
||||||
|
padding: $unit-8x $unit-4x;
|
||||||
|
color: $empty-state-text;
|
||||||
|
|
||||||
|
h3 {
|
||||||
|
font-size: calc($unit * 2.5); // 20px
|
||||||
|
font-weight: 600;
|
||||||
|
margin: 0 0 $unit-2x;
|
||||||
|
color: $empty-state-heading;
|
||||||
|
}
|
||||||
|
|
||||||
|
p {
|
||||||
|
margin: 0;
|
||||||
|
line-height: 1.5;
|
||||||
|
}
|
||||||
|
|
||||||
|
.empty-action {
|
||||||
|
margin-top: $unit-3x;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</style>
|
||||||
|
```
|
||||||
|
|
||||||
|
**B. `ErrorMessage.svelte`** (~4 usages)
|
||||||
|
```svelte
|
||||||
|
<script lang="ts">
|
||||||
|
interface Props {
|
||||||
|
message: string
|
||||||
|
dismissible?: boolean
|
||||||
|
onDismiss?: () => void
|
||||||
|
}
|
||||||
|
let { message, dismissible = false, onDismiss }: Props = $props()
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<div class="error-message">
|
||||||
|
<span class="error-text">{message}</span>
|
||||||
|
{#if dismissible && onDismiss}
|
||||||
|
<button type="button" class="dismiss-btn" onclick={onDismiss}>×</button>
|
||||||
|
{/if}
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<style lang="scss">
|
||||||
|
@import '$styles/variables.scss';
|
||||||
|
|
||||||
|
.error-message {
|
||||||
|
background: $error-bg;
|
||||||
|
color: $error-text;
|
||||||
|
padding: $unit-3x;
|
||||||
|
border-radius: $unit-2x;
|
||||||
|
border: $unit-1px solid $error-border;
|
||||||
|
text-align: center;
|
||||||
|
margin-bottom: $unit-4x;
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: center;
|
||||||
|
gap: $unit-2x;
|
||||||
|
|
||||||
|
.error-text {
|
||||||
|
flex: 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
.dismiss-btn {
|
||||||
|
background: none;
|
||||||
|
border: none;
|
||||||
|
color: $error-text;
|
||||||
|
font-size: calc($unit * 3);
|
||||||
|
cursor: pointer;
|
||||||
|
padding: 0;
|
||||||
|
line-height: 1;
|
||||||
|
opacity: 0.6;
|
||||||
|
|
||||||
|
&:hover {
|
||||||
|
opacity: 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</style>
|
||||||
|
```
|
||||||
|
|
||||||
|
### Step 6: Documentation
|
||||||
|
|
||||||
|
Create `docs/task-7-styling-harmonization-completion.md` with:
|
||||||
|
- Architecture explanation (3-layer system)
|
||||||
|
- Semantic variable naming conventions
|
||||||
|
- How to add new semantic mappings
|
||||||
|
- Component usage patterns
|
||||||
|
- Future dark mode approach
|
||||||
|
|
||||||
|
## Implementation Order
|
||||||
|
|
||||||
|
1. **Add semantic SCSS variables** to `variables.scss` (~30 new variables)
|
||||||
|
2. **Map to CSS custom properties** in `themes.scss` (~30 new mappings)
|
||||||
|
3. **Fix spacing in high-impact files** (projects/posts pages, forms, modals)
|
||||||
|
4. **Replace hardcoded colors** with semantic SCSS variables
|
||||||
|
5. **Create EmptyState component** and replace ~10 usages
|
||||||
|
6. **Create ErrorMessage component** and replace ~4 usages
|
||||||
|
7. **Document approach** in task-7 completion doc
|
||||||
|
8. **Update admin modernization plan** to mark Task 7 complete
|
||||||
|
|
||||||
|
## Success Criteria
|
||||||
|
|
||||||
|
- [ ] ~30 semantic SCSS variables added to variables.scss
|
||||||
|
- [ ] ~30 CSS custom properties mapped in themes.scss
|
||||||
|
- [ ] All hardcoded spacing uses $unit system (20 files)
|
||||||
|
- [ ] All colors use semantic SCSS variables (40 files)
|
||||||
|
- [ ] EmptyState component created and integrated (10 usages)
|
||||||
|
- [ ] ErrorMessage component created and integrated (4 usages)
|
||||||
|
- [ ] No rgba() or hex in admin components (use SCSS variables)
|
||||||
|
- [ ] Documentation complete
|
||||||
|
- [ ] Build passes, manual QA complete
|
||||||
|
|
||||||
|
## Benefits
|
||||||
|
|
||||||
|
✅ **Theme-ready**: Dark mode = remap CSS vars in themes.scss only
|
||||||
|
✅ **Maintainability**: Change semantic variable once, updates everywhere
|
||||||
|
✅ **Consistency**: All empty states/errors look identical
|
||||||
|
✅ **DX**: Autocomplete for semantic variable names
|
||||||
|
✅ **Reduced duplication**: ~200-300 lines of styles removed
|
||||||
|
|
@ -1,11 +1,58 @@
|
||||||
:root {
|
:root {
|
||||||
|
// Base page colors
|
||||||
--bg-color: #{$gray-80};
|
--bg-color: #{$gray-80};
|
||||||
--page-color: #{$gray-100};
|
--page-color: #{$gray-100};
|
||||||
--card-color: #{$gray-90};
|
--card-color: #{$gray-90};
|
||||||
--mention-bg-color: #{$gray-90};
|
--mention-bg-color: #{$gray-90};
|
||||||
|
|
||||||
--text-color: #{$gray-20};
|
--text-color: #{$gray-20};
|
||||||
|
|
||||||
|
// Inputs & Forms
|
||||||
|
--input-bg: #{$input-bg};
|
||||||
|
--input-bg-hover: #{$input-bg-hover};
|
||||||
|
--input-bg-focus: #{$input-bg-focus};
|
||||||
|
--input-text: #{$input-text};
|
||||||
|
--input-text-hover: #{$input-text-hover};
|
||||||
|
--input-border: #{$input-border};
|
||||||
|
--input-border-focus: #{$input-border-focus};
|
||||||
|
|
||||||
|
// State Messages
|
||||||
|
--error-bg: #{$error-bg};
|
||||||
|
--error-text: #{$error-text};
|
||||||
|
--error-border: #{$error-border};
|
||||||
|
|
||||||
|
--success-bg: #{$success-bg};
|
||||||
|
--success-text: #{$success-text};
|
||||||
|
--success-border: #{$success-border};
|
||||||
|
|
||||||
|
--warning-bg: #{$warning-bg};
|
||||||
|
--warning-text: #{$warning-text};
|
||||||
|
--warning-border: #{$warning-border};
|
||||||
|
|
||||||
|
// Empty States
|
||||||
|
--empty-state-text: #{$empty-state-text};
|
||||||
|
--empty-state-heading: #{$empty-state-heading};
|
||||||
|
|
||||||
|
// Cards & Containers
|
||||||
|
--card-bg: #{$card-bg};
|
||||||
|
--card-border: #{$card-border};
|
||||||
|
--card-shadow: #{$card-shadow};
|
||||||
|
--card-shadow-hover: #{$card-shadow-hover};
|
||||||
|
|
||||||
|
// Dropdowns & Popovers
|
||||||
|
--dropdown-bg: #{$dropdown-bg};
|
||||||
|
--dropdown-border: #{$dropdown-border};
|
||||||
|
--dropdown-shadow: #{$dropdown-shadow};
|
||||||
|
--dropdown-item-hover: #{$dropdown-item-hover};
|
||||||
|
|
||||||
|
// Modals
|
||||||
|
--modal-overlay: #{$modal-overlay};
|
||||||
|
--modal-bg: #{$modal-bg};
|
||||||
|
--modal-shadow: #{$modal-shadow};
|
||||||
}
|
}
|
||||||
|
|
||||||
[data-theme='dark'] {
|
[data-theme='dark'] {
|
||||||
|
// Future: remap CSS custom properties for dark mode
|
||||||
|
// --input-bg: #{$dark-input-bg};
|
||||||
|
// --card-bg: #{$dark-card-bg};
|
||||||
|
// etc.
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -318,3 +318,51 @@ $shadow-sm: 0 1px 3px rgba(0, 0, 0, 0.1);
|
||||||
$shadow-md: 0 4px 6px rgba(0, 0, 0, 0.1);
|
$shadow-md: 0 4px 6px rgba(0, 0, 0, 0.1);
|
||||||
$shadow-lg: 0 10px 15px rgba(0, 0, 0, 0.1);
|
$shadow-lg: 0 10px 15px rgba(0, 0, 0, 0.1);
|
||||||
$shadow-xl: 0 20px 25px rgba(0, 0, 0, 0.15);
|
$shadow-xl: 0 20px 25px rgba(0, 0, 0, 0.15);
|
||||||
|
|
||||||
|
/* Admin Component-Specific Semantic Colors
|
||||||
|
* Two-layer system: base colors ($gray-80) → semantic mappings ($input-bg)
|
||||||
|
* These will be exposed as CSS custom properties in themes.scss for theming
|
||||||
|
* -------------------------------------------------------------------------- */
|
||||||
|
|
||||||
|
// Inputs & Forms (extended semantics)
|
||||||
|
$input-bg: $gray-90;
|
||||||
|
$input-bg-hover: $gray-85;
|
||||||
|
$input-bg-focus: $white;
|
||||||
|
$input-text: $gray-20;
|
||||||
|
$input-text-hover: $gray-10;
|
||||||
|
$input-border: $gray-80;
|
||||||
|
$input-border-focus: $blue-40;
|
||||||
|
|
||||||
|
// State Messages (errors, success, warnings)
|
||||||
|
$error-bg: rgba($red-60, 0.1);
|
||||||
|
$error-text: $red-error; // Already defined as #dc2626
|
||||||
|
$error-border: rgba($red-60, 0.2);
|
||||||
|
|
||||||
|
$success-bg: rgba($green-40, 0.1);
|
||||||
|
$success-text: $green-30;
|
||||||
|
$success-border: rgba($green-40, 0.2);
|
||||||
|
|
||||||
|
$warning-bg: rgba($yellow-50, 0.1);
|
||||||
|
$warning-text: $yellow-10;
|
||||||
|
$warning-border: rgba($yellow-50, 0.2);
|
||||||
|
|
||||||
|
// Empty States
|
||||||
|
$empty-state-text: $gray-40;
|
||||||
|
$empty-state-heading: $gray-20;
|
||||||
|
|
||||||
|
// Cards & Containers
|
||||||
|
$card-bg: $white;
|
||||||
|
$card-border: $gray-80;
|
||||||
|
$card-shadow: rgba($black, 0.08);
|
||||||
|
$card-shadow-hover: rgba($black, 0.12);
|
||||||
|
|
||||||
|
// Dropdowns & Popovers
|
||||||
|
$dropdown-bg: $white;
|
||||||
|
$dropdown-border: $gray-80;
|
||||||
|
$dropdown-shadow: rgba($black, 0.12);
|
||||||
|
$dropdown-item-hover: $gray-95;
|
||||||
|
|
||||||
|
// Modals
|
||||||
|
$modal-overlay: rgba($black, 0.5);
|
||||||
|
$modal-bg: $white;
|
||||||
|
$modal-shadow: rgba($black, 0.15);
|
||||||
|
|
|
||||||
59
src/lib/components/admin/EmptyState.svelte
Normal file
59
src/lib/components/admin/EmptyState.svelte
Normal file
|
|
@ -0,0 +1,59 @@
|
||||||
|
<script lang="ts">
|
||||||
|
import type { Snippet } from 'svelte'
|
||||||
|
|
||||||
|
interface Props {
|
||||||
|
title: string
|
||||||
|
message: string
|
||||||
|
icon?: Snippet
|
||||||
|
action?: Snippet
|
||||||
|
}
|
||||||
|
|
||||||
|
let { title, message, icon, action }: Props = $props()
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<div class="empty-state">
|
||||||
|
{#if icon}
|
||||||
|
<div class="empty-icon">
|
||||||
|
{@render icon()}
|
||||||
|
</div>
|
||||||
|
{/if}
|
||||||
|
<h3>{title}</h3>
|
||||||
|
<p>{message}</p>
|
||||||
|
{#if action}
|
||||||
|
<div class="empty-action">
|
||||||
|
{@render action()}
|
||||||
|
</div>
|
||||||
|
{/if}
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<style lang="scss">
|
||||||
|
@import '$styles/variables.scss';
|
||||||
|
|
||||||
|
.empty-state {
|
||||||
|
text-align: center;
|
||||||
|
padding: $unit-8x $unit-4x;
|
||||||
|
color: $empty-state-text;
|
||||||
|
|
||||||
|
.empty-icon {
|
||||||
|
font-size: calc($unit * 6); // 48px
|
||||||
|
margin-bottom: $unit-3x;
|
||||||
|
opacity: 0.5;
|
||||||
|
}
|
||||||
|
|
||||||
|
h3 {
|
||||||
|
font-size: calc($unit * 2.5); // 20px
|
||||||
|
font-weight: 600;
|
||||||
|
margin: 0 0 $unit-2x;
|
||||||
|
color: $empty-state-heading;
|
||||||
|
}
|
||||||
|
|
||||||
|
p {
|
||||||
|
margin: 0;
|
||||||
|
line-height: 1.5;
|
||||||
|
}
|
||||||
|
|
||||||
|
.empty-action {
|
||||||
|
margin-top: $unit-3x;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</style>
|
||||||
54
src/lib/components/admin/ErrorMessage.svelte
Normal file
54
src/lib/components/admin/ErrorMessage.svelte
Normal file
|
|
@ -0,0 +1,54 @@
|
||||||
|
<script lang="ts">
|
||||||
|
interface Props {
|
||||||
|
message: string
|
||||||
|
dismissible?: boolean
|
||||||
|
onDismiss?: () => void
|
||||||
|
}
|
||||||
|
|
||||||
|
let { message, dismissible = false, onDismiss }: Props = $props()
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<div class="error-message">
|
||||||
|
<span class="error-text">{message}</span>
|
||||||
|
{#if dismissible && onDismiss}
|
||||||
|
<button type="button" class="dismiss-btn" onclick={onDismiss} aria-label="Dismiss">×</button>
|
||||||
|
{/if}
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<style lang="scss">
|
||||||
|
@import '$styles/variables.scss';
|
||||||
|
|
||||||
|
.error-message {
|
||||||
|
background: $error-bg;
|
||||||
|
color: $error-text;
|
||||||
|
padding: $unit-3x;
|
||||||
|
border-radius: $unit-2x;
|
||||||
|
border: $unit-1px solid $error-border;
|
||||||
|
text-align: center;
|
||||||
|
margin-bottom: $unit-4x;
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: center;
|
||||||
|
gap: $unit-2x;
|
||||||
|
|
||||||
|
.error-text {
|
||||||
|
flex: 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
.dismiss-btn {
|
||||||
|
background: none;
|
||||||
|
border: none;
|
||||||
|
color: $error-text;
|
||||||
|
font-size: calc($unit * 3); // 24px
|
||||||
|
cursor: pointer;
|
||||||
|
padding: 0;
|
||||||
|
line-height: 1;
|
||||||
|
opacity: 0.6;
|
||||||
|
transition: opacity $transition-fast ease;
|
||||||
|
|
||||||
|
&:hover {
|
||||||
|
opacity: 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</style>
|
||||||
|
|
@ -6,6 +6,8 @@
|
||||||
import PostListItem from '$lib/components/admin/PostListItem.svelte'
|
import PostListItem from '$lib/components/admin/PostListItem.svelte'
|
||||||
import InlineComposerModal from '$lib/components/admin/InlineComposerModal.svelte'
|
import InlineComposerModal from '$lib/components/admin/InlineComposerModal.svelte'
|
||||||
import DeleteConfirmationModal from '$lib/components/admin/DeleteConfirmationModal.svelte'
|
import DeleteConfirmationModal from '$lib/components/admin/DeleteConfirmationModal.svelte'
|
||||||
|
import EmptyState from '$lib/components/admin/EmptyState.svelte'
|
||||||
|
import ErrorMessage from '$lib/components/admin/ErrorMessage.svelte'
|
||||||
import Button from '$lib/components/admin/Button.svelte'
|
import Button from '$lib/components/admin/Button.svelte'
|
||||||
import Select from '$lib/components/admin/Select.svelte'
|
import Select from '$lib/components/admin/Select.svelte'
|
||||||
import { createListFilters, commonSorts } from '$lib/admin/listFilters.svelte'
|
import { createListFilters, commonSorts } from '$lib/admin/listFilters.svelte'
|
||||||
|
|
@ -165,22 +167,20 @@ const statusFilterOptions = [
|
||||||
</AdminFilters>
|
</AdminFilters>
|
||||||
|
|
||||||
{#if actionError}
|
{#if actionError}
|
||||||
<div class="error-message">{actionError}</div>
|
<ErrorMessage message={actionError} />
|
||||||
{/if}
|
{/if}
|
||||||
|
|
||||||
{#if filters.items.length === 0}
|
{#if filters.items.length === 0}
|
||||||
<div class="empty-state">
|
<EmptyState
|
||||||
<div class="empty-icon">📝</div>
|
title="No posts found"
|
||||||
<h3>No posts found</h3>
|
message={filters.values.type === 'all' && filters.values.status === 'all'
|
||||||
<p>
|
? 'Create your first post to get started!'
|
||||||
{#if filters.values.type === 'all' && filters.values.status === 'all'}
|
: 'No posts found matching the current filters. Try adjusting your filters or create a new post.'}
|
||||||
Create your first post to get started!
|
>
|
||||||
{:else}
|
{#snippet icon()}
|
||||||
No posts found matching the current filters. Try adjusting your filters or create a new
|
📝
|
||||||
post.
|
{/snippet}
|
||||||
{/if}
|
</EmptyState>
|
||||||
</p>
|
|
||||||
</div>
|
|
||||||
{:else}
|
{:else}
|
||||||
<div class="posts-list">
|
<div class="posts-list">
|
||||||
{#each filters.items as post (post.id)}
|
{#each filters.items as post (post.id)}
|
||||||
|
|
@ -217,44 +217,11 @@ const statusFilterOptions = [
|
||||||
<style lang="scss">
|
<style lang="scss">
|
||||||
@import '$styles/variables.scss';
|
@import '$styles/variables.scss';
|
||||||
|
|
||||||
.error-message {
|
|
||||||
background: rgba(239, 68, 68, 0.1);
|
|
||||||
color: #dc2626;
|
|
||||||
padding: $unit-3x;
|
|
||||||
border-radius: $unit-2x;
|
|
||||||
border: 1px solid rgba(239, 68, 68, 0.2);
|
|
||||||
text-align: center;
|
|
||||||
margin-bottom: $unit-4x;
|
|
||||||
}
|
|
||||||
|
|
||||||
.composer-section {
|
.composer-section {
|
||||||
margin-bottom: $unit-4x;
|
margin-bottom: $unit-4x;
|
||||||
padding: 0 $unit;
|
padding: 0 $unit;
|
||||||
}
|
}
|
||||||
|
|
||||||
.empty-state {
|
|
||||||
text-align: center;
|
|
||||||
padding: $unit-8x $unit-4x;
|
|
||||||
color: $gray-40;
|
|
||||||
|
|
||||||
.empty-icon {
|
|
||||||
font-size: 3rem;
|
|
||||||
margin-bottom: $unit-3x;
|
|
||||||
}
|
|
||||||
|
|
||||||
h3 {
|
|
||||||
font-size: 1.25rem;
|
|
||||||
font-weight: 600;
|
|
||||||
margin: 0 0 $unit-2x;
|
|
||||||
color: $gray-20;
|
|
||||||
}
|
|
||||||
|
|
||||||
p {
|
|
||||||
margin: 0;
|
|
||||||
line-height: 1.5;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
.posts-list {
|
.posts-list {
|
||||||
display: flex;
|
display: flex;
|
||||||
flex-direction: column;
|
flex-direction: column;
|
||||||
|
|
|
||||||
|
|
@ -5,6 +5,8 @@
|
||||||
import AdminFilters from '$lib/components/admin/AdminFilters.svelte'
|
import AdminFilters from '$lib/components/admin/AdminFilters.svelte'
|
||||||
import ProjectListItem from '$lib/components/admin/ProjectListItem.svelte'
|
import ProjectListItem from '$lib/components/admin/ProjectListItem.svelte'
|
||||||
import DeleteConfirmationModal from '$lib/components/admin/DeleteConfirmationModal.svelte'
|
import DeleteConfirmationModal from '$lib/components/admin/DeleteConfirmationModal.svelte'
|
||||||
|
import EmptyState from '$lib/components/admin/EmptyState.svelte'
|
||||||
|
import ErrorMessage from '$lib/components/admin/ErrorMessage.svelte'
|
||||||
import Button from '$lib/components/admin/Button.svelte'
|
import Button from '$lib/components/admin/Button.svelte'
|
||||||
import Select from '$lib/components/admin/Select.svelte'
|
import Select from '$lib/components/admin/Select.svelte'
|
||||||
import { createListFilters, commonSorts } from '$lib/admin/listFilters.svelte'
|
import { createListFilters, commonSorts } from '$lib/admin/listFilters.svelte'
|
||||||
|
|
@ -151,21 +153,16 @@
|
||||||
</AdminFilters>
|
</AdminFilters>
|
||||||
|
|
||||||
{#if actionError}
|
{#if actionError}
|
||||||
<div class="error">{actionError}</div>
|
<ErrorMessage message={actionError} />
|
||||||
{/if}
|
{/if}
|
||||||
|
|
||||||
{#if filters.items.length === 0}
|
{#if filters.items.length === 0}
|
||||||
<div class="empty-state">
|
<EmptyState
|
||||||
<h3>No projects found</h3>
|
title="No projects found"
|
||||||
<p>
|
message={filters.values.type === 'all' && filters.values.status === 'all'
|
||||||
{#if filters.values.type === 'all' && filters.values.status === 'all'}
|
? 'Create your first project to get started!'
|
||||||
Create your first project to get started!
|
: 'No projects found matching the current filters. Try adjusting your filters or create a new project.'}
|
||||||
{:else}
|
/>
|
||||||
No projects found matching the current filters. Try adjusting your filters or create a new
|
|
||||||
project.
|
|
||||||
{/if}
|
|
||||||
</p>
|
|
||||||
</div>
|
|
||||||
{:else}
|
{:else}
|
||||||
<div class="projects-list">
|
<div class="projects-list">
|
||||||
{#each filters.items as project (project.id)}
|
{#each filters.items as project (project.id)}
|
||||||
|
|
@ -202,34 +199,6 @@
|
||||||
<style lang="scss">
|
<style lang="scss">
|
||||||
@import '$styles/variables.scss';
|
@import '$styles/variables.scss';
|
||||||
|
|
||||||
.error {
|
|
||||||
background: rgba(239, 68, 68, 0.1);
|
|
||||||
color: #dc2626;
|
|
||||||
padding: $unit-3x;
|
|
||||||
border-radius: $unit-2x;
|
|
||||||
border: 1px solid rgba(239, 68, 68, 0.2);
|
|
||||||
text-align: center;
|
|
||||||
margin-bottom: $unit-4x;
|
|
||||||
}
|
|
||||||
|
|
||||||
.empty-state {
|
|
||||||
text-align: center;
|
|
||||||
padding: $unit-8x $unit-4x;
|
|
||||||
color: $gray-40;
|
|
||||||
|
|
||||||
h3 {
|
|
||||||
font-size: 1.25rem;
|
|
||||||
font-weight: 600;
|
|
||||||
margin: 0 0 $unit-2x;
|
|
||||||
color: $gray-20;
|
|
||||||
}
|
|
||||||
|
|
||||||
p {
|
|
||||||
margin: 0;
|
|
||||||
line-height: 1.5;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
.projects-list {
|
.projects-list {
|
||||||
display: flex;
|
display: flex;
|
||||||
flex-direction: column;
|
flex-direction: column;
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue