From dc7376a37cd306196d03e5170c2c356bfdf7bdf8 Mon Sep 17 00:00:00 2001 From: Andreas Date: Tue, 21 Jan 2025 18:14:10 +0100 Subject: [PATCH] User store extended settings store (#828) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * ➕ Extend subscriber model with additional settings * ➕ Extend user store with additional settings * 🔨 Move settings related functions from utils * 🔨 Replace deprecated exists function with authenticated * 🔨 Use init call for whole user store * 🔨 Load default locale from user settings * 🔨 Only update settings on settings page * 🔨 Fix user store tests * 🔨 Fix user store tests (2nd try) * 📜 Update documentation * Update backend/src/appointment/database/models.py Co-authored-by: Mel <97147377+MelissaAutumn@users.noreply.github.com> * Update backend/src/appointment/database/models.py Co-authored-by: Mel <97147377+MelissaAutumn@users.noreply.github.com> * Update backend/src/appointment/database/schemas.py Co-authored-by: Mel <97147377+MelissaAutumn@users.noreply.github.com> * Update backend/src/appointment/migrations/versions/2025_01_15_1340-4a15d01919b8_add_config_fields_to_subscribers_table.py Co-authored-by: Mel <97147377+MelissaAutumn@users.noreply.github.com> * Update backend/src/appointment/migrations/versions/2025_01_15_1340-4a15d01919b8_add_config_fields_to_subscribers_table.py Co-authored-by: Mel <97147377+MelissaAutumn@users.noreply.github.com> * Update backend/src/appointment/routes/api.py Co-authored-by: Mel <97147377+MelissaAutumn@users.noreply.github.com> * Update backend/src/appointment/routes/auth.py Co-authored-by: Mel <97147377+MelissaAutumn@users.noreply.github.com> * Update docs/README.md Co-authored-by: Mel <97147377+MelissaAutumn@users.noreply.github.com> * 🔨 Fix more colour naming * 🔨 Fix more colour naming --------- Co-authored-by: Mel <97147377+MelissaAutumn@users.noreply.github.com> --- backend/README.md | 8 +- backend/src/appointment/database/models.py | 20 ++- backend/src/appointment/database/schemas.py | 4 + ..._add_config_fields_to_subscribers_table.py | 33 ++++ backend/src/appointment/routes/api.py | 5 +- backend/src/appointment/routes/auth.py | 3 + docs/README.md | 6 +- frontend/index.html | 10 +- frontend/src/App.vue | 35 +++-- frontend/src/components/BookingModal.vue | 6 +- frontend/src/components/CalendarQalendar.vue | 23 +-- frontend/src/components/FTUE/Finish.vue | 7 +- frontend/src/components/FTUE/SetupProfile.vue | 5 +- .../src/components/FTUE/SetupSchedule.vue | 2 +- frontend/src/components/NavBar.vue | 2 +- frontend/src/components/ScheduleCreation.vue | 10 +- frontend/src/components/SettingsAccount.vue | 9 +- .../src/components/SettingsConnections.vue | 2 +- frontend/src/components/SettingsGeneral.vue | 87 ++++------- .../bookingView/BookingViewError.vue | 2 +- .../bookingView/BookingViewSuccess.vue | 2 +- frontend/src/composables/i18n.ts | 5 +- frontend/src/definitions.ts | 4 +- frontend/src/locales/de.json | 2 +- frontend/src/main.ts | 5 +- frontend/src/models.ts | 36 +++-- frontend/src/stores/schedule-store.ts | 6 +- frontend/src/stores/user-store.ts | 144 ++++++++++++++---- frontend/src/utils.ts | 58 ++----- frontend/src/views/ContactView.vue | 2 +- frontend/src/views/LoginView.vue | 8 +- frontend/src/views/LogoutView.vue | 8 +- frontend/src/views/PostLoginView.vue | 7 +- frontend/src/views/ProfileView.vue | 12 +- frontend/test/stores/user-store.test.js | 41 +++-- 35 files changed, 373 insertions(+), 246 deletions(-) create mode 100644 backend/src/appointment/migrations/versions/2025_01_15_1340-4a15d01919b8_add_config_fields_to_subscribers_table.py diff --git a/backend/README.md b/backend/README.md index bc659c1b1..4f44e9db2 100644 --- a/backend/README.md +++ b/backend/README.md @@ -31,7 +31,7 @@ pip install ruff Commands ```bash -ruff check +ruff check ``` ### Authentication @@ -60,6 +60,12 @@ To generate a database migration, bash into a running backend container and run: alembic revision -m "create ... table" ``` +To roll back one migration, run: + +```bash +alembic downgrade -1 +``` + ## Commands Backend has a light selection of cli commands available to be run inside a container. diff --git a/backend/src/appointment/database/models.py b/backend/src/appointment/database/models.py index 08595cdaa..dc8fb6d1c 100644 --- a/backend/src/appointment/database/models.py +++ b/backend/src/appointment/database/models.py @@ -85,6 +85,17 @@ class InviteStatus(enum.Enum): revoked = 2 # The code is no longer valid and cannot be used for sign up anymore +class ColourScheme(enum.Enum): + system = 'system' + dark = 'dark' + light = 'light' + + +class TimeMode(enum.Enum): + h12 = 12 + h24 = 24 + + def encrypted_type(column_type, length: int = 255, **kwargs) -> StringEncryptedType: """Helper to reduce visual noise when creating model columns""" return StringEncryptedType(column_type, secret, AesEngine, 'pkcs5', length=length, **kwargs) @@ -134,12 +145,15 @@ class Subscriber(HasSoftDelete, Base): name = Column(encrypted_type(String), index=True) level = Column(Enum(SubscriberLevel), default=SubscriberLevel.basic, index=True) - language = Column(encrypted_type(String), nullable=False, default=FALLBACK_LOCALE, index=True) - timezone = Column(encrypted_type(String), index=True) avatar_url = Column(encrypted_type(String, length=2048), index=False) - short_link_hash = Column(encrypted_type(String), index=False) + # General settings + language = Column(encrypted_type(String), nullable=False, default=FALLBACK_LOCALE, index=True) + timezone = Column(encrypted_type(String), index=True) + colour_scheme = Column(Enum(ColourScheme), default=ColourScheme.system, nullable=False, index=True) + time_mode = Column(Enum(TimeMode), default=TimeMode.h24, nullable=False, index=True) + # Only accept the times greater than the one specified in the `iat` claim of the jwt token minimum_valid_iat_time = Column('minimum_valid_iat_time', encrypted_type(DateTime)) diff --git a/backend/src/appointment/database/schemas.py b/backend/src/appointment/database/schemas.py index c0d5e94a8..7443238f1 100644 --- a/backend/src/appointment/database/schemas.py +++ b/backend/src/appointment/database/schemas.py @@ -27,6 +27,8 @@ ExternalConnectionType, MeetingLinkProviderType, InviteStatus, + ColourScheme, + TimeMode, ) from .. import utils, defines @@ -299,6 +301,8 @@ class SubscriberIn(BaseModel): avatar_url: str | None = None secondary_email: str | None = None language: str | None = FALLBACK_LOCALE + colour_scheme: ColourScheme = ColourScheme.system + time_mode: TimeMode = TimeMode.h24 class SubscriberBase(SubscriberIn): diff --git a/backend/src/appointment/migrations/versions/2025_01_15_1340-4a15d01919b8_add_config_fields_to_subscribers_table.py b/backend/src/appointment/migrations/versions/2025_01_15_1340-4a15d01919b8_add_config_fields_to_subscribers_table.py new file mode 100644 index 000000000..7302a18d3 --- /dev/null +++ b/backend/src/appointment/migrations/versions/2025_01_15_1340-4a15d01919b8_add_config_fields_to_subscribers_table.py @@ -0,0 +1,33 @@ +"""Add config fields to subscribers table + +Revision ID: 4a15d01919b8 +Revises: 0c99f6a02f3b +Create Date: 2025-01-15 13:40:12.022117 + +""" +import os +from alembic import op +import sqlalchemy as sa +from sqlalchemy_utils import StringEncryptedType +from sqlalchemy_utils.types.encrypted.encrypted_type import AesEngine +from appointment.database.models import ColourScheme, TimeMode + + +def secret(): + return os.getenv('DB_SECRET') + +# revision identifiers, used by Alembic. +revision = '4a15d01919b8' +down_revision = '0c99f6a02f3b' +branch_labels = None +depends_on = None + + +def upgrade() -> None: + op.add_column('subscribers', sa.Column('colour_scheme', sa.Enum(ColourScheme), default=ColourScheme.system, nullable=False, index=True)) + op.add_column('subscribers', sa.Column('time_mode', sa.Enum(TimeMode), default=TimeMode.h24, nullable=False, index=True)) + + +def downgrade() -> None: + op.drop_column('subscribers', 'colour_scheme') + op.drop_column('subscribers', 'time_mode') diff --git a/backend/src/appointment/routes/api.py b/backend/src/appointment/routes/api.py index 99a4d96df..39eca8ec4 100644 --- a/backend/src/appointment/routes/api.py +++ b/backend/src/appointment/routes/api.py @@ -82,7 +82,10 @@ def update_me( is_setup=me.is_setup, avatar_url=me.avatar_url, schedule_links=schedule_links_by_subscriber(db, subscriber), - unique_hash=me.unique_hash + unique_hash=me.unique_hash, + language=me.language, + colour_scheme=me.colour_scheme, + time_mode=me.time_mode, ) diff --git a/backend/src/appointment/routes/auth.py b/backend/src/appointment/routes/auth.py index 9d8ec3904..2afc007c1 100644 --- a/backend/src/appointment/routes/auth.py +++ b/backend/src/appointment/routes/auth.py @@ -388,6 +388,9 @@ def me( is_setup=subscriber.is_setup, schedule_links=schedule_links_by_subscriber(db, subscriber), unique_hash=hash, + language=subscriber.language, + colour_scheme=subscriber.colour_scheme, + time_mode=subscriber.time_mode, ) diff --git a/docs/README.md b/docs/README.md index 3c50291b1..d2bec2a4d 100644 --- a/docs/README.md +++ b/docs/README.md @@ -69,16 +69,18 @@ erDiagram string email "FxA account email and email used for password auth" string name "Preferred display name" enum level "Subscription level [basic, plus, pro, admin]" - int timezone "User selected home timezone, UTC offset" string avatar_url "Public link to an avatar image" string short_link_hash "Hash for verifying user link" + string language "Lang code for subscribers preferred locale" + int timezone "User selected home timezone, UTC offset" + enum colour_scheme "Frontend theme [system, dark, light]" + enum time_mode "Format for displaying times [h12, h24]" string minimum_valid_iat_time "Minimum valid time to accept for JWT tokens" date time_created "UTC timestamp of subscriber creation" date time_updated "UTC timestamp of last subscriber modification" string secondary_email "Secondary email address" date time_deleted "UTC timestamp of deletion (soft delete)" int ftue_level "Version of the FTUE the user has completed" - string language "Lang code for subscribers preferred locale" } SUBSCRIBERS ||--o{ CALENDARS : own CALENDARS { diff --git a/frontend/index.html b/frontend/index.html index 6e670f758..cf4d73d3f 100644 --- a/frontend/index.html +++ b/frontend/index.html @@ -6,10 +6,14 @@ Thunderbird Appointment