🧙♂️ Transform LifeRPG into The Wizard's Grimoire - Production-Ready Application
✨ Major Features Added: - Complete magical theming and rebranding from LifeRPG to The Wizard's Grimoire - Production-grade React frontend with Tailwind CSS v4 and magical aesthetics - Comprehensive analytics dashboard with Recharts integration (ScryingPortal) - Push notifications system with PWA service worker support - Drag & drop functionality using @dnd-kit for habit reordering - Social features with friends system and leaderboards - Performance optimization tools and monitoring - Mobile app enhancement with PWA installation support 🏗️ Technical Infrastructure: - Advanced service worker with offline support and background sync - Zustand state management for scalable application state - Production-ready UI component system with enhanced Button, Card, Input - Progressive Web App (PWA) with manifest and app installation - FastAPI backend with comprehensive API endpoints - Docker containerization and CI/CD pipeline setup 📱 Progressive Web App Features: - Offline functionality with intelligent caching - Push notification support for habit reminders - App installation on mobile and desktop platforms - Background sync for offline data management - Performance monitoring and optimization tools 🎨 User Experience: - Magical wizard/grimoire theming throughout application - Responsive design optimized for all device sizes - Drag & drop habit management with smooth animations - Interactive analytics with multiple chart types - Social connectivity with friends and competitive features - Comprehensive notification and performance settings 🔧 Developer Experience: - Modern development stack with Vite and React - Comprehensive testing setup and CI/CD pipelines - Code quality tools with pre-commit hooks - Docker development environment - Detailed documentation and implementation guides This represents a complete transformation from prototype to production-ready application with enterprise-grade features and magical user experience.
This commit is contained in:
@@ -0,0 +1,62 @@
|
||||
import os
|
||||
from logging.config import fileConfig
|
||||
from sqlalchemy import engine_from_config, pool
|
||||
from alembic import context
|
||||
|
||||
# this is the Alembic Config object, which provides access to the values within the .ini file in use.
|
||||
config = context.config
|
||||
|
||||
# Interpret the config file for Python logging.
|
||||
if config.config_file_name is not None:
|
||||
fileConfig(config.config_file_name)
|
||||
|
||||
# add your model's MetaData object here for 'autogenerate' support
|
||||
from modern.backend import models # noqa: E402
|
||||
|
||||
target_metadata = models.Base.metadata
|
||||
|
||||
def get_url():
|
||||
return os.getenv('DATABASE_URL', 'sqlite:///./modern_dev.db')
|
||||
|
||||
|
||||
def run_migrations_offline():
|
||||
url = get_url()
|
||||
context.configure(
|
||||
url=url,
|
||||
target_metadata=target_metadata,
|
||||
literal_binds=True,
|
||||
dialect_opts={"paramstyle": "named"},
|
||||
compare_type=True,
|
||||
compare_server_default=True,
|
||||
)
|
||||
|
||||
with context.begin_transaction():
|
||||
context.run_migrations()
|
||||
|
||||
|
||||
def run_migrations_online():
|
||||
configuration = config.get_section(config.config_ini_section)
|
||||
configuration["sqlalchemy.url"] = get_url()
|
||||
|
||||
connectable = engine_from_config(
|
||||
configuration,
|
||||
prefix="sqlalchemy.",
|
||||
poolclass=pool.NullPool,
|
||||
)
|
||||
|
||||
with connectable.connect() as connection:
|
||||
context.configure(
|
||||
connection=connection,
|
||||
target_metadata=target_metadata,
|
||||
compare_type=True,
|
||||
compare_server_default=True,
|
||||
)
|
||||
|
||||
with context.begin_transaction():
|
||||
context.run_migrations()
|
||||
|
||||
|
||||
if context.is_offline_mode():
|
||||
run_migrations_offline()
|
||||
else:
|
||||
run_migrations_online()
|
||||
@@ -0,0 +1,32 @@
|
||||
"""add integration item map with unique constraint
|
||||
|
||||
Revision ID: 0001_add_integration_item_map
|
||||
Revises:
|
||||
Create Date: 2025-08-28 00:00:00.000000
|
||||
|
||||
"""
|
||||
from alembic import op
|
||||
import sqlalchemy as sa
|
||||
|
||||
# revision identifiers, used by Alembic.
|
||||
revision = '0001_add_integration_item_map'
|
||||
down_revision = None
|
||||
branch_labels = None
|
||||
depends_on = None
|
||||
|
||||
def upgrade():
|
||||
op.create_table(
|
||||
'integration_item_map',
|
||||
sa.Column('id', sa.Integer(), primary_key=True),
|
||||
sa.Column('integration_id', sa.Integer(), nullable=False),
|
||||
sa.Column('external_id', sa.String(), nullable=False),
|
||||
sa.Column('entity_type', sa.String(), nullable=False),
|
||||
sa.Column('entity_id', sa.Integer(), nullable=False),
|
||||
sa.Column('updated_at', sa.DateTime(), server_default=sa.text('CURRENT_TIMESTAMP')),
|
||||
sa.Column('created_at', sa.DateTime(), server_default=sa.text('CURRENT_TIMESTAMP')),
|
||||
sa.UniqueConstraint('integration_id', 'external_id', 'entity_type', name='uq_integration_item'),
|
||||
)
|
||||
|
||||
|
||||
def downgrade():
|
||||
op.drop_table('integration_item_map')
|
||||
@@ -0,0 +1,25 @@
|
||||
"""add status/due_date/labels to habit
|
||||
|
||||
Revision ID: 0002_add_habit_fields
|
||||
Revises: 0001_add_integration_item_map
|
||||
Create Date: 2025-08-28 00:10:00.000000
|
||||
|
||||
"""
|
||||
from alembic import op
|
||||
import sqlalchemy as sa
|
||||
|
||||
revision = '0002_add_habit_fields'
|
||||
down_revision = '0001_add_integration_item_map'
|
||||
branch_labels = None
|
||||
depends_on = None
|
||||
|
||||
def upgrade():
|
||||
op.add_column('habits', sa.Column('status', sa.String(), server_default='active'))
|
||||
op.add_column('habits', sa.Column('due_date', sa.DateTime(), nullable=True))
|
||||
op.add_column('habits', sa.Column('labels', sa.Text(), nullable=True))
|
||||
|
||||
|
||||
def downgrade():
|
||||
op.drop_column('habits', 'labels')
|
||||
op.drop_column('habits', 'due_date')
|
||||
op.drop_column('habits', 'status')
|
||||
@@ -0,0 +1,33 @@
|
||||
"""add public_tokens table
|
||||
|
||||
Revision ID: 0004_add_public_tokens
|
||||
Revises: 0002_add_habit_fields
|
||||
Create Date: 2025-08-28 00:30:00.000000
|
||||
|
||||
"""
|
||||
from alembic import op
|
||||
import sqlalchemy as sa
|
||||
|
||||
|
||||
# revision identifiers, used by Alembic.
|
||||
revision = '0004_add_public_tokens'
|
||||
down_revision = '0002_add_habit_fields'
|
||||
branch_labels = None
|
||||
depends_on = None
|
||||
|
||||
|
||||
def upgrade():
|
||||
op.create_table(
|
||||
'public_tokens',
|
||||
sa.Column('id', sa.Integer(), primary_key=True),
|
||||
sa.Column('user_id', sa.Integer(), sa.ForeignKey('users.id'), nullable=False),
|
||||
sa.Column('name', sa.String(), nullable=False),
|
||||
sa.Column('scope', sa.String(), server_default='read:widgets'),
|
||||
sa.Column('token_hash', sa.String(), nullable=False, unique=True),
|
||||
sa.Column('created_at', sa.DateTime(), server_default=sa.text('CURRENT_TIMESTAMP')),
|
||||
sa.Column('last_used_at', sa.DateTime(), nullable=True),
|
||||
)
|
||||
|
||||
|
||||
def downgrade():
|
||||
op.drop_table('public_tokens')
|
||||
@@ -0,0 +1,32 @@
|
||||
"""add oidc_login_state table
|
||||
|
||||
Revision ID: 0005_add_oidc_login_state
|
||||
Revises: 0004_add_public_tokens
|
||||
Create Date: 2025-08-28 00:40:00.000000
|
||||
|
||||
"""
|
||||
from alembic import op
|
||||
import sqlalchemy as sa
|
||||
|
||||
|
||||
revision = '0005_add_oidc_login_state'
|
||||
down_revision = '0004_add_public_tokens'
|
||||
branch_labels = None
|
||||
depends_on = None
|
||||
|
||||
|
||||
def upgrade():
|
||||
op.create_table(
|
||||
'oidc_login_state',
|
||||
sa.Column('id', sa.Integer(), primary_key=True),
|
||||
sa.Column('state', sa.String(), unique=True, nullable=False),
|
||||
sa.Column('provider', sa.String(), nullable=False),
|
||||
sa.Column('code_verifier', sa.String(), nullable=False),
|
||||
sa.Column('redirect_to', sa.String(), nullable=True),
|
||||
sa.Column('created_at', sa.DateTime(), server_default=sa.text('CURRENT_TIMESTAMP')),
|
||||
sa.Column('expires_at', sa.DateTime(), nullable=True),
|
||||
)
|
||||
|
||||
|
||||
def downgrade():
|
||||
op.drop_table('oidc_login_state')
|
||||
@@ -0,0 +1,27 @@
|
||||
"""add totp fields to users
|
||||
|
||||
Revision ID: 0006_add_totp_fields
|
||||
Revises: 0005_add_oidc_login_state
|
||||
Create Date: 2025-08-28 01:05:00.000000
|
||||
|
||||
"""
|
||||
from alembic import op
|
||||
import sqlalchemy as sa
|
||||
|
||||
|
||||
revision = '0006_add_totp_fields'
|
||||
down_revision = '0005_add_oidc_login_state'
|
||||
branch_labels = None
|
||||
depends_on = None
|
||||
|
||||
|
||||
def upgrade():
|
||||
op.add_column('users', sa.Column('totp_secret', sa.String(), nullable=True))
|
||||
op.add_column('users', sa.Column('totp_enabled', sa.Integer(), server_default='0'))
|
||||
op.add_column('users', sa.Column('recovery_codes', sa.Text(), nullable=True))
|
||||
|
||||
|
||||
def downgrade():
|
||||
op.drop_column('users', 'recovery_codes')
|
||||
op.drop_column('users', 'totp_enabled')
|
||||
op.drop_column('users', 'totp_secret')
|
||||
Reference in New Issue
Block a user