Skip to content

Conversation

@ysmoradi
Copy link
Member

@ysmoradi ysmoradi commented Nov 30, 2025

closes #11779

Summary by CodeRabbit

  • New Features

    • Added offline Todo management with full sync capabilities, supporting add, edit, delete, search, sort, and filter operations.
  • Documentation

    • Added Data Synchronization section to Entity Framework Core documentation with implementation conventions.
    • Updated Blazor navigation and PWA documentation to reflect offline todo functionality.

✏️ Tip: You can customize this high-level summary in your review settings.

@coderabbitai
Copy link

coderabbitai bot commented Nov 30, 2025

Important

Review skipped

Auto incremental reviews are disabled on this repository.

Please check the settings in the CodeRabbit UI or the .coderabbit.yaml file in this repository. To trigger a single review, invoke the @coderabbitai review command.

You can disable this status message by setting the reviews.review_status to false in the CodeRabbit configuration file.

Note

Other AI code review bot(s) detected

CodeRabbit has detected other AI code review bot(s) in this pull request and will avoid duplicating their findings in the review comments. This may lead to a less comprehensive review.

Walkthrough

This PR implements offline-first synchronization for Todo items in the Bit Boilerplate template. It introduces a new SyncService leveraging CommunityToolkit.DataSync, replaces the generic OfflineDatabaseDemoPage with a functional OfflineTodoPage, updates the database schema from UserDto to TodoItemDto with sync tables, and reconfigures navigation, URLs, and server-side APIs to support the feature.

Changes

Cohort / File(s) Summary
Offline Sync Infrastructure
src/Client/Boilerplate.Client.Core/Services/SyncService.cs, src/Client/Boilerplate.Client.Core/Data/AppOfflineDbContext.cs, src/Client/Boilerplate.Client.Core/Data/CompiledModel/AppOfflineDbContextModel*.cs, src/Client/Boilerplate.Client.Core/Extensions/IClientCoreServiceCollectionExtensions.cs, src/Server/Boilerplate.Server.Api/Program.Services.cs
New SyncService class implementing Push/Pull/Sync operations with error handling and telemetry. AppOfflineDbContext now extends OfflineDbContext, adds TodoItems DbSet, SaveChangesAsync override for UpdatedAt tracking, and OnDatasyncInitialization configuration. Registers Datasync services conditionally.
Datasync Entity Type Mappings
src/Client/Boilerplate.Client.Core/Data/CompiledModel/DatasyncDeltaTokenEntityType.cs, src/Client/Boilerplate.Client.Core/Data/CompiledModel/DatasyncOperationEntityType.cs, src/Client/Boilerplate.Client.Core/Data/CompiledModel/TodoItemDtoEntityType.cs
New EF Core runtime entity type mappings for Datasync infrastructure (DeltaToken, Operation) and TodoItemDto, replacing removed UserDtoEntityType.
Database Migrations
src/Client/Boilerplate.Client.Core/Data/Migrations/20251128225322_Initial.* (removed), src/Client/Boilerplate.Client.Core/Data/Migrations/20251130225551_Initial.* (added), src/Client/Boilerplate.Client.Core/Data/Migrations/AppOfflineDbContextModelSnapshot.cs
Old migration creating Users table removed; new migration creates TodoItems, DatasyncDeltaTokens, and DatasyncOperationsQueue tables. Model snapshot updated to reflect TodoItemDto-based schema.
Offline Todo Feature
src/Client/Boilerplate.Client.Core/Components/Pages/OfflineTodoPage.razor* (new), src/Client/Boilerplate.Client.Core/Components/Pages/OfflineDatabaseDemoPage.razor* (removed)
New comprehensive OfflineTodoPage component with CRUD, filtering, sorting, search, edit mode, and delete dialogs. Includes backing logic class and SCSS styling. Removed generic OfflineDatabaseDemoPage component.
Server Todo API
src/Server/Boilerplate.Server.Api/Controllers/Todo/TodoItemController.cs, src/Server/Boilerplate.Server.Api/Controllers/Todo/TodoItemTableController.cs (new)
TodoItemController Get/Delete methods changed from Guid to string IDs. New TodoItemTableController with TableController inheritance and TodoItemTableRepository for user-scoped CRUD via Datasync.
Todo Data Models
src/Shared/Dtos/Todo/TodoItemDto.cs, src/Server/Boilerplate.Server.Api/Models/Todo/TodoItem.cs, src/Shared/Controllers/Todo/ITodoItemController.cs
TodoItemDto: Id changed from Guid to string, UpdatedAt now nullable, added Deleted and Version properties. TodoItem: conditional inheritance from BaseEntityTableData when offlineDb enabled. ITodoItemController: Get/Delete signatures updated to use string IDs.
Navigation & URLs
src/Shared/PageUrls.cs, src/Client/Boilerplate.Client.Core/Components/Layout/NavBar.razor, src/Client/Boilerplate.Client.Core/Components/Layout/MainLayout.razor.items.cs, src/Client/Boilerplate.Client.Maui/Platforms/Android/MainActivity.cs, src/Client/Boilerplate.Client.Web/wwwroot/manifest.json, src/Client/Boilerplate.Client.Web/wwwroot/service-worker.published.js
New OfflineTodo page URL constant. Removed OfflineDatabaseDemo URL. Navigation items refactored to show OfflineTodo when offlineDb enabled, Todo when sample enabled. Updated deep links and manifest shortcuts.
Database Configuration & JSON Context
src/Server/Boilerplate.Server.Api/Data/AppDbContext.cs, src/Shared/Dtos/AppJsonContext.cs, src/Server/Boilerplate.Server.Api/Models/Identity/User.cs
AppDbContext: Todo condition expanded to `(sample == true
Package Dependencies
src/Client/Boilerplate.Client.Core/Boilerplate.Client.Core.csproj, src/Server/Boilerplate.Server.Api/Boilerplate.Server.Api.csproj, src/Directory.Packages.props
Added conditional PackageReferences for CommunityToolkit.Datasync.Client (client), CommunityToolkit.Datasync.Server and Server.EntityFrameworkCore (server) when offlineDb enabled.
Resource Strings
src/Shared/Resources/AppStrings.resx, src/Shared/Resources/AppStrings.fa.resx
OfflineDatabaseDemoTitle renamed to OfflineTodoTitle. Added new strings: SyncPushFailed, SyncPullFailed, SyncPushSuccess for sync operation feedback.
Documentation & Configuration
.docs/01- Entity Framework Core.md, .docs/08- Blazor Pages, Components, Styling & Navigation.md, .docs/12- Blazor Modes, PreRendering & PWA.md, .github/copilot-instructions.md, .github/prompts/getting-started.prompt.md (removed), .template.config/template.json
EF Core docs: added Data Synchronization section. Removed several route constants from PageUrls docs. Updated demo links. Copilot instructions: replaced getting-started directive with RTL handling. Removed entire getting-started.prompt.md file. Template config: broadened sample/offlineDb exclusion conditions.
Server Configuration
src/Server/Boilerplate.Server.Api/Data/Configurations/Chatbot/SystemPromptConfiguration.cs, src/Server/Boilerplate.Server.Api/Mappers/TodoMapper.cs
SystemPrompt: refactored conditional preprocessor blocks, added Offline Todo List section. TodoMapper: minor file encoding adjustment.
Website References
src/Websites/Platform/src/Bit.Websites.Platform.Client/Shared/Urls.cs
Updated TodoOfflineWebApp URL from /offline-database-demo to /offline-todo.

Sequence Diagram

sequenceDiagram
    actor User
    participant OfflineTodoPage as Offline Todo Page
    participant SyncService
    participant DbContext as App Offline DbContext
    participant LocalDb as SQLite (Local)
    participant HttpClient
    participant Server as Todo API Server

    User->>OfflineTodoPage: Load page / Trigger sync
    OfflineTodoPage->>SyncService: Call Sync()
    
    rect rgb(200, 220, 240)
    Note over SyncService,Server: Push Phase
    SyncService->>DbContext: Create context
    SyncService->>LocalDb: Read pending changes
    SyncService->>HttpClient: POST changes to server
    HttpClient->>Server: Send modified/added/deleted items
    Server->>Server: Process & persist
    Server-->>HttpClient: Return result
    HttpClient-->>SyncService: Success/error response
    end
    
    rect rgb(220, 240, 200)
    Note over SyncService,LocalDb: Pull Phase (optional)
    SyncService->>HttpClient: GET recent changes
    HttpClient->>Server: Request delta
    Server-->>HttpClient: Return changes
    HttpClient-->>SyncService: Changes received
    SyncService->>LocalDb: Merge into local DB
    SyncService->>DbContext: SaveChangesAsync()
    end
    
    alt Success
        SyncService->>OfflineTodoPage: Show success message
        OfflineTodoPage->>User: Display ✓ sync confirmation
    else Error
        SyncService->>OfflineTodoPage: Wrap error in BadRequestException
        OfflineTodoPage->>User: Display ✗ error message
    end
Loading

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~60 minutes

  • High-density logic: SyncService implements async push/pull with cancellation token management, error wrapping, and telemetry integration. OfflineTodoPage manages multi-faceted local state (filtering, sorting, search, edit mode) with virtualized list rendering.
  • Heterogeneous changes: Modifications span UI components (Razor/C#/SCSS), database schema and migrations, server API controllers, DTOs across shared/client/server projects, and conditional compilation patterns.
  • Schema redesign: Database migration from Users to TodoItems, addition of Datasync infrastructure tables, changes to entity types across compiled model files.
  • Cross-cutting concerns: Updates affect navigation, URLs, resource strings, package dependencies, and template configuration across multiple layers.

Areas requiring extra attention:

  • SyncService.cs: Validate async/await patterns, cancellation token propagation, and error-handling strategy with exception wrapping.
  • OfflineTodoPage.razor.cs: Verify FilterViewTodoItems logic correctness, edit mode state machine, and synchronization hooks after mutations.
  • AppOfflineDbContext.cs: Confirm SaveChangesAsync UpdatedAt logic applies only to entities with the property; validate OnDatasyncInitialization HttpClient and conflict resolution setup.
  • TodoItemTableController.cs & TodoItemTableRepository: Validate user-scoped filtering (AsQueryableAsync) and ensure CreateAsync/ReplaceAsync correctly assign UserId.
  • Database migrations & model snapshot: Cross-check entity mappings (TodoItemDto, DatasyncDeltaToken, DatasyncOperation) against generated model files for consistency.
  • Conditional compilation: Review all preprocessor directives (#if offlineDb, sample, etc.) to ensure correct feature gating across client/server/shared code.

Poem

🐰 A Tale of Offline Hops

Once our todos lived alone, in caves so dark and deep,
Now they sync with gentle push, no data lost in sleep!
Pull the changes, merge with grace, through CommunityToolkit's embrace,
Offline first, then back online—a rabbit's perfect trace! 🌙✨

Pre-merge checks and finishing touches

❌ Failed checks (1 warning)
Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 7.94% which is insufficient. The required threshold is 80.00%. You can run @coderabbitai generate docstrings to improve docstring coverage.
✅ Passed checks (4 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title accurately and concisely summarizes the primary change: adding a sync feature to the offline database in the bit Boilerplate client app.
Linked Issues check ✅ Passed The PR successfully implements the synchronization feature for the offline database as required by issue #11779, including SyncService, data synchronization infrastructure, and offline-first Todo management.
Out of Scope Changes check ✅ Passed All changes are scoped to implementing the offline database sync feature, including replacing the OfflineDatabaseDemo with OfflineTodoPage, updating documentation, and removing obsolete structures.

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 4

🧹 Nitpick comments (14)
src/Templates/Boilerplate/Bit.Boilerplate/.github/copilot-instructions.md (1)

102-102: Clarify the relationship between this documentation change and the sync feature PR objective.

This line adds guidance for RTL language handling in Copilot responses. While the directive is clearly worded and the technical content exclusion is well-specified, this documentation change appears orthogonal to the primary PR objective of adding offline-database sync functionality.

Consider clarifying whether this is:

  1. A necessary supporting change for the sync feature implementation, or
  2. A separate improvement that should be in its own PR for easier tracking and review.

If this belongs in this PR, consider updating the PR description to reflect the documentation improvement alongside the sync feature work.

src/Templates/Boilerplate/Bit.Boilerplate/src/Client/Boilerplate.Client.Core/Extensions/IClientCoreServiceCollectionExtensions.cs (3)

137-139: Avoid Task.Run around MigrateAsync in dbContextInitializer

dbContext.Database.MigrateAsync() is already asynchronous; wrapping it in Task.Run adds an extra layer of tasks, unnecessary thread-pool usage, and makes the initializer harder to read without functional benefit.

You can simplify to a single await on the migrator:

-        }
-        , dbContextInitializer: async (sp, dbContext) => await Task.Run(async () => await dbContext.Database.MigrateAsync())
-        , lifetime: ServiceLifetime.Scoped);
+        }
+        , dbContextInitializer: async (sp, dbContext) => await dbContext.Database.MigrateAsync()
+        , lifetime: ServiceLifetime.Scoped);

137-139: Confirm intended lifetime for AppOfflineDbContext factory

You’ve explicitly set the BESQL DbContext factory lifetime to ServiceLifetime.Scoped. That’s fine for Blazor Server/WebAssembly, but in Blazor Hybrid you currently use AddSessioned elsewhere to effectively share services between the two scopes for a single client app.

If you want the offline DbContext factory to follow the same “per client session” semantics as other sessioned services, consider:

-        , lifetime: ServiceLifetime.Scoped);
+        , lifetime: AppPlatform.IsBlazorHybrid ? ServiceLifetime.Singleton : ServiceLifetime.Scoped);

Otherwise, please double-check that having separate scoped factories per Hybrid scope is intentional.


140-140: Consider using AddSessioned for SyncService in Hybrid scenarios

SyncService holds state (CancellationTokenSource) and coordinates sync across the client session. With AddScoped<SyncService>(), Blazor Hybrid will get two separate instances (one per scope), unlike other “sessioned” services here that use AddSessioned to share a single instance across scopes.

If you want consistent per-client behavior (especially for cancellation and snackbar feedback), consider:

-        services.AddScoped<SyncService>();
+        services.AddSessioned<SyncService>();

If SyncService is only ever used from the Blazor WebView scope, keeping it scoped is also fine—just confirm that’s the case.

src/Templates/Boilerplate/Bit.Boilerplate/src/Client/Boilerplate.Client.Core/Components/Pages/OfflineTodoPage.razor.scss (1)

1-53: LGTM!

The SCSS follows project conventions with proper imports, responsive breakpoints, and Blazor-compatible ::deep scoping. The layout and responsive adjustments are well-structured.

Consider extracting the magic numbers in height calculations (14rem, 17rem, 18rem on lines 44, 47, 51) into named SCSS variables for maintainability, especially if these offsets are reused elsewhere or need coordinated updates with header/footer heights.

src/Templates/Boilerplate/Bit.Boilerplate/src/Client/Boilerplate.Client.Core/Components/Pages/TodoPage.razor (1)

90-90: Consider adding null safety for UpdatedAt display.

The null-forgiving operator (!) assumes UpdatedAt is never null when rendering. While this may be valid for synced items, consider a safer approach to handle edge cases where UpdatedAt might be null (e.g., newly created items before first sync).

-<BitText Typography="BitTypography.Body2">@todo.UpdatedAt!.Value.ToLocalTime().ToString("F")</BitText>
+<BitText Typography="BitTypography.Body2">@(todo.UpdatedAt?.ToLocalTime().ToString("F") ?? "-")</BitText>
src/Templates/Boilerplate/Bit.Boilerplate/src/Server/Boilerplate.Server.Api/Controllers/Todo/TodoItemController.cs (2)

34-41: Consider adding input validation for the string id parameter.

The ID type change from Guid to string aligns with CommunityToolkit.Datasync requirements. However, string IDs should be validated for null/empty values before querying.

 [HttpGet("{id}")]
 public async Task<TodoItemDto> Get(string id, CancellationToken cancellationToken)
 {
+    ArgumentException.ThrowIfNullOrWhiteSpace(id);
+
     var dto = await Get().FirstOrDefaultAsync(t => t.Id == id, cancellationToken)
         ?? throw new ResourceNotFoundException(Localizer[nameof(AppStrings.ToDoItemCouldNotBeFound)]);

     return dto;
 }

72-81: Add input validation for the Delete method as well.

For consistency with the suggested change in Get, the Delete method should also validate the id parameter.

 [HttpDelete("{id}")]
 public async Task Delete(string id, CancellationToken cancellationToken)
 {
+    ArgumentException.ThrowIfNullOrWhiteSpace(id);
+
     DbContext.TodoItems.Remove(new() { Id = id });

     var affectedRows = await DbContext.SaveChangesAsync(cancellationToken);

     if (affectedRows < 1)
         throw new ResourceNotFoundException(Localizer[nameof(AppStrings.ToDoItemCouldNotBeFound)]);
 }
src/Templates/Boilerplate/Bit.Boilerplate/src/Shared/Dtos/Todo/TodoItemDto.cs (1)

17-17: Default value for UpdatedAt is set at object construction time.

Setting DateTimeOffset.UtcNow as a default value means each new TodoItemDto instance captures the time when the object was instantiated, not when it's persisted. This is likely fine since SaveChangesAsync in AppOfflineDbContext overwrites UpdatedAt before saving. However, consider whether the default should be null instead to make it clearer that the actual value is set during persistence.

src/Templates/Boilerplate/Bit.Boilerplate/src/Client/Boilerplate.Client.Core/Data/AppOfflineDbContext.cs (1)

48-54: Consider documenting the ClientWinsConflictResolver strategy.

Using ClientWinsConflictResolver means local offline changes always overwrite server data during sync conflicts. This is a valid strategy for offline-first apps, but it may cause unintended data loss if multiple clients edit the same item. Consider adding a code comment explaining this design decision, or evaluate whether ServerWinsConflictResolver or a custom resolver might be more appropriate depending on the use case.

src/Templates/Boilerplate/Bit.Boilerplate/src/Client/Boilerplate.Client.Core/Services/SyncService.cs (2)

36-48: Potential race condition in CancellationTokenSource management.

There's a race condition between lines 38-41: if Sync is called concurrently from multiple threads, the assignment cts = new() on line 41 could be interleaved with another thread reading cts on line 38. Consider using Interlocked.Exchange for thread-safe swapping.

 private async Task Sync(CancellationToken cancellationToken, bool pullRecentChanges = true)
 {
-    using var localCts = cts;
-    await localCts.TryCancel();
-
-    cts = new();
+    var newCts = new CancellationTokenSource();
+    var oldCts = Interlocked.Exchange(ref cts, newCts);
+    await oldCts.TryCancel();
+    oldCts.Dispose();

-    using var linkedCts = CancellationTokenSource.CreateLinkedTokenSource(cancellationToken, cts.Token);
+    using var linkedCts = CancellationTokenSource.CreateLinkedTokenSource(cancellationToken, newCts.Token);

     await using var dbContext = await dbContextFactory.CreateDbContextAsync(linkedCts.Token);

     await Sync(dbContext, pullRecentChanges, linkedCts.Token);
 }

21-24: Consider accepting a CancellationToken in Push() for consistency.

Unlike Pull and Sync, Push passes default for the cancellation token, meaning callers cannot cancel push operations. If this is intentional (e.g., pushes should always complete), consider documenting this behavior.

src/Templates/Boilerplate/Bit.Boilerplate/src/Client/Boilerplate.Client.Core/Components/Pages/OfflineTodoPage.razor (1)

86-89: Consider using Value instead of DefaultValue for checkbox.

Using DefaultValue means the checkbox won't update if todo.IsDone changes from an external source (e.g., pull sync). If bi-directional binding is needed, consider using Value with a controlled pattern.

src/Templates/Boilerplate/Bit.Boilerplate/src/Client/Boilerplate.Client.Core/Components/Pages/OfflineTodoPage.razor.cs (1)

189-205: Consider validating underEditTodoItemTitle before saving.

underEditTodoItemTitle is nullable (string?), and setting todoItem.Title to null when the schema requires Title could cause database constraint violations. Consider adding validation similar to AddTodoItem which checks string.IsNullOrWhiteSpace(newTodoTitle).

 private async Task SaveTodoItem(TodoItemDto todoItem)
 {
-    if (isLoading) return;
+    if (isLoading || string.IsNullOrWhiteSpace(underEditTodoItemTitle)) return;

     isLoading = true;
📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

Knowledge base: Disabled due to Reviews -> Disable Knowledge Base setting

📥 Commits

Reviewing files that changed from the base of the PR and between 832e496 and e4344fc.

📒 Files selected for processing (50)
  • src/Templates/Boilerplate/Bit.Boilerplate/.docs/01- Entity Framework Core.md (2 hunks)
  • src/Templates/Boilerplate/Bit.Boilerplate/.docs/08- Blazor Pages, Components, Styling & Navigation.md (0 hunks)
  • src/Templates/Boilerplate/Bit.Boilerplate/.docs/12- Blazor Modes, PreRendering & PWA.md (1 hunks)
  • src/Templates/Boilerplate/Bit.Boilerplate/.github/copilot-instructions.md (1 hunks)
  • src/Templates/Boilerplate/Bit.Boilerplate/.github/prompts/getting-started.prompt.md (0 hunks)
  • src/Templates/Boilerplate/Bit.Boilerplate/.template.config/template.json (2 hunks)
  • src/Templates/Boilerplate/Bit.Boilerplate/src/Client/Boilerplate.Client.Core/Boilerplate.Client.Core.csproj (1 hunks)
  • src/Templates/Boilerplate/Bit.Boilerplate/src/Client/Boilerplate.Client.Core/Components/Layout/MainLayout.razor.items.cs (1 hunks)
  • src/Templates/Boilerplate/Bit.Boilerplate/src/Client/Boilerplate.Client.Core/Components/Layout/NavBar.razor (2 hunks)
  • src/Templates/Boilerplate/Bit.Boilerplate/src/Client/Boilerplate.Client.Core/Components/Pages/OfflineDatabaseDemoPage.razor (0 hunks)
  • src/Templates/Boilerplate/Bit.Boilerplate/src/Client/Boilerplate.Client.Core/Components/Pages/OfflineDatabaseDemoPage.razor.cs (0 hunks)
  • src/Templates/Boilerplate/Bit.Boilerplate/src/Client/Boilerplate.Client.Core/Components/Pages/OfflineDatabaseDemoPage.razor.scss (0 hunks)
  • src/Templates/Boilerplate/Bit.Boilerplate/src/Client/Boilerplate.Client.Core/Components/Pages/OfflineTodoPage.razor (1 hunks)
  • src/Templates/Boilerplate/Bit.Boilerplate/src/Client/Boilerplate.Client.Core/Components/Pages/OfflineTodoPage.razor.cs (1 hunks)
  • src/Templates/Boilerplate/Bit.Boilerplate/src/Client/Boilerplate.Client.Core/Components/Pages/OfflineTodoPage.razor.scss (1 hunks)
  • src/Templates/Boilerplate/Bit.Boilerplate/src/Client/Boilerplate.Client.Core/Components/Pages/TodoPage.razor (1 hunks)
  • src/Templates/Boilerplate/Bit.Boilerplate/src/Client/Boilerplate.Client.Core/Data/AppOfflineDbContext.cs (2 hunks)
  • src/Templates/Boilerplate/Bit.Boilerplate/src/Client/Boilerplate.Client.Core/Data/CompiledModel/AppOfflineDbContextModel.cs (1 hunks)
  • src/Templates/Boilerplate/Bit.Boilerplate/src/Client/Boilerplate.Client.Core/Data/CompiledModel/AppOfflineDbContextModelBuilder.cs (1 hunks)
  • src/Templates/Boilerplate/Bit.Boilerplate/src/Client/Boilerplate.Client.Core/Data/CompiledModel/DatasyncDeltaTokenEntityType.cs (1 hunks)
  • src/Templates/Boilerplate/Bit.Boilerplate/src/Client/Boilerplate.Client.Core/Data/CompiledModel/DatasyncOperationEntityType.cs (1 hunks)
  • src/Templates/Boilerplate/Bit.Boilerplate/src/Client/Boilerplate.Client.Core/Data/CompiledModel/TodoItemDtoEntityType.cs (1 hunks)
  • src/Templates/Boilerplate/Bit.Boilerplate/src/Client/Boilerplate.Client.Core/Data/CompiledModel/UserDtoEntityType.cs (0 hunks)
  • src/Templates/Boilerplate/Bit.Boilerplate/src/Client/Boilerplate.Client.Core/Data/Migrations/20251128225322_Initial.Designer.cs (0 hunks)
  • src/Templates/Boilerplate/Bit.Boilerplate/src/Client/Boilerplate.Client.Core/Data/Migrations/20251128225322_Initial.cs (0 hunks)
  • src/Templates/Boilerplate/Bit.Boilerplate/src/Client/Boilerplate.Client.Core/Data/Migrations/20251130225551_Initial.Designer.cs (1 hunks)
  • src/Templates/Boilerplate/Bit.Boilerplate/src/Client/Boilerplate.Client.Core/Data/Migrations/20251130225551_Initial.cs (1 hunks)
  • src/Templates/Boilerplate/Bit.Boilerplate/src/Client/Boilerplate.Client.Core/Data/Migrations/AppOfflineDbContextModelSnapshot.cs (1 hunks)
  • src/Templates/Boilerplate/Bit.Boilerplate/src/Client/Boilerplate.Client.Core/Extensions/IClientCoreServiceCollectionExtensions.cs (1 hunks)
  • src/Templates/Boilerplate/Bit.Boilerplate/src/Client/Boilerplate.Client.Core/Services/SyncService.cs (1 hunks)
  • src/Templates/Boilerplate/Bit.Boilerplate/src/Client/Boilerplate.Client.Maui/Platforms/Android/MainActivity.cs (2 hunks)
  • src/Templates/Boilerplate/Bit.Boilerplate/src/Client/Boilerplate.Client.Web/wwwroot/manifest.json (1 hunks)
  • src/Templates/Boilerplate/Bit.Boilerplate/src/Client/Boilerplate.Client.Web/wwwroot/service-worker.published.js (1 hunks)
  • src/Templates/Boilerplate/Bit.Boilerplate/src/Directory.Packages.props (1 hunks)
  • src/Templates/Boilerplate/Bit.Boilerplate/src/Server/Boilerplate.Server.Api/Boilerplate.Server.Api.csproj (2 hunks)
  • src/Templates/Boilerplate/Bit.Boilerplate/src/Server/Boilerplate.Server.Api/Controllers/Todo/TodoItemController.cs (2 hunks)
  • src/Templates/Boilerplate/Bit.Boilerplate/src/Server/Boilerplate.Server.Api/Controllers/Todo/TodoItemTableController.cs (1 hunks)
  • src/Templates/Boilerplate/Bit.Boilerplate/src/Server/Boilerplate.Server.Api/Data/AppDbContext.cs (5 hunks)
  • src/Templates/Boilerplate/Bit.Boilerplate/src/Server/Boilerplate.Server.Api/Data/Configurations/Chatbot/SystemPromptConfiguration.cs (4 hunks)
  • src/Templates/Boilerplate/Bit.Boilerplate/src/Server/Boilerplate.Server.Api/Mappers/TodoMapper.cs (1 hunks)
  • src/Templates/Boilerplate/Bit.Boilerplate/src/Server/Boilerplate.Server.Api/Models/Identity/User.cs (2 hunks)
  • src/Templates/Boilerplate/Bit.Boilerplate/src/Server/Boilerplate.Server.Api/Models/Todo/TodoItem.cs (1 hunks)
  • src/Templates/Boilerplate/Bit.Boilerplate/src/Server/Boilerplate.Server.Api/Program.Services.cs (2 hunks)
  • src/Templates/Boilerplate/Bit.Boilerplate/src/Shared/Controllers/Todo/ITodoItemController.cs (2 hunks)
  • src/Templates/Boilerplate/Bit.Boilerplate/src/Shared/Dtos/AppJsonContext.cs (2 hunks)
  • src/Templates/Boilerplate/Bit.Boilerplate/src/Shared/Dtos/Todo/TodoItemDto.cs (1 hunks)
  • src/Templates/Boilerplate/Bit.Boilerplate/src/Shared/PageUrls.cs (1 hunks)
  • src/Templates/Boilerplate/Bit.Boilerplate/src/Shared/Resources/AppStrings.fa.resx (2 hunks)
  • src/Templates/Boilerplate/Bit.Boilerplate/src/Shared/Resources/AppStrings.resx (2 hunks)
  • src/Websites/Platform/src/Bit.Websites.Platform.Client/Shared/Urls.cs (1 hunks)
💤 Files with no reviewable changes (8)
  • src/Templates/Boilerplate/Bit.Boilerplate/src/Client/Boilerplate.Client.Core/Components/Pages/OfflineDatabaseDemoPage.razor
  • src/Templates/Boilerplate/Bit.Boilerplate/src/Client/Boilerplate.Client.Core/Data/CompiledModel/UserDtoEntityType.cs
  • src/Templates/Boilerplate/Bit.Boilerplate/.docs/08- Blazor Pages, Components, Styling & Navigation.md
  • src/Templates/Boilerplate/Bit.Boilerplate/src/Client/Boilerplate.Client.Core/Components/Pages/OfflineDatabaseDemoPage.razor.scss
  • src/Templates/Boilerplate/Bit.Boilerplate/src/Client/Boilerplate.Client.Core/Data/Migrations/20251128225322_Initial.Designer.cs
  • src/Templates/Boilerplate/Bit.Boilerplate/src/Client/Boilerplate.Client.Core/Data/Migrations/20251128225322_Initial.cs
  • src/Templates/Boilerplate/Bit.Boilerplate/src/Client/Boilerplate.Client.Core/Components/Pages/OfflineDatabaseDemoPage.razor.cs
  • src/Templates/Boilerplate/Bit.Boilerplate/.github/prompts/getting-started.prompt.md
🧰 Additional context used
🧬 Code graph analysis (14)
src/Templates/Boilerplate/Bit.Boilerplate/src/Server/Boilerplate.Server.Api/Program.Services.cs (2)
src/Templates/Boilerplate/Bit.Boilerplate/src/Shared/Services/AppFeatures.cs (1)
  • Todo (47-50)
src/Templates/Boilerplate/Bit.Boilerplate/src/Server/Boilerplate.Server.Api/Controllers/Todo/TodoItemTableController.cs (1)
  • TodoItemTableRepository (29-51)
src/Templates/Boilerplate/Bit.Boilerplate/src/Client/Boilerplate.Client.Core/Extensions/IClientCoreServiceCollectionExtensions.cs (1)
src/Templates/Boilerplate/Bit.Boilerplate/src/Client/Boilerplate.Client.Core/Services/SyncService.cs (1)
  • SyncService (11-87)
src/Templates/Boilerplate/Bit.Boilerplate/src/Client/Boilerplate.Client.Core/Data/AppOfflineDbContext.cs (5)
src/Besql/Demo/Bit.Besql.Demo.Client/Data/OfflineDbContext.cs (1)
  • OfflineDbContext (7-25)
src/Templates/Boilerplate/Bit.Boilerplate/src/Server/Boilerplate.Server.Api/Mappers/TodoMapper.cs (1)
  • TodoItemDto (14-14)
src/Templates/Boilerplate/Bit.Boilerplate/src/Client/Boilerplate.Client.Core/Services/SyncService.cs (5)
  • Task (21-24)
  • Task (26-29)
  • Task (31-34)
  • Task (36-48)
  • Task (55-81)
src/Templates/Boilerplate/Bit.Boilerplate/src/Server/Boilerplate.Server.Api/Data/AppDbContext.cs (1)
  • Task (113-125)
src/Templates/Boilerplate/Bit.Boilerplate/src/Client/Boilerplate.Client.Core/Services/AbsoluteServerAddressProvider.cs (1)
  • AbsoluteServerAddressProvider (10-28)
src/Templates/Boilerplate/Bit.Boilerplate/src/Client/Boilerplate.Client.Core/Data/CompiledModel/TodoItemDtoEntityType.cs (3)
src/Templates/Boilerplate/Bit.Boilerplate/src/Client/Boilerplate.Client.Core/Data/CompiledModel/DatasyncDeltaTokenEntityType.cs (4)
  • EntityFrameworkInternal (13-59)
  • RuntimeEntityType (16-44)
  • CreateAnnotations (46-56)
  • Customize (58-58)
src/Templates/Boilerplate/Bit.Boilerplate/src/Client/Boilerplate.Client.Core/Data/CompiledModel/DatasyncOperationEntityType.cs (4)
  • EntityFrameworkInternal (14-127)
  • RuntimeEntityType (17-112)
  • CreateAnnotations (114-124)
  • Customize (126-126)
src/Templates/Boilerplate/Bit.Boilerplate/src/Client/Boilerplate.Client.Core/Data/CompiledModel/AppOfflineDbContextModel.cs (1)
  • Customize (45-45)
src/Templates/Boilerplate/Bit.Boilerplate/src/Client/Boilerplate.Client.Core/Data/CompiledModel/DatasyncOperationEntityType.cs (3)
src/Templates/Boilerplate/Bit.Boilerplate/src/Client/Boilerplate.Client.Core/Data/CompiledModel/DatasyncDeltaTokenEntityType.cs (3)
  • RuntimeEntityType (16-44)
  • CreateAnnotations (46-56)
  • Customize (58-58)
src/Templates/Boilerplate/Bit.Boilerplate/src/Client/Boilerplate.Client.Core/Data/CompiledModel/TodoItemDtoEntityType.cs (3)
  • RuntimeEntityType (17-72)
  • CreateAnnotations (74-84)
  • Customize (86-86)
src/Templates/Boilerplate/Bit.Boilerplate/src/Client/Boilerplate.Client.Core/Data/CompiledModel/AppOfflineDbContextModel.cs (1)
  • Customize (45-45)
src/Templates/Boilerplate/Bit.Boilerplate/src/Shared/Dtos/Todo/TodoItemDto.cs (2)
src/Templates/Boilerplate/Bit.Boilerplate/src/Shared/Dtos/Categories/CategoryDto.cs (1)
  • DtoResourceType (3-19)
src/Templates/Boilerplate/Bit.Boilerplate/src/Server/Boilerplate.Server.Api/Mappers/TodoMapper.cs (1)
  • TodoItemDto (14-14)
src/Templates/Boilerplate/Bit.Boilerplate/src/Client/Boilerplate.Client.Core/Components/Pages/OfflineTodoPage.razor.cs (3)
src/Templates/Boilerplate/Bit.Boilerplate/src/Client/Boilerplate.Client.Core/Services/SyncService.cs (1)
  • SyncService (11-87)
src/Templates/Boilerplate/Bit.Boilerplate/src/Client/Boilerplate.Client.Core/Data/AppOfflineDbContext.cs (1)
  • AppOfflineDbContext (11-56)
src/Templates/Boilerplate/Bit.Boilerplate/src/Server/Boilerplate.Server.Api/Mappers/TodoMapper.cs (1)
  • TodoItemDto (14-14)
src/Templates/Boilerplate/Bit.Boilerplate/src/Client/Boilerplate.Client.Core/Components/Layout/MainLayout.razor.items.cs (1)
src/Templates/Boilerplate/Bit.Boilerplate/src/Shared/PageUrls.cs (1)
  • PageUrls (4-47)
src/Templates/Boilerplate/Bit.Boilerplate/src/Client/Boilerplate.Client.Core/Services/SyncService.cs (6)
src/Templates/Boilerplate/Bit.Boilerplate/src/Client/Boilerplate.Client.Core/Services/SnackBarService.cs (1)
  • SnackBarService (3-15)
src/Templates/Boilerplate/Bit.Boilerplate/src/Shared/Resources/StringLocalizerProvider.cs (1)
  • IStringLocalizer (6-9)
src/Templates/Boilerplate/Bit.Boilerplate/src/Client/Boilerplate.Client.Core/Data/AppOfflineDbContext.cs (2)
  • AppOfflineDbContext (11-56)
  • Task (24-35)
src/Templates/Boilerplate/Bit.Boilerplate/src/Server/Boilerplate.Server.Api/Data/AppDbContext.cs (1)
  • Task (113-125)
src/Templates/Boilerplate/Bit.Boilerplate/src/Shared/Services/SharedExceptionHandler.cs (1)
  • Exception (34-46)
src/Templates/Boilerplate/Bit.Boilerplate/src/Client/Boilerplate.Client.Core/Services/ClientExceptionHandlerBase.cs (1)
  • ExceptionDisplayKind (78-87)
src/Templates/Boilerplate/Bit.Boilerplate/src/Server/Boilerplate.Server.Api/Models/Todo/TodoItem.cs (2)
src/Templates/Boilerplate/Bit.Boilerplate/src/Shared/Services/AppFeatures.cs (1)
  • Todo (47-50)
src/Templates/Boilerplate/Bit.Boilerplate/src/Server/Boilerplate.Server.Api/Mappers/TodoMapper.cs (1)
  • TodoItem (15-15)
src/Templates/Boilerplate/Bit.Boilerplate/src/Client/Boilerplate.Client.Core/Data/Migrations/20251130225551_Initial.Designer.cs (3)
src/Templates/Boilerplate/Bit.Boilerplate/src/Client/Boilerplate.Client.Core/Data/CompiledModel/AppOfflineDbContextModel.cs (1)
  • DbContext (10-46)
src/Templates/Boilerplate/Bit.Boilerplate/src/Client/Boilerplate.Client.Core/Data/Migrations/AppOfflineDbContextModelSnapshot.cs (1)
  • DbContext (11-109)
src/Templates/Boilerplate/Bit.Boilerplate/src/Client/Boilerplate.Client.Core/Data/Migrations/20251130225551_Initial.cs (1)
  • Initial (8-80)
src/Templates/Boilerplate/Bit.Boilerplate/src/Client/Boilerplate.Client.Maui/Platforms/Android/MainActivity.cs (1)
src/Templates/Boilerplate/Bit.Boilerplate/src/Shared/PageUrls.cs (1)
  • PageUrls (4-47)
src/Templates/Boilerplate/Bit.Boilerplate/src/Shared/Controllers/Todo/ITodoItemController.cs (1)
src/Templates/Boilerplate/Bit.Boilerplate/src/Server/Boilerplate.Server.Api/Controllers/Todo/TodoItemController.cs (3)
  • HttpGet (11-19)
  • HttpGet (21-32)
  • HttpGet (34-41)
src/Templates/Boilerplate/Bit.Boilerplate/src/Client/Boilerplate.Client.Core/Data/CompiledModel/AppOfflineDbContextModelBuilder.cs (4)
src/Templates/Boilerplate/Bit.Boilerplate/src/Client/Boilerplate.Client.Core/Data/CompiledModel/AppOfflineDbContextModel.cs (2)
  • AppOfflineDbContextModel (16-38)
  • Initialize (43-43)
src/Templates/Boilerplate/Bit.Boilerplate/src/Client/Boilerplate.Client.Core/Data/CompiledModel/DatasyncDeltaTokenEntityType.cs (1)
  • CreateAnnotations (46-56)
src/Templates/Boilerplate/Bit.Boilerplate/src/Client/Boilerplate.Client.Core/Data/CompiledModel/DatasyncOperationEntityType.cs (1)
  • CreateAnnotations (114-124)
src/Templates/Boilerplate/Bit.Boilerplate/src/Client/Boilerplate.Client.Core/Data/CompiledModel/TodoItemDtoEntityType.cs (1)
  • CreateAnnotations (74-84)
🪛 Biome (2.1.2)
src/Templates/Boilerplate/Bit.Boilerplate/src/Client/Boilerplate.Client.Web/wwwroot/manifest.json

[error] 45-45: Expected an array, an object, or a literal but instead found '//#if (offlineDb == true)'.

Expected an array, an object, or a literal here.

(parse)


[error] 46-49: End of file expected

Use an array for a sequence of values: [1, 2]

(parse)


[error] 49-50: End of file expected

Use an array for a sequence of values: [1, 2]

(parse)

🪛 LanguageTool
src/Templates/Boilerplate/Bit.Boilerplate/.docs/01- Entity Framework Core.md

[grammar] ~456-~456: Ensure spelling is correct
Context: ...r database. Conventions: - Entity must inhert from BaseEntityTableData Example: [`/...

(QB_NEW_EN_ORTHOGRAPHY_ERROR_IDS_1)

⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (2)
  • GitHub Check: build Bit.Websites
  • GitHub Check: build Bit.Templates
🔇 Additional comments (54)
src/Templates/Boilerplate/Bit.Boilerplate/.docs/12- Blazor Modes, PreRendering & PWA.md (1)

208-208: Documentation URL update looks good.

The URL change from offline-database-demo to offline-todo correctly reflects the page rename introduced by this PR, maintaining documentation accuracy aligned with the new offline-todo page implementation.

src/Templates/Boilerplate/Bit.Boilerplate/src/Shared/Dtos/AppJsonContext.cs (2)

2-4: Broadened Todo using condition correctly covers offlineDb scenario

Extending the guard to sample == true || offlineDb == true ensures TodoItemDto is available whenever either sample or offline DB features are enabled, which matches the PR’s sync/offline intent, and the //#endif pairing is correct.


43-47: JsonSerializable entries now aligned with offlineDb condition

Using the same (sample == true || offlineDb == true) guard for the TodoItemDto and related PagedResponse/List serializations keeps the source-generated context in sync with the using directive above and correctly enables serialization for offline DB templates.

src/Templates/Boilerplate/Bit.Boilerplate/.template.config/template.json (1)

478-502: Verify intended exclusion logic—redundant path entries and conditional overlap detected.

Lines 478–502 contain two conditional blocks with overlapping exclusions:

  1. Redundant entries identified:

    • TodoMapper.cs appears in block 1 (line 483) and block 2 (line 498)
    • TodoPage.* appears in block 1 (line 487) and block 2 (line 499)
    • Todo/** controllers appear in block 1 (line 486) and block 2 (line 500)
  2. Conditional logic overlap:

    • Block 1 applies when: sample=false AND offlineDb=false
    • Block 2 applies when: offlineDb=false (any sample value)
    • Result: When sample=true && offlineDb=false, only block 2 applies and excludes TodoMapper.cs, TodoPage.*, and TodoItemController.cs
  3. Verify intent: Confirm whether TodoPage and TodoMapper should be excluded in all scenarios where offlineDb=false, or only when both sample=false and offlineDb=false. If the latter, remove the redundant entries from block 2 (lines 498–500).

src/Templates/Boilerplate/Bit.Boilerplate/src/Server/Boilerplate.Server.Api/Data/Configurations/Chatbot/SystemPromptConfiguration.cs (3)

124-136: LGTM!

The new Offline Todo List section is well-structured and correctly uses the elseif pattern to ensure mutual exclusivity with the regular Todo section. The content aligns with the PR objective of adding offline sync support.


202-225: LGTM!

The nested conditional structure is correctly implemented with matching //#endif directives. The Car Recommendation Rules section will only be included when both conditions are satisfied.


95-136: The quote style in the changed lines (95–136) is actually consistent—all string comparisons use single quotes ('Admin', 'Sales'). Line 202, which the comment references as using double quotes, falls outside the provided changed lines and cannot be verified from this snippet. If line 202 uses a different quote style, it would be pre-existing code and not part of this change. Within the scope of this PR's modifications, no quote style inconsistency exists.

src/Templates/Boilerplate/Bit.Boilerplate/src/Client/Boilerplate.Client.Web/wwwroot/service-worker.published.js (1)

107-107: LGTM!

The documentation URL update aligns with the new offline Todo feature naming convention introduced in this PR.

src/Templates/Boilerplate/Bit.Boilerplate/src/Server/Boilerplate.Server.Api/Mappers/TodoMapper.cs (1)

1-1: No substantive changes.

This file only has a BOM (byte order mark) character added at the start. The mapper definitions remain unchanged and correctly map between TodoItem and TodoItemDto.

src/Templates/Boilerplate/Bit.Boilerplate/src/Shared/Resources/AppStrings.resx (2)

275-286: LGTM!

The sync-related resource strings are clear and user-friendly. The format placeholder {0} in SyncPushSuccess is appropriate for displaying the change count.

Consider whether a SyncPullSuccess message is also needed to notify users when changes are successfully pulled from the server, for consistency with SyncPushSuccess. If the sync pull operation is silent by design, this can be ignored.


948-948: LGTM!

Expanding the conditional to include offlineDb == true correctly shares the Todo-related strings with the new OfflineTodo feature.

src/Templates/Boilerplate/Bit.Boilerplate/src/Server/Boilerplate.Server.Api/Program.Services.cs (2)

44-46: LGTM!

The conditional using directive is properly scoped under the offlineDb feature flag.


290-295: LGTM! AddDatasyncServices() is the correct method name.

The Datasync service registration is well-placed after the DbContext setup. The AddDatasyncServices() extension method is provided by CommunityToolkit.Datasync.Server and correctly registers OData support, protocol handlers, and required middleware dependencies. The TodoItemTableRepository scoped registration aligns with its dependency on AppDbContext.

src/Templates/Boilerplate/Bit.Boilerplate/src/Server/Boilerplate.Server.Api/Data/AppDbContext.cs (2)

6-8: LGTM: Conditional compilation extended for offlineDb support.

The change to include offlineDb == true alongside sample == true aligns with the PR objective to enable Todo functionality for the offline database sync feature.


127-145: LGTM: Method renamed and extended for sync support.

The changes look good:

  • Renaming SetConcurrencyStamp to OnSavingChanges better reflects the expanded responsibility.
  • Setting UpdatedAt on Deleted entries is correct for soft-delete sync scenarios where the server needs to track when records were deleted.
  • The version handling simplification removes unnecessary provider-specific branching.
src/Templates/Boilerplate/Bit.Boilerplate/src/Client/Boilerplate.Client.Core/Data/CompiledModel/AppOfflineDbContextModel.cs (1)

1-46: Auto-generated EF Core compiled model.

This is an auto-generated file from EF Core's compiled model tooling. The thread initialization pattern with the 10MB stack (lines 26-28) is EF Core's workaround for Issue 31751 (stack overflow during model initialization with complex models). No manual changes should be needed here.

src/Templates/Boilerplate/Bit.Boilerplate/src/Client/Boilerplate.Client.Core/Boilerplate.Client.Core.csproj (1)

22-25: CommunityToolkit.Datasync.Client package reference is appropriate.

The newly added CommunityToolkit.Datasync.Client (v9.0.5 as of Nov 2025) is a legitimate and maintained package from the CommunityToolkit Datasync project, providing client-side data synchronization capabilities. The conditional pattern matches existing offlineDb-related dependencies on this file.

src/Templates/Boilerplate/Bit.Boilerplate/.docs/01- Entity Framework Core.md (1)

451-466: The fourth file path contains a duplicate "Controllers" directory segment.

The documentation references /src/Server/Boilerplate.Server.Api/Controllers/Controllers/Todo/TodoItemTableController.cs for the Repository example, but this path has Controllers/Controllers, which appears to be a copy-paste error. Verify the correct path and update accordingly. Additionally, confirm the other referenced paths exist and are accurate:

  • /src/Server/Boilerplate.Server.Api/Models/Todo/TodoItem.cs
  • /src/Shared/Dtos/Todo/TodoItemDto.cs
  • /src/Server/Boilerplate.Server.Api/Controllers/Todo/TodoItemTableController.cs
src/Templates/Boilerplate/Bit.Boilerplate/src/Client/Boilerplate.Client.Web/wwwroot/manifest.json (1)

45-50: Static analysis errors are false positives.

The Biome parse errors flagged by static analysis are expected because this file contains preprocessor directives (//#if, //#elseif, //#endif) that will be processed by the template engine before the JSON is parsed. These directives are not valid JSON syntax but are intentionally used in template files.

The URL change from /offline-database-demo to /offline-todo and the preprocessor condition update align with the PR objectives.

src/Websites/Platform/src/Bit.Websites.Platform.Client/Shared/Urls.cs (1)

52-52: LGTM!

The URL update from /offline-database-demo to /offline-todo is consistent with the PR's objective to introduce a dedicated offline Todo synchronization feature.

src/Templates/Boilerplate/Bit.Boilerplate/src/Server/Boilerplate.Server.Api/Models/Identity/User.cs (1)

2-4: LGTM!

Expanding the preprocessor condition to include offlineDb == true appropriately makes the TodoItems navigation property available when the offline database feature is enabled, consistent with the PR's offline synchronization objectives.

Also applies to: 43-45

src/Templates/Boilerplate/Bit.Boilerplate/src/Client/Boilerplate.Client.Maui/Platforms/Android/MainActivity.cs (2)

31-35: LGTM!

The deep link configuration correctly updates to reference PageUrls.OfflineTodo under the offlineDb condition, replacing the previous OfflineDatabaseDemo route. This aligns with the broader navigation refactoring in the PR.


82-83: LGTM!

Minor formatting improvement for code clarity.

src/Templates/Boilerplate/Bit.Boilerplate/src/Client/Boilerplate.Client.Core/Components/Layout/NavBar.razor (2)

9-17: LGTM!

The conditional rendering correctly provides separate Todo navigation items for offline mode (OfflineTodo with ToDoLogoOutline) and sample mode (Todo with ToDoLogoInverse), allowing the template to support both configurations with distinct visual indicators.


31-33: LGTM!

The updated condition correctly excludes the About page when either sample or offlineDb modes are active, consistent with the template's feature configuration.

src/Templates/Boilerplate/Bit.Boilerplate/src/Shared/Controllers/Todo/ITodoItemController.cs (1)

9-18: Verify that the ID type change from Guid to string is handled consistently.

The Get and Delete methods now accept string id instead of Guid id. This is a breaking API change required for CommunityToolkit.Datasync compatibility, which uses string-based identifiers.

Ensure that:

  1. All client-side callers have been updated to use string IDs
  2. Any existing data migration strategy is in place (if applicable)
  3. The TodoItemDto.Id property has been updated to string type throughout the codebase
  4. Database queries and mappings handle the string ID correctly
src/Templates/Boilerplate/Bit.Boilerplate/src/Client/Boilerplate.Client.Core/Data/Migrations/20251130225551_Initial.cs (1)

1-80: Schema aligns with CommunityToolkit.Datasync requirements.

The migration correctly implements the three tables needed for offline synchronization:

  • TodoItems with required Id (string), UpdatedAt (as long for SQLite millisecond precision), and Version (byte[])
  • DatasyncDeltaTokens for tracking sync state
  • DatasyncOperationsQueue with index on ItemId and EntityType for efficient queue lookups

The schema matches Datasync specifications. Note: UpdatedAt is stored as INTEGER (long) rather than a DateTimeOffset, which is the correct approach for SQLite to maintain millisecond accuracy when combined with EF Core value converters.

src/Templates/Boilerplate/Bit.Boilerplate/src/Server/Boilerplate.Server.Api/Boilerplate.Server.Api.csproj (1)

50-51: Verify transitive dependency versions for known vulnerabilities.

The packages CommunityToolkit.Datasync.Server and CommunityToolkit.Datasync.Server.EntityFrameworkCore have no published security advisories. However, ensure that transitive dependencies (such as System.Text.Json and related .NET libraries) are at patched versions by running dotnet list package --include-transitive and cross-referencing against GitHub Security Advisories and CVE databases.

src/Templates/Boilerplate/Bit.Boilerplate/src/Client/Boilerplate.Client.Core/Data/CompiledModel/AppOfflineDbContextModelBuilder.cs (1)

9-30: Auto-generated EF Core compiled model looks correct.

The model properly registers all three entity types required for the offline sync feature: TodoItemDto, DatasyncDeltaToken, and DatasyncOperation. The entity count (3) in the constructor matches the number of entities created in Initialize().

src/Templates/Boilerplate/Bit.Boilerplate/src/Client/Boilerplate.Client.Core/Data/CompiledModel/TodoItemDtoEntityType.cs (1)

1-88: Auto-generated EF Core entity type mapping is correctly structured.

The compiled model properly defines:

  • String-based Id as primary key with appropriate save behavior
  • Deleted flag for soft-delete pattern required by Datasync
  • UpdatedAt with DateTimeOffsetToBinaryConverter for SQLite compatibility
  • Version for optimistic concurrency control

The structure aligns with the other Datasync entity types (DatasyncDeltaTokenEntityType, DatasyncOperationEntityType) in the codebase.

src/Templates/Boilerplate/Bit.Boilerplate/src/Shared/PageUrls.cs (1)

27-31: Verify conditional references to PageUrls.Todo match the preprocessor directives.

The conditional structure means PageUrls.Todo is only defined when sample == true (and offlineDb is false/unset). Any code referencing PageUrls.Todo must either be similarly guarded with matching preprocessor directives or only exist in contexts where offlineDb is not enabled. Ensure all references to this constant are compatible with the conditional compilation logic.

src/Templates/Boilerplate/Bit.Boilerplate/src/Directory.Packages.props (1)

85-88: Package additions are correct and consistent.

The CommunityToolkit.Datasync packages (Server, Server.EntityFrameworkCore, and Client) are added with version 9.0.5, which is current and available on NuGet. Conditional gating follows the existing pattern in the file.

src/Templates/Boilerplate/Bit.Boilerplate/src/Shared/Dtos/Todo/TodoItemDto.cs (2)

21-24: LGTM!

The Version property for concurrency control and the [NotMapped] attribute on IsInEditMode are appropriate additions for the offline sync feature.


7-7: Verify server-side TodoItem entity also uses string Id.

The Id type changed from Guid to string for CommunityToolkit.DataSync compatibility. Ensure the server-side TodoItem entity and the TodoMapper (which maps TodoItem to TodoItemDto) are updated consistently to use string-based identifiers.

src/Templates/Boilerplate/Bit.Boilerplate/src/Client/Boilerplate.Client.Core/Components/Layout/MainLayout.razor.items.cs (1)

69-88: LGTM!

The conditional compilation correctly separates the offline Todo navigation item from the regular Todo item. The authorization check is properly applied to both branches, and the URLs and localization keys are consistent with the corresponding definitions in PageUrls.cs and AppStrings.

src/Templates/Boilerplate/Bit.Boilerplate/src/Client/Boilerplate.Client.Core/Data/AppOfflineDbContext.cs (2)

24-35: LGTM - UpdatedAt is correctly updated for soft-deleted entities.

The SaveChangesAsync override correctly updates UpdatedAt for Added, Modified, and Deleted states. Including Deleted is appropriate for soft-delete scenarios where tracking deletion time is important for sync operations.


39-46: Verify the HTTP handler chain is configured correctly.

The httpMessageHandlersChainFactory.Invoke() should return a properly configured DelegatingHandler chain that includes authentication handlers. Ensure that the factory creates handlers suitable for datasync operations.

src/Templates/Boilerplate/Bit.Boilerplate/src/Shared/Resources/AppStrings.fa.resx (2)

948-948: LGTM!

The conditional compilation comment correctly includes offlineDb == true to ensure Todo-related strings are available for both sample and offline database scenarios.


275-286: LGTM - Sync message translations are appropriate.

The new sync-related resource strings (SyncPushFailed, SyncPullFailed, SyncPushSuccess) are well-translated. Verify whether SyncPullSuccess should be added for consistency with the push operation pair.

src/Templates/Boilerplate/Bit.Boilerplate/src/Client/Boilerplate.Client.Core/Data/CompiledModel/DatasyncOperationEntityType.cs (1)

1-127: LGTM - Auto-generated EF Core compiled model.

This is auto-generated code for the DatasyncOperation entity type from CommunityToolkit.Datasync. The entity mapping, properties, and index configuration are correct for offline sync operations.

src/Templates/Boilerplate/Bit.Boilerplate/src/Client/Boilerplate.Client.Core/Services/SyncService.cs (1)

55-81: Sync logic implementation looks correct.

The push-then-pull flow with conditional pull based on pullRecentChanges is appropriate. The exception handling correctly uses NonInterrupting when online (to avoid blocking UI) and None when offline. The success notification threshold (CompletedOperations > 0) prevents noise for no-op syncs.

src/Templates/Boilerplate/Bit.Boilerplate/src/Server/Boilerplate.Server.Api/Controllers/Todo/TodoItemTableController.cs (3)

11-23: Controller setup looks correct for Datasync offline sync.

The dual authorization policies, soft delete enablement, and logger assignment follow the expected pattern for a TableController.


45-50: ReplaceAsync correctly enforces user ownership.

Setting UserId before calling the base ensures the entity is always associated with the current user. Combined with AsQueryableAsync filtering, this prevents users from modifying others' items.


29-50: Consider defensive null-checking for HttpContext access in repository methods.

The repository uses httpContextAccessor.HttpContext! without explicit null validation in AsQueryableAsync, CreateAsync, and ReplaceAsync methods. While this pattern is generally safe when the repository is used exclusively within HTTP request scope (e.g., by controllers), explicitly checking for null and throwing a descriptive exception would improve error clarity if the repository is accidentally used in non-HTTP contexts (e.g., background jobs, scheduled tasks). Consider adding a guard clause or helper method to validate the context is available.

src/Templates/Boilerplate/Bit.Boilerplate/src/Client/Boilerplate.Client.Core/Components/Pages/OfflineTodoPage.razor (1)

10-129: Well-structured offline Todo UI implementation.

Good patterns observed:

  • Debounced input (300ms) prevents excessive updates
  • Virtualized list for performance with large datasets
  • Pull-to-refresh for mobile UX
  • Inline editing with clear Cancel/Save flow
  • Delete confirmation dialog prevents accidental deletions
src/Templates/Boilerplate/Bit.Boilerplate/src/Client/Boilerplate.Client.Core/Data/Migrations/20251130225551_Initial.Designer.cs (1)

1-112: Auto-generated migration designer file.

This file is auto-generated by EF Core and correctly defines the schema for offline sync support:

  • TodoItems table for local Todo storage
  • DatasyncDeltaTokens for tracking sync state
  • DatasyncOperationsQueue with appropriate index for pending operations

No manual modifications should be made to this file.

src/Templates/Boilerplate/Bit.Boilerplate/src/Server/Boilerplate.Server.Api/Models/Todo/TodoItem.cs (2)

25-31: Model properties and relationships look correct.

The [Required] attribute on Title combined with nullable reference type (string?) is valid - the attribute enforces database constraints while the nullability allows EF tracking states.


9-23: Class name mismatch: BaseEntityTableData does not exist in CommunityToolkit.Datasync.Server.

The CommunityToolkit.Datasync.Server package does not expose a BaseEntityTableData type. The actual available base classes are EntityTableData, SqliteEntityTableData, CosmosEntityTableData, or RepositoryControlledEntityTableData—each specific to their respective backends. These classes implement ITableData, which provides Id, UpdatedAt, Version, and Deleted properties.

Verify which datasync entity class is the correct parent for TodoItem in this template and ensure the inheritance is updated accordingly.

Likely an incorrect or invalid review comment.

src/Templates/Boilerplate/Bit.Boilerplate/src/Client/Boilerplate.Client.Core/Data/CompiledModel/DatasyncDeltaTokenEntityType.cs (1)

1-59: Auto-generated EF Core compiled model looks correct.

This is standard auto-generated code for EF Core runtime model mapping. The entity type configuration for DatasyncDeltaToken with Id (string key) and Value (long with sentinel 0L) follows expected patterns for the CommunityToolkit.Datasync library.

src/Templates/Boilerplate/Bit.Boilerplate/src/Client/Boilerplate.Client.Core/Components/Pages/OfflineTodoPage.razor.cs (3)

1-44: Well-structured offline-first component setup.

Good use of IDbContextFactory<AppOfflineDbContext> for creating short-lived DbContext instances, and proper initialization with default filter/sort values. The keyboard shortcut registration in OnAfterFirstRenderAsync is appropriate.


72-97: Filtering and visibility logic is correct.

The FilterViewTodoItems method properly handles sorting with ascending/descending options, and TodoItemIsVisible correctly combines search and filter conditions.


145-149: Verify Version property initialization for new TodoItemDto.

The TodoItemDto created at lines 145-149 is initialized with only Id and Title set. If Version (byte[]) is marked as required in the database schema or model configuration, ensure it is either assigned a default value, has a property initializer, or is handled automatically by the Datasync library/EF Core during sync operations. Review the TodoItemDto class definition and AppOfflineDbContext configuration to confirm proper initialization.

src/Templates/Boilerplate/Bit.Boilerplate/src/Client/Boilerplate.Client.Core/Data/Migrations/AppOfflineDbContextModelSnapshot.cs (2)

19-44: TodoItemDto entity configuration looks correct.

The schema properly includes all fields required for Datasync synchronization: Id (string), Deleted (soft-delete flag), UpdatedAt (sync timestamp), and Version (concurrency token). The Title being required aligns with business requirements.


46-106: Datasync infrastructure entities are well-configured.

The DatasyncDeltaToken and DatasyncOperation entities follow CommunityToolkit.Datasync conventions. The composite index on (ItemId, EntityType) in DatasyncOperationsQueue is appropriate for efficient sync operation lookups.

@ysmoradi ysmoradi requested a review from Copilot December 1, 2025 09:22
Copilot finished reviewing on behalf of ysmoradi December 1, 2025 09:24
Copy link
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

This pull request transforms the offline database demo from a simple profile editing form into a fully-featured offline Todo management system with bidirectional sync capabilities. The implementation leverages CommunityToolkit.Datasync for client-server synchronization and replaces the previous demo with a production-ready offline-first Todo application supporting add, edit, delete, search, sort, and filter operations.

Key Changes:

  • Introduces bidirectional sync functionality between client offline database and server using CommunityToolkit.Datasync
  • Replaces the simple offline database demo (profile editing) with a comprehensive offline Todo feature
  • Implements breaking changes to TodoItemDto (ID type changed from Guid to string, UpdatedAt made nullable)

Reviewed changes

Copilot reviewed 48 out of 50 changed files in this pull request and generated 7 comments.

Show a summary per file
File Description
src/Websites/Platform/src/Bit.Websites.Platform.Client/Shared/Urls.cs Updates demo URL from /offline-database-demo to /offline-todo
src/Templates/Boilerplate/Bit.Boilerplate/src/Shared/Resources/AppStrings.resx Replaces offline database demo strings with offline Todo and sync-related messages
src/Templates/Boilerplate/Bit.Boilerplate/src/Shared/Resources/AppStrings.fa.resx Adds Farsi translations for new offline Todo and sync messages
src/Templates/Boilerplate/Bit.Boilerplate/src/Shared/PageUrls.cs Reorganizes page URLs, moving OfflineTodo constant to conditional block
src/Templates/Boilerplate/Bit.Boilerplate/src/Shared/Dtos/Todo/TodoItemDto.cs Changes ID type to string, adds Deleted property, makes UpdatedAt nullable, adds Version byte array
src/Templates/Boilerplate/Bit.Boilerplate/src/Shared/Controllers/Todo/ITodoItemController.cs Updates method signatures to accept string IDs instead of Guid
src/Templates/Boilerplate/Bit.Boilerplate/src/Server/Boilerplate.Server.Api/Program.Services.cs Registers CommunityToolkit.Datasync services and repositories
src/Templates/Boilerplate/Bit.Boilerplate/src/Server/Boilerplate.Server.Api/Models/Todo/TodoItem.cs Makes TodoItem inherit from BaseEntityTableData for datasync support
src/Templates/Boilerplate/Bit.Boilerplate/src/Server/Boilerplate.Server.Api/Data/AppDbContext.cs Refactors concurrency handling, renames SetConcurrencyStamp to OnSavingChanges, updates UpdatedAt automatically
src/Templates/Boilerplate/Bit.Boilerplate/src/Server/Boilerplate.Server.Api/Controllers/Todo/TodoItemTableController.cs New controller providing datasync TableController with user-scoped data access
src/Templates/Boilerplate/Bit.Boilerplate/src/Server/Boilerplate.Server.Api/Controllers/Todo/TodoItemController.cs Updates to use string IDs for Todo items
src/Templates/Boilerplate/Bit.Boilerplate/src/Client/Boilerplate.Client.Core/Services/SyncService.cs New service managing push/pull synchronization with server
src/Templates/Boilerplate/Bit.Boilerplate/src/Client/Boilerplate.Client.Core/Extensions/IClientCoreServiceCollectionExtensions.cs Registers SyncService and updates DbContext registration
src/Templates/Boilerplate/Bit.Boilerplate/src/Client/Boilerplate.Client.Core/Data/AppOfflineDbContext.cs Replaces UserDto DbSet with TodoItemDto, configures datasync with server endpoint
src/Templates/Boilerplate/Bit.Boilerplate/src/Client/Boilerplate.Client.Core/Data/Migrations/* New migrations for TodoItems and datasync tables, removes old User migrations
src/Templates/Boilerplate/Bit.Boilerplate/src/Client/Boilerplate.Client.Core/Data/CompiledModel/* Updated EF Core compiled models for new schema
src/Templates/Boilerplate/Bit.Boilerplate/src/Client/Boilerplate.Client.Core/Components/Pages/OfflineTodoPage.* New comprehensive offline Todo page with full CRUD, search, filter, and sort capabilities
src/Templates/Boilerplate/Bit.Boilerplate/src/Client/Boilerplate.Client.Core/Components/Pages/OfflineDatabaseDemoPage.* Removed old profile editing demo
src/Templates/Boilerplate/Bit.Boilerplate/src/Client/Boilerplate.Client.Core/Components/Pages/TodoPage.razor Updates nullable handling for UpdatedAt property
src/Templates/Boilerplate/Bit.Boilerplate/src/Client/Boilerplate.Client.Core/Components/Layout/NavBar.razor Updates navigation to use OfflineTodo URL with different icon
src/Templates/Boilerplate/Bit.Boilerplate/src/Client/Boilerplate.Client.Core/Components/Layout/MainLayout.razor.items.cs Reorganizes nav panel items for offline Todo
src/Templates/Boilerplate/Bit.Boilerplate/src/Client/Boilerplate.Client.Web/wwwroot/service-worker.published.js Updates demo URL reference in comments
src/Templates/Boilerplate/Bit.Boilerplate/src/Client/Boilerplate.Client.Web/wwwroot/manifest.json Updates PWA manifest shortcut URL
src/Templates/Boilerplate/Bit.Boilerplate/src/Client/Boilerplate.Client.Maui/Platforms/Android/MainActivity.cs Updates deep link configuration for offline Todo
src/Templates/Boilerplate/Bit.Boilerplate/src/Directory.Packages.props Adds CommunityToolkit.Datasync package versions
src/Templates/Boilerplate/Bit.Boilerplate/.template.config/template.json Updates template exclusion conditions for offline database feature
src/Templates/Boilerplate/Bit.Boilerplate/.github/prompts/getting-started.prompt.md Removed entire getting started prompt file
src/Templates/Boilerplate/Bit.Boilerplate/.github/copilot-instructions.md Removes reference to getting-started prompt, adds RTL language instruction
Files not reviewed (2)
  • src/Templates/Boilerplate/Bit.Boilerplate/src/Client/Boilerplate.Client.Core/Data/Migrations/20251128225322_Initial.Designer.cs: Language not supported
  • src/Templates/Boilerplate/Bit.Boilerplate/src/Client/Boilerplate.Client.Core/Data/Migrations/20251130225551_Initial.Designer.cs: Language not supported

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

bit Boilerplate's client app's offline database doesn't have sync feature

1 participant