Merge branch 'dark-fucking-mode' into 'master'
Implement comprehensive dark mode functionality See merge request engineering/make-post-sell/make_post_sell!54
This commit is contained in:
commit
8a014a6e61
21 changed files with 965 additions and 109 deletions
24
CLAUDE.md
24
CLAUDE.md
|
|
@ -93,31 +93,29 @@ When making changes to database models, always create Alembic migrations:
|
|||
# Activate environment first
|
||||
source env/bin/activate
|
||||
|
||||
# Create a new migration
|
||||
# Create a new migration (manual)
|
||||
alembic -c data/development.ini revision -m "description of change"
|
||||
|
||||
# Edit the generated migration file in make_post_sell/scripts/alembic/versions/
|
||||
# Add your upgrade() and downgrade() logic
|
||||
# OR: Create autogenerated migration (compares DB with models)
|
||||
alembic -c data/development.ini revision --autogenerate -m "description of change"
|
||||
|
||||
# Apply the migration
|
||||
alembic -c data/development.ini upgrade head
|
||||
# Edit the generated migration file in make_post_sell/scripts/alembic/versions/
|
||||
```
|
||||
|
||||
### Migration Commands
|
||||
### Running Migrations
|
||||
```bash
|
||||
# Check current database revision
|
||||
# Apply all pending migrations
|
||||
alembic -c data/development.ini upgrade head
|
||||
|
||||
# Check current migration status
|
||||
alembic -c data/development.ini current
|
||||
|
||||
# View migration history
|
||||
alembic -c data/development.ini history
|
||||
|
||||
# Upgrade to latest
|
||||
alembic -c data/development.ini upgrade head
|
||||
|
||||
# Auto-generate migration from model changes (review before applying!)
|
||||
alembic -c data/development.ini revision --autogenerate -m "auto-generated changes"
|
||||
```
|
||||
|
||||
**IMPORTANT**: Always backup the database before running migrations!
|
||||
|
||||
### Important Migration Notes
|
||||
|
||||
**SQLite Column Defaults**: When adding NOT NULL columns with defaults to existing tables in SQLite, use `server_default` with raw SQL values:
|
||||
|
|
|
|||
329
THEME_AUDIT.md
Normal file
329
THEME_AUDIT.md
Normal file
|
|
@ -0,0 +1,329 @@
|
|||
# CSS Theme Audit Report
|
||||
|
||||
## Executive Summary
|
||||
|
||||
This audit documents all DOM elements, CSS classes, and inline styles across the Make Post Sell application templates to prepare for theme implementation. The application uses a mobile-first CSS Grid layout with semantic class naming.
|
||||
|
||||
## CSS Architecture Overview
|
||||
|
||||
- **Layout System**: CSS Grid (exclusively, no Flexbox)
|
||||
- **Mobile-First**: Responsive design starting from mobile
|
||||
- **Main CSS File**: `make_post_sell/static/css/common.css` (1,072 lines)
|
||||
- **Template Engine**: Jinja2 (.j2 files)
|
||||
- **Total Templates**: 42 templates across 48 files
|
||||
|
||||
## 1. INLINE STYLES AUDIT
|
||||
|
||||
### High Priority - Need CSS Classes
|
||||
|
||||
**shop_settings.j2** (16 instances)
|
||||
- `style="color: #666;"` - Status messages (lines 176, 271, 314, 329, 392)
|
||||
- `style="color: green;"` - Success indicators (lines 308, 366)
|
||||
- `style="color: #d44;"` - Error indicators (lines 312, 370)
|
||||
- `style="display: inline;"` - Inline labels (lines 136, 266)
|
||||
- `style="width: 32px;"` - Favicon preview (line 434)
|
||||
|
||||
**crypto_checkout.j2** (25 instances)
|
||||
- Complex grid layouts: `style="display: grid; grid-template-columns: auto 1fr; gap: 20px;"` (line 23)
|
||||
- Button layouts: `style="display: grid; grid-template-columns: 1fr 1fr 1fr;"` (line 38)
|
||||
- Status boxes: `style="background: #f8f9fa; border: 1px solid #dee2e6;"` (line 48)
|
||||
- Warning/info boxes: `style="background: #d1fae5; border: 1px solid #10b981;"` (line 539)
|
||||
|
||||
**cart_checkout.j2** (18 instances)
|
||||
- Payment method buttons with crypto icons
|
||||
- Status indicators and warning boxes
|
||||
- Grid layouts for pending payment display
|
||||
|
||||
**cart.j2** (16 instances)
|
||||
- Float layouts: `style="float: right;"` (line 34)
|
||||
- Form styling: `style="display: inline;"` (lines 113, 121)
|
||||
- Shop names: `style="font-size: 1.5em;"` (line 90)
|
||||
|
||||
### Medium Priority
|
||||
|
||||
**snippets/comments.j2** (6 instances)
|
||||
- Comment indentation: `style="margin-left: {{ comment.depth * 20 }}px;"`
|
||||
- Form hiding: `style="display: none;"`
|
||||
|
||||
**product.j2**, **content.j2** (2 instances each)
|
||||
- Subtitle styling: `style="font-size: 0.6em; font-weight: normal;"`
|
||||
|
||||
**Other templates** (30+ instances across remaining files)
|
||||
- Various display, color, and layout inline styles
|
||||
|
||||
## 2. CSS CLASSES ANALYSIS
|
||||
|
||||
### Well-Designed Semantic Classes (Keep)
|
||||
|
||||
**Layout Classes**
|
||||
- `.one-column` (24 uses) - Single column layout, max-width 600px
|
||||
- `.two-column` (4 uses) - Two column responsive layout
|
||||
- `.one-column-thin` (1 use) - Narrower single column, max-width 400px
|
||||
- `.well`, `.well2` (9 uses) - Content containers with background
|
||||
|
||||
**Component Classes**
|
||||
- `.mps-button` family - Comprehensive button system
|
||||
- `.serp` family - Search results display
|
||||
- `.cart-*` family - Cart-specific components
|
||||
- `.product-*` family - Product display components
|
||||
- `.comment-*` family - Comment system components
|
||||
|
||||
**State Classes**
|
||||
- `.hidden-control` - Toggle visibility
|
||||
- `.opacity-60` - Transparency
|
||||
- `.discounted` - Price styling
|
||||
- `.highlight` - Text highlighting
|
||||
|
||||
### Button System Analysis
|
||||
|
||||
**Primary Buttons** (semantic and consistent)
|
||||
```css
|
||||
.mps-button - Base button class
|
||||
.mps-button-blue - #98b6fa (info/secondary)
|
||||
.mps-button-green - #a3c765 (success/primary)
|
||||
.mps-button-red - #bc2131 (danger)
|
||||
.mps-button-small - Smaller variant for desktop
|
||||
```
|
||||
|
||||
**Specialized Buttons** (good semantic naming)
|
||||
```css
|
||||
.cart-checkout-button
|
||||
.cart-save-button
|
||||
.cart-activate-button
|
||||
.cart-delete-button
|
||||
.product-download-button
|
||||
.product-edit-button
|
||||
.shop-new-button
|
||||
.payment-toggle-button
|
||||
```
|
||||
|
||||
### Areas Needing Attention
|
||||
|
||||
**Inconsistent Classes**
|
||||
- Multiple button size combinations (`.mps-button-small mps-button-green`)
|
||||
- Mixed semantic levels (`.product-edit-button mps-button mps-button-small`)
|
||||
|
||||
**Missing Classes** (have inline styles instead)
|
||||
- Status indicators (success, warning, error)
|
||||
- Crypto payment layouts
|
||||
- Form spacing
|
||||
- Icon sizing
|
||||
|
||||
## 3. COLOR PALETTE ANALYSIS
|
||||
|
||||
### Current Theme Colors
|
||||
```css
|
||||
/* Primary Brand Colors */
|
||||
#5871ad - Primary blue (navbar, buttons)
|
||||
#82A8FF - Gradient end
|
||||
#98b6fa - Secondary blue (info buttons)
|
||||
|
||||
/* Action Colors */
|
||||
#a3c765 - Success green (primary actions)
|
||||
#bc2131 - Danger red (destructive actions)
|
||||
#CC6958 - Delete red (cart delete)
|
||||
|
||||
/* Status Colors */
|
||||
#28a745 - Success green (confirmations)
|
||||
#ffc107 - Warning yellow
|
||||
#dc3545 - Error red
|
||||
#6c757d - Muted gray
|
||||
|
||||
/* Background Colors */
|
||||
#F9F9FA - Light gray (navbar, wells)
|
||||
#f2f2f2 - Even table rows
|
||||
#ffffff - Odd table rows, content background
|
||||
|
||||
/* Text Colors */
|
||||
#5f6368 - Link color
|
||||
#666666 - Muted text
|
||||
#721c24 - Error text
|
||||
#856404 - Warning text
|
||||
#0c5460 - Info text
|
||||
#155724 - Success text
|
||||
```
|
||||
|
||||
## 4. LAYOUT SYSTEM ANALYSIS
|
||||
|
||||
### CSS Grid Implementation
|
||||
- Mobile-first responsive design
|
||||
- Breakpoints: 800px (tablet), 960px (desktop), 1200px (large)
|
||||
- Grid areas for complex layouts (product pages)
|
||||
- Auto-fit columns for responsive grids
|
||||
|
||||
### Layout Classes Usage
|
||||
```
|
||||
one-column: 24 instances - Most common layout
|
||||
shop-settings well: 9 instances - Settings pages
|
||||
two-column: 4 instances - Product/content pages
|
||||
well: 7 instances - Content containers
|
||||
```
|
||||
|
||||
## 5. THEME TARGETS FOR IMPLEMENTATION
|
||||
|
||||
### Essential CSS Variables Needed
|
||||
|
||||
```css
|
||||
:root {
|
||||
/* Brand Colors */
|
||||
--color-primary: #5871ad;
|
||||
--color-primary-light: #82A8FF;
|
||||
--color-secondary: #98b6fa;
|
||||
|
||||
/* Action Colors */
|
||||
--color-success: #a3c765;
|
||||
--color-danger: #bc2131;
|
||||
--color-warning: #ffc107;
|
||||
--color-info: #0c5460;
|
||||
|
||||
/* Status Colors */
|
||||
--color-status-success: #28a745;
|
||||
--color-status-error: #dc3545;
|
||||
--color-status-warning: #856404;
|
||||
--color-status-muted: #6c757d;
|
||||
|
||||
/* Background Colors */
|
||||
--bg-navbar: #F9F9FA;
|
||||
--bg-content: #ffffff;
|
||||
--bg-well: #F9F9FA;
|
||||
--bg-table-even: #f2f2f2;
|
||||
|
||||
/* Text Colors */
|
||||
--text-link: #5f6368;
|
||||
--text-muted: #666666;
|
||||
--text-body: #333333;
|
||||
|
||||
/* Spacing */
|
||||
--spacing-xs: 4px;
|
||||
--spacing-sm: 8px;
|
||||
--spacing-md: 14px;
|
||||
--spacing-lg: 20px;
|
||||
--spacing-xl: 40px;
|
||||
}
|
||||
```
|
||||
|
||||
### New Classes Needed (Replace Inline Styles)
|
||||
|
||||
```css
|
||||
/* Status Indicators */
|
||||
.status-success { color: var(--color-status-success); }
|
||||
.status-error { color: var(--color-status-error); }
|
||||
.status-warning { color: var(--color-status-warning); }
|
||||
.status-muted { color: var(--color-status-muted); }
|
||||
|
||||
/* Crypto Payment Layouts */
|
||||
.crypto-payment-grid {
|
||||
display: grid;
|
||||
grid-template-columns: auto 1fr;
|
||||
gap: 20px;
|
||||
margin: 16px 0;
|
||||
align-items: start;
|
||||
}
|
||||
|
||||
.crypto-button-grid {
|
||||
display: grid;
|
||||
grid-template-columns: 1fr 1fr 1fr;
|
||||
gap: 8px;
|
||||
margin: 8px 0;
|
||||
}
|
||||
|
||||
/* Info Boxes */
|
||||
.info-box {
|
||||
background: #f8f9fa;
|
||||
border: 1px solid #dee2e6;
|
||||
border-radius: 4px;
|
||||
padding: 15px;
|
||||
margin: 10px 0;
|
||||
}
|
||||
|
||||
.success-box {
|
||||
background: #d1fae5;
|
||||
border: 1px solid #10b981;
|
||||
border-radius: 4px;
|
||||
padding: 15px;
|
||||
margin: 20px 0;
|
||||
}
|
||||
|
||||
.warning-box {
|
||||
background: #fef3c7;
|
||||
border: 1px solid #f59e0b;
|
||||
border-radius: 4px;
|
||||
padding: 15px;
|
||||
margin: 20px 0;
|
||||
}
|
||||
|
||||
/* Form Elements */
|
||||
.inline-label { display: inline; }
|
||||
.form-row { margin-bottom: var(--spacing-md); }
|
||||
|
||||
/* Icon Sizes */
|
||||
.icon-sm { width: 32px; height: 32px; }
|
||||
.icon-md { width: 48px; height: 48px; }
|
||||
.icon-lg { width: 150px; height: 150px; }
|
||||
|
||||
/* Typography */
|
||||
.text-large { font-size: 1.5em; }
|
||||
.text-small { font-size: 0.6em; font-weight: normal; }
|
||||
.text-monospace { font-family: monospace; }
|
||||
|
||||
/* Layout Utilities */
|
||||
.float-right { float: right; }
|
||||
.text-right { text-align: right; }
|
||||
.margin-top-none { margin-top: 0; }
|
||||
.margin-bottom-none { margin-bottom: 0; }
|
||||
```
|
||||
|
||||
## 6. RECOMMENDATIONS
|
||||
|
||||
### High Priority
|
||||
1. **Replace all inline styles** with semantic CSS classes
|
||||
2. **Implement CSS custom properties** for theme colors
|
||||
3. **Create component-specific classes** for crypto payments, status indicators
|
||||
4. **Establish consistent spacing system** using CSS variables
|
||||
|
||||
### Medium Priority
|
||||
1. **Consolidate button variants** to reduce complexity
|
||||
2. **Create utility classes** for common patterns (float, text-align, etc.)
|
||||
3. **Implement theme switching mechanism** using CSS custom properties
|
||||
4. **Add dark mode support** with color scheme media queries
|
||||
|
||||
### Low Priority
|
||||
1. **Optimize CSS delivery** by splitting into component files
|
||||
2. **Add CSS Grid fallbacks** for older browsers if needed
|
||||
3. **Implement print styles** for invoices and receipts
|
||||
|
||||
## 7. THEME IMPLEMENTATION STRATEGY
|
||||
|
||||
### Phase 1: Foundation
|
||||
- Convert all inline styles to CSS classes
|
||||
- Implement CSS custom properties system
|
||||
- Create component-specific classes
|
||||
|
||||
### Phase 2: Theming
|
||||
- Build theme switching system
|
||||
- Create default light/dark themes
|
||||
- Add theme preview functionality
|
||||
|
||||
### Phase 3: Customization
|
||||
- Allow color customization per shop
|
||||
- Implement theme marketplace/sharing
|
||||
- Add advanced theming options
|
||||
|
||||
## Files Modified in This Audit
|
||||
- `make_post_sell/static/css/common.css` (analyzed)
|
||||
- All 48 template files in `make_post_sell/templates/` (analyzed)
|
||||
|
||||
## Theme Target Summary
|
||||
|
||||
The application is well-structured for theming with:
|
||||
- ✅ Semantic CSS class naming
|
||||
- ✅ CSS Grid layout system
|
||||
- ✅ Mobile-first responsive design
|
||||
- ✅ Consistent button system
|
||||
- ❌ Too many inline styles (83 instances)
|
||||
- ❌ Missing CSS custom properties
|
||||
- ❌ No theme switching mechanism
|
||||
|
||||
**Total Elements Requiring Attention**: 83 inline styles across 21 template files
|
||||
**Estimated Effort**: Medium (2-3 weeks for complete theme system)
|
||||
**Theme Readiness**: 65% (needs cleanup before theming)
|
||||
|
|
@ -103,6 +103,9 @@ class Shop(RBase, Base):
|
|||
BigInteger, nullable=False, default=3600
|
||||
) # Default 60 minutes
|
||||
|
||||
# Default theme for shop visitors (0=dark, 1=light)
|
||||
default_theme = Column(BigInteger, nullable=False, default=1) # Default light mode
|
||||
|
||||
# many to many uses association_proxy.
|
||||
users = association_proxy("shop_users", "user", creator=lambda u: UserShop(user=u))
|
||||
|
||||
|
|
|
|||
|
|
@ -0,0 +1,32 @@
|
|||
"""add default_theme to shop
|
||||
|
||||
Revision ID: b090f873502e
|
||||
Revises: 16b9fd6d0f70
|
||||
Create Date: 2025-10-04 14:11:13.516680
|
||||
|
||||
"""
|
||||
|
||||
from alembic import op
|
||||
import sqlalchemy as sa
|
||||
|
||||
|
||||
# revision identifiers, used by Alembic.
|
||||
revision = "b090f873502e"
|
||||
down_revision = "16b9fd6d0f70"
|
||||
branch_labels = None
|
||||
depends_on = None
|
||||
|
||||
from make_post_sell.models.meta import UUIDType
|
||||
|
||||
|
||||
def upgrade():
|
||||
# Add default_theme column to mps_shop table with default 1 (light mode)
|
||||
op.add_column(
|
||||
"mps_shop",
|
||||
sa.Column("default_theme", sa.BigInteger(), nullable=False, server_default="1"),
|
||||
)
|
||||
|
||||
|
||||
def downgrade():
|
||||
# Remove default_theme column from mps_shop table
|
||||
op.drop_column("mps_shop", "default_theme")
|
||||
|
|
@ -272,6 +272,7 @@ img.product-main {
|
|||
border: none;
|
||||
border-radius: 4px;
|
||||
color: white;
|
||||
background-color: #D4D0C8;
|
||||
display: inline-block;
|
||||
font-size: 14px;
|
||||
font-weight: bold;
|
||||
|
|
@ -285,21 +286,92 @@ img.product-main {
|
|||
cursor: pointer;
|
||||
}
|
||||
|
||||
[data-theme="dark"] .mps-button {
|
||||
background-color: #2d3748;
|
||||
background-image: url("/static/img/trans-blue.png");
|
||||
color: var(--blue-color);
|
||||
border: 2px solid var(--blue-color);
|
||||
}
|
||||
|
||||
[data-theme="dark"] .mps-button:hover {
|
||||
background-color: #2d3748;
|
||||
background-image: url("/static/img/trans-blue2.png");
|
||||
color: #ffffff;
|
||||
border: 2px solid var(--blue-color);
|
||||
}
|
||||
|
||||
a.mps-button-blue,
|
||||
button.mps-button-blue {
|
||||
background-color: #98b6fa;
|
||||
}
|
||||
|
||||
[data-theme="dark"] a.mps-button-blue,
|
||||
[data-theme="dark"] button.mps-button-blue,
|
||||
[data-theme="dark"] input.mps-button-blue {
|
||||
background-color: #2d3748;
|
||||
background-image: url("/static/img/trans-blue.png");
|
||||
color: var(--blue-color);
|
||||
border: 2px solid var(--blue-color);
|
||||
}
|
||||
|
||||
[data-theme="dark"] a.mps-button-blue:hover,
|
||||
[data-theme="dark"] button.mps-button-blue:hover,
|
||||
[data-theme="dark"] input.mps-button-blue:hover {
|
||||
background-color: #2d3748;
|
||||
background-image: url("/static/img/trans-blue2.png");
|
||||
color: #ffffff;
|
||||
border: 2px solid var(--blue-color);
|
||||
}
|
||||
|
||||
a.mps-button-green,
|
||||
button.mps-button-green {
|
||||
background-color: #a3c765;
|
||||
}
|
||||
|
||||
[data-theme="dark"] a.mps-button-green,
|
||||
[data-theme="dark"] button.mps-button-green,
|
||||
[data-theme="dark"] input.mps-button-green {
|
||||
background-color: #2d3748;
|
||||
background-image: url("/static/img/trans-green.png");
|
||||
color: var(--green-color);
|
||||
border: 2px solid var(--green-color);
|
||||
}
|
||||
|
||||
[data-theme="dark"] a.mps-button-green:hover,
|
||||
[data-theme="dark"] button.mps-button-green:hover,
|
||||
[data-theme="dark"] input.mps-button-green:hover {
|
||||
background-color: #2d3748;
|
||||
background-image: url("/static/img/trans-green2.png");
|
||||
color: #ffffff;
|
||||
border: 2px solid var(--green-color);
|
||||
}
|
||||
|
||||
a.mps-button-red,
|
||||
button.mps-button-red {
|
||||
button.mps-button-red,
|
||||
input.mps-button-red {
|
||||
background-color: #bc2131;
|
||||
opacity: 0.8;
|
||||
}
|
||||
|
||||
[data-theme="dark"] a.mps-button-red,
|
||||
[data-theme="dark"] button.mps-button-red,
|
||||
[data-theme="dark"] input.mps-button-red {
|
||||
background-color: #2d3748;
|
||||
background-image: url("/static/img/trans-red.png");
|
||||
color: var(--red-color);
|
||||
border: 2px solid var(--red-color);
|
||||
opacity: 1.0;
|
||||
}
|
||||
|
||||
[data-theme="dark"] a.mps-button-red:hover,
|
||||
[data-theme="dark"] button.mps-button-red:hover,
|
||||
[data-theme="dark"] input.mps-button-red:hover {
|
||||
background-color: #2d3748;
|
||||
background-image: url("/static/img/trans-red2.png");
|
||||
color: #ffffff;
|
||||
border: 2px solid var(--red-color);
|
||||
}
|
||||
|
||||
.mps-cancel-button,
|
||||
.mps-comment-form-button {
|
||||
display: inline-block;
|
||||
|
|
@ -374,6 +446,67 @@ a.product-edit-button {
|
|||
background-color: #98b6fa;
|
||||
}
|
||||
|
||||
[data-theme="dark"] a.mps-button.product-edit-button {
|
||||
background-color: #2d3748;
|
||||
background-image: url("/static/img/trans-blue.png");
|
||||
color: #7ab9ff;
|
||||
border: 2px solid #7ab9ff;
|
||||
}
|
||||
|
||||
[data-theme="dark"] a.mps-button.product-edit-button:hover {
|
||||
background-color: #2d3748;
|
||||
background-image: url("/static/img/trans-blue2.png");
|
||||
color: #ffffff;
|
||||
border: 2px solid #7ab9ff;
|
||||
}
|
||||
|
||||
[data-theme="dark"] a.mps-button.shop-new-button {
|
||||
background-color: #2d3748;
|
||||
background-image: url("/static/img/trans-green.png");
|
||||
color: var(--green-color);
|
||||
border: 2px solid var(--green-color);
|
||||
}
|
||||
|
||||
[data-theme="dark"] a.mps-button.shop-new-button:hover {
|
||||
background-color: #2d3748;
|
||||
background-image: url("/static/img/trans-green2.png");
|
||||
color: #ffffff;
|
||||
border: 2px solid var(--green-color);
|
||||
}
|
||||
|
||||
/* Product download and preview buttons - make them green in dark mode */
|
||||
[data-theme="dark"] a.product-download-button,
|
||||
[data-theme="dark"] button.product-download-button {
|
||||
background-color: #2d3748;
|
||||
background-image: url("/static/img/trans-green.png");
|
||||
color: var(--green-color);
|
||||
border: 2px solid var(--green-color);
|
||||
}
|
||||
|
||||
[data-theme="dark"] a.product-download-button:hover,
|
||||
[data-theme="dark"] button.product-download-button:hover {
|
||||
background-color: #2d3748;
|
||||
background-image: url("/static/img/trans-green2.png");
|
||||
color: #ffffff;
|
||||
border: 2px solid var(--green-color);
|
||||
}
|
||||
|
||||
[data-theme="dark"] a.product-preview-button,
|
||||
[data-theme="dark"] button.product-preview-button {
|
||||
background-color: #2d3748;
|
||||
background-image: url("/static/img/trans-green.png");
|
||||
color: var(--green-color);
|
||||
border: 2px solid var(--green-color);
|
||||
}
|
||||
|
||||
[data-theme="dark"] a.product-preview-button:hover,
|
||||
[data-theme="dark"] button.product-preview-button:hover {
|
||||
background-color: #2d3748;
|
||||
background-image: url("/static/img/trans-green2.png");
|
||||
color: #ffffff;
|
||||
border: 2px solid var(--green-color);
|
||||
}
|
||||
|
||||
a.product-preview-button {
|
||||
background-color: #98b6fa;
|
||||
}
|
||||
|
|
@ -436,6 +569,20 @@ button.cart-remove-link:hover {
|
|||
text-decoration: underline;
|
||||
}
|
||||
|
||||
/* Links should use the theme link color */
|
||||
[data-theme="dark"] a {
|
||||
color: var(--link-color);
|
||||
}
|
||||
|
||||
[data-theme="dark"] button.cart-remove-link {
|
||||
color: var(--blue-color);
|
||||
}
|
||||
|
||||
[data-theme="dark"] button.cart-remove-link:hover {
|
||||
color: var(--blue-color);
|
||||
text-decoration: underline;
|
||||
}
|
||||
|
||||
/* “Add To Cart”, “Download”, “Preview” buttons */
|
||||
button.product-download-button,
|
||||
button.product-preview-button {
|
||||
|
|
@ -455,6 +602,21 @@ button.cart-checkout-button {
|
|||
button.cart-save-button {
|
||||
background-color: #98b6fa;
|
||||
}
|
||||
|
||||
/* Dark mode cart buttons */
|
||||
[data-theme="dark"] button.cart-checkout-button {
|
||||
background-color: #2d3748;
|
||||
background-image: url("/static/img/trans-green.png");
|
||||
color: var(--green-color);
|
||||
border: 2px solid var(--green-color);
|
||||
}
|
||||
|
||||
[data-theme="dark"] button.cart-checkout-button:hover {
|
||||
background-color: #2d3748;
|
||||
background-image: url("/static/img/trans-green2.png");
|
||||
color: #ffffff;
|
||||
border: 2px solid var(--green-color);
|
||||
}
|
||||
button.cart-activate-button {
|
||||
background-color: #5871ad;
|
||||
}
|
||||
|
|
@ -519,6 +681,20 @@ div.serp-item:hover {
|
|||
filter: brightness(88%);
|
||||
}
|
||||
|
||||
[data-theme="dark"] div.serp-item:hover {
|
||||
background-color: #2d3748;
|
||||
background-image: url("/static/img/trans-blue2.png");
|
||||
border: 2px solid #7ab9ff;
|
||||
color: #ffffff;
|
||||
-webkit-filter: none;
|
||||
-moz-filter: none;
|
||||
filter: none;
|
||||
}
|
||||
|
||||
[data-theme="dark"] div.serp-item:hover a {
|
||||
color: #ffffff;
|
||||
}
|
||||
|
||||
img.serp-thumbnail {
|
||||
border-radius: 4px;
|
||||
}
|
||||
|
|
@ -1041,9 +1217,9 @@ div.message-ribbon {
|
|||
}
|
||||
|
||||
.alert-info, .alert-notice {
|
||||
color: #0c5460;
|
||||
background-color: #98b6fa;
|
||||
border-color: #bee5eb;
|
||||
color: var(--text-primary);
|
||||
background-color: color-mix(in srgb, var(--info-color) 20%, var(--bg-primary) 80%);
|
||||
border-color: var(--info-color);
|
||||
}
|
||||
|
||||
.alert-success {
|
||||
|
|
@ -1143,8 +1319,9 @@ div.message-ribbon {
|
|||
}
|
||||
|
||||
.status-box {
|
||||
background: #f8f9fa;
|
||||
border: 1px solid #dee2e6;
|
||||
background: var(--bg-secondary);
|
||||
border: 1px solid var(--border-color);
|
||||
color: var(--text-primary);
|
||||
border-radius: 4px;
|
||||
padding: 15px;
|
||||
margin: 10px 0;
|
||||
|
|
@ -1167,16 +1344,18 @@ div.message-ribbon {
|
|||
}
|
||||
|
||||
.success-notice {
|
||||
background: #d1fae5;
|
||||
border: 1px solid #10b981;
|
||||
background: color-mix(in srgb, var(--success-color) 15%, var(--bg-primary) 85%);
|
||||
border: 1px solid var(--success-color);
|
||||
color: var(--text-primary);
|
||||
border-radius: 4px;
|
||||
padding: 15px;
|
||||
margin: 20px 0;
|
||||
}
|
||||
|
||||
.warning-notice {
|
||||
background: #fef3c7;
|
||||
border: 1px solid #f59e0b;
|
||||
background: color-mix(in srgb, var(--warning-color) 15%, var(--bg-primary) 85%);
|
||||
border: 1px solid var(--warning-color);
|
||||
color: var(--text-primary);
|
||||
border-radius: 4px;
|
||||
padding: 15px;
|
||||
margin: 20px 0;
|
||||
|
|
@ -1184,7 +1363,8 @@ div.message-ribbon {
|
|||
|
||||
.monospace-address {
|
||||
font-family: monospace;
|
||||
background: #f0f0f0;
|
||||
background: var(--bg-secondary);
|
||||
color: var(--text-primary);
|
||||
padding: 8px;
|
||||
border-radius: 3px;
|
||||
word-break: break-all;
|
||||
|
|
@ -1204,6 +1384,15 @@ div.message-ribbon {
|
|||
cursor: not-allowed;
|
||||
}
|
||||
|
||||
[data-theme="dark"] .disabled-crypto-button {
|
||||
background-color: #1a1a1a !important;
|
||||
background-image: none !important;
|
||||
color: #666666 !important;
|
||||
border: 2px solid #444444 !important;
|
||||
cursor: not-allowed;
|
||||
opacity: 0.6;
|
||||
}
|
||||
|
||||
.crypto-button-icon {
|
||||
width: 32px;
|
||||
height: 32px;
|
||||
|
|
@ -1228,8 +1417,9 @@ div.message-ribbon {
|
|||
}
|
||||
|
||||
.warning-banner-alt {
|
||||
background: #fff3cd;
|
||||
border: 1px solid #ffc107;
|
||||
background: color-mix(in srgb, var(--info-color) 15%, var(--bg-primary) 85%);
|
||||
border: 1px solid var(--info-color);
|
||||
color: var(--text-primary);
|
||||
border-radius: 4px;
|
||||
padding: 15px;
|
||||
margin: 20px 0;
|
||||
|
|
@ -1249,24 +1439,29 @@ div.message-ribbon {
|
|||
}
|
||||
|
||||
.pending-quote-item {
|
||||
background: #f8f9fa;
|
||||
border: 1px solid #dee2e6;
|
||||
background: transparent;
|
||||
border: none;
|
||||
color: var(--text-primary);
|
||||
border-radius: 3px;
|
||||
padding: 10px;
|
||||
margin: 8px 0;
|
||||
}
|
||||
|
||||
.pending-quote-grid {
|
||||
display: grid;
|
||||
grid-template-columns: 1fr auto;
|
||||
align-items: start;
|
||||
.pending-quote-single {
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
.pending-quote-status {
|
||||
color: #666;
|
||||
margin-left: 10px;
|
||||
.pending-quote-cancel {
|
||||
text-align: center;
|
||||
margin-top: 10px;
|
||||
}
|
||||
|
||||
.full-width-button {
|
||||
width: 100%;
|
||||
display: block;
|
||||
}
|
||||
|
||||
|
||||
.view-quote-button {
|
||||
background: #17a2b8;
|
||||
font-size: 12px;
|
||||
|
|
@ -1542,3 +1737,206 @@ div.message-ribbon {
|
|||
font-size: 0.9em;
|
||||
font-family: monospace;
|
||||
}
|
||||
|
||||
/* Dark Mode Theme System */
|
||||
:root {
|
||||
/* Light mode colors (default) */
|
||||
--bg-primary: #ffffff;
|
||||
--bg-secondary: #f8f9fa;
|
||||
--bg-tertiary: #e9ecef;
|
||||
--text-primary: #333333;
|
||||
--text-secondary: #6c757d;
|
||||
--text-muted: #666666;
|
||||
--border-color: #dee2e6;
|
||||
--border-light: #e9ecef;
|
||||
--link-color: #5f6368;
|
||||
--blue-color: #98b6fa;
|
||||
--green-color: #a3c765;
|
||||
--red-color: #bc2131;
|
||||
--success-color: #28a745;
|
||||
--warning-color: #ffc107;
|
||||
--error-color: #dc3545;
|
||||
--info-color: #17a2b8;
|
||||
|
||||
/* Form elements */
|
||||
--input-bg: #ffffff;
|
||||
--input-border: #cccccc;
|
||||
--input-text: #333333;
|
||||
|
||||
/* Navigation */
|
||||
--nav-bg: #f9f9fa;
|
||||
--nav-text: #333333;
|
||||
|
||||
/* Cards and containers */
|
||||
--card-bg: #f8f9fa;
|
||||
--well-bg: #f9f9fa;
|
||||
|
||||
/* Status notices */
|
||||
--notice-warning-bg: #fff3cd;
|
||||
--notice-warning-border: #ffeaa7;
|
||||
--notice-error-bg: #f8d7da;
|
||||
--notice-error-border: #f5c6cb;
|
||||
--notice-info-bg: #d1ecf1;
|
||||
--notice-info-border: #bee5eb;
|
||||
}
|
||||
|
||||
[data-theme="dark"] {
|
||||
/* Dark mode colors - zealotrush.com inspired gaming theme */
|
||||
--bg-primary: #0d1117;
|
||||
--bg-secondary: #161b22;
|
||||
--bg-tertiary: #21262d;
|
||||
--text-primary: #ffffff;
|
||||
--text-secondary: #cccccc;
|
||||
--text-muted: #999999;
|
||||
--border-color: #7ab9ff;
|
||||
--border-light: #5599dd;
|
||||
--link-color: #7ab9ff;
|
||||
--blue-color: #7ab9ff;
|
||||
--green-color: #08e700;
|
||||
--red-color: #F34C31;
|
||||
--success-color: #08e700;
|
||||
--warning-color: #f34c31;
|
||||
--error-color: #f34c31;
|
||||
--info-color: #7ab9ff;
|
||||
|
||||
/* Form elements */
|
||||
--input-bg: #161b22;
|
||||
--input-border: #7ab9ff;
|
||||
--input-text: #ffffff;
|
||||
|
||||
/* Navigation */
|
||||
--nav-bg: #0d1117;
|
||||
--nav-text: #ffffff;
|
||||
|
||||
/* Cards and containers */
|
||||
--card-bg: #2d2d2d;
|
||||
--well-bg: #2d2d2d;
|
||||
|
||||
/* Status notices */
|
||||
--notice-warning-bg: #3e2723;
|
||||
--notice-warning-border: #5d4037;
|
||||
--notice-error-bg: #3f1a1a;
|
||||
--notice-error-border: #5c2626;
|
||||
--notice-info-bg: #1a365d;
|
||||
--notice-info-border: #2c5282;
|
||||
}
|
||||
|
||||
/* Apply theme variables to existing elements */
|
||||
body {
|
||||
background-color: var(--bg-primary);
|
||||
color: var(--text-primary);
|
||||
}
|
||||
|
||||
section.content {
|
||||
background-color: var(--bg-primary);
|
||||
color: var(--text-primary);
|
||||
}
|
||||
|
||||
section.nav {
|
||||
background-color: var(--nav-bg);
|
||||
color: var(--nav-text);
|
||||
}
|
||||
|
||||
a {
|
||||
color: var(--link-color);
|
||||
}
|
||||
|
||||
input[type="text"],
|
||||
input[type="tel"],
|
||||
input[type="password"],
|
||||
input[type="number"],
|
||||
input[type="date"],
|
||||
textarea {
|
||||
background-color: var(--input-bg);
|
||||
border-color: var(--input-border);
|
||||
color: var(--input-text);
|
||||
}
|
||||
|
||||
.well, .well2 {
|
||||
background-color: var(--well-bg);
|
||||
color: var(--text-primary);
|
||||
}
|
||||
|
||||
[data-theme="dark"] .well,
|
||||
[data-theme="dark"] .well2 {
|
||||
background-color: #2d3748;
|
||||
background-image: url("/static/img/trans-blue.png");
|
||||
border: 2px solid #7ab9ff;
|
||||
box-shadow: 0 0 10px rgba(122, 185, 255, 0.3);
|
||||
color: var(--text-primary);
|
||||
}
|
||||
|
||||
/* Dark mode specific overrides for existing classes */
|
||||
[data-theme="dark"] table tr:nth-child(even) {
|
||||
background-color: #2d2d2d;
|
||||
}
|
||||
|
||||
[data-theme="dark"] table tr:nth-child(odd) {
|
||||
background-color: #333333;
|
||||
}
|
||||
|
||||
[data-theme="dark"] .crypto-payment-card {
|
||||
background: var(--card-bg);
|
||||
border-color: var(--border-color);
|
||||
color: var(--text-primary);
|
||||
}
|
||||
|
||||
[data-theme="dark"] .status-warning-notice {
|
||||
background: var(--notice-warning-bg);
|
||||
border-color: var(--notice-warning-border);
|
||||
color: var(--text-primary);
|
||||
}
|
||||
|
||||
[data-theme="dark"] .status-error-notice {
|
||||
background: var(--notice-error-bg);
|
||||
border-color: var(--notice-error-border);
|
||||
color: var(--text-primary);
|
||||
}
|
||||
|
||||
[data-theme="dark"] .status-info-notice {
|
||||
background: var(--notice-info-bg);
|
||||
border-color: var(--notice-info-border);
|
||||
color: var(--text-primary);
|
||||
}
|
||||
|
||||
[data-theme="dark"] .crypto-help-section {
|
||||
background: var(--bg-tertiary);
|
||||
color: var(--text-primary);
|
||||
}
|
||||
|
||||
[data-theme="dark"] .invoice-payment-box {
|
||||
background-color: #1a365d;
|
||||
border-color: #2c5282;
|
||||
color: var(--text-primary);
|
||||
}
|
||||
|
||||
/* Crypto wallet success indicators */
|
||||
.success-indicator {
|
||||
color: var(--success-color);
|
||||
font-weight: bold;
|
||||
}
|
||||
|
||||
[data-theme="dark"] .success-indicator {
|
||||
color: var(--green-color);
|
||||
font-weight: bold;
|
||||
}
|
||||
|
||||
/* Theme toggle button */
|
||||
.theme-toggle {
|
||||
background: none;
|
||||
border: 1px solid var(--border-color);
|
||||
color: var(--text-primary);
|
||||
padding: 8px 12px;
|
||||
border-radius: 4px;
|
||||
cursor: pointer;
|
||||
font-size: 14px;
|
||||
transition: background-color 0.2s ease;
|
||||
}
|
||||
|
||||
.theme-toggle:hover {
|
||||
background-color: var(--bg-secondary);
|
||||
}
|
||||
|
||||
.theme-toggle-icon {
|
||||
margin-right: 5px;
|
||||
}
|
||||
|
|
|
|||
BIN
make_post_sell/static/img/trans-blue.png
Normal file
BIN
make_post_sell/static/img/trans-blue.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 81 B |
BIN
make_post_sell/static/img/trans-blue2.png
Normal file
BIN
make_post_sell/static/img/trans-blue2.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 81 B |
BIN
make_post_sell/static/img/trans-green.png
Normal file
BIN
make_post_sell/static/img/trans-green.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 80 B |
BIN
make_post_sell/static/img/trans-green2.png
Normal file
BIN
make_post_sell/static/img/trans-green2.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 152 B |
BIN
make_post_sell/static/img/trans-red.png
Normal file
BIN
make_post_sell/static/img/trans-red.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 80 B |
BIN
make_post_sell/static/img/trans-red2.png
Normal file
BIN
make_post_sell/static/img/trans-red2.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 152 B |
|
|
@ -10,28 +10,28 @@
|
|||
|
||||
<br/>
|
||||
<br/>
|
||||
<a href="/c/new" class="shop-new-button mps-button">+ New Content</a>
|
||||
<a href="/c/new" class="mps-button shop-new-button">+ New Content</a>
|
||||
|
||||
{% if request.user in request.shop.owners %}
|
||||
<br/>
|
||||
<br/>
|
||||
<a href="/coupon/new" class="shop-new-button mps-button">+ New Coupon</a>
|
||||
<a href="/coupon/new" class="mps-button shop-new-button">+ New Coupon</a>
|
||||
{% endif %}
|
||||
|
||||
<br/>
|
||||
<br/>
|
||||
<a href="/p/new" class="shop-new-button mps-button">+ New Product</a>
|
||||
<a href="/p/new" class="mps-button shop-new-button">+ New Product</a>
|
||||
|
||||
{% if request.user in request.shop.owners %}
|
||||
<br/>
|
||||
<br/>
|
||||
<a href="/s/locations/new" class="shop-new-button mps-button">+ New Shop Location</a>
|
||||
<a href="/s/locations/new" class="mps-button shop-new-button">+ New Shop Location</a>
|
||||
{% endif %}
|
||||
|
||||
{% if request.is_saas_domain %}
|
||||
<br />
|
||||
<br />
|
||||
<a href="/s/new" class="shop-new-button mps-button mps-button">+ New Shop</a>
|
||||
<a href="/s/new" class="mps-button shop-new-button">+ New Shop</a>
|
||||
{% endif %}
|
||||
|
||||
{% else %}
|
||||
|
|
@ -39,7 +39,7 @@
|
|||
{% if request.is_saas_domain %}
|
||||
<p>Create your first shop to get started!</p>
|
||||
<br/>
|
||||
<a href="/s/new" class="shop-new-button mps-button">+ Create Your First Shop</a>
|
||||
<a href="/s/new" class="mps-button shop-new-button">+ Create Your First Shop</a>
|
||||
{% else %}
|
||||
<p>No shop configured for this domain.</p>
|
||||
{% endif %}
|
||||
|
|
|
|||
|
|
@ -10,38 +10,38 @@
|
|||
|
||||
<br/>
|
||||
<br/>
|
||||
<a href="/s/{{ request.shop.id }}/{{ request.shop.slug }}" class="product-edit-button mps-button">  View Shop</a>
|
||||
<a href="/s/{{ request.shop.id }}/{{ request.shop.slug }}" class="mps-button product-edit-button">  View Shop</a>
|
||||
|
||||
<br/>
|
||||
<br/>
|
||||
<a href="/s/{{ request.shop.id }}/products" class="product-edit-button mps-button">  View Products</a>
|
||||
<a href="/s/{{ request.shop.id }}/products" class="mps-button product-edit-button">  View Products</a>
|
||||
|
||||
{% if request.user in request.shop.owners %}
|
||||
<br/>
|
||||
<br/>
|
||||
<a href="/s/{{ request.shop.id }}/coupons" class="product-edit-button mps-button">  View Coupons</a>
|
||||
<a href="/s/{{ request.shop.id }}/coupons" class="mps-button product-edit-button">  View Coupons</a>
|
||||
|
||||
<br/>
|
||||
<br/>
|
||||
<a href="/s/{{ request.shop.id }}/users" class="product-edit-button mps-button">👤   Shop Users</a>
|
||||
<a href="/s/{{ request.shop.id }}/users" class="mps-button product-edit-button">👤   Shop Users</a>
|
||||
|
||||
<br/>
|
||||
<br/>
|
||||
<a href="/s/{{ request.shop.id }}/sales" class="product-edit-button mps-button">💰   Shop Sales</a>
|
||||
<a href="/s/{{ request.shop.id }}/sales" class="mps-button product-edit-button">💰   Shop Sales</a>
|
||||
|
||||
<br/>
|
||||
<br/>
|
||||
<a href="/s/{{ request.shop.id }}/settings" class="product-edit-button mps-button">⚙   Shop Settings</a>
|
||||
<a href="/s/{{ request.shop.id }}/settings" class="mps-button product-edit-button">⚙   Shop Settings</a>
|
||||
{% endif %}
|
||||
|
||||
<br/>
|
||||
<br/>
|
||||
<a href="/s/locations" class="product-edit-button mps-button">🏠   Shop Locations</a>
|
||||
<a href="/s/locations" class="mps-button product-edit-button">🏠   Shop Locations</a>
|
||||
|
||||
{% if request.is_saas_domain %}
|
||||
<br/>
|
||||
<br/>
|
||||
<a href="/u/shops" class="product-edit-button mps-button">⛶ Switch Shop</a>
|
||||
<a href="/u/shops" class="mps-button product-edit-button">⛶ Switch Shop</a>
|
||||
{% endif %}
|
||||
|
||||
{% endif %}
|
||||
|
|
|
|||
|
|
@ -7,11 +7,53 @@
|
|||
<!--product_created={{ request.product.human_created_timestamp }}-->
|
||||
<!--product_updated={{ request.product.human_updated_timestamp }}-->
|
||||
{%- endif %}
|
||||
<html lang="en" data-theme="{{ request.html_data_theme or 'dark' }}">
|
||||
{% set user_theme_id = request.user.theme_id if request.user and request.user.authenticated else none %}
|
||||
{% set shop_default_theme = request.shop.default_theme if request.shop else 1 %}
|
||||
{% if user_theme_id is not none %}
|
||||
{% set theme_preference = 'light' if (user_theme_id == 1) else 'dark' %}
|
||||
{% else %}
|
||||
{% set theme_preference = 'light' if (shop_default_theme == 1) else 'dark' %}
|
||||
{% endif %}
|
||||
<html lang="en" data-theme="{{ theme_preference }}" data-user-theme="{{ user_theme_id or 'null' }}" data-shop-default="{{ shop_default_theme }}">
|
||||
<head>
|
||||
|
||||
<!-- stylesheet to-end-all stylesheets, pico.fluid.classless.css ! woot -->
|
||||
<link rel="stylesheet" href="/static/css/common.css">
|
||||
|
||||
<!-- Dark mode theme switching -->
|
||||
<script>
|
||||
(function() {
|
||||
// Theme priority: localStorage > user preference > shop default > system default (light)
|
||||
const userThemeId = {{ user_theme_id or 'null' }};
|
||||
const shopDefaultTheme = {{ shop_default_theme }};
|
||||
|
||||
// Get theme from localStorage
|
||||
const savedTheme = localStorage.getItem('theme-preference');
|
||||
|
||||
let finalTheme;
|
||||
if (savedTheme && (savedTheme === 'light' || savedTheme === 'dark')) {
|
||||
// User has explicitly chosen a theme via toggle
|
||||
finalTheme = savedTheme;
|
||||
} else if (userThemeId !== null) {
|
||||
// Use user's profile setting (0=dark, 1=light)
|
||||
finalTheme = userThemeId === 0 ? 'dark' : 'light';
|
||||
} else {
|
||||
// Use shop default (0=dark, 1=light)
|
||||
finalTheme = shopDefaultTheme === 0 ? 'dark' : 'light';
|
||||
}
|
||||
|
||||
// Apply theme immediately to prevent flash
|
||||
document.documentElement.setAttribute('data-theme', finalTheme);
|
||||
|
||||
// Theme switching function (for programmatic use)
|
||||
window.setTheme = function(theme) {
|
||||
if (theme === 'dark' || theme === 'light') {
|
||||
document.documentElement.setAttribute('data-theme', theme);
|
||||
localStorage.setItem('theme-preference', theme);
|
||||
}
|
||||
};
|
||||
})();
|
||||
</script>
|
||||
|
||||
{% include 'snippets/analytics.j2' %}
|
||||
|
||||
|
|
|
|||
|
|
@ -9,9 +9,14 @@
|
|||
|
||||
{% block content -%}
|
||||
|
||||
{% if cart.is_empty %}
|
||||
<section class="one-column">
|
||||
<section class="well">
|
||||
{% else %}
|
||||
<section class="cart-grid">
|
||||
|
||||
<section class="cart-left">
|
||||
{% endif %}
|
||||
|
||||
{% for coupon in cart.coupons %}
|
||||
<section class="coupon">
|
||||
|
|
@ -64,15 +69,16 @@
|
|||
<center>
|
||||
<h2>This cart is empty.</h2>
|
||||
Nothing to see here.
|
||||
|
||||
{% if request.user and request.user.authenticated and request.user.carts.count() > 1 %}
|
||||
<br/>
|
||||
<br/>
|
||||
<a href="/u/carts" class="product-edit-button mps-button mps-button-small">View Saved Carts</a>
|
||||
<br/>
|
||||
<br/> or
|
||||
<br/>
|
||||
<br/>
|
||||
{% endif %}
|
||||
<br/>
|
||||
<br/>
|
||||
|
||||
<a href="/" class="cart-checkout-button mps-button mps-button-small mps-button-green">Let's go shopping!</a>
|
||||
</center>
|
||||
|
|
@ -185,6 +191,7 @@
|
|||
|
||||
</section>
|
||||
|
||||
{% if not cart.is_empty %}
|
||||
<section class="cart-right well">
|
||||
|
||||
{% if cart.is_discounted %}
|
||||
|
|
@ -275,6 +282,7 @@
|
|||
</center>
|
||||
|
||||
</section>
|
||||
{% endif %}
|
||||
</section>
|
||||
|
||||
|
||||
|
|
|
|||
|
|
@ -42,8 +42,7 @@
|
|||
|
||||
<br/>
|
||||
{% if cart.requires_payment %}
|
||||
Are you sure you want to confirm checkout & pay <br/>
|
||||
<b>${{ '{:,.2f}'.format(cart.total) }}</b>?
|
||||
Are you sure you want to confirm checkout & pay <b>${{ '{:,.2f}'.format(cart.total) }}</b>?
|
||||
{% else %}
|
||||
Are you sure you want to confirm checkout?
|
||||
{% endif %}
|
||||
|
|
@ -61,7 +60,7 @@
|
|||
{% if monero_enabled and xmr_processor_enabled and cart.requires_payment and cart.shop_product_dict|length == 1 %}
|
||||
<br/>
|
||||
{% if pending_crypto_quotes %}
|
||||
<button type="button" class="mps-button disabled-crypto-button" disabled title="Complete or cancel your pending crypto payments first"><img src="{{ request.static_url('make_post_sell:static/monero-symbol-480.png') }}" class="crypto-button-icon">Monero (XMR) - Complete Pending Payments First</button>
|
||||
<button type="button" class="mps-button disabled-crypto-button" disabled title="Complete or cancel your pending crypto payments first"><img src="{{ request.static_url('make_post_sell:static/monero-symbol-480.png') }}" class="crypto-button-icon">Monero (XMR) - Complete or Cancel Pending Payment</button>
|
||||
{% else %}
|
||||
<form method="POST" action="{{ request.route_url('crypto_xmr_start') }}">
|
||||
{% include "snippets/csrf.j2" %}
|
||||
|
|
@ -80,7 +79,7 @@
|
|||
{% if dogecoin_enabled and doge_processor_enabled and cart.requires_payment and cart.shop_product_dict|length == 1 %}
|
||||
<br/>
|
||||
{% if pending_crypto_quotes %}
|
||||
<button type="button" class="mps-button disabled-crypto-button" disabled title="Complete or cancel your pending crypto payments first"><img src="{{ request.static_url('make_post_sell:static/dogecoin-logo.png') }}" class="crypto-button-icon">Dogecoin (DOGE) - Complete Pending Payments First</button>
|
||||
<button type="button" class="mps-button disabled-crypto-button" disabled title="Complete or cancel your pending crypto payments first"><img src="{{ request.static_url('make_post_sell:static/dogecoin-logo.png') }}" class="crypto-button-icon">Dogecoin (DOGE) - Complete or Cancel Pending Payment</button>
|
||||
{% else %}
|
||||
<form method="POST" action="{{ request.route_url('crypto_doge_start') }}">
|
||||
{% include "snippets/csrf.j2" %}
|
||||
|
|
@ -124,27 +123,18 @@
|
|||
{# Show pending crypto quotes if user has any #}
|
||||
{% if pending_crypto_quotes %}
|
||||
<div class="warning-banner-alt">
|
||||
<h3>📋 {{ pending_crypto_quotes|length }} Pending Crypto Quote{{ 's' if pending_crypto_quotes|length != 1 else '' }}</h3>
|
||||
<p class="warning-banner-content">You have {{ pending_crypto_quotes|length }} pending crypto payment{{ 's' if pending_crypto_quotes|length != 1 else '' }}:</p>
|
||||
<h3>📋 Pending {{ pending_crypto_quotes[0].coin_type }} Payment</h3>
|
||||
{% for quote in pending_crypto_quotes %}
|
||||
<div class="pending-quote-item">
|
||||
<div class="pending-quote-grid">
|
||||
<div>
|
||||
<strong>{{ quote.coin_type }}</strong>
|
||||
<span class="pending-quote-status">
|
||||
{{ quote.status.title() }}
|
||||
{% if quote.current_confirmations > 0 %}
|
||||
({{ quote.confirmation_status }})
|
||||
{% endif %}
|
||||
</span>
|
||||
</div>
|
||||
<div>
|
||||
<a href="{{ request.route_url('crypto_quote', payment_id=quote.id) }}" class="mps-button view-quote-button">View Quote</a>
|
||||
</div>
|
||||
</div>
|
||||
<div class="pending-quote-details">
|
||||
Amount: {{ '%.8f' % (quote.expected_amount / (1000000000000 if quote.coin_type == 'XMR' else 100000000)) }} {{ quote.coin_type }}
|
||||
</div>
|
||||
<div class="pending-quote-single">
|
||||
<a href="{{ request.route_url('crypto_quote', payment_id=quote.id) }}" class="mps-button mps-button-blue full-width-button">View Quote</a>
|
||||
</div>
|
||||
<div class="pending-quote-cancel">
|
||||
<button onclick="cancelQuote('{{ quote.id }}')" class="mps-button mps-button-red payment-button-cancel full-width-button">Cancel</button>
|
||||
</div>
|
||||
</div>
|
||||
{% endfor %}
|
||||
</div>
|
||||
|
|
@ -169,8 +159,11 @@ function cancelQuote(paymentId) {
|
|||
.then(response => response.json())
|
||||
.then(data => {
|
||||
if (data.success) {
|
||||
alert('Payment quote cancelled successfully');
|
||||
window.location.reload();
|
||||
if (data.cart_url) {
|
||||
window.location.href = data.cart_url;
|
||||
} else {
|
||||
window.location.reload();
|
||||
}
|
||||
} else {
|
||||
alert('Failed to cancel quote: ' + (data.message || 'Unknown error'));
|
||||
}
|
||||
|
|
|
|||
|
|
@ -163,9 +163,6 @@
|
|||
/>
|
||||
|
||||
<br />
|
||||
<br />
|
||||
</div>
|
||||
|
||||
<br />
|
||||
|
||||
{% if request.shop.stripe_enabled %}
|
||||
|
|
@ -173,9 +170,12 @@
|
|||
<input type="submit" name="disable_stripe" class="payment-toggle-button disable" value="Disable Stripe" />
|
||||
{% else %}
|
||||
<input type="submit" name="submit" class="payment-toggle-button enable" value="Re-enable Stripe" />
|
||||
<small class="status-message">Enter your API keys to re-enable Stripe payments</small>
|
||||
{% endif %}
|
||||
|
||||
</div>
|
||||
|
||||
<br />
|
||||
|
||||
<br />
|
||||
<br />
|
||||
|
||||
|
|
@ -504,6 +504,16 @@
|
|||
<br />
|
||||
<br />
|
||||
|
||||
<label for="default_theme_input">Default Theme for Visitors</label>
|
||||
<select name="default_theme" id="default_theme_input" class="full-width-input">
|
||||
<option value="1" {% if default_theme == 1 %}selected{% endif %}>Light Mode</option>
|
||||
<option value="0" {% if default_theme == 0 %}selected{% endif %}>Dark Mode</option>
|
||||
</select>
|
||||
<small class="note-text">Choose the default theme that new visitors see. Users can override this in their profile settings.</small>
|
||||
|
||||
<br />
|
||||
<br />
|
||||
|
||||
<input type="submit" name="submit" class="mps-submit" value="Save Settings" />
|
||||
|
||||
<br />
|
||||
|
|
|
|||
|
|
@ -28,32 +28,16 @@
|
|||
placeholder = "Your {{ coin }} address for automatic refunds"
|
||||
/>
|
||||
|
||||
<br />
|
||||
|
||||
<label for="{{ coin.lower() }}_label_input">Label (Optional)</label>
|
||||
<input
|
||||
name = "label"
|
||||
type = "text"
|
||||
id = "{{ coin.lower() }}_label_input"
|
||||
class = "mps-crypto-label full-width-input"
|
||||
value = "{% if addresses.get(coin) %}{{ addresses[coin].label }}{% endif %}"
|
||||
placeholder = "e.g., My Hardware Wallet"
|
||||
/>
|
||||
|
||||
<br />
|
||||
<br />
|
||||
|
||||
{% if addresses.get(coin) %}
|
||||
<input type="submit" name="submit" class="mps-submit" value="Update {{ coin }} Address" />
|
||||
{% else %}
|
||||
<input type="submit" name="submit" class="mps-submit" value="Save {{ coin }} Address" />
|
||||
{% endif %}
|
||||
<input type="submit" name="submit" class="mps-submit" value="Save {{ coin }} Refund Address" />
|
||||
|
||||
<br />
|
||||
<br />
|
||||
|
||||
</form>
|
||||
<hr />
|
||||
{% if not loop.last %}<hr />{% endif %}
|
||||
{% endfor %}
|
||||
{% else %}
|
||||
<p>No cryptocurrencies are currently enabled on this platform.</p>
|
||||
|
|
|
|||
|
|
@ -35,19 +35,8 @@
|
|||
placeholder = "Full Name"
|
||||
required />
|
||||
|
||||
<!-- not implemented.
|
||||
<fieldset>
|
||||
|
||||
<legend>User Theme</legend>
|
||||
|
||||
<input type="radio" name="user_theme" id="light_mode" value="1" {% if user_theme == 1 %}checked{% endif %} />
|
||||
<label for="user_theme" class="inline-label">Light mode</label>
|
||||
|
||||
<input type="radio" name="user_theme" id="visibility_unlisted" value="0" {% if user_theme == 0 %}checked{% endif %} />
|
||||
<label for="user_theme" class="inline-label">Dark mode</label>
|
||||
|
||||
</fieldset>
|
||||
-->
|
||||
<br />
|
||||
<br />
|
||||
|
||||
<input type="submit" name="submit" class="mps-submit" value="Save Settings" />
|
||||
|
||||
|
|
@ -56,6 +45,53 @@
|
|||
</form>
|
||||
</section>
|
||||
</section>
|
||||
|
||||
<br />
|
||||
|
||||
<section class="one-column">
|
||||
<section class="well">
|
||||
<h3>Theme Preference</h3>
|
||||
|
||||
<form method="post" action="/u/settings" onsubmit="submit.disabled = true; return true;">
|
||||
<input type="radio" name="theme_id" id="light_mode" value="1" {% if theme_id == 1 %}checked{% endif %} />
|
||||
<label for="light_mode" class="inline-label">Light Mode</label>
|
||||
<br />
|
||||
|
||||
<input type="radio" name="theme_id" id="dark_mode" value="0" {% if theme_id == 0 %}checked{% endif %} />
|
||||
<label for="dark_mode" class="inline-label">Dark Mode</label>
|
||||
</form>
|
||||
|
||||
<script>
|
||||
// Handle theme preference form specifically
|
||||
document.addEventListener('DOMContentLoaded', function() {
|
||||
const themeForm = document.querySelector('form:has(input[name="theme_id"])');
|
||||
const themeRadios = document.querySelectorAll('input[name="theme_id"]');
|
||||
|
||||
if (themeForm && themeRadios.length > 0) {
|
||||
themeForm.addEventListener('submit', function() {
|
||||
// Set localStorage to match the selected theme preference
|
||||
const selectedRadio = document.querySelector('input[name="theme_id"]:checked');
|
||||
if (selectedRadio) {
|
||||
const newTheme = selectedRadio.value === '0' ? 'dark' : 'light';
|
||||
localStorage.setItem('theme-preference', newTheme);
|
||||
}
|
||||
});
|
||||
|
||||
// Also update localStorage immediately when radio button changes
|
||||
themeRadios.forEach(function(radio) {
|
||||
radio.addEventListener('change', function() {
|
||||
// Apply new theme immediately
|
||||
const newTheme = this.value === '0' ? 'dark' : 'light';
|
||||
document.documentElement.setAttribute('data-theme', newTheme);
|
||||
// Set localStorage to match user preference so it persists across pages
|
||||
localStorage.setItem('theme-preference', newTheme);
|
||||
});
|
||||
});
|
||||
}
|
||||
});
|
||||
</script>
|
||||
</section>
|
||||
</section>
|
||||
|
||||
<br />
|
||||
|
||||
|
|
|
|||
|
|
@ -382,6 +382,9 @@ def shop_settings(request):
|
|||
ribbon_color_1 = request.params.get("ribbon_color_1", shop.ribbon_color_1).strip()
|
||||
ribbon_color_2 = request.params.get("ribbon_color_2", shop.ribbon_color_2).strip()
|
||||
|
||||
# Theme settings
|
||||
default_theme = request.params.get("default_theme", shop.default_theme)
|
||||
|
||||
google_analytics_id = request.params.get(
|
||||
"google_analytics_id", shop.google_analytics_id
|
||||
)
|
||||
|
|
@ -551,6 +554,16 @@ def shop_settings(request):
|
|||
)
|
||||
request.session.flash(msg)
|
||||
|
||||
# Handle default theme setting
|
||||
if default_theme and int(default_theme) != shop.default_theme:
|
||||
shop.default_theme = int(default_theme)
|
||||
theme_name = "Dark Mode" if int(default_theme) == 0 else "Light Mode"
|
||||
msg = (
|
||||
f"Shop default theme set to {theme_name}.",
|
||||
"success",
|
||||
)
|
||||
request.session.flash(msg)
|
||||
|
||||
# Handle stripe settings form
|
||||
if form_section == "stripe-settings":
|
||||
# Handle disable action
|
||||
|
|
@ -877,6 +890,7 @@ def shop_settings(request):
|
|||
"ribbon_text_color": shop.ribbon_text_color or "",
|
||||
"ribbon_color_1": shop.ribbon_color_1 or "",
|
||||
"ribbon_color_2": shop.ribbon_color_2 or "",
|
||||
"default_theme": shop.default_theme,
|
||||
"google_analytics_id": shop.google_analytics_id or "",
|
||||
"plausible_domain_name": shop.plausible_domain_name or "",
|
||||
"stripe_public_api_key": shop.stripe_public_api_key or "",
|
||||
|
|
|
|||
|
|
@ -50,6 +50,7 @@ def user_settings(request):
|
|||
|
||||
name = request.params.get("name", user.name).strip()
|
||||
full_name = request.params.get("full_name", user.full_name or "").strip()
|
||||
theme_id = request.params.get("theme_id", user.theme_id)
|
||||
|
||||
if name and name != user.name:
|
||||
if not is_user_name_valid(name):
|
||||
|
|
@ -74,9 +75,17 @@ def user_settings(request):
|
|||
msg = ("You set your private full name.", "success")
|
||||
request.session.flash(msg)
|
||||
|
||||
# Handle theme preference changes
|
||||
if theme_id and int(theme_id) != user.theme_id:
|
||||
user.theme_id = int(theme_id)
|
||||
theme_name = "Dark Mode" if int(theme_id) == 0 else "Light Mode"
|
||||
msg = (f"Theme preference set to {theme_name}.", "success")
|
||||
request.session.flash(msg)
|
||||
|
||||
return {
|
||||
"name": name,
|
||||
"full_name": full_name,
|
||||
"theme_id": user.theme_id,
|
||||
}
|
||||
|
||||
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue