Full Code of dkorecko/Ticky for AI

main 971fd24c4179 cached
241 files
1.3 MB
274.8k tokens
308 symbols
1 requests
Download .txt
Showing preview only (1,364K chars total). Download the full file or copy to clipboard to get everything.
Repository: dkorecko/Ticky
Branch: main
Commit: 971fd24c4179
Files: 241
Total size: 1.3 MB

Directory structure:
gitextract_dmiie922/

├── .gitattributes
├── .github/
│   ├── FUNDING.yml
│   ├── ISSUE_TEMPLATE/
│   │   ├── bug_report.md
│   │   └── feature_request.md
│   └── workflows/
│       ├── build.yml
│       ├── docker-release.yml
│       └── docker-unstable.yml
├── .gitignore
├── .vscode/
│   └── settings.json
├── CONTRIBUTING.md
├── Dockerfile
├── LICENSE.txt
├── README.md
├── Ticky.Base/
│   ├── Constants.cs
│   ├── Converters/
│   │   ├── ColorToInt32Converter.cs
│   │   └── StringToTimeSpanConverter.cs
│   ├── DTOs/
│   │   ├── InformationDTO.cs
│   │   ├── Notification.cs
│   │   ├── Trello/
│   │   │   ├── TrelloCardDTO.cs
│   │   │   ├── TrelloCheckItemDTO.cs
│   │   │   ├── TrelloChecklistDTO.cs
│   │   │   ├── TrelloLabelDTO.cs
│   │   │   ├── TrelloListDTO.cs
│   │   │   ├── TrelloMemberDTO.cs
│   │   │   └── TrelloPreferencesDTO.cs
│   │   └── TrelloImportDTO.cs
│   ├── Entities/
│   │   ├── Abstractions/
│   │   │   ├── AbstractDbEntity.cs
│   │   │   ├── IAssignable.cs
│   │   │   ├── IDbEntry.cs
│   │   │   ├── IDeletable.cs
│   │   │   └── IOrderable.cs
│   │   ├── Activity.cs
│   │   ├── Attachment.cs
│   │   ├── Board.cs
│   │   ├── BoardMembership.cs
│   │   ├── Card.cs
│   │   ├── CardLink.cs
│   │   ├── Code.cs
│   │   ├── Column.cs
│   │   ├── Comment.cs
│   │   ├── Favorite.cs
│   │   ├── Label.cs
│   │   ├── LastVisit.cs
│   │   ├── Owned/
│   │   │   └── RepeatInfo.cs
│   │   ├── Project.cs
│   │   ├── ProjectMembership.cs
│   │   ├── Reminder.cs
│   │   ├── Subtask.cs
│   │   ├── TimeRecord.cs
│   │   └── User.cs
│   ├── Enums/
│   │   ├── CardPlacement.cs
│   │   ├── CardPriority.cs
│   │   ├── CodePurpose.cs
│   │   ├── DeadlineColor.cs
│   │   ├── ImportSource.cs
│   │   ├── ImportType.cs
│   │   ├── NotificationType.cs
│   │   ├── OperationType.cs
│   │   ├── OrderRule.cs
│   │   ├── RepeatType.cs
│   │   └── TrelloArchivedHandlingType.cs
│   ├── GlobalUsings.cs
│   ├── Models/
│   │   ├── BoardPreferencesModel.cs
│   │   ├── CloneBoardModel.cs
│   │   ├── CreateCardModel.cs
│   │   ├── CredentialsModel.cs
│   │   ├── DeadlineModel.cs
│   │   ├── FilterCardsModel.cs
│   │   ├── ImportModel.cs
│   │   ├── LabelModel.cs
│   │   ├── LinkCardsModel.cs
│   │   ├── ReminderModel.cs
│   │   ├── RepeatCardModel.cs
│   │   ├── SnoozeCardModel.cs
│   │   ├── SubtaskModel.cs
│   │   ├── TimeRecordModel.cs
│   │   └── UserModel.cs
│   ├── Ticky.Base.csproj
│   └── Validation/
│       ├── IsValidTimeSpan.cs
│       └── RequiredIf.cs
├── Ticky.Internal/
│   ├── Data/
│   │   ├── DataContext.cs
│   │   ├── DataMigrator.cs
│   │   └── DataSeeder.cs
│   ├── GlobalUsings.cs
│   ├── Helpers/
│   │   ├── AttachmentHelper.cs
│   │   ├── IndexHelper.cs
│   │   ├── StringHelper.cs
│   │   └── TimeHelper.cs
│   ├── Migrations/
│   │   ├── 20250523175138_Initial.Designer.cs
│   │   ├── 20250523175138_Initial.cs
│   │   ├── 20250527093505_Favorites.Designer.cs
│   │   ├── 20250527093505_Favorites.cs
│   │   ├── 20250606115441_ForceCredentialsChange.Designer.cs
│   │   ├── 20250606115441_ForceCredentialsChange.cs
│   │   ├── 20250615181842_TextToNameCard.Designer.cs
│   │   ├── 20250615181842_TextToNameCard.cs
│   │   ├── 20250617140549_SnoozeCards.Designer.cs
│   │   ├── 20250617140549_SnoozeCards.cs
│   │   ├── 20250618090806_AtRemoval.Designer.cs
│   │   ├── 20250618090806_AtRemoval.cs
│   │   ├── 20250618114219_SelfAssign.Designer.cs
│   │   ├── 20250618114219_SelfAssign.cs
│   │   ├── 20250703124731_DisableBoardAnimations.Designer.cs
│   │   ├── 20250703124731_DisableBoardAnimations.cs
│   │   ├── 20250709130027_RepeatingCards.Designer.cs
│   │   ├── 20250709130027_RepeatingCards.cs
│   │   ├── 20250826115734_NewCardPlacement.Designer.cs
│   │   ├── 20250826115734_NewCardPlacement.cs
│   │   ├── 20251005111419_BlockToFlagged.Designer.cs
│   │   ├── 20251005111419_BlockToFlagged.cs
│   │   ├── 20251005174743_SubtaskAssignee.Designer.cs
│   │   ├── 20251005174743_SubtaskAssignee.cs
│   │   ├── 20251011134337_Information.Designer.cs
│   │   ├── 20251011134337_Information.cs
│   │   └── DataContextModelSnapshot.cs
│   ├── Services/
│   │   ├── AvatarService.cs
│   │   ├── CardNumberingService.cs
│   │   ├── CodeService.cs
│   │   ├── EmailService.cs
│   │   ├── Hosted/
│   │   │   ├── AbstractHostedService.cs
│   │   │   ├── CleanupHostedService.cs
│   │   │   ├── ReminderHostedService.cs
│   │   │   ├── RepeatHostedService.cs
│   │   │   └── SnoozeHostedService.cs
│   │   ├── InformationService.cs
│   │   ├── SearchService.cs
│   │   └── TrelloImportService.cs
│   └── Ticky.Internal.csproj
├── Ticky.Units/
│   ├── CardTest.cs
│   ├── Constants.cs
│   ├── GlobalUsings.cs
│   └── Ticky.Units.csproj
├── Ticky.Web/
│   ├── Components/
│   │   ├── App.razor
│   │   ├── Dialogs/
│   │   │   ├── AbstractModal.razor
│   │   │   ├── AddLabelModal.razor
│   │   │   ├── AddOrEditRepeatModal.razor
│   │   │   ├── AddReminderModal.razor
│   │   │   ├── AddSubtaskModal.razor
│   │   │   ├── AddTimeRecordModal.razor
│   │   │   ├── AddUserModal.razor
│   │   │   ├── AssigneesModal.razor
│   │   │   ├── CloneBoardModal.razor
│   │   │   ├── CreateBoardModal.razor
│   │   │   ├── CreateColumnModal.razor
│   │   │   ├── CreateProjectModal.razor
│   │   │   ├── DeleteConfirmationDialog.razor
│   │   │   ├── EditBoardMembershipsModal.razor
│   │   │   ├── EditBoardModal.razor
│   │   │   ├── EditCardModal.razor
│   │   │   ├── EditColumnModal.razor
│   │   │   ├── EditDeadlineModal.razor
│   │   │   ├── EditLabelModal.razor
│   │   │   ├── EditProjectMembershipsModal.razor
│   │   │   ├── EditProjectModal.razor
│   │   │   ├── EditSubtaskModal.razor
│   │   │   ├── EditTimeRecordModal.razor
│   │   │   ├── EditUserModal.razor
│   │   │   ├── FilterCardsModal.razor
│   │   │   ├── ImportModal.razor
│   │   │   ├── InformationModal.razor
│   │   │   ├── LabelModal.razor
│   │   │   ├── LinkCardsModal.razor
│   │   │   ├── PriorityModal.razor
│   │   │   ├── SearchModal.razor
│   │   │   ├── SnoozeCardModal.razor
│   │   │   └── UserInfoModal.razor
│   │   ├── Elements/
│   │   │   ├── ActionModal.razor
│   │   │   ├── BoardCard.razor
│   │   │   ├── CardView.razor
│   │   │   ├── ColumnView.razor
│   │   │   ├── DisabledBadge.razor
│   │   │   ├── Dropdown.razor
│   │   │   ├── LabelView.razor
│   │   │   ├── Modal.razor
│   │   │   ├── Notifications.razor
│   │   │   ├── PriorityLabel.razor
│   │   │   ├── Sortable/
│   │   │   │   ├── SortableList.razor
│   │   │   │   ├── SortableList.razor.cs
│   │   │   │   ├── SortableList.razor.css
│   │   │   │   └── SortableList.razor.js
│   │   │   ├── Spinner.razor
│   │   │   ├── SubtaskView.razor
│   │   │   ├── TimeSelect.razor
│   │   │   └── Tooltip.razor
│   │   ├── Layout/
│   │   │   ├── MainLayout.razor
│   │   │   └── NavMenu.razor
│   │   ├── Pages/
│   │   │   ├── Abstractions/
│   │   │   │   └── NotifiableBase.razor
│   │   │   ├── AdminPanel.razor
│   │   │   ├── Auth/
│   │   │   │   ├── ChangePassword.cshtml
│   │   │   │   ├── ChangePassword.cshtml.cs
│   │   │   │   ├── ConfirmMail.cshtml
│   │   │   │   ├── ConfirmMail.cshtml.cs
│   │   │   │   ├── ForgotPassword.cshtml
│   │   │   │   ├── ForgotPassword.cshtml.cs
│   │   │   │   ├── Login.cshtml
│   │   │   │   ├── Login.cshtml.cs
│   │   │   │   ├── Logout.cshtml
│   │   │   │   ├── Logout.cshtml.cs
│   │   │   │   ├── MailConfirmed.cshtml
│   │   │   │   ├── MailConfirmed.cshtml.cs
│   │   │   │   ├── PasswordChanged.cshtml
│   │   │   │   ├── PasswordChanged.cshtml.cs
│   │   │   │   ├── Register.cshtml
│   │   │   │   ├── Register.cshtml.cs
│   │   │   │   ├── _AuthLayout.cshtml
│   │   │   │   ├── _ViewImports.cshtml
│   │   │   │   └── _ViewStart.cshtml
│   │   │   ├── BoardSettings.razor
│   │   │   ├── BoardView.razor
│   │   │   ├── Error.razor
│   │   │   ├── Home.razor
│   │   │   └── UserSettings.razor
│   │   ├── Routes.razor
│   │   └── _Imports.razor
│   ├── Controllers/
│   │   ├── AttachmentsController.cs
│   │   └── HealthController.cs
│   ├── GlobalUsings.cs
│   ├── Hubs/
│   │   └── UpdateHub.cs
│   ├── Program.cs
│   ├── Properties/
│   │   └── launchSettings.json
│   ├── Ticky.Web.csproj
│   ├── app.css
│   ├── appsettings.Development.json
│   ├── appsettings.json
│   ├── package.json
│   ├── tailwind.config.js
│   ├── tailwind.extension.json
│   └── wwwroot/
│       ├── css/
│       │   └── app.css
│       ├── emails/
│       │   ├── DeadlineReminder.html
│       │   ├── ForgottenPassword.html
│       │   ├── Reminder.html
│       │   └── VerifyEmail.html
│       ├── fonts/
│       │   ├── Noto-Sans.css
│       │   └── Poppins.css
│       ├── information.json
│       └── js/
│           ├── darkTheme.js
│           ├── downloadFile.js
│           ├── focusTrap.js
│           ├── main.js
│           └── screenHeight.js
└── Ticky.sln

================================================
FILE CONTENTS
================================================

================================================
FILE: .gitattributes
================================================
###############################################################################
# Set default behavior to automatically normalize line endings.
###############################################################################
* text=auto

###############################################################################
# Set default behavior for command prompt diff.
#
# This is need for earlier builds of msysgit that does not have it on by
# default for csharp files.
# Note: This is only used by command line
###############################################################################
#*.cs     diff=csharp

###############################################################################
# Set the merge driver for project and solution files
#
# Merging from the command prompt will add diff markers to the files if there
# are conflicts (Merging from VS is not affected by the settings below, in VS
# the diff markers are never inserted). Diff markers may cause the following 
# file extensions to fail to load in VS. An alternative would be to treat
# these files as binary and thus will always conflict and require user
# intervention with every merge. To do so, just uncomment the entries below
###############################################################################
#*.sln       merge=binary
#*.csproj    merge=binary
#*.vbproj    merge=binary
#*.vcxproj   merge=binary
#*.vcproj    merge=binary
#*.dbproj    merge=binary
#*.fsproj    merge=binary
#*.lsproj    merge=binary
#*.wixproj   merge=binary
#*.modelproj merge=binary
#*.sqlproj   merge=binary
#*.wwaproj   merge=binary

###############################################################################
# behavior for image files
#
# image files are treated as binary by default.
###############################################################################
#*.jpg   binary
#*.png   binary
#*.gif   binary

###############################################################################
# diff behavior for common document formats
# 
# Convert binary document formats to text before diffing them. This feature
# is only available from the command line. Turn it on by uncommenting the 
# entries below.
###############################################################################
#*.doc   diff=astextplain
#*.DOC   diff=astextplain
#*.docx  diff=astextplain
#*.DOCX  diff=astextplain
#*.dot   diff=astextplain
#*.DOT   diff=astextplain
#*.pdf   diff=astextplain
#*.PDF   diff=astextplain
#*.rtf   diff=astextplain
#*.RTF   diff=astextplain


================================================
FILE: .github/FUNDING.yml
================================================
# These are supported funding model platforms

github: [dkorecko]
patreon: # Replace with a single Patreon username
open_collective: # Replace with a single Open Collective username
ko_fi: # Replace with a single Ko-fi username
tidelift: # Replace with a single Tidelift platform-name/package-name e.g., npm/babel
community_bridge: # Replace with a single Community Bridge project-name e.g., cloud-foundry
liberapay: # Replace with a single Liberapay username
issuehunt: # Replace with a single IssueHunt username
lfx_crowdfunding: # Replace with a single LFX Crowdfunding project-name e.g., cloud-foundry
polar: # Replace with a single Polar username
buy_me_a_coffee: dkorecko
thanks_dev: # Replace with a single thanks.dev username
custom: # Replace with up to 4 custom sponsorship URLs e.g., ['link1', 'link2']


================================================
FILE: .github/ISSUE_TEMPLATE/bug_report.md
================================================
---
name: Bug report
about: Create a report to help us improve
title: ''
labels: bug
assignees: ''

---

**Describe the bug**
A clear and concise description of what the bug is.

**Screenshots**
If applicable, add screenshots to help explain your problem.

**docker-compose.yaml**
```yaml
Provide your docker-compose.yaml file here.
```

**Logs**
Provide logs here if applicable.


================================================
FILE: .github/ISSUE_TEMPLATE/feature_request.md
================================================
---
name: Feature request
about: Suggest an idea for this project
title: "[Feature]"
labels: enhancement
assignees: ''

---

**Describe the solution you'd like**
A clear and concise description of what you want to happen.

**Describe alternatives you've considered**
A clear and concise description of any alternative solutions or features you've considered.


================================================
FILE: .github/workflows/build.yml
================================================
name: Build app

on:
  pull_request:

jobs:
  build:
    runs-on: ubuntu-latest
    permissions:
      contents: read
      packages: write
    steps:
      - name: Checkout
        uses: actions/checkout@v4
        with:
          ref: ${{ github.ref }}

      - name: Setup .NET
        uses: actions/setup-dotnet@v4
        with:
          dotnet-version: 9

      - name: Restore dependencies
        run: dotnet restore

      - name: Build
        run: dotnet build -c Release --no-restore

      - name: Test
        run: dotnet test -c Release --no-build


================================================
FILE: .github/workflows/docker-release.yml
================================================
name: Build and Publish Docker image to GHCR

on:
  release:
    types: [published]

jobs:
  build-and-push:
    runs-on: ubuntu-latest
    permissions:
      contents: read
      packages: write
    steps:
      - name: Checkout code
        uses: actions/checkout@v4

      - name: Set up Docker Buildx
        uses: docker/setup-buildx-action@v3
        with:
          platforms: linux/amd64,linux/arm64,linux/arm/v7

      - name: Log in to GitHub Container Registry
        uses: docker/login-action@v3
        with:
          registry: ghcr.io
          username: ${{ secrets.GHCR_USERNAME }}
          password: ${{ secrets.GHCR_TOKEN }}

      - name: Extract release version
        id: vars
        run: echo "RELEASE_VERSION=${GITHUB_REF#refs/tags/}" >> $GITHUB_ENV

      - name: Set lowercase repository name
        id: repo-name
        run: echo "REPO_LOWER=${GITHUB_REPOSITORY,,}" >> $GITHUB_ENV

      - name: Build and push Docker image
        uses: docker/build-push-action@v5
        with:
          context: .
          platforms: linux/amd64,linux/arm64,linux/arm
          file: ./Dockerfile
          push: true
          tags: |
            ghcr.io/${{ env.REPO_LOWER }}:${{ env.RELEASE_VERSION }}
            ghcr.io/${{ env.REPO_LOWER }}:latest


================================================
FILE: .github/workflows/docker-unstable.yml
================================================
name: Build and publish unstable Docker image

on:
  push:
    branches:
      - main
  workflow_dispatch:
    inputs:
      branch:
        description: "Branch to deploy"
        required: true
        default: "main"

concurrency:
  group: docker-unstable-${{ github.ref }}
  cancel-in-progress: true

jobs:
  build-and-push:
    runs-on: ubuntu-latest
    permissions:
      contents: read
      packages: write
    steps:
      - name: Checkout code
        uses: actions/checkout@v4
        with:
          ref: ${{ github.event.inputs.branch || github.ref }}

      - name: Set up Docker Buildx
        uses: docker/setup-buildx-action@v3
        with:
          platforms: linux/amd64,linux/arm64,linux/arm

      - name: Log in to GitHub Container Registry
        uses: docker/login-action@v3
        with:
          registry: ghcr.io
          username: ${{ secrets.GHCR_USERNAME }}
          password: ${{ secrets.GHCR_TOKEN }}

      - name: Set lowercase repository name
        id: repo-name
        run: echo "REPO_LOWER=${GITHUB_REPOSITORY,,}" >> $GITHUB_ENV

      - name: Build and push Docker image
        uses: docker/build-push-action@v6
        with:
          context: .
          platforms: linux/amd64,linux/arm64,linux/arm
          file: ./Dockerfile
          push: true
          tags: ghcr.io/${{ env.REPO_LOWER }}:unstable

  test-multi-arch:
    needs: build-and-push
    runs-on: ubuntu-latest
    permissions:
      contents: read
      packages: read
    strategy:
      matrix:
        arch: [amd64, arm64, arm]
    steps:
      - name: Set up QEMU
        uses: docker/setup-qemu-action@v3
        with:
          platforms: linux/${{ matrix.arch }}

      - name: Log in to GitHub Container Registry
        uses: docker/login-action@v3
        with:
          registry: ghcr.io
          username: ${{ secrets.GHCR_USERNAME }}
          password: ${{ secrets.GHCR_TOKEN }}

      - name: Set lowercase repository name
        id: repo-name
        run: echo "REPO_LOWER=${GITHUB_REPOSITORY,,}" >> $GITHUB_ENV

      - name: Pull Docker image for ${{ matrix.arch }}
        run: |
          docker pull --platform linux/${{ matrix.arch }} ghcr.io/${{ env.REPO_LOWER }}:unstable

      - name: Run test container for ${{ matrix.arch }}
        run: |
          docker run -p 8080:8080 -d --name ticky-test --platform linux/${{ matrix.arch }} ghcr.io/${{ env.REPO_LOWER }}:unstable
          sleep 50
          docker logs ticky-test
          docker stop ticky-test
          docker rm ticky-test


================================================
FILE: .gitignore
================================================
## Ignore Visual Studio temporary files, build results, and
## files generated by popular Visual Studio add-ons.
##
## Get latest from https://github.com/github/gitignore/blob/master/VisualStudio.gitignore

# User-specific files
*.rsuser
*.suo
*.user
*.userosscache
*.sln.docstates

# User-specific files (MonoDevelop/Xamarin Studio)
*.userprefs

# Mono auto generated files
mono_crash.*

# Build results
[Dd]ebug/
[Dd]ebugPublic/
[Rr]elease/
[Rr]eleases/
x64/
x86/
[Ww][Ii][Nn]32/
[Aa][Rr][Mm]/
[Aa][Rr][Mm]64/
bld/
[Bb]in/
[Oo]bj/
[Oo]ut/
[Ll]og/
[Ll]ogs/

# Visual Studio 2015/2017 cache/options directory
.vs/
# Uncomment if you have tasks that create the project's static files in wwwroot
#wwwroot/

# Visual Studio 2017 auto generated files
Generated\ Files/

# MSTest test Results
[Tt]est[Rr]esult*/
[Bb]uild[Ll]og.*

# NUnit
*.VisualState.xml
TestResult.xml
nunit-*.xml

# Build Results of an ATL Project
[Dd]ebugPS/
[Rr]eleasePS/
dlldata.c

# Benchmark Results
BenchmarkDotNet.Artifacts/

# .NET Core
project.lock.json
project.fragment.lock.json
artifacts/

# ASP.NET Scaffolding
ScaffoldingReadMe.txt

# StyleCop
StyleCopReport.xml

# Files built by Visual Studio
*_i.c
*_p.c
*_h.h
*.ilk
*.meta
*.obj
*.iobj
*.pch
*.pdb
*.ipdb
*.pgc
*.pgd
*.rsp
*.sbr
*.tlb
*.tli
*.tlh
*.tmp
*.tmp_proj
*_wpftmp.csproj
*.log
*.vspscc
*.vssscc
.builds
*.pidb
*.svclog
*.scc

# Chutzpah Test files
_Chutzpah*

# Visual C++ cache files
ipch/
*.aps
*.ncb
*.opendb
*.opensdf
*.sdf
*.cachefile
*.VC.db
*.VC.VC.opendb

# Visual Studio profiler
*.psess
*.vsp
*.vspx
*.sap

# Visual Studio Trace Files
*.e2e

# TFS 2012 Local Workspace
$tf/

# Guidance Automation Toolkit
*.gpState

# ReSharper is a .NET coding add-in
_ReSharper*/
*.[Rr]e[Ss]harper
*.DotSettings.user

# TeamCity is a build add-in
_TeamCity*

# DotCover is a Code Coverage Tool
*.dotCover

# AxoCover is a Code Coverage Tool
.axoCover/*
!.axoCover/settings.json

# Coverlet is a free, cross platform Code Coverage Tool
coverage*.json
coverage*.xml
coverage*.info

# Visual Studio code coverage results
*.coverage
*.coveragexml

# NCrunch
_NCrunch_*
.*crunch*.local.xml
nCrunchTemp_*

# MightyMoose
*.mm.*
AutoTest.Net/

# Web workbench (sass)
.sass-cache/

# Installshield output folder
[Ee]xpress/

# DocProject is a documentation generator add-in
DocProject/buildhelp/
DocProject/Help/*.HxT
DocProject/Help/*.HxC
DocProject/Help/*.hhc
DocProject/Help/*.hhk
DocProject/Help/*.hhp
DocProject/Help/Html2
DocProject/Help/html

# Click-Once directory
publish/

# Publish Web Output
*.[Pp]ublish.xml
*.azurePubxml
# Note: Comment the next line if you want to checkin your web deploy settings,
# but database connection strings (with potential passwords) will be unencrypted
*.pubxml
*.publishproj

# Microsoft Azure Web App publish settings. Comment the next line if you want to
# checkin your Azure Web App publish settings, but sensitive information contained
# in these scripts will be unencrypted
PublishScripts/

# NuGet Packages
*.nupkg
# NuGet Symbol Packages
*.snupkg
# The packages folder can be ignored because of Package Restore
**/[Pp]ackages/*
# except build/, which is used as an MSBuild target.
!**/[Pp]ackages/build/
# Uncomment if necessary however generally it will be regenerated when needed
#!**/[Pp]ackages/repositories.config
# NuGet v3's project.json files produces more ignorable files
*.nuget.props
*.nuget.targets

# Microsoft Azure Build Output
csx/
*.build.csdef

# Microsoft Azure Emulator
ecf/
rcf/

# Windows Store app package directories and files
AppPackages/
BundleArtifacts/
Package.StoreAssociation.xml
_pkginfo.txt
*.appx
*.appxbundle
*.appxupload

# Visual Studio cache files
# files ending in .cache can be ignored
*.[Cc]ache
# but keep track of directories ending in .cache
!?*.[Cc]ache/

# Others
ClientBin/
~$*
*~
*.dbmdl
*.dbproj.schemaview
*.jfm
*.pfx
*.publishsettings
orleans.codegen.cs

# Including strong name files can present a security risk
# (https://github.com/github/gitignore/pull/2483#issue-259490424)
#*.snk

# Since there are multiple workflows, uncomment next line to ignore bower_components
# (https://github.com/github/gitignore/pull/1529#issuecomment-104372622)
#bower_components/

# RIA/Silverlight projects
Generated_Code/

# Backup & report files from converting an old project file
# to a newer Visual Studio version. Backup files are not needed,
# because we have git ;-)
_UpgradeReport_Files/
Backup*/
UpgradeLog*.XML
UpgradeLog*.htm
ServiceFabricBackup/
*.rptproj.bak

# SQL Server files
*.mdf
*.ldf
*.ndf

# Business Intelligence projects
*.rdl.data
*.bim.layout
*.bim_*.settings
*.rptproj.rsuser
*- [Bb]ackup.rdl
*- [Bb]ackup ([0-9]).rdl
*- [Bb]ackup ([0-9][0-9]).rdl

# Microsoft Fakes
FakesAssemblies/

# GhostDoc plugin setting file
*.GhostDoc.xml

# Node.js Tools for Visual Studio
.ntvs_analysis.dat
node_modules/

# Visual Studio 6 build log
*.plg

# Visual Studio 6 workspace options file
*.opt

# Visual Studio 6 auto-generated workspace file (contains which files were open etc.)
*.vbw

# Visual Studio LightSwitch build output
**/*.HTMLClient/GeneratedArtifacts
**/*.DesktopClient/GeneratedArtifacts
**/*.DesktopClient/ModelManifest.xml
**/*.Server/GeneratedArtifacts
**/*.Server/ModelManifest.xml
_Pvt_Extensions

# Paket dependency manager
.paket/paket.exe
paket-files/

# FAKE - F# Make
.fake/

# CodeRush personal settings
.cr/personal

# Python Tools for Visual Studio (PTVS)
__pycache__/
*.pyc

# Cake - Uncomment if you are using it
# tools/**
# !tools/packages.config

# Tabs Studio
*.tss

# Telerik's JustMock configuration file
*.jmconfig

# BizTalk build output
*.btp.cs
*.btm.cs
*.odx.cs
*.xsd.cs

# OpenCover UI analysis results
OpenCover/

# Azure Stream Analytics local run output
ASALocalRun/

# MSBuild Binary and Structured Log
*.binlog

# NVidia Nsight GPU debugger configuration file
*.nvuser

# MFractors (Xamarin productivity tool) working folder
.mfractor/

# Local History for Visual Studio
.localhistory/

# BeatPulse healthcheck temp database
healthchecksdb

# Backup folder for Package Reference Convert tool in Visual Studio 2017
MigrationBackup/

# Ionide (cross platform F# VS Code tools) working folder
.ionide/

# Fody - auto-generated XML schema
FodyWeavers.xsd

**/uploaded/**/*

# Idea
.idea/

================================================
FILE: .vscode/settings.json
================================================
{
  "kiroAgent.configureMCP": "Disabled"
}


================================================
FILE: CONTRIBUTING.md
================================================
# Contributing to Ticky

Thank you for considering contributing to Ticky! This document outlines the process for contributing to the project.

## How Can I Contribute?

### Reporting Bugs

Before creating bug reports, please check the issue tracker to see if the problem has already been reported. If it has and the issue is still open, add a comment to the existing issue instead of opening a new one (or add a reaction).

When you are creating a bug report, please include as many details as possible:

- Use a clear and descriptive title
- Describe the exact steps to reproduce the problem
- Describe the behavior you observed and what you expected to see
- Include screenshots if possible
- Include details about your environment (OS, browser, etc.)

### Suggesting Enhancements

Enhancement suggestions are tracked as GitHub issues. When creating an enhancement suggestion, please include:

- A clear and descriptive title
- A detailed description of the proposed functionality
- Any possible implementation details or ideas
- Why this enhancement would be useful to most users

### Pull Requests

Please follow these steps for submitting a pull request:

1. Fork the repository
2. Create a new branch for your feature (`git checkout -b feature/name-of-feature`)
3. Make your changes
4. Verify the changes
5. Commit your changes (`git commit -m 'Add some feature'`)
6. Push to your branch (`git push origin feature/name-of-feature`)
7. Open a Pull Request

#### Pull Request Guidelines

- Update the README.md with details of changes if applicable
- Follow the code style of the project
- Reference any relevant issues in your PR description

## Development Setup

### Prerequisites

- .NET 9.0 SDK
- MySQL Server 8.0+
- IDE (Visual Studio, VS Code, etc.)

### Setup Steps

1. Clone your fork of the repository
2. Configure your database and e-mail connection in user secrets

```json
{
  "Logging": {
    "LogLevel": {
      "Default": "Information",
      "Microsoft.AspNetCore": "Warning",
      "Microsoft.EntityFrameworkCore.Database": "Warning"
    }
  },
  "AllowedHosts": "*",
  "ConnectionStrings": {
    "Production": "Server=localhost;Database=ticky;Uid=ticky;Pwd={PASSWORD_HERE};",
    "Development": "Server=localhost;Database=ticky;Uid=ticky;Pwd={PASSWORD_HERE};"
  },
  "Email": {
    "Server": "{SMTP_HOST}",
    "Port": 465,
    "SenderName": "Ticky",
    "SenderEmail": "{SMTP_EMAIL}",
    "Account": "{SMTP_USERNAME}",
    "Password": "{SMTP_PASSWORD}",
    "Security": true
  }
}
```

3. Start the application: `dotnet watch --project Ticky.Web/Ticky.Web.csproj`

## Coding Conventions

- Use the built-in code formatter
- Follow C# naming conventions
- Add comments for complex logic
- Write descriptive commit messages

## Documentation

Good documentation is essential. Please update relevant documentation when making changes:

- Update the README.md if relevant
- Consider adding to the wiki for significant features

## Questions?

If you have any questions about contributing, please open an issue with your question.

Thank you for contributing to Ticky!


================================================
FILE: Dockerfile
================================================
# Declare ARGs for build platform and target architecture for clarity
ARG BUILDPLATFORM

FROM mcr.microsoft.com/dotnet/aspnet:9.0 AS base
WORKDIR /app
EXPOSE 8080

FROM --platform=$BUILDPLATFORM mcr.microsoft.com/dotnet/sdk:9.0 AS build
ARG TARGETARCH
WORKDIR /src

# Copy project files first for layer caching
COPY ["Ticky.Web/Ticky.Web.csproj", "Ticky.Web/"]
COPY ["Ticky.Internal/Ticky.Internal.csproj", "Ticky.Internal/"]
COPY ["Ticky.Base/Ticky.Base.csproj", "Ticky.Base/"]

# Map Docker's TARGETARCH to the arch used in .NET RIDs and restore dependencies
RUN export DOTNET_ARCH=$(case ${TARGETARCH} in \
	"amd64") echo "x64" ;; \
	"arm64") echo "arm64" ;; \
	"arm") echo "arm" ;; \
	*) echo "Unsupported architecture: ${TARGETARCH}"; exit 1 ;; \
	esac) && \
	dotnet restore "Ticky.Web/Ticky.Web.csproj" -r "linux-${DOTNET_ARCH}"

# Copy the rest of the source code
COPY . .

# Build and publish the application for the target runtime
RUN export DOTNET_ARCH=$(case ${TARGETARCH} in \
	"amd64") echo "x64" ;; \
	"arm64") echo "arm64" ;; \
	"arm") echo "arm" ;; \
	esac) && \
	dotnet publish "Ticky.Web/Ticky.Web.csproj" \
	-c Release \
	-o /app/publish \
	-r "linux-${DOTNET_ARCH}" \
	--no-restore \
	--self-contained false

# Final image stage
FROM base AS final
WORKDIR /app
COPY --from=build /app/publish .
ENTRYPOINT ["dotnet", "Ticky.Web.dll"]

================================================
FILE: LICENSE.txt
================================================
MIT License

Copyright (c) [year] [fullname]

Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:

The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.

THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.


================================================
FILE: README.md
================================================
<p align="center">
    <img src="images/ticky_logo.png" width="300" title="Ticky logo">
</p>

# Ticky

Ticky is a modern, feature-rich task management system with Kanban-style boards, built using ASP.NET Core Blazor. It is designed to help you manage your projects and tasks efficiently, whether for personal use or team collaboration. Ticky is fully open-source and free to use (and will always be), with a focus on simplicity and usability.

## ❓ The why

You might be thinking, "Another Kanban app?" and that's fair! As someone who navigated between Trello for personal tasks and commercial projects, and Jira at work, I often found myself searching endlessly for "power-ups" that eventually turned into paid features, or trying open-source solutions that didn't quite hit the mark on what I needed or simply lacked the right feel. Ticky was born from that experience - a desire to build something truly comprehensive, intuitive, and always accessible. It's my answer to those frustrations, and it means Ticky will always be free and fully open-source.

## 🌟 Features

Ticky is packed with powerful features designed to make your task management seamless and enjoyable:

- **Projects**: Create and manage projects to group your boards.
- **Boards**: Create and manage your own Kanban boards. You can even make them favorites to stay atop the list.
- **Templates**: You can clone boards, therefore allowing you to use boards as templates.
- **Columns**: Each board can have any amount of columns, all of which are collapsible. You can specify a max card limit, automatically mark the cards within the column as finished and automatically order them.
- **Cards**: Create, edit, and move task cards between columns with drag-and-drop functionality. At a glance see all important information about a card. Decide where new cards are placed.
- **Subtasks**: Break down tasks into smaller, manageable subtasks with completion tracking.
- **Deadline Management**: Set and track deadlines with color-coded indicators.
- **Time Tracking**: Track time spent on tasks with built-in timer functionality. Additionally view how much time has been spent on a specific column in the stats section.
- **Labels and Priorities**: Organize tasks with custom labels and priority levels. Label colors are fully customizable.
- **Attachments**: Upload and manage files associated with tasks.
- **Reminders**: Set email reminders for tasks.
- **Card Linking**: Link related cards together (Jira-like).
- **Activity Tracking**: Monitor all changes and activities on tasks.
- **Comments**: Leave comments on cards to discuss and provide other useful information.
- **User Management**: You can add users on the project level or on the board level, choosing between a Member and an Admin role. Also possible to disable user signups.
- **Admin Panel**: For creating, editing and deleting users as an admin. Mostly for when not using SMTP.
- **Email Notifications**: Receive notifications for deadlines, and reminders.
- **Progress**: Track your progress within a board by seeing how many tasks have already been completed.
- **App-wide Search**: Find cards from other boards based on their unique identificator (like TEST-1), jump directly to them.
- **Recent board**: Immediately go back to your most recent board.
- **Auto-generated avatars** (optional): To make things more colorful.
- **Able to do run offline**: Ability to run fully offline, disabling the avatar service, having all the files bundled on the server and not using SMTP.
- **Dark Mode**: A sleek dark mode for less strain on your eyes.
- **Snooze Cards**: Reduce clutter by snoozing cards that cannot be worked on just yet.
- **Repeat Cards**: Automatically repeat cards at specified intervals, perfect for recurring tasks.
- **Responsive Design**: Take your tasks with you, no matter whether you are at your computer or running errands with just your phone.
- **Completion Confetti**: Feel the satisfaction of completing each task with a bunch of confetti, there to celebrate your success.
- **Filtering**: Easily find and organize tasks based on various criteria.
- **Trello import**: You can import your Trello boards, including the ability to map all the assigned members from your Trello board to Ticky users.
- ... and more!

## 📋 Prerequisites

- **Docker** installed on your system
  - Windows: Download and install Docker Desktop from [docker.com](https://www.docker.com/products/docker-desktop/)
  - macOS: Download and install Docker Desktop from [docker.com](https://www.docker.com/products/docker-desktop/)
  - Linux: Install Docker Engine and Docker Compose via your package manager
- **Basic text editor** (Notepad, VS Code, etc.) to create/edit the configuration file
- **SMTP Server** (optional) - for email notifications and password resets
  - Gmail, Outlook, or any email provider that supports SMTP
  - If you don't have SMTP, Ticky will work without email features

## 🚀 Getting Started

### Using Docker (Recommended)

**Step 1: Verify Docker Installation**
Open a terminal/command prompt and run:

```bash
docker --version
```

If these commands work, you're ready to proceed!

**Step 2: Create Project Directory**
Create a new folder for Ticky on your computer:

```bash
mkdir ticky-app
cd ticky-app
```

**Step 3: Create Configuration File**
Create a new file called `docker-compose.yaml` in your project folder and copy the following content. **Important**: Replace `your-secure-password` with a strong password of your choice (use the same password in all places where it appears):

```yaml
services:
  ticky-app:
    image: ghcr.io/dkorecko/ticky:latest # or pin to a specific version (like v1.0.0) for manual updates
    container_name: ticky-app
    ports:
      - "4088:8080"
    restart: unless-stopped
    volumes:
      - ./data/app/uploaded:/app/wwwroot/uploaded
    environment:
      - DB_HOST=ticky-db
      - DB_NAME=ticky # Database name, can be customized
      - DB_USERNAME=ticky # Database username, can be customized
      - DB_PASSWORD=your-secure-password
      #- FULLY_OFFLINE=true # Uncomment this if you want to disable the avatar service and run fully offline.
      #- DISABLE_USER_SIGNUPS=true # Uncomment to disable user self-registration. When true, only admins can create new users via the Admin Panel.
      - BASE_URL=http://localhost:4088 # Base URL for generating clickable links in emails (e.g., reminder emails). Change to the base URL you use to access Ticky.
      - SMTP_ENABLED=true # Change this to false to ignore SMTP configuration and disable SMTP setup. Resetting password via typical password reset won't work (will need to be reset by an admin via the Admin Panel), as well as reminders and notifications. Can be enabled at any time.
      - SMTP_HOST=your-smtp-host
      - SMTP_PORT=your-smtp-port
      - SMTP_DISPLAY_NAME=Ticky
      - SMTP_EMAIL=your-email@example.com
      - SMTP_USERNAME=your-smtp-username
      - SMTP_PASSWORD=your-smtp-password
      - SMTP_SECURITY=true
    depends_on:
      ticky-db:
        condition: service_healthy
  ticky-db:
    image: mysql:8
    container_name: ticky-db
    restart: unless-stopped
    environment:
      MYSQL_DATABASE: ticky # This should match DB_NAME in ticky-app container
      MYSQL_USER: ticky # This should match DB_USERNAME in ticky-app container
      MYSQL_ROOT_PASSWORD: your-secure-password
      MYSQL_PASSWORD: your-secure-password # This should match DB_PASSWORD
    healthcheck:
      test: ["CMD", "mysqladmin", "ping", "-h", "localhost"]
      timeout: 2s
      retries: 30
    volumes:
      - ./data/mysql:/var/lib/mysql
```

**Step 4: Configure Email (Optional)**
If you want email notifications and password reset functionality:

- Replace `your-smtp-host`, `your-smtp-port`, `your-email@example.com`, etc. with your actual email provider settings
- For Gmail: Use `smtp.gmail.com`, port `587`, and create an [App Password](https://support.google.com/accounts/answer/185833)
- If you don't want email features, change `SMTP_ENABLED=true` to `SMTP_ENABLED=false`

**Step 5: Start Ticky**
In your terminal/command prompt, navigate to your project folder and run:

```bash
docker compose up -d
```

This will:

- Download the necessary Docker images (this may take a few minutes the first time)
- Create and start the Ticky application and database
- Set up data storage folders automatically

**Step 6: Access Your Ticky Instance**

1. Open your web browser and go to: `http://localhost:4088`
2. Log in with the default admin account:
   - **Email**: `admin@ticky.com`
   - **Password**: `abc123`
3. You'll be prompted to change these credentials immediately for improved security
4. After changing your password, you'll be logged out - just log back in with your new credentials

**Step 7: Add Users (If SMTP Disabled)**
If you disabled SMTP, you'll need to create user accounts manually through the Admin Panel. If SMTP is enabled, users can register themselves.

### Troubleshooting

- **Port already in use**: Change `4088:8080` to `4089:8080` (or any other available port) in the docker-compose.yaml file
- **Permission errors**: Make sure Docker Desktop is running and you have proper permissions. In Linux, sudo can be used in front of the commands.
- **Can't access the app**: Wait a minute after starting - the database needs time to initialize on first run

### Manual Setup

1. Clone the repository:

   ```
   git clone https://github.com/dkorecko/Ticky.git
   cd Ticky
   ```

2. Set up your environment variables.

3. Run the application:
   ```
   dotnet run --project Ticky.Web/Ticky.Web.csproj
   ```

## 📷 Preview

![Image of the landing page in light mode](images/landing_light.png)
![Image of the board in light mode](images/board_light.png)
![Image of the card in light mode](images/card_light.png)
![Image of the landing page in dark mode](images/landing_dark.png)
![Image of the board in dark mode](images/board_dark.png)
![Image of the card in dark mode](images/card_dark.png)

## 🤝 Community

If you have any questions, need help setting up, want to share your feedback or discuss ideas and new features, then I have created a [Discord](https://discord.gg/DHCZqYwUUb) server. I’m always eager to hear from users and improve Ticky based on your needs.

## 🛠️ Project Structure

- **Ticky.Base**: Core entities, models, and shared components.
- **Ticky.Internal**: Data access, services, and business logic.
- **Ticky.Web**: Blazor web application, UI components, and user interface.

## 🔧 Configuration

### Database Setup

The application automatically applies migrations on startup.

## 🧪 Development

### Building

```
dotnet build
```

### Running Tests

```
dotnet test
```

### Watch Mode

```
dotnet watch
```

## 🔄 CI/CD

The project includes GitHub Actions workflows for CI/CD:

- Automated builds and tests on pull requests
- Docker image publishing to GitHub Container Registry (GHCR) on releases

## 📝 Contributing

Contributions are welcome! See the [CONTRIBUTING.md](CONTRIBUTING.md) file for details.

## 📄 License

This project is licensed under the MIT License - see the [LICENSE](LICENSE) file for details.

## 👏 Acknowledgements

- [Blazor](https://dotnet.microsoft.com/apps/aspnet/web-apps/blazor) - Web framework used
- [Entity Framework Core](https://docs.microsoft.com/en-us/ef/core/) - ORM used
- [TailwindCSS](https://tailwindcss.com/) - CSS framework
- [Font Awesome](https://fontawesome.com/) - Icons
- [Sortable.js](https://github.com/SortableJS/Sortable) - Drag-and-drop functionality

## 📞 Contact

Have questions or feedback? I'd love to hear from you! Please feel free to open an issue on this repository.

---

Made with ❤️


================================================
FILE: Ticky.Base/Constants.cs
================================================
namespace Ticky.Base
{
    public static class Constants
    {
        public static bool SMTP_ENABLED = true;
        public static bool FULLY_OFFLINE;
        public static bool DISABLE_USER_SIGNUPS = false;
        public static string BASE_URL = string.Empty;
#if DEBUG
        public const string APP_NAME = "Ticky [DEV]";
#else
        public const string APP_NAME = "Ticky";
#endif

        public static class CascadingParameters
        {
            public const string CurrentAccount = "CurrentAccount";
            public const string MainLayout = "MainLayout";
        }

        public static class Defaults
        {
            public const string ADMIN_EMAIL = "admin@ticky.com";
            public const string ADMIN_PASSWORD = "abc123";
        }

        public static class Emails
        {
            public static readonly string BASE_PATH = Path.Combine(WWW_ROOT, "emails");

            public static class Mappings
            {
                public const string VERIFICATION_CODE = "{VERIFICATION_CODE}";
                public const string CARD_CODE = "{CARD_CODE}";
                public const string CARD_TEXT = "{CARD_TEXT}";
                public const string CARD_SCHEDULED_FOR = "{CARD_SCHEDULED_FOR}";
                public const string CARD_DESCRIPTION = "{CARD_DESCRIPTION}";
                public const string CARD_SUBTASKS = "{CARD_SUBTASKS}";
                public const string CARD_URL = "{CARD_URL}";
                public const string BASE_URL = "{BASE_URL}";
                public const string CARD_DEADLINE = "{CARD_DEADLINE}";
                public const string CARD_DEADLINE_SECTION = "{CARD_DEADLINE_SECTION}";
                public const string CARD_DESCRIPTION_SECTION = "{CARD_DESCRIPTION_SECTION}";
                public const string CARD_SUBTASKS_SECTION = "{CARD_SUBTASKS_SECTION}";
                public const string SUBTASK_TEXT = "{SUBTASK_TEXT}";
                public const string SUBTASK_ICON = "{SUBTASK_ICON}";
                public const string SUBTASK_ICON_COLOR = "{SUBTASK_ICON_COLOR}";
                public const string SUBTASK_COMPLETED_CLASS = "{SUBTASK_COMPLETED_CLASS}";
            }
        }

        public static class Hubs
        {
            public const string UPDATE_HUB = "/updatehub";
        }

        public static class Limits
        {
            public const int MINIMUM_SECOND_HOSTED_SERVICE_DELAY = 15;
            public const int DEFAULT_NOTIFICATION_TIME_IN_MS = 5000;
            public const int FILE_NAME_LENGTH = 10;
            public const long MAX_FILE_SIZE = 15360 * 1024;
            public const long MAX_IMAGE_SIZE = MAX_FILE_SIZE;
            public const long MAX_JSON_SIZE = MAX_FILE_SIZE;
            public const int MAX_FILES = 20;
            public const int DEBOUNCE_TIME_IN_MS = 1000;
        }

        public static class Mappings
        {
            public const string LOGIN_PATH = "/auth/login";
            public const string LOGOUT_PATH = "/auth/logout";
            public const string BOARD_PATH = "/boards";
            public const string ATTACHMENTS_API_PATH = "/api/attachments";
            public const string ATTACHMENTS_DOWNLOAD_PATH = ATTACHMENTS_API_PATH + "/download";
        }

        public static class Policies
        {
            public const string RequireAdmin = "RequireAdmin";
        }

        public static class Roles
        {
            public const string Admin = "Admin";
        }

        public static class StorageKeys
        {
            public const string BoardPreferences = "Ticky_BoardPreferences";
            public const string FilterPreferencesPrefix = "Ticky_FilterPreferences";
        }

        public static readonly string WWW_ROOT = Path.Combine(
            AppDomain.CurrentDomain.BaseDirectory,
            "wwwroot"
        );
        public static readonly string INFORMATION_PATH = Path.Combine(WWW_ROOT, "information.json");
        public static readonly string SAVE_UPLOADED_PATH = $"wwwroot/uploaded";
        public static readonly string SAVE_UPLOADED_IMAGES_PATH = $"{SAVE_UPLOADED_PATH}/images";
        public static readonly string SAVE_UPLOADED_FILES_PATH = $"{SAVE_UPLOADED_PATH}/files";
        public static readonly string ACCESS_UPLOADED_PATH = $"./uploaded";
        public static readonly string ACCESS_UPLOADED_IMAGES_PATH =
            $"{ACCESS_UPLOADED_PATH}/images";
        public static readonly string ACCESS_UPLOADED_FILES_PATH = $"{ACCESS_UPLOADED_PATH}/files";

        public const string REPEATED_KEY = "is repeated by";

        public static readonly Dictionary<string, string> LINK_TYPE_PAIRS =
            new()
            {
                { "is blocked by", "blocks" },
                { "is tested by", "tests" },
                { "relates to", "relates to" },
                { REPEATED_KEY, "repeats" }
            };
    }
}


================================================
FILE: Ticky.Base/Converters/ColorToInt32Converter.cs
================================================
using Microsoft.EntityFrameworkCore.Storage.ValueConversion;

namespace Ticky.Base.Converters;

public class ColorToInt32Converter : ValueConverter<Color, int>
{
    public ColorToInt32Converter()
        : base(c => c.ToArgb(), v => Color.FromArgb(v)) { }
}


================================================
FILE: Ticky.Base/Converters/StringToTimeSpanConverter.cs
================================================
namespace Ticky.Base.Converters;

public static class StringToTimeSpanConverter
{
    public static TimeSpan ConvertToTimeSpan(this string str)
    {
        var time = new TimeSpan();

        foreach (var part in str.Split(' '))
        {
            if (part.EndsWith("h", StringComparison.OrdinalIgnoreCase))
                time = time.Add(TimeSpan.FromHours(int.Parse(part.TrimEnd('h'))));
            else if (part.EndsWith("m", StringComparison.OrdinalIgnoreCase))
                time = time.Add(TimeSpan.FromMinutes(int.Parse(part.TrimEnd('m'))));
            else if (part.EndsWith("s", StringComparison.OrdinalIgnoreCase))
                time = time.Add(TimeSpan.FromSeconds(int.Parse(part.TrimEnd('s'))));
            else
                throw new InvalidDataException();
        }

        return time;
    }

    public static string ConvertToString(this TimeSpan time)
    {
        string result = string.Empty;

        if (time.Hours > 0)
            result += $"{time.Hours}h ";

        if (time.Minutes > 0)
            result += $"{time.Minutes}m ";

        if (time.Seconds > 0 || string.IsNullOrWhiteSpace(result))
            result += $"{time.Seconds}s ";

        return result.Trim();
    }
}


================================================
FILE: Ticky.Base/DTOs/InformationDTO.cs
================================================
namespace Ticky.Base.DTOs;

public class InformationDTO
{
    public required string Title { get; set; }

    public required string Message { get; set; }

    public required int Id { get; set; }
}


================================================
FILE: Ticky.Base/DTOs/Notification.cs
================================================
namespace Ticky.Base.DTOs;

public record Notification(string text, NotificationType type = NotificationType.Success)
{
    public NotificationType Type { get; set; } = type;
    public string Text { get; set; } = text;
}


================================================
FILE: Ticky.Base/DTOs/Trello/TrelloCardDTO.cs
================================================
namespace Ticky.Base.DTOs.Trello;

public class TrelloCardDTO
{
    [JsonPropertyName("id")]
    public required string Id { get; set; }

    [JsonPropertyName("name")]
    public required string Name { get; set; }

    [JsonPropertyName("desc")]
    public required string Description { get; set; }

    [JsonPropertyName("closed")]
    public required bool Closed { get; set; }

    [JsonPropertyName("due")]
    public required DateTime? Due { get; set; }

    [JsonPropertyName("idList")]
    public required string IdList { get; set; }

    [JsonPropertyName("idLabels")]
    public required List<string> IdLabels { get; set; }

    [JsonPropertyName("mirrorSourceId")]
    public required string MirrorSourceId { get; set; }

    [JsonPropertyName("dueComplete")]
    public required bool DueComplete { get; set; }

    [JsonPropertyName("dateCompleted")]
    public required DateTime? DateCompleted { get; set; }

    [JsonPropertyName("dueReminder")]
    public required int? DueReminder { get; set; }

    [JsonPropertyName("idMembers")]
    public required List<string> IdMembers { get; set; }

    [JsonPropertyName("idMemberCreator")]
    public required string IdMemberCreator { get; set; }
}


================================================
FILE: Ticky.Base/DTOs/Trello/TrelloCheckItemDTO.cs
================================================
namespace Ticky.Base.DTOs.Trello;

public class TrelloCheckItemDTO
{
    [JsonPropertyName("id")]
    public required string Id { get; set; }

    [JsonPropertyName("name")]
    public required string Name { get; set; }

    [JsonPropertyName("idChecklist")]
    public required string IdChecklist { get; set; }

    [JsonPropertyName("state")]
    public required string State { get; set; }
}


================================================
FILE: Ticky.Base/DTOs/Trello/TrelloChecklistDTO.cs
================================================
namespace Ticky.Base.DTOs.Trello;

public class TrelloChecklistDTO
{
    [JsonPropertyName("id")]
    public required string Id { get; set; }

    [JsonPropertyName("name")]
    public required string Name { get; set; }

    [JsonPropertyName("idCard")]
    public required string IdCard { get; set; }

    [JsonPropertyName("checkItems")]
    public required List<TrelloCheckItemDTO> CheckItems { get; set; }
}


================================================
FILE: Ticky.Base/DTOs/Trello/TrelloLabelDTO.cs
================================================
namespace Ticky.Base.DTOs.Trello
{
    public class TrelloLabelDTO
    {
        [JsonPropertyName("id")]
        public required string Id { get; set; }

        [JsonPropertyName("name")]
        public required string Name { get; set; }

        [JsonPropertyName("color")]
        public required string Color { get; set; }
    }
}


================================================
FILE: Ticky.Base/DTOs/Trello/TrelloListDTO.cs
================================================
namespace Ticky.Base.DTOs.Trello;

public class TrelloListDTO
{
    [JsonPropertyName("id")]
    public required string Id { get; set; }

    [JsonPropertyName("name")]
    public required string Name { get; set; }

    [JsonPropertyName("closed")]
    public bool Closed { get; set; }

    [JsonPropertyName("softLimit")]
    public int? SoftLimit { get; set; }
}


================================================
FILE: Ticky.Base/DTOs/Trello/TrelloMemberDTO.cs
================================================
namespace Ticky.Base.DTOs.Trello;

public class TrelloMemberDTO
{
    [JsonPropertyName("id")]
    public required string Id { get; set; }

    [JsonPropertyName("fullName")]
    public required string FullName { get; set; }

    [JsonPropertyName("username")]
    public required string Username { get; set; }
}


================================================
FILE: Ticky.Base/DTOs/Trello/TrelloPreferencesDTO.cs
================================================
namespace Ticky.Base.DTOs.Trello;

public class TrelloPreferencesDTO
{
    [JsonPropertyName("selfJoin")]
    public required bool SelfJoin { get; set; }
}


================================================
FILE: Ticky.Base/DTOs/TrelloImportDTO.cs
================================================
using Ticky.Base.DTOs.Trello;

namespace Ticky.Base.DTOs;

public class TrelloImportDTO
{
    [JsonPropertyName("name")]
    public required string Name { get; set; }

    [JsonPropertyName("desc")]
    public required string Description { get; set; }

    [JsonPropertyName("starred")]
    public required bool Starred { get; set; }

    [JsonPropertyName("prefs")]
    public required TrelloPreferencesDTO Preferences { get; set; }

    [JsonPropertyName("lists")]
    public required List<TrelloListDTO> Lists { get; set; }

    [JsonPropertyName("cards")]
    public required List<TrelloCardDTO> Cards { get; set; }

    [JsonPropertyName("checklists")]
    public required List<TrelloChecklistDTO> Checklists { get; set; }

    [JsonPropertyName("labels")]
    public required List<TrelloLabelDTO> Labels { get; set; }

    [JsonPropertyName("members")]
    public required List<TrelloMemberDTO> Members { get; set; }
}


================================================
FILE: Ticky.Base/Entities/Abstractions/AbstractDbEntity.cs
================================================
namespace Ticky.Base.Entities.Abstractions;

public abstract class AbstractDbEntity : IDbEntry
{
    public int Id { get; set; }

    public DateTime CreatedAt { get; init; } = DateTime.Now;
}


================================================
FILE: Ticky.Base/Entities/Abstractions/IAssignable.cs
================================================
namespace Ticky.Base.Entities.Abstractions;

public interface IAssignable
{
    public List<User> Assignees { get; }
}


================================================
FILE: Ticky.Base/Entities/Abstractions/IDbEntry.cs
================================================
namespace Ticky.Base.Entities.Abstractions;

public interface IDbEntry
{
    public int Id { get; set; }

    public DateTime CreatedAt { get; }
}


================================================
FILE: Ticky.Base/Entities/Abstractions/IDeletable.cs
================================================
namespace Ticky.Base.Entities.Abstractions;

public interface IDeletable : IDbEntry
{
    public string Name { get; }
}


================================================
FILE: Ticky.Base/Entities/Abstractions/IOrderable.cs
================================================
namespace Ticky.Base.Entities.Abstractions;

public interface IOrderable
{
    public int Index { get; set; }
}


================================================
FILE: Ticky.Base/Entities/Activity.cs
================================================
namespace Ticky.Base.Entities;

public class Activity : AbstractDbEntity
{
    public required int UserId { get; set; }
    public virtual User User { get; set; } = null!;
    public required string Text { get; set; }
    public required int CardId { get; set; }
    public virtual Card Card { get; set; } = null!;
}


================================================
FILE: Ticky.Base/Entities/Attachment.cs
================================================
namespace Ticky.Base.Entities;

public class Attachment : AbstractDbEntity
{
    public required string FileName { get; set; }

    public required string OriginalName { get; set; }

    public required int CardId { get; set; }

    public virtual Card Card { get; set; } = null!;
}


================================================
FILE: Ticky.Base/Entities/Board.cs
================================================
namespace Ticky.Base.Entities;

public class Board : AbstractDbEntity, IDeletable
{
    [Required(AllowEmptyStrings = false)]
    public required string Name { get; set; }
    public required string Description { get; set; }

    [Required(AllowEmptyStrings = false)]
    [MinLength(1)]
    [MaxLength(5)]
    [RegularExpression("^[A-Z]*$", ErrorMessage = "The code must be in upper-case.")]
    public required string Code { get; set; }
    public required int ProjectId { get; set; }
    public virtual Project Project { get; set; } = null!;
    public virtual List<Column> Columns { get; set; } = [];
    public virtual List<BoardMembership> Memberships { get; set; } = [];
    public virtual List<Label> Labels { get; set; } = [];
    public virtual List<LastVisit> LastVisits { get; set; } = [];
    public virtual List<Favorite> Favorites { get; set; } = [];
    public bool DisableSortingAnimations { get; set; }

    public bool VerifyAccess(User user) =>
        Memberships.Any(x => x.UserId.Equals(user.Id))
        || Project.Memberships.Any(x => x.UserId.Equals(user.Id));
}


================================================
FILE: Ticky.Base/Entities/BoardMembership.cs
================================================
namespace Ticky.Base.Entities;

public class BoardMembership : AbstractDbEntity
{
    public required int UserId { get; set; }
    public virtual User User { get; set; } = null!;

    public required int BoardId { get; set; }
    public virtual Board Board { get; set; } = null!;

    public required bool IsAdmin { get; set; }

    public required DateTime AddedAt { get; set; }
}


================================================
FILE: Ticky.Base/Entities/Card.cs
================================================
using Ticky.Base.Entities.Owned;

namespace Ticky.Base.Entities;

public class Card : AbstractDbEntity, IOrderable, IDeletable, IAssignable
{
    public required string Name { get; set; }
    public string Description { get; set; } = string.Empty;
    public required int Number { get; set; }
    public required int Index { get; set; }
    public required int ColumnId { get; set; }
    public CardPriority Priority { get; set; } = CardPriority.Normal;
    public DateTime? Deadline { get; set; }
    public bool DeadlineProcessed { get; set; }
    public bool Flagged { get; set; }
    public required int CreatedById { get; set; }
    public virtual User CreatedBy { get; set; } = null!;
    public virtual Column Column { get; set; } = null!;
    public virtual List<Comment> Comments { get; set; } = [];
    public virtual List<User> Assignees { get; set; } = [];
    public virtual List<Attachment> Attachments { get; set; } = [];
    public virtual List<Activity> Activities { get; set; } = [];
    public virtual List<Subtask> Subtasks { get; set; } = [];
    public virtual List<Reminder> Reminders { get; set; } = [];
    public virtual List<Label> Labels { get; set; } = [];
    public virtual List<TimeRecord> TimeRecords { get; set; } = [];
    public virtual List<CardLink> LinkedIssuesOne { get; set; } = [];
    public virtual List<CardLink> LinkedIssuesTwo { get; set; } = [];

    public RepeatInfo? RepeatInfo { get; set; }

    public DateTime? SnoozedUntil { get; set; }

    public TimeSpan GetTotalTime() =>
        TimeRecords.Select(x => x.GetTotalTime()).Sum();

    public DateTime CalculateNextRepeat(DateTime from)
    {
        if (RepeatInfo is null)
            throw new Exception("Cannot calculate next repeat on card with no repeat.");

        var startDate = RepeatInfo.LastRepeat;
        var finalDate = new DateTime(DateOnly.FromDateTime(startDate.Date), RepeatInfo.Time);

        if (
            (int)RepeatInfo.Type < (int)RepeatType.EveryXthDay
            && RepeatInfo.Time < TimeOnly.FromDateTime(RepeatInfo.LastRepeat)
        )
        {
            finalDate = finalDate.AddDays(1);
        }

        switch (RepeatInfo.Type)
        {
            case RepeatType.Daily:
                return finalDate;
            case RepeatType.WeekDays:
            {
                var allowedDaysOfWeek = RepeatInfo.Selected!.Split(',').Select(x => x).ToList();

                while (!allowedDaysOfWeek.Any(x => finalDate.DayOfWeek.ToString().Contains(x)))
                    finalDate = finalDate.AddDays(1);

                return finalDate;
            }
            case RepeatType.MonthDayNumber:
            {
                var allowedDaysOfMonth = RepeatInfo.Selected!.Split(',').Select(x => x).ToList();

                while (!allowedDaysOfMonth.Contains(finalDate.Day.ToString()))
                    finalDate = finalDate.AddDays(1);

                return finalDate;
            }
            case RepeatType.EveryXthDay:
                return finalDate.AddDays(RepeatInfo.Number!.Value);
            case RepeatType.EveryXthWeek:
                return finalDate.AddDays(RepeatInfo.Number!.Value * 7);
            case RepeatType.EveryXthMonth:
                return finalDate.AddMonths(RepeatInfo.Number!.Value);
            case RepeatType.EveryXthYear:
                return finalDate.AddYears(RepeatInfo.Number!.Value);
        }

        throw new Exception("Unresolved repeat type.");
    }
}


================================================
FILE: Ticky.Base/Entities/CardLink.cs
================================================
namespace Ticky.Base.Entities;

public class CardLink : AbstractDbEntity
{
    public required int CardOneId { get; set; }

    public virtual Card CardOne { get; set; } = null!;

    public required int CardTwoId { get; set; }

    public virtual Card CardTwo { get; set; } = null!;

    public required string Category { get; set; }
}


================================================
FILE: Ticky.Base/Entities/Code.cs
================================================
namespace Ticky.Base.Entities;

public class Code : AbstractDbEntity
{
    public required string Value { get; set; }

    public required CodePurpose CodePurpose { get; set; }

    public required int UserId { get; set; }

    public virtual User User { get; set; } = default!;
}


================================================
FILE: Ticky.Base/Entities/Column.cs
================================================
namespace Ticky.Base.Entities;

public class Column : AbstractDbEntity, IOrderable, IDeletable
{
    public required string Name { get; set; }
    public required int BoardId { get; set; }
    public required int Index { get; set; }

    [Display(Name = "Max Cards (0 = unlimited)")]
    public int MaxCards { get; set; }

    [Display(Name = "Count cards within this column as finished")]
    public bool Finished { get; set; }
    public bool Collapsed { get; set; }

    [Obsolete("This property is deprecated and will be removed in next major release.")]
    [Display(Name = "Automatic card ordering")]
    public OrderRule OrderRule { get; set; }
    public virtual Board Board { get; set; } = null!;
    public virtual List<Card> Cards { get; set; } = [];

    [Display(Name = "New card placement")]
    public CardPlacement NewCardPlacement { get; set; } = CardPlacement.Bottom;
}


================================================
FILE: Ticky.Base/Entities/Comment.cs
================================================
namespace Ticky.Base.Entities;

public class Comment : AbstractDbEntity
{
    public required string Text { get; set; }
    public required int CreatedById { get; set; }
    public virtual User CreatedBy { get; set; } = null!;
    public required int CardId { get; set; }
    public virtual Card Card { get; set; } = null!;
}


================================================
FILE: Ticky.Base/Entities/Favorite.cs
================================================
namespace Ticky.Base.Entities;

public class Favorite : AbstractDbEntity
{
    public required int UserId { get; set; }

    public virtual User User { get; set; } = null!;

    public required int BoardId { get; set; }

    public virtual Board Board { get; set; } = null!;
}


================================================
FILE: Ticky.Base/Entities/Label.cs
================================================
namespace Ticky.Base.Entities
{
    public class Label : AbstractDbEntity, IDeletable
    {
        public required string Name { get; set; }

        public required Color TextColor { get; set; }

        public required Color BackgroundColor { get; set; }

        public required int BoardId { get; set; }

        public virtual Board Board { get; set; } = null!;

        public virtual List<Card> OnCards { get; set; } = [];
    }
}


================================================
FILE: Ticky.Base/Entities/LastVisit.cs
================================================
namespace Ticky.Base.Entities;

public class LastVisit : AbstractDbEntity
{
    public required int BoardId { get; set; }
    public virtual Board Board { get; set; } = null!;

    public required DateTime VisitTime { get; set; }

    public required int UserId { get; set; }
    public virtual User User { get; set; } = null!;
}


================================================
FILE: Ticky.Base/Entities/Owned/RepeatInfo.cs
================================================
namespace Ticky.Base.Entities.Owned;

[Owned]
public class RepeatInfo
{
    public required RepeatType Type { get; set; }

    public required TimeOnly Time { get; set; }

    public int? Number { get; set; }

    public string? Selected { get; set; }

    public DateTime LastRepeat { get; set; } = DateTime.Now;

    public required CardPlacement CardPlacement { get; set; }

    public int? TargetColumnId { get; set; }

    public string GetRepeatString() =>
        Type switch
        {
            RepeatType.Daily => "Daily",
            RepeatType.WeekDays => $"On each {string.Join(", ", Selected!.Split(','))}",
            RepeatType.MonthDayNumber
                => $"On day {string.Join(", ", Selected!.Split(','))} of the month",
            RepeatType.EveryXthDay => $"Every {Number} days",
            RepeatType.EveryXthWeek => $"Every {Number} weeks",
            RepeatType.EveryXthMonth => $"Every {Number} months",
            RepeatType.EveryXthYear => $"Every {Number} years",
            _ => "No repeat"
        };
}


================================================
FILE: Ticky.Base/Entities/Project.cs
================================================
namespace Ticky.Base.Entities;

public class Project : AbstractDbEntity, IDeletable
{
    [Required(AllowEmptyStrings = false)]
    public required string Name { get; set; }

    public virtual List<Board> Boards { get; set; } = [];

    public virtual List<ProjectMembership> Memberships { get; set; } = [];
}


================================================
FILE: Ticky.Base/Entities/ProjectMembership.cs
================================================
namespace Ticky.Base.Entities;

public class ProjectMembership : AbstractDbEntity
{
    public required int UserId { get; set; }
    public virtual User User { get; set; } = null!;

    public required int ProjectId { get; set; }
    public virtual Project Project { get; set; } = null!;

    public required bool IsAdmin { get; set; }

    public required DateTime AddedAt { get; set; }
}


================================================
FILE: Ticky.Base/Entities/Reminder.cs
================================================
namespace Ticky.Base.Entities;

public class Reminder : AbstractDbEntity
{
    public required DateTime At { get; set; }
    public required int CardId { get; set; }
    public virtual Card Card { get; set; } = null!;
}


================================================
FILE: Ticky.Base/Entities/Subtask.cs
================================================
namespace Ticky.Base.Entities;

public class Subtask : AbstractDbEntity, IOrderable, IAssignable
{
    public required int Index { get; set; }
    public required string Text { get; set; }
    public bool Completed { get; set; }
    public required int CardId { get; set; }
    public virtual Card Card { get; set; } = null!;
    public virtual List<User> Assignees { get; set; } = [];
}


================================================
FILE: Ticky.Base/Entities/TimeRecord.cs
================================================
namespace Ticky.Base.Entities;

public class TimeRecord : AbstractDbEntity
{
    public required int CardId { get; set; }

    public virtual Card Card { get; set; } = null!;

    public required int UserId { get; set; }

    public virtual User User { get; set; } = null!;

    public required DateTime StartedAt { get; set; }

    public DateTime? EndedAt { get; set; }

    public TimeSpan GetTotalTime() =>
        (EndedAt ?? DateTime.Now) - StartedAt;
}


================================================
FILE: Ticky.Base/Entities/User.cs
================================================
namespace Ticky.Base.Entities;

public class User : IdentityUser<int>, IDbEntry, IDeletable
{
    [NotMapped]
    public string Name
    {
        get => DisplayName;
    }

    public virtual Code? EmailVerificationCode { get; set; }

    public virtual List<ProjectMembership> ProjectMemberships { get; set; } = [];

    public virtual List<BoardMembership> BoardMemberships { get; set; } = [];

    public virtual List<Card> CreatedCards { get; set; } = [];

    public virtual List<Comment> CreatedComments { get; set; } = [];

    public virtual List<Card> AssignedTo { get; set; } = [];

    public virtual List<Activity> Activities { get; set; } = [];

    public virtual List<TimeRecord> TimeRecords { get; set; } = [];

    public virtual List<LastVisit> LastVisits { get; set; } = [];

    public virtual List<Favorite> Favorites { get; set; } = [];

    public virtual List<Subtask> Subtasks { get; set; } = [];

    public required string DisplayName { get; set; }

    public bool InstantDelete { get; set; }

    public bool AutomaticCardEdit { get; set; }

    public bool AutomaticDeadlineReminder { get; set; }

    public bool AutomaticAssign { get; set; }

    public string? ProfilePictureFileName { get; set; }

    public int? LastVisitedBoardId { get; set; }

    public bool NeedsNewCredentials { get; set; }

    public DateTime CreatedAt { get; set; } = DateTime.Now;

    public int? LastViewedInformation { get; set; }

    public DateTime? InformationSnoozeUntil { get; set; }
}


================================================
FILE: Ticky.Base/Enums/CardPlacement.cs
================================================
namespace Ticky.Base.Enums;

public enum CardPlacement
{
    Top = 0,
    Bottom = 1
}


================================================
FILE: Ticky.Base/Enums/CardPriority.cs
================================================
namespace Ticky.Base.Enums;

public enum CardPriority
{
    Normal,
    Medium,
    High,
    Critical
}


================================================
FILE: Ticky.Base/Enums/CodePurpose.cs
================================================
namespace Ticky.Base.Enums;

public enum CodePurpose
{
    NewAccount,
    ForgottenPassword
}


================================================
FILE: Ticky.Base/Enums/DeadlineColor.cs
================================================
namespace Ticky.Base.Enums;

public enum DeadlineColor
{
    Default,
    Yellow,
    Red,
    Green
}


================================================
FILE: Ticky.Base/Enums/ImportSource.cs
================================================
namespace Ticky.Base.Enums;

public enum ImportSource
{
    Trello
}


================================================
FILE: Ticky.Base/Enums/ImportType.cs
================================================


================================================
FILE: Ticky.Base/Enums/NotificationType.cs
================================================
namespace Ticky.Base.Enums;

public enum NotificationType
{
    Success,
    Fail
}


================================================
FILE: Ticky.Base/Enums/OperationType.cs
================================================
namespace Ticky.Base.Enums;

public enum OperationType
{
    [Display(Name = "added")]
    Added,

    [Display(Name = "edited")]
    Edited,

    [Display(Name = "deleted")]
    Deleted,

    [Display(Name = "had their favorites status changed")]
    Favorited
}


================================================
FILE: Ticky.Base/Enums/OrderRule.cs
================================================
namespace Ticky.Base.Enums;

public enum OrderRule
{
    [Display(Name = "No automatic ordering")]
    None = 0,

    [Display(Name = "By closest due date")]
    ClosestDueDate = 1,

    [Display(Name = "By latest due date")]
    LatestDueDate = 2,

    [Display(Name = "By highest priority")]
    HighestPriority = 3,

    [Display(Name = "By lowest priority")]
    LowestPriority = 4,

    [Display(Name = "By newest first")]
    NewestFirst = 5,

    [Display(Name = "By oldest first")]
    OldestFirst = 6,

    [Display(Name = "Migrated")]
    Migrated = -1
}


================================================
FILE: Ticky.Base/Enums/RepeatType.cs
================================================
namespace Ticky.Base.Enums;

public enum RepeatType
{
    [Display(Name = "Daily")]
    Daily = 1,

    [Display(Name = "On specific days of the week (Monday, Tuesday, ...)")]
    WeekDays = 10,

    [Display(Name = "On specific days of the month (eg. 1st, 15th, ...)")]
    MonthDayNumber = 20,

    // Make sure order is correct, all above this line handle days differently from the ones below
    [Display(Name = "Every x days")]
    EveryXthDay = 100,

    [Display(Name = "Every x weeks")]
    EveryXthWeek = 110,

    [Display(Name = "Every x months")]
    EveryXthMonth = 120,

    [Display(Name = "Every x years")]
    EveryXthYear = 130
}


================================================
FILE: Ticky.Base/Enums/TrelloArchivedHandlingType.cs
================================================
namespace Ticky.Base.Enums;

public enum TrelloArchivedHandlingType
{
    [Display(Name = "Do not add to the board")]
    DontAdd,

    [Display(Name = "Add to the board")]
    Add
}


================================================
FILE: Ticky.Base/GlobalUsings.cs
================================================
global using Devity.Extensions;
global using System.ComponentModel.DataAnnotations;
global using System.ComponentModel.DataAnnotations.Schema;
global using System.Drawing;
global using System.Text.Json.Serialization;
global using Microsoft.AspNetCore.Identity;
global using Microsoft.EntityFrameworkCore;
global using Ticky.Base.Converters;
global using Ticky.Base.DTOs;
global using Ticky.Base.Entities.Abstractions;
global using Ticky.Base.Enums;
global using Ticky.Base.Validation;


================================================
FILE: Ticky.Base/Models/BoardPreferencesModel.cs
================================================
namespace Ticky.Base.Models;

public class BoardPreferencesModel
{
    public bool ShowStats { get; set; } = false;
}


================================================
FILE: Ticky.Base/Models/CloneBoardModel.cs
================================================
namespace Ticky.Base.Models;

public class CloneBoardModel
{
    [Display(Name = "Target project")]
    [Required]
    public int? TargetProjectId { get; set; }

    [Display(Name = "Board code/identifier")]
    [Required(AllowEmptyStrings = false)]
    [MinLength(1)]
    [MaxLength(5)]
    [RegularExpression("^[A-Z]*$", ErrorMessage = "The code must be in upper-case.")]
    public string Code { get; set; } = string.Empty;
}


================================================
FILE: Ticky.Base/Models/CreateCardModel.cs
================================================
namespace Ticky.Base.Models;

public class CreateCardModel
{
    public string Text { get; set; } = string.Empty;
}


================================================
FILE: Ticky.Base/Models/CredentialsModel.cs
================================================
namespace Ticky.Base.Models;

public class CredentialsModel
{
    [Display(Name = "Current e-mail address (username)")]
    [Required(AllowEmptyStrings = false)]
    [EmailAddress]
    public string OldEmailAddress { get; set; } = string.Empty;

    [Display(Name = "New e-mail address (username)")]
    [Required(AllowEmptyStrings = false)]
    [EmailAddress]
    public string NewEmailAddress { get; set; } = string.Empty;

    [Display(Name = "Current password")]
    [DataType(DataType.Password)]
    public string OldPassword { get; set; } = string.Empty;

    [Display(Name = "New password")]
    [DataType(DataType.Password)]
    public string NewPassword { get; set; } = string.Empty;

    [Display(Name = "Repeat new password")]
    [DataType(DataType.Password)]
    [Compare(nameof(NewPassword))]
    public string RepeatPassword { get; set; } = string.Empty;
}


================================================
FILE: Ticky.Base/Models/DeadlineModel.cs
================================================
namespace Ticky.Base.Models;

public class DeadlineModel
{
    [Required(AllowEmptyStrings = false)]
    [DataType(DataType.DateTime)]
    public DateTime At { get; set; } = DateTime.Now;

    [Required(AllowEmptyStrings = false)]
    [RegularExpression(
        "^([01][0-9]|2[0-3]):[0-5][0-9]$",
        ErrorMessage = "The time must be in HH:mm format."
    )]
    public string Time { get; set; } = string.Empty;
}


================================================
FILE: Ticky.Base/Models/FilterCardsModel.cs
================================================
namespace Ticky.Base.Models;

public class FilterCardsModel
{
    public string Text { get; set; } = string.Empty;

    public HashSet<int> AssignedUserIds { get; set; } = [];

    public HashSet<int> LabelIds { get; set; } = [];

    public bool IncludeUnassigned { get; set; }

    public bool ExcludeCompleted { get; set; }

    public bool ExpandAssignedUsersSection { get; set; }

    public bool ExpandLabelsSection { get; set; }

    public bool IsAnyFilterApplied()
    {
        return !string.IsNullOrWhiteSpace(Text)
            || AssignedUserIds.Count > 0
            || LabelIds.Count > 0
            || IncludeUnassigned
            || ExcludeCompleted;
    }

    public void ClearFilters()
    {
        Text = string.Empty;
        AssignedUserIds.Clear();
        LabelIds.Clear();
        IncludeUnassigned = false;
        ExcludeCompleted = false;
    }
}


================================================
FILE: Ticky.Base/Models/ImportModel.cs
================================================
namespace Ticky.Base.Models;

public class ImportModel
{
    public ImportSource Source { get; set; } = ImportSource.Trello;

    [Display(Name = "What to do with an item that was archived")]
    public TrelloArchivedHandlingType ArchivedCardsHandling { get; set; } =
        TrelloArchivedHandlingType.DontAdd;

    [Required(ErrorMessage = "There was no valid uploaded file.")]
    public TrelloImportDTO? ImportDto { get; set; }

    [Required(AllowEmptyStrings = false)]
    [MinLength(1)]
    [MaxLength(5)]
    [RegularExpression("^[A-Z]*$", ErrorMessage = "The code must be in upper-case.")]
    public string Code { get; set; } = string.Empty;

    public string[]? MemberIdentifiers { get; set; }
}


================================================
FILE: Ticky.Base/Models/LabelModel.cs
================================================
namespace Ticky.Base.Models;

public class LabelModel
{
    [Required(AllowEmptyStrings = false)]
    [Display(Name = "Label text")]
    public string Text { get; set; } = string.Empty;

    [Required(AllowEmptyStrings = false)]
    [Display(Name = "Text color")]
    public Color? TextColor { get; set; }

    [Required(AllowEmptyStrings = false)]
    [Display(Name = "Background color")]
    public Color? BackgroundColor { get; set; }
}


================================================
FILE: Ticky.Base/Models/LinkCardsModel.cs
================================================
namespace Ticky.Base.Models;

public class LinkCardsModel
{
    [Required(AllowEmptyStrings = false)]
    [Display(Name = "Text")]
    public string Text { get; set; } = string.Empty;
}


================================================
FILE: Ticky.Base/Models/ReminderModel.cs
================================================
namespace Ticky.Base.Models;

public class ReminderModel
{
    [Required(AllowEmptyStrings = false)]
    [DataType(DataType.DateTime)]
    public DateTime At { get; set; } = DateTime.Now;

    [Required(AllowEmptyStrings = false)]
    [RegularExpression(
        "^([01][0-9]|2[0-3]):[0-5][0-9]$",
        ErrorMessage = "The time must be in HH:mm format."
    )]
    public string Time { get; set; } =
        DateTime.Now.AddHours(1).AddMinutes(-DateTime.Now.Minute).ToString("HH:mm");
}


================================================
FILE: Ticky.Base/Models/RepeatCardModel.cs
================================================
using System.ComponentModel;

namespace Ticky.Base.Models;

public class RepeatCardModel
{
    public RepeatType Type { get; set; } = RepeatType.Daily;

    [Display(Name = "Days within month (1,15,31 format)")]
    [RequiredIf(nameof(Type), RepeatType.MonthDayNumber)]
    [RegularExpression(
        "^(3[01]|[12][0-9]|[1-9])(?:,(3[01]|[12][0-9]|[1-9]))*$",
        ErrorMessage = "This field must be in this format: 1,15,31"
    )]
    public string? SelectedMonthDays { get; set; }

    [Display(Name = "Days to repeat on (Mon,Tue,Wed format)")]
    [RequiredIf(nameof(Type), RepeatType.WeekDays)]
    [RegularExpression(
        "^(Mon|Tue|Wed|Thu|Fri|Sat|Sun)(?:,(Mon|Tue|Wed|Thu|Fri|Sat|Sun))*$",
        ErrorMessage = "This field must be in this format: Mon,Tue,Wed"
    )]
    public string? SelectedWeekDays { get; set; }

    [RequiredIf(
        nameof(Type),
        RepeatType.EveryXthDay,
        RepeatType.EveryXthWeek,
        RepeatType.EveryXthMonth,
        RepeatType.EveryXthYear
    )]
    [Range(1, 999)]
    public int? Number { get; set; }

    [Display(Name = "Start from date")]
    [Required(AllowEmptyStrings = false)]
    [DataType(DataType.Date)]
    public DateOnly StartDate { get; set; } = DateOnly.FromDateTime(DateTime.Now);

    [Display(Name = "Time of day for the repeat to occur")]
    [Required(AllowEmptyStrings = false)]
    [RegularExpression(
        "^([01][0-9]|2[0-3]):[0-5][0-9]$",
        ErrorMessage = "The time must be in HH:mm format."
    )]
    public string Time { get; set; } = string.Empty;

    [Display(Name = "Target column for the new card")]
    [Required]
    public int? TargetColumnId { get; set; }

    [Display(Name = "Where in column to place the new card")]
    public CardPlacement CardPlacement { get; set; } = CardPlacement.Top;

    public string? GetRelevantSelectedValue() =>
        Type switch
        {
            RepeatType.MonthDayNumber => SelectedMonthDays,
            RepeatType.WeekDays => SelectedWeekDays,
            _ => null
        };

    public void SetRelevantSelectedValue(string? value)
    {
        switch (Type)
        {
            case RepeatType.MonthDayNumber:
                SelectedMonthDays = value;
                break;
            case RepeatType.WeekDays:
                SelectedWeekDays = value;
                break;
        }
    }
}


================================================
FILE: Ticky.Base/Models/SnoozeCardModel.cs
================================================
namespace Ticky.Base.Models;

public class SnoozeCardModel
{
    [Required(AllowEmptyStrings = false)]
    [DataType(DataType.DateTime)]
    public DateTime At { get; set; } = DateTime.Now;

    [Required(AllowEmptyStrings = false)]
    [RegularExpression(
        "^([01][0-9]|2[0-3]):[0-5][0-9]$",
        ErrorMessage = "The time must be in HH:mm format."
    )]
    public string Time { get; set; } = string.Empty;
}


================================================
FILE: Ticky.Base/Models/SubtaskModel.cs
================================================
namespace Ticky.Base.Models;

public class SubtaskModel
{
    [Required(AllowEmptyStrings = false)]
    [Display(Name = "Subtask text")]
    public string Text { get; set; } = string.Empty;
}


================================================
FILE: Ticky.Base/Models/TimeRecordModel.cs
================================================
namespace Ticky.Base.Models;

public class TimeRecordModel
{
    [Display(Name = "Spent time (0h 0m 0s)")]
    [Required(AllowEmptyStrings = false)]
    [IsValidTimeSpan]
    public string Time { get; set; } = string.Empty;
}


================================================
FILE: Ticky.Base/Models/UserModel.cs
================================================
namespace Ticky.Base.Models;

public class UserModel
{
    [Display(Name = "Full name")]
    [Required]
    public string DisplayName { get; set; } = string.Empty;

    [Display(Name = "Email address")]
    [Required]
    [EmailAddress]
    public string Email { get; set; } = string.Empty;

    [Display(Name = "Password")]
    [Required]
    [DataType(DataType.Password)]
    public string Password { get; set; } = string.Empty;

    [Display(Name = "Repeat password")]
    [Required]
    [DataType(DataType.Password)]
    [Compare(nameof(Password))]
    public string ConfirmPassword { get; set; } = string.Empty;
}


================================================
FILE: Ticky.Base/Ticky.Base.csproj
================================================
<Project Sdk="Microsoft.NET.Sdk">

  <PropertyGroup>
    <TargetFramework>net9.0</TargetFramework>
    <ImplicitUsings>enable</ImplicitUsings>
    <Nullable>enable</Nullable>
  </PropertyGroup>

  <ItemGroup>
    <PackageReference Include="Devity.Extensions" Version="2026.1.4.1450" />
    <PackageReference Include="Microsoft.AspNetCore.Identity.EntityFrameworkCore" Version="9.0.9" />
  </ItemGroup>

</Project>


================================================
FILE: Ticky.Base/Validation/IsValidTimeSpan.cs
================================================
namespace Ticky.Base.Validation;

public class IsValidTimeSpan() : ValidationAttribute
{
    private const string ERROR_MESSAGE = "This field must be in the 0h 0m 0s format.";

    protected override ValidationResult? IsValid(object? value, ValidationContext validationContext)
    {
        var errorMessage = ErrorMessage ?? ERROR_MESSAGE;

        if (value == null)
            return ValidationResult.Success;

        if (validationContext.MemberName is null)
            throw new Exception("What");

        if (value is not string str)
            return new ValidationResult(errorMessage, [validationContext.MemberName]);

        try
        {
            str.ConvertToTimeSpan();
        }
        catch
        {
            return new ValidationResult(errorMessage, [validationContext.MemberName]);
        }

        return ValidationResult.Success;
    }
}


================================================
FILE: Ticky.Base/Validation/RequiredIf.cs
================================================
namespace Ticky.Base.Validation;

public class RequiredIfAttribute : ValidationAttribute
{
    private readonly string _propertyName;
    private readonly object[] _allowedValues;

    public RequiredIfAttribute(string propertyName, params object[] allowedValues)
    {
        _propertyName = propertyName ?? throw new ArgumentNullException(nameof(propertyName));
        _allowedValues = allowedValues;
    }

    public override string FormatErrorMessage(string name)
    {
        var errorMessage = $"Property {name} is required.";
        return ErrorMessage ?? errorMessage;
    }

    protected override ValidationResult? IsValid(object? value, ValidationContext validationContext)
    {
        if (validationContext.MemberName is null)
            throw new Exception("What");

        var property = validationContext.ObjectType.GetProperty(_propertyName);

        if (property == null)
        {
            throw new NotSupportedException(
                $"Can't find {_propertyName} on searched type: {validationContext.ObjectType.Name}"
            );
        }

        var requiredIfTypeActualValue = property.GetValue(validationContext.ObjectInstance);

        if (requiredIfTypeActualValue == null && _allowedValues != null)
        {
            return ValidationResult.Success;
        }

        if (
            requiredIfTypeActualValue == null
            || _allowedValues!.Any(x => requiredIfTypeActualValue.Equals(x))
        )
        {
            return value == null
                ? new ValidationResult(
                    FormatErrorMessage(validationContext.DisplayName),
                    [validationContext.MemberName]
                )
                : ValidationResult.Success;
        }

        return ValidationResult.Success;
    }
}


================================================
FILE: Ticky.Internal/Data/DataContext.cs
================================================
using Microsoft.AspNetCore.DataProtection.EntityFrameworkCore;
using Microsoft.AspNetCore.Identity;
using Ticky.Base.Converters;

namespace Ticky.Internal.Data
{
    public class DataContext
        : IdentityDbContext<User, IdentityRole<int>, int>,
            IDataProtectionKeyContext
    {
        public DbSet<Code> Codes { get; set; } = default!;
        public DbSet<Project> Projects { get; set; } = default!;
        public DbSet<Board> Boards { get; set; } = default!;
        public DbSet<Column> Columns { get; set; } = default!;
        public DbSet<Card> Cards { get; set; } = default!;
        public DbSet<Comment> Comments { get; set; } = default!;
        public DbSet<ProjectMembership> ProjectMemberships { get; set; } = default!;
        public DbSet<BoardMembership> BoardMemberships { get; set; } = default!;
        public DbSet<Activity> Activities { get; set; } = default!;
        public DbSet<Attachment> Attachments { get; set; } = default!;
        public DbSet<Subtask> Subtasks { get; set; } = default!;
        public DbSet<Reminder> Reminders { get; set; } = default!;
        public DbSet<Label> Labels { get; set; } = default!;
        public DbSet<TimeRecord> TimeRecords { get; set; } = default!;
        public DbSet<LastVisit> LastVisits { get; set; } = default!;
        public DbSet<CardLink> CardLinks { get; set; } = default!;
        public DbSet<Favorite> Favorites { get; set; } = default!;
        public DbSet<DataProtectionKey> DataProtectionKeys { get; set; } = default!;

        public DataContext(DbContextOptions<DataContext> options)
            : base(options) { }

        protected override void OnModelCreating(ModelBuilder modelBuilder)
        {
            base.OnModelCreating(modelBuilder);

            modelBuilder
                .Entity<User>()
                .HasOne(x => x.EmailVerificationCode)
                .WithOne(x => x.User)
                .HasForeignKey<Code>(x => x.UserId)
                .OnDelete(DeleteBehavior.Cascade);

            modelBuilder
                .Entity<Project>()
                .HasMany(x => x.Boards)
                .WithOne(x => x.Project)
                .HasForeignKey(x => x.ProjectId)
                .OnDelete(DeleteBehavior.Cascade);

            modelBuilder
                .Entity<Board>()
                .HasMany(x => x.Columns)
                .WithOne(x => x.Board)
                .HasForeignKey(x => x.BoardId)
                .OnDelete(DeleteBehavior.Cascade);

            modelBuilder
                .Entity<Column>()
                .HasMany(x => x.Cards)
                .WithOne(x => x.Column)
                .HasForeignKey(x => x.ColumnId)
                .OnDelete(DeleteBehavior.Cascade);

            modelBuilder
                .Entity<User>()
                .HasMany(x => x.ProjectMemberships)
                .WithOne(x => x.User)
                .HasForeignKey(x => x.UserId)
                .OnDelete(DeleteBehavior.Cascade);

            modelBuilder
                .Entity<Project>()
                .HasMany(x => x.Memberships)
                .WithOne(x => x.Project)
                .HasForeignKey(x => x.ProjectId)
                .OnDelete(DeleteBehavior.Cascade);

            modelBuilder
                .Entity<User>()
                .HasMany(x => x.BoardMemberships)
                .WithOne(x => x.User)
                .HasForeignKey(x => x.UserId)
                .OnDelete(DeleteBehavior.Cascade);

            modelBuilder
                .Entity<Board>()
                .HasMany(x => x.Memberships)
                .WithOne(x => x.Board)
                .HasForeignKey(x => x.BoardId)
                .OnDelete(DeleteBehavior.Cascade);

            modelBuilder
                .Entity<Card>()
                .HasOne(x => x.CreatedBy)
                .WithMany(x => x.CreatedCards)
                .HasForeignKey(x => x.CreatedById)
                .OnDelete(DeleteBehavior.Cascade);

            modelBuilder
                .Entity<User>()
                .HasMany(x => x.CreatedComments)
                .WithOne(x => x.CreatedBy)
                .HasForeignKey(x => x.CreatedById)
                .OnDelete(DeleteBehavior.Cascade);

            modelBuilder
                .Entity<Card>()
                .HasMany(x => x.Comments)
                .WithOne(x => x.Card)
                .HasForeignKey(x => x.CardId)
                .OnDelete(DeleteBehavior.Cascade);

            modelBuilder.Entity<Card>().HasMany(x => x.Assignees).WithMany(x => x.AssignedTo);

            modelBuilder.Entity<Subtask>().HasMany(x => x.Assignees).WithMany(x => x.Subtasks);

            modelBuilder
                .Entity<Card>()
                .HasMany(x => x.Attachments)
                .WithOne(x => x.Card)
                .HasForeignKey(x => x.CardId)
                .OnDelete(DeleteBehavior.Cascade);

            modelBuilder
                .Entity<Card>()
                .HasMany(x => x.Activities)
                .WithOne(x => x.Card)
                .HasForeignKey(x => x.CardId)
                .OnDelete(DeleteBehavior.Cascade);

            modelBuilder
                .Entity<User>()
                .HasMany(x => x.Activities)
                .WithOne(x => x.User)
                .HasForeignKey(x => x.UserId)
                .OnDelete(DeleteBehavior.Cascade);

            modelBuilder
                .Entity<Card>()
                .HasMany(x => x.Subtasks)
                .WithOne(x => x.Card)
                .HasForeignKey(x => x.CardId)
                .OnDelete(DeleteBehavior.Cascade);

            modelBuilder
                .Entity<Card>()
                .HasMany(x => x.Reminders)
                .WithOne(x => x.Card)
                .HasForeignKey(x => x.CardId)
                .OnDelete(DeleteBehavior.Cascade);

            modelBuilder
                .Entity<Board>()
                .HasMany(x => x.Labels)
                .WithOne(x => x.Board)
                .HasForeignKey(x => x.BoardId)
                .OnDelete(DeleteBehavior.Cascade);

            modelBuilder.Entity<Card>().HasMany(x => x.Labels).WithMany(x => x.OnCards);

            modelBuilder
                .Entity<User>()
                .HasMany(x => x.TimeRecords)
                .WithOne(x => x.User)
                .HasForeignKey(x => x.UserId)
                .OnDelete(DeleteBehavior.Cascade);

            modelBuilder
                .Entity<Card>()
                .HasMany(x => x.TimeRecords)
                .WithOne(x => x.Card)
                .HasForeignKey(x => x.CardId)
                .OnDelete(DeleteBehavior.Cascade);

            modelBuilder
                .Entity<User>()
                .HasMany(x => x.LastVisits)
                .WithOne(x => x.User)
                .HasForeignKey(x => x.UserId)
                .OnDelete(DeleteBehavior.Cascade);

            modelBuilder
                .Entity<Board>()
                .HasMany(x => x.LastVisits)
                .WithOne(x => x.Board)
                .HasForeignKey(x => x.BoardId)
                .OnDelete(DeleteBehavior.Cascade);

            modelBuilder
                .Entity<Card>()
                .HasMany(x => x.LinkedIssuesOne)
                .WithOne(x => x.CardOne)
                .HasForeignKey(x => x.CardOneId)
                .OnDelete(DeleteBehavior.Cascade);

            modelBuilder
                .Entity<Card>()
                .HasMany(x => x.LinkedIssuesTwo)
                .WithOne(x => x.CardTwo)
                .HasForeignKey(x => x.CardTwoId)
                .OnDelete(DeleteBehavior.Cascade);

            modelBuilder
                .Entity<Label>()
                .Property(x => x.BackgroundColor)
                .HasConversion<ColorToInt32Converter>();

            modelBuilder
                .Entity<Label>()
                .Property(x => x.TextColor)
                .HasConversion<ColorToInt32Converter>();

            modelBuilder
                .Entity<Board>()
                .HasMany(x => x.Favorites)
                .WithOne(x => x.Board)
                .HasForeignKey(x => x.BoardId)
                .OnDelete(DeleteBehavior.Cascade);

            modelBuilder
                .Entity<User>()
                .HasMany(x => x.Favorites)
                .WithOne(x => x.User)
                .HasForeignKey(x => x.UserId)
                .OnDelete(DeleteBehavior.Cascade);
        }
    }
}


================================================
FILE: Ticky.Internal/Data/DataMigrator.cs
================================================
namespace Ticky.Internal.Data;

public class DataMigrator
{
    public static async Task Seed(IServiceProvider serviceProvider)
    {
        var dataContext = serviceProvider.GetRequiredService<DataContext>();

        var topRules = new HashSet<OrderRule>
        {
            OrderRule.ClosestDueDate,
            OrderRule.HighestPriority,
            OrderRule.NewestFirst
        };

        await dataContext
            .Columns.Where(c => c.OrderRule != OrderRule.Migrated && topRules.Contains(c.OrderRule))
            .ExecuteUpdateAsync(s =>
                s.SetProperty(c => c.NewCardPlacement, CardPlacement.Top)
                    .SetProperty(c => c.OrderRule, OrderRule.Migrated)
            );

        await dataContext
            .Columns.Where(c =>
                c.OrderRule != OrderRule.Migrated && !topRules.Contains(c.OrderRule)
            )
            .ExecuteUpdateAsync(s =>
                s.SetProperty(c => c.NewCardPlacement, CardPlacement.Bottom)
                    .SetProperty(c => c.OrderRule, OrderRule.Migrated)
            );
    }
}


================================================
FILE: Ticky.Internal/Data/DataSeeder.cs
================================================
using System.Drawing;
using System.Security.Claims;
using Microsoft.AspNetCore.Identity;

namespace Ticky.Internal.Data;

public class DataSeeder
{
    public static async Task Seed(IServiceProvider serviceProvider)
    {
        var userManager = serviceProvider.GetRequiredService<UserManager<User>>();
        var dataContext = serviceProvider.GetRequiredService<DataContext>();
        var avatarService = serviceProvider.GetRequiredService<AvatarService>();

        var adminClaim = new Claim(ClaimTypes.Role, Constants.Roles.Admin);
        var adminUsers = await userManager.GetUsersForClaimAsync(adminClaim);

        if (!adminUsers.Any())
        {
            var adminUser = new User
            {
                DisplayName = "Default Admin",
                UserName = Constants.Defaults.ADMIN_EMAIL,
                Email = Constants.Defaults.ADMIN_EMAIL,
                EmailConfirmed = true,
                NeedsNewCredentials = true
            };

            adminUser.ProfilePictureFileName = await avatarService.FetchAvatarAsync(
                adminUser.DisplayName
            );

            var result = await userManager.CreateAsync(
                adminUser,
                Constants.Defaults.ADMIN_PASSWORD
            );

            if (!result.Succeeded)
                throw new Exception(
                    $"Failed to create admin account. Errors:\n- {string.Join("\n- ", result.Errors)}"
                );

            result = await userManager.AddClaimAsync(adminUser, adminClaim);

            if (!result.Succeeded)
                throw new Exception(
                    $"Failed to add claims to the admin account. Errors:\n- {string.Join("\n- ", result.Errors)}"
                );
        }

        if (await dataContext.Users.AnyAsync())
            return;

#if DEBUG
        var testUser = new User
        {
            DisplayName = "Testing User",
            UserName = "user@ticky.com",
            Email = "user@ticky.com",
            EmailConfirmed = true,
            AutomaticDeadlineReminder = true
        };

        testUser.ProfilePictureFileName = await avatarService.FetchAvatarAsync(
            testUser.DisplayName
        );

        await userManager.CreateAsync(testUser, "abc123");

        var testProject = new Project { Name = "DAZN" };

        var projectMembership = new ProjectMembership
        {
            IsAdmin = true,
            ProjectId = testProject.Id,
            UserId = testUser.Id,
            AddedAt = DateTime.Now
        };

        testProject.Memberships.Add(projectMembership);

        var testBoard = new Board
        {
            ProjectId = testProject.Id,
            Code = "TB",
            Name = "Testing board",
            Description = "This is an example of a testing board with a description."
        };

        testProject.Boards.Add(testBoard);

        var label = new Label
        {
            Name = "Important",
            BoardId = testBoard.Id,
            BackgroundColor = Color.FromArgb(255, 251, 207, 232),
            TextColor = Color.FromArgb(255, 157, 23, 77)
        };

        testBoard.Labels = [label];

        var boardMembership = new BoardMembership
        {
            IsAdmin = true,
            BoardId = testBoard.Id,
            UserId = testUser.Id,
            AddedAt = DateTime.Now
        };

        var readyColumn = new Column
        {
            BoardId = testBoard.Id,
            Name = "Ready",
            Index = 0
        };

        var inProgressColumn = new Column
        {
            BoardId = testBoard.Id,
            Name = "WIP",
            Index = 1,
            MaxCards = 1
        };

        var doneColumn = new Column
        {
            BoardId = testBoard.Id,
            Name = "Done",
            Index = 2,
            Finished = true
        };

        testBoard.Columns.Add(readyColumn);
        testBoard.Columns.Add(inProgressColumn);
        testBoard.Columns.Add(doneColumn);

        var testCard = new Card
        {
            Name =
                "This is a pretty long example task, just trying out the word wrapping and stuff.",
            Description = "AC:",
            Number = 1,
            Index = 0,
            CreatedAt = DateTime.Now,
            Deadline = DateTime.Today.AddDays(5),
            ColumnId = readyColumn.Id,
            CreatedById = testUser.Id
        };

        readyColumn.Cards.Add(testCard);

        testCard.Labels.Add(label);

        testCard.Assignees = new List<User> { testUser };

        var subtask1 = new Subtask
        {
            CardId = testCard.Id,
            Index = 0,
            Text = "This is the first sub-task",
            Completed = true
        };

        var subtask2 = new Subtask
        {
            CardId = testCard.Id,
            Index = 1,
            Text = "This is the second sub-task",
            Completed = true
        };

        var subtask3 = new Subtask
        {
            CardId = testCard.Id,
            Index = 1,
            Text = "This is the third sub-task"
        };

        testCard.Subtasks = new List<Subtask> { subtask1, subtask2, subtask3 };

        var timeRecord = new TimeRecord
        {
            CardId = testCard.Id,
            UserId = testUser.Id,
            StartedAt = DateTime.Now.AddHours(-1).AddMinutes(-19),
            EndedAt = DateTime.Now
        };

        testCard.TimeRecords.Add(timeRecord);

        dataContext.Projects.Add(testProject);

        await dataContext.SaveChangesAsync();
#endif
    }
}


================================================
FILE: Ticky.Internal/GlobalUsings.cs
================================================
global using Devity.Extensions;
global using Microsoft.AspNetCore.Identity.EntityFrameworkCore;
global using Microsoft.EntityFrameworkCore;
global using Microsoft.Extensions.DependencyInjection;
global using Microsoft.Extensions.Logging;
global using Ticky.Base;
global using Ticky.Base.DTOs;
global using Ticky.Base.Entities;
global using Ticky.Base.Entities.Abstractions;
global using Ticky.Base.Enums;
global using Ticky.Internal.Data;
global using Ticky.Internal.Helpers;
global using Ticky.Internal.Services;


================================================
FILE: Ticky.Internal/Helpers/AttachmentHelper.cs
================================================
namespace Ticky.Internal.Helpers;

public static class AttachmentHelper
{
    public static string GetFileTypeFromAttachment(Attachment attachment)
    {
        if (attachment.OriginalName.Contains(".doc"))
            return "DOC";
        else if (
            attachment.OriginalName.Contains(".jpg") || attachment.OriginalName.Contains(".png")
        )
            return "Image";
        else if (attachment.OriginalName.Contains(".pdf"))
            return "PDF";
        else if (attachment.OriginalName.Contains(".ppt"))
            return "PowerPoint";
        else if (attachment.OriginalName.Contains(".sql"))
            return "Script";
        else if (attachment.OriginalName.Contains(".txt"))
            return "Text";
        else if (attachment.OriginalName.Contains(".xls"))
            return "Excel";
        else if (attachment.OriginalName.Contains(".xml"))
            return "XML";
        else if (attachment.OriginalName.Contains(".zip"))
            return "Archive";

        return "Other";
    }

    public static string GetImageNameFromAttachment(Attachment attachment)
    {
        if (attachment.OriginalName.Contains(".doc"))
            return "doc.png";
        else if (attachment.OriginalName.Contains(".jpg"))
            return "jpg.png";
        else if (attachment.OriginalName.Contains(".png"))
            return "png.png";
        else if (attachment.OriginalName.Contains(".pdf"))
            return "pdf.png";
        else if (attachment.OriginalName.Contains(".ppt"))
            return "ppt.png";
        else if (attachment.OriginalName.Contains(".sql"))
            return "sql.png";
        else if (attachment.OriginalName.Contains(".txt"))
            return "txt.png";
        else if (attachment.OriginalName.Contains(".xls"))
            return "xls.png";
        else if (attachment.OriginalName.Contains(".xml"))
            return "xml.png";
        else if (attachment.OriginalName.Contains(".zip"))
            return "zip.png";

        return "txt.png";
    }
}


================================================
FILE: Ticky.Internal/Helpers/IndexHelper.cs
================================================
namespace Ticky.Internal.Helpers;

public static class IndexHelper
{
    public static int GetNextIndex<T>(this List<T> current)
        where T : IOrderable
    {
        var result = current.Max(x => (int?)x.Index);

        if (result is null)
            return 0;

        return (int)result + 1;
    }

    public static void FixIndices<T>(this List<T> current)
        where T : IOrderable, IDbEntry
    {
        var orderedElements = current.OrderBy(x => x.Index).ThenBy(x => x.Id).ToList();

        for (int i = 0; i < orderedElements.Count; i++)
            orderedElements[i].Index = i;
    }

    public static void ChangeOrderOfItem<T>(this List<T> current, int currentIndex, int newIndex)
        where T : IOrderable
    {
        if (currentIndex == newIndex)
            return;

        var orderedElements = current.OrderBy(x => x.Index).ToList();
        var targetElement = orderedElements.ElementAt(currentIndex);

        orderedElements.RemoveAt(currentIndex);
        orderedElements.Insert(newIndex, targetElement);

        for (int i = 0; i < orderedElements.Count; i++)
        {
            orderedElements.ElementAt(i).Index = i;
        }
    }
}


================================================
FILE: Ticky.Internal/Helpers/StringHelper.cs
================================================
using System.Text;

namespace Ticky.Internal.Helpers;

public static class StringHelper
{
    public static string ToFriendlyName(this string str)
    {
        var sb = new StringBuilder();

        for (int i = 0; i < str.Length; i++)
        {
            if (i != 0 && char.IsUpper(str[i]))
            {
                sb.Append(' ');
                sb.Append(char.ToLower(str[i]));
            }
            else
                sb.Append(str[i]);
        }

        return sb.ToString();
    }
}


================================================
FILE: Ticky.Internal/Helpers/TimeHelper.cs
================================================
namespace Ticky.Internal.Helpers;

public static class TimeHelper
{
    public static string ToElapsedString(this DateTime dateTime)
    {
        var difference = DateTime.Now - dateTime;

        if (difference.TotalMinutes < 1)
            return "now";
        else if (difference.TotalMinutes < 2)
            return $"{Math.Round(difference.TotalMinutes)} min ago";
        else if (difference.TotalHours < 1)
            return $"{Math.Round(difference.TotalMinutes)} mins ago";
        else if (difference.TotalHours < 2)
            return $"{Math.Round(difference.TotalHours)} hr ago";
        else if (difference.TotalDays < 1)
            return $"{Math.Round(difference.TotalHours)} hrs ago";
        else if (difference.TotalDays <= 2)
            return $"yesterday, {dateTime.ToString("HH:mm")}";
        else if (difference.TotalDays < 7)
            return $"{Math.Round(difference.TotalDays)} days ago";
        else if (dateTime.Year == DateTime.Now.Year)
            return dateTime.ToString("dd. MMM");

        return dateTime.ToString("dd. MMM yyyy");
    }

    public static string ToElapsedString(this TimeSpan timeSpan, bool cutOffSeconds = false)
    {
        string result = string.Empty;

        var hoursValue = Math.Floor(timeSpan.TotalHours);
        if (hoursValue != 0)
            result += $" {hoursValue}h";

        if (timeSpan.Minutes != 0)
            result += $" {timeSpan.Minutes}m";

        if (!cutOffSeconds)
            result += $" {timeSpan.Seconds}s";

        return result.Trim();
    }

    public static string ToShortString(this DateTime dateTime) =>
        dateTime.Date == DateTime.Now.Date
            ? dateTime.ToString("HH:mm")
            : dateTime.ToString("MMM d");
}


================================================
FILE: Ticky.Internal/Migrations/20250523175138_Initial.Designer.cs
================================================
// <auto-generated />
using System;
using Microsoft.EntityFrameworkCore;
using Microsoft.EntityFrameworkCore.Infrastructure;
using Microsoft.EntityFrameworkCore.Migrations;
using Microsoft.EntityFrameworkCore.Storage.ValueConversion;
using Ticky.Internal.Data;

#nullable disable

namespace Ticky.Internal.Migrations
{
    [DbContext(typeof(DataContext))]
    [Migration("20250523175138_Initial")]
    partial class Initial
    {
        /// <inheritdoc />
        protected override void BuildTargetModel(ModelBuilder modelBuilder)
        {
#pragma warning disable 612, 618
            modelBuilder
                .HasAnnotation("ProductVersion", "7.0.20")
                .HasAnnotation("Relational:MaxIdentifierLength", 64);

            modelBuilder.Entity("CardLabel", b =>
                {
                    b.Property<int>("LabelsId")
                        .HasColumnType("int");

                    b.Property<int>("OnCardsId")
                        .HasColumnType("int");

                    b.HasKey("LabelsId", "OnCardsId");

                    b.HasIndex("OnCardsId");

                    b.ToTable("CardLabel");
                });

            modelBuilder.Entity("CardUser", b =>
                {
                    b.Property<int>("AssignedToId")
                        .HasColumnType("int");

                    b.Property<int>("AssigneesId")
                        .HasColumnType("int");

                    b.HasKey("AssignedToId", "AssigneesId");

                    b.HasIndex("AssigneesId");

                    b.ToTable("CardUser");
                });

            modelBuilder.Entity("Microsoft.AspNetCore.DataProtection.EntityFrameworkCore.DataProtectionKey", b =>
                {
                    b.Property<int>("Id")
                        .ValueGeneratedOnAdd()
                        .HasColumnType("int");

                    b.Property<string>("FriendlyName")
                        .HasColumnType("longtext");

                    b.Property<string>("Xml")
                        .HasColumnType("longtext");

                    b.HasKey("Id");

                    b.ToTable("DataProtectionKeys");
                });

            modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityRole<int>", b =>
                {
                    b.Property<int>("Id")
                        .ValueGeneratedOnAdd()
                        .HasColumnType("int");

                    b.Property<string>("ConcurrencyStamp")
                        .IsConcurrencyToken()
                        .HasColumnType("longtext");

                    b.Property<string>("Name")
                        .HasMaxLength(256)
                        .HasColumnType("varchar(256)");

                    b.Property<string>("NormalizedName")
                        .HasMaxLength(256)
                        .HasColumnType("varchar(256)");

                    b.HasKey("Id");

                    b.HasIndex("NormalizedName")
                        .IsUnique()
                        .HasDatabaseName("RoleNameIndex");

                    b.ToTable("AspNetRoles", (string)null);
                });

            modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityRoleClaim<int>", b =>
                {
                    b.Property<int>("Id")
                        .ValueGeneratedOnAdd()
                        .HasColumnType("int");

                    b.Property<string>("ClaimType")
                        .HasColumnType("longtext");

                    b.Property<string>("ClaimValue")
                        .HasColumnType("longtext");

                    b.Property<int>("RoleId")
                        .HasColumnType("int");

                    b.HasKey("Id");

                    b.HasIndex("RoleId");

                    b.ToTable("AspNetRoleClaims", (string)null);
                });

            modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserClaim<int>", b =>
                {
                    b.Property<int>("Id")
                        .ValueGeneratedOnAdd()
                        .HasColumnType("int");

                    b.Property<string>("ClaimType")
                        .HasColumnType("longtext");

                    b.Property<string>("ClaimValue")
                        .HasColumnType("longtext");

                    b.Property<int>("UserId")
                        .HasColumnType("int");

                    b.HasKey("Id");

                    b.HasIndex("UserId");

                    b.ToTable("AspNetUserClaims", (string)null);
                });

            modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserLogin<int>", b =>
                {
                    b.Property<string>("LoginProvider")
                        .HasColumnType("varchar(255)");

                    b.Property<string>("ProviderKey")
                        .HasColumnType("varchar(255)");

                    b.Property<string>("ProviderDisplayName")
                        .HasColumnType("longtext");

                    b.Property<int>("UserId")
                        .HasColumnType("int");

                    b.HasKey("LoginProvider", "ProviderKey");

                    b.HasIndex("UserId");

                    b.ToTable("AspNetUserLogins", (string)null);
                });

            modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserRole<int>", b =>
                {
                    b.Property<int>("UserId")
                        .HasColumnType("int");

                    b.Property<int>("RoleId")
                        .HasColumnType("int");

                    b.HasKey("UserId", "RoleId");

                    b.HasIndex("RoleId");

                    b.ToTable("AspNetUserRoles", (string)null);
                });

            modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserToken<int>", b =>
                {
                    b.Property<int>("UserId")
                        .HasColumnType("int");

                    b.Property<string>("LoginProvider")
                        .HasColumnType("varchar(255)");

                    b.Property<string>("Name")
                        .HasColumnType("varchar(255)");

                    b.Property<string>("Value")
                        .HasColumnType("longtext");

                    b.HasKey("UserId", "LoginProvider", "Name");

                    b.ToTable("AspNetUserTokens", (string)null);
                });

            modelBuilder.Entity("Ticky.Base.Entities.Activity", b =>
                {
                    b.Property<int>("Id")
                        .ValueGeneratedOnAdd()
                        .HasColumnType("int");

                    b.Property<DateTime>("At")
                        .HasColumnType("datetime(6)");

                    b.Property<int>("CardId")
                        .HasColumnType("int");

                    b.Property<DateTime>("CreatedAt")
                        .HasColumnType("datetime(6)");

                    b.Property<string>("Text")
                        .IsRequired()
                        .HasColumnType("longtext");

                    b.Property<int>("UserId")
                        .HasColumnType("int");

                    b.HasKey("Id");

                    b.HasIndex("CardId");

                    b.HasIndex("UserId");

                    b.ToTable("Activities");
                });

            modelBuilder.Entity("Ticky.Base.Entities.Attachment", b =>
                {
                    b.Property<int>("Id")
                        .ValueGeneratedOnAdd()
                        .HasColumnType("int");

                    b.Property<int>("CardId")
                        .HasColumnType("int");

                    b.Property<DateTime>("CreatedAt")
                        .HasColumnType("datetime(6)");

                    b.Property<string>("FileName")
                        .IsRequired()
                        .HasColumnType("longtext");

                    b.Property<string>("OriginalName")
                        .IsRequired()
                        .HasColumnType("longtext");

                    b.HasKey("Id");

                    b.HasIndex("CardId");

                    b.ToTable("Attachments");
                });

            modelBuilder.Entity("Ticky.Base.Entities.Board", b =>
                {
                    b.Property<int>("Id")
                        .ValueGeneratedOnAdd()
                        .HasColumnType("int");

                    b.Property<string>("Code")
                        .IsRequired()
                        .HasMaxLength(5)
                        .HasColumnType("varchar(5)");

                    b.Property<DateTime>("CreatedAt")
                        .HasColumnType("datetime(6)");

                    b.Property<string>("Description")
                        .IsRequired()
                        .HasColumnType("longtext");

                    b.Property<bool>("IsFavorite")
                        .HasColumnType("tinyint(1)");

                    b.Property<string>("Name")
                        .IsRequired()
                        .HasColumnType("longtext");

                    b.Property<int>("ProjectId")
                        .HasColumnType("int");

                    b.HasKey("Id");

                    b.HasIndex("ProjectId");

                    b.ToTable("Boards");
                });

            modelBuilder.Entity("Ticky.Base.Entities.BoardMembership", b =>
                {
                    b.Property<int>("Id")
                        .ValueGeneratedOnAdd()
                        .HasColumnType("int");

                    b.Property<DateTime>("AddedAt")
                        .HasColumnType("datetime(6)");

                    b.Property<int>("BoardId")
                        .HasColumnType("int");

                    b.Property<DateTime>("CreatedAt")
                        .HasColumnType("datetime(6)");

                    b.Property<bool>("IsAdmin")
                        .HasColumnType("tinyint(1)");

                    b.Property<int>("UserId")
                        .HasColumnType("int");

                    b.HasKey("Id");

                    b.HasIndex("BoardId");

                    b.HasIndex("UserId");

                    b.ToTable("BoardMemberships");
                });

            modelBuilder.Entity("Ticky.Base.Entities.Card", b =>
                {
                    b.Property<int>("Id")
                        .ValueGeneratedOnAdd()
                        .HasColumnType("int");

                    b.Property<bool>("Blocked")
                        .HasColumnType("tinyint(1)");

                    b.Property<int>("ColumnId")
                        .HasColumnType("int");

                    b.Property<DateTime>("CreatedAt")
                        .HasColumnType("datetime(6)");

                    b.Property<int>("CreatedById")
                        .HasColumnType("int");

                    b.Property<DateTime?>("Deadline")
                        .HasColumnType("datetime(6)");

                    b.Property<bool>("DeadlineProcessed")
                        .HasColumnType("tinyint(1)");

                    b.Property<string>("Description")
                        .IsRequired()
                        .HasColumnType("longtext");

                    b.Property<int>("Index")
                        .HasColumnType("int");

                    b.Property<int>("Number")
                        .HasColumnType("int");

                    b.Property<int>("Priority")
                        .HasColumnType("int");

                    b.Property<string>("Text")
                        .IsRequired()
                        .HasColumnType("longtext");

                    b.HasKey("Id");

                    b.HasIndex("ColumnId");

                    b.HasIndex("CreatedById");

                    b.ToTable("Cards");
                });

            modelBuilder.Entity("Ticky.Base.Entities.CardLink", b =>
                {
                    b.Property<int>("Id")
                        .ValueGeneratedOnAdd()
                        .HasColumnType("int");

                    b.Property<int>("CardOneId")
                        .HasColumnType("int");

                    b.Property<int>("CardTwoId")
                        .HasColumnType("int");

                    b.Property<string>("Category")
                        .IsRequired()
                        .HasColumnType("longtext");

                    b.Property<DateTime>("CreatedAt")
                        .HasColumnType("datetime(6)");

                    b.HasKey("Id");

                    b.HasIndex("CardOneId");

                    b.HasIndex("CardTwoId");

                    b.ToTable("CardLinks");
                });

            modelBuilder.Entity("Ticky.Base.Entities.Code", b =>
                {
                    b.Property<int>("Id")
                        .ValueGeneratedOnAdd()
                        .HasColumnType("int");

                    b.Property<int>("CodePurpose")
                        .HasColumnType("int");

                    b.Property<DateTime>("CreatedAt")
                        .HasColumnType("datetime(6)");

                    b.Property<int>("UserId")
                        .HasColumnType("int");

                    b.Property<string>("Value")
                        .IsRequired()
                        .HasColumnType("longtext");

                    b.HasKey("Id");

                    b.HasIndex("UserId")
                        .IsUnique();

                    b.ToTable("Codes");
                });

            modelBuilder.Entity("Ticky.Base.Entities.Column", b =>
                {
                    b.Property<int>("Id")
                        .ValueGeneratedOnAdd()
                        .HasColumnType("int");

                    b.Property<int>("BoardId")
                        .HasColumnType("int");

                    b.Property<bool>("Collapsed")
                        .HasColumnType("tinyint(1)");

                    b.Property<DateTime>("CreatedAt")
                        .HasColumnType("datetime(6)");

                    b.Property<bool>("Finished")
                        .HasColumnType("tinyint(1)");

                    b.Property<int>("Index")
                        .HasColumnType("int");

                    b.Property<int>("MaxCards")
                        .HasColumnType("int");

                    b.Property<string>("Name")
                        .IsRequired()
                        .HasColumnType("longtext");

                    b.Property<int>("OrderRule")
                        .HasColumnType("int");

                    b.HasKey("Id");

                    b.HasIndex("BoardId");

                    b.ToTable("Columns");
                });

            modelBuilder.Entity("Ticky.Base.Entities.Comment", b =>
                {
                    b.Property<int>("Id")
                        .ValueGeneratedOnAdd()
                        .HasColumnType("int");

                    b.Property<int>("CardId")
                        .HasColumnType("int");

                    b.Property<DateTime>("CreatedAt")
                        .HasColumnType("datetime(6)");

                    b.Property<int>("CreatedById")
                        .HasColumnType("int");

                    b.Property<string>("Text")
                        .IsRequired()
                        .HasColumnType("longtext");

                    b.HasKey("Id");

                    b.HasIndex("CardId");

                    b.HasIndex("CreatedById");

                    b.ToTable("Comments");
                });

            modelBuilder.Entity("Ticky.Base.Entities.Label", b =>
                {
                    b.Property<int>("Id")
                        .ValueGeneratedOnAdd()
                        .HasColumnType("int");

                    b.Property<int>("BackgroundColor")
                        .HasColumnType("int");

                    b.Property<int>("BoardId")
                        .HasColumnType("int");

                    b.Property<DateTime>("CreatedAt")
                        .HasColumnType("datetime(6)");

                    b.Property<string>("Text")
                        .IsRequired()
                        .HasColumnType("longtext");

                    b.Property<int>("TextColor")
                        .HasColumnType("int");

                    b.HasKey("Id");

                    b.HasIndex("BoardId");

                    b.ToTable("Labels");
                });

            modelBuilder.Entity("Ticky.Base.Entities.LastVisit", b =>
                {
                    b.Property<int>("Id")
                        .ValueGeneratedOnAdd()
                        .HasColumnType("int");

                    b.Property<int>("BoardId")
                        .HasColumnType("int");

                    b.Property<DateTime>("CreatedAt")
                        .HasColumnType("datetime(6)");

                    b.Property<int>("UserId")
                        .HasColumnType("int");

                    b.Property<DateTime>("VisitTime")
                        .HasColumnType("datetime(6)");

                    b.HasKey("Id");

                    b.HasIndex("BoardId");

                    b.HasIndex("UserId");

                    b.ToTable("LastVisits");
                });

            modelBuilder.Entity("Ticky.Base.Entities.Project", b =>
                {
                    b.Property<int>("Id")
                        .ValueGeneratedOnAdd()
                        .HasColumnType("int");

                    b.Property<DateTime>("CreatedAt")
                        .HasColumnType("datetime(6)");

                    b.Property<string>("Name")
                        .IsRequired()
                        .HasColumnType("longtext");

                    b.HasKey("Id");

                    b.ToTable("Projects");
                });

            modelBuilder.Entity("Ticky.Base.Entities.ProjectMembership", b =>
                {
                    b.Property<int>("Id")
                        .ValueGeneratedOnAdd()
                        .HasColumnType("int");

                    b.Property<DateTime>("AddedAt")
                        .HasColumnType("datetime(6)");

                    b.Property<DateTime>("CreatedAt")
                        .HasColumnType("datetime(6)");

                    b.Property<bool>("IsAdmin")
                        .HasColumnType("tinyint(1)");

                    b.Property<int>("ProjectId")
                        .HasColumnType("int");

                    b.Property<int>("UserId")
                        .HasColumnType("int");

                    b.HasKey("Id");

                    b.HasIndex("ProjectId");

                    b.HasIndex("UserId");

                    b.ToTable("ProjectMemberships");
                });

            modelBuilder.Entity("Ticky.Base.Entities.Reminder", b =>
                {
                    b.Property<int>("Id")
                        .ValueGeneratedOnAdd()
                        .HasColumnType("int");

                    b.Property<DateTime>("At")
                        .HasColumnType("datetime(6)");

                    b.Property<int>("CardId")
                        .HasColumnType("int");

                    b.Property<DateTime>("CreatedAt")
                        .HasColumnType("datetime(6)");

                    b.HasKey("Id");

                    b.HasIndex("CardId");

                    b.ToTable("Reminders");
                });

            modelBuilder.Entity("Ticky.Base.Entities.Subtask", b =>
                {
                    b.Property<int>("Id")
                        .ValueGeneratedOnAdd()
                        .HasColumnType("int");

                    b.Property<int>("CardId")
                        .HasColumnType("int");

                    b.Property<bool>("Completed")
                        .HasColumnType("tinyint(1)");

                    b.Property<DateTime>("CreatedAt")
                        .HasColumnType("datetime(6)");

                    b.Property<int>("Index")
                        .HasColumnType("int");

                    b.Property<string>("Text")
                        .IsRequired()
                        .HasColumnType("longtext");

                    b.HasKey("Id");

                    b.HasIndex("CardId");

                    b.ToTable("Subtasks");
                });

            modelBuilder.Entity("Ticky.Base.Entities.TimeRecord", b =>
                {
                    b.Property<int>("Id")
                        .ValueGeneratedOnAdd()
                        .HasColumnType("int");

                    b.Property<int>("CardId")
                        .HasColumnType("int");

                    b.Property<DateTime>("CreatedAt")
                        .HasColumnType("datetime(6)");

                    b.Property<DateTime?>("EndedAt")
                        .HasColumnType("datetime(6)");

                    b.Property<DateTime>("StartedAt")
                        .HasColumnType("datetime(6)");

                    b.Property<int>("UserId")
                        .HasColumnType("int");

                    b.HasKey("Id");

                    b.HasIndex("CardId");

                    b.HasIndex("UserId");

                    b.ToTable("TimeRecords");
                });

            modelBuilder.Entity("Ticky.Base.Entities.User", b =>
                {
                    b.Property<int>("Id")
                        .ValueGeneratedOnAdd()
                        .HasColumnType("int");

                    b.Property<int>("AccessFailedCount")
                        .HasColumnType("int");

                    b.Property<bool>("AutomaticCardEdit")
                        .HasColumnType("tinyint(1)");

                    b.Property<bool>("AutomaticDeadlineReminder")
                        .HasColumnType("tinyint(1)");

                    b.Property<string>("ConcurrencyStamp")
                        .IsConcurrencyToken()
                        .HasColumnType("longtext");

                    b.Property<DateTime>("CreatedAt")
                        .HasColumnType("datetime(6)");

                    b.Property<string>("DisplayName")
                        .IsRequired()
                        .HasColumnType("longtext");

                    b.Property<string>("Email")
                        .HasMaxLength(256)
                        .HasColumnType("varchar(256)");

                    b.Property<bool>("EmailConfirmed")
                        .HasColumnType("tinyint(1)");

                    b.Property<bool>("InstantDelete")
                        .HasColumnType("tinyint(1)");

                    b.Property<int?>("LastVisitedBoardId")
                        .HasColumnType("int");

                    b.Property<bool>("LockoutEnabled")
                        .HasColumnType("tinyint(1)");

                    b.Property<DateTimeOffset?>("LockoutEnd")
                        .HasColumnType("datetime(6)");

                    b.Property<string>("NormalizedEmail")
                        .HasMaxLength(256)
                        .HasColumnType("varchar(256)");

                    b.Property<string>("NormalizedUserName")
                        .HasMaxLength(256)
                        .HasColumnType("varchar(256)");

                    b.Property<string>("PasswordHash")
                        .HasColumnType("longtext");

                    b.Property<string>("PhoneNumber")
                        .HasColumnType("longtext");

                    b.Property<bool>("PhoneNumberConfirmed")
                        .HasColumnType("tinyint(1)");

                    b.Property<string>("ProfilePictureFileName")
                        .HasColumnType("longtext");

                    b.Property<string>("SecurityStamp")
                        .HasColumnType("longtext");

                    b.Property<bool>("TwoFactorEnabled")
                        .HasColumnType("tinyint(1)");

                    b.Property<string>("UserName")
                        .HasMaxLength(256)
                        .HasColumnType("varchar(256)");

                    b.HasKey("Id");

                    b.HasIndex("NormalizedEmail")
                        .HasDatabaseName("EmailIndex");

                    b.HasIndex("NormalizedUserName")
                        .IsUnique()
                        .HasDatabaseName("UserNameIndex");

                    b.ToTable("AspNetUsers", (string)null);
                });

            modelBuilder.Entity("CardLabel", b =>
                {
                    b.HasOne("Ticky.Base.Entities.Label", null)
                        .WithMany()
                        .HasForeignKey("LabelsId")
                        .OnDelete(DeleteBehavior.Cascade)
                        .IsRequired();

                    b.HasOne("Ticky.Base.Entities.Card", null)
                        .WithMany()
                        .HasForeignKey("OnCardsId")
                        .OnDelete(DeleteBehavior.Cascade)
                        .IsRequired();
                });

            modelBuilder.Entity("CardUser", b =>
                {
                    b.HasOne("Ticky.Base.Entities.Card", null)
                        .WithMany()
                        .HasForeignKey("AssignedToId")
                        .OnDelete(DeleteBehavior.Cascade)
                        .IsRequired();

                    b.HasOne("Ticky.Base.Entities.User", null)
                        .WithMany()
                        .HasForeignKey("AssigneesId")
                        .OnDelete(DeleteBehavior.Cascade)
                        .IsRequired();
                });

            modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityRoleClaim<int>", b =>
                {
                    b.HasOne("Microsoft.AspNetCore.Identity.IdentityRole<int>", null)
                        .WithMany()
                        .HasForeignKey("RoleId")
                        .OnDelete(DeleteBehavior.Cascade)
                        .IsRequired();
                });

            modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserClaim<int>", b =>
                {
                    b.HasOne("Ticky.Base.Entities.User", null)
                        .WithMany()
                        .HasForeignKey("UserId")
                        .OnDelete(DeleteBehavior.Cascade)
                        .IsRequired();
                });

            modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserLogin<int>", b =>
                {
                    b.HasOne("Ticky.Base.Entities.User", null)
                        .WithMany()
                        .HasForeignKey("UserId")
                        .OnDelete(DeleteBehavior.Cascade)
                        .IsRequired();
                });

            modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserRole<int>", b =>
                {
                    b.HasOne("Microsoft.AspNetCore.Identity.IdentityRole<int>", null)
                        .WithMany()
                        .HasForeignKey("RoleId")
                        .OnDelete(DeleteBehavior.Cascade)
                        .IsRequired();

                    b.HasOne("Ticky.Base.Entities.User", null)
                        .WithMany()
                        .HasForeignKey("UserId")
                        .OnDelete(DeleteBehavior.Cascade)
                        .IsRequired();
                });

            modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserToken<int>", b =>
                {
                    b.HasOne("Ticky.Base.Entities.User", null)
                        .WithMany()
                        .HasForeignKey("UserId")
                        .OnDelete(DeleteBehavior.Cascade)
                        .IsRequired();
                });

            modelBuilder.Entity("Ticky.Base.Entities.Activity", b =>
                {
                    b.HasOne("Ticky.Base.Entities.Card", "Card")
                        .WithMany("Activities")
                        .HasForeignKey("CardId")
                        .OnDelete(DeleteBehavior.Cascade)
                        .IsRequired();

                    b.HasOne("Ticky.Base.Entities.User", "User")
                        .WithMany("Activities")
                        .HasForeignKey("UserId")
                        .OnDelete(DeleteBehavior.Cascade)
                        .IsRequired();

                    b.Navigation("Card");

                    b.Navigation("User");
                });

            modelBuilder.Entity("Ticky.Base.Entities.Attachment", b =>
                {
                    b.HasOne("Ticky.Base.Entities.Card", "Card")
                        .WithMany("Attachments")
                        .HasForeignKey("CardId")
                        .OnDelete(DeleteBehavior.Cascade)
                        .IsRequired();

                    b.Navigation("Card");
                });

            modelBuilder.Entity("Ticky.Base.Entities.Board", b =>
                {
                    b.HasOne("Ticky.Base.Entities.Project", "Project")
                        .WithMany("Boards")
                        .HasForeignKey("ProjectId")
                        .OnDelete(DeleteBehavior.Cascade)
                        .IsRequired();

                    b.Navigation("Project");
                });

            modelBuilder.Entity("Ticky.Base.Entities.BoardMembership", b =>
                {
                    b.HasOne("Ticky.Base.Entities.Board", "Board")
                        .WithMany("Memberships")
                        .HasForeignKey("BoardId")
                        .OnDelete(DeleteBehavior.Cascade)
                        .IsRequired();

                    b.HasOne("Ticky.Base.Entities.User", "User")
                        .WithMany("BoardMemberships")
                        .HasForeignKey("UserId")
                        .OnDelete(DeleteBehavior.Cascade)
                        .IsRequired();

                    b.Navigation("Board");

                    b.Navigation("User");
                });

            modelBuilder.Entity("Ticky.Base.Entities.Card", b =>
                {
                    b.HasOne("Ticky.Base.Entities.Column", "Column")
                        .WithMany("Cards")
                        .HasForeignKey("ColumnId")
                        .OnDelete(DeleteBehavior.Cascade)
                        .IsRequired();

                    b.HasOne("Ticky.Base.Entities.User", "CreatedBy")
                        .WithMany("CreatedCards")
                        .HasForeignKey("CreatedById")
                        .OnDelete(DeleteBehavior.Cascade)
                        .IsRequired();

                    b.Navigation("Column");

                    b.Navigation("CreatedBy");
                });

            modelBuilder.Entity("Ticky.Base.Entities.CardLink", b =>
                {
                    b.HasOne("Ticky.Base.Entities.Card", "CardOne")
                        .WithMany("LinkedIssuesOne")
                        .HasForeignKey("CardOneId")
                        .OnDelete(DeleteBehavior.Cascade)
                        .IsRequired();

                    b.HasOne("Ticky.Base.Entities.Card", "CardTwo")
                        .WithMany("LinkedIssuesTwo")
                        .HasForeignKey("CardTwoId")
                        .OnDelete(DeleteBehavior.Cascade)
                        .IsRequired();

                    b.Navigation("CardOne");

                    b.Navigation("CardTwo");
                });

            modelBuilder.Entity("Ticky.Base.Entities.Code", b =>
                {
                    b.HasOne("Ticky.Base.Entities.User", "User")
                        .WithOne("EmailVerificationCode")
                        .HasForeignKey("Ticky.Base.Entities.Code", "UserId")
                        .OnDelete(DeleteBehavior.Cascade)
                        .IsRequired();

                    b.Navigation("User");
                });

            modelBuilder.Entity("Ticky.Base.Entities.Column", b =>
                {
                    b.HasOne("Ticky.Base.Entities.Board", "Board")
                        .WithMany("Columns")
                        .HasForeignKey("BoardId")
                        .OnDelete(DeleteBehavior.Cascade)
                        .IsRequired();

                    b.Navigation("Board");
                });

            modelBuilder.Entity("Ticky.Base.Entities.Comment", b =>
                {
                    b.HasOne("Ticky.Base.Entities.Card", "Card")
                        .WithMany("Comments")
                        .HasForeignKey("CardId")
                        .OnDelete(DeleteBehavior.Cascade)
                        .IsRequired();

                    b.HasOne("Ticky.Base.Entities.User", "CreatedBy")
                        .WithMany("CreatedComments")
                        .HasForeignKey("CreatedById")
                        .OnDelete(DeleteBehavior.Cascade)
                        .IsRequired();

                    b.Navigation("Card");

                    b.Navigation("CreatedBy");
                });

            modelBuilder.Entity("Ticky.Base.Entities.Label", b =>
                {
                    b.HasOne("Ticky.Base.Entities.Board", "Board")
                        .WithMany("Labels")
                        .HasForeignKey("BoardId")
                        .OnDelete(DeleteBehavior.Cascade)
                        .IsRequired();

                    b.Navigation("Board");
                });

            modelBuilder.Entity("Ticky.Base.Entities.LastVisit", b =>
                {
                    b.HasOne("Ticky.Base.Entities.Board", "Board")
                        .WithMany("LastVisits")
                        .HasForeignKey("BoardId")
                        .OnDelete(DeleteBehavior.Cascade)
                        .IsRequired();

                    b.HasOne("Ticky.Base.Entities.User", "User")
                        .WithMany("LastVisits")
                        .HasForeignKey("UserId")
                        .OnDelete(DeleteBehavior.Cascade)
                        .IsRequired();

                    b.Navigation("Board");

                    b.Navigation("User");
                });

            modelBuilder.Entity("Ticky.Base.Entities.ProjectMembership", b =>
                {
                    b.HasOne("Ticky.Base.Entities.Project", "Project")
                        .WithMany("Memberships")
                        .HasForeignKey("ProjectId")
                        .OnDelete(DeleteBehavior.Cascade)
                        .IsRequired();

                    b.HasOne("Ticky.Base.Entities.User", "User")
                        .WithMany("ProjectMemberships")
                        .HasForeignKey("UserId")
                        .OnDelete(DeleteBehavior.Cascade)
                        .IsRequired();

                    b.Navigation("Project");

                    b.Navigation("User");
                });

            modelBuilder.Entity("Ticky.Base.Entities.Reminder", b =>
                {
                    b.HasOne("Ticky.Base.Entities.Card", "Card")
                        .WithMany("Reminders")
                        .HasForeignKey("CardId")
                        .OnDelete(DeleteBehavior.Cascade)
                        .IsRequired();

                    b.Navigation("Card");
                });

            modelBuilder.Entity("Ticky.Base.Entities.Subtask", b =>
                {
                    b.HasOne("Ticky.Base.Entities.Card", "Card")
                        .WithMany("Subtasks")
                        .HasForeignKey("CardId")
                        .OnDelete(DeleteBehavior.Cascade)
                        .IsRequired();

                    b.Navigation("Card");
                });

            modelBuilder.Entity("Ticky.Base.Entities.TimeRecord", b =>
                {
                    b.HasOne("Ticky.Base.Entities.Card", "Card")
                        .WithMany("TimeRecords")
                        .HasForeignKey("CardId")
                        .OnDelete(DeleteBehavior.Cascade)
                        .IsRequired();

                    b.HasOne("Ticky.Base.Entities.User", "User")
                        .WithMany("TimeRecords")
                        .HasForeignKey("UserId")
                        .OnDelete(DeleteBehavior.Cascade)
                        .IsRequired();

                    b.Navigation("Card");

                    b.Navigation("User");
                });

            modelBuilder.Entity("Ticky.Base.Entities.Board", b =>
                {
                    b.Navigation("Columns");

                    b.Navigation("Labels");

                    b.Navigation("LastVisits");

                    b.Navigation("Memberships");
                });

            modelBuilder.Entity("Ticky.Base.Entities.Card", b =>
                {
                    b.Navigation("Activities");

                    b.Navigation("Attachments");

                    b.Navigation("Comments");

                    b.Navigation("LinkedIssuesOne");

                    b.Navigation("LinkedIssuesTwo");

                    b.Navigation("Reminders");

                    b.Navigation("Subtasks");

                    b.Navigation("TimeRecords");
                });

            modelBuilder.Entity("Ticky.Base.Entities.Column", b =>
                {
                    b.Navigation("Cards");
                });

            modelBuilder.Entity("Ticky.Base.Entities.Project", b =>
                {
                    b.Navigation("Boards");

                    b.Navigation("Memberships");
                });

            modelBuilder.Entity("Ticky.Base.Entities.User", b =>
                {
                    b.Navigation("Activities");

                    b.Navigation("BoardMemberships");

                    b.Navigation("CreatedCards");

                    b.Navigation("CreatedComments");

                    b.Navigation("EmailVerificationCode");

                    b.Navigation("LastVisits");

                    b.Navigation("ProjectMemberships");

                    b.Navigation("TimeRecords");
                });
#pragma warning restore 612, 618
        }
    }
}


================================================
FILE: Ticky.Internal/Migrations/20250523175138_Initial.cs
================================================
using System;
using Microsoft.EntityFrameworkCore.Metadata;
using Microsoft.EntityFrameworkCore.Migrations;

#nullable disable

namespace Ticky.Internal.Migrations
{
    /// <inheritdoc />
    public partial class Initial : Migration
    {
        /// <inheritdoc />
        protected override void Up(MigrationBuilder migrationBuilder)
        {
            migrationBuilder.AlterDatabase()
                .Annotation("MySql:CharSet", "utf8mb4");

            migrationBuilder.CreateTable(
                name: "AspNetRoles",
                columns: table => new
                {
                    Id = table.Column<int>(type: "int", nullable: false)
                        .Annotation("MySql:ValueGenerationStrategy", MySqlValueGenerationStrategy.IdentityColumn),
                    Name = table.Column<string>(type: "varchar(256)", maxLength: 256, nullable: true)
                        .Annotation("MySql:CharSet", "utf8mb4"),
                    NormalizedName = table.Column<string>(type: "varchar(256)", maxLength: 256, nullable: true)
                        .Annotation("MySql:CharSet", "utf8mb4"),
                    ConcurrencyStamp = table.Column<string>(type: "longtext", nullable: true)
                        .Annotation("MySql:CharSet", "utf8mb4")
                },
                constraints: table =>
                {
                    table.PrimaryKey("PK_AspNetRoles", x => x.Id);
                })
                .Annotation("MySql:CharSet", "utf8mb4");

            migrationBuilder.CreateTable(
                name: "AspNetUsers",
                columns: table => new
                {
                    Id = table.Column<int>(type: "int", nullable: false)
                        .Annotation("MySql:ValueGenerationStrategy", MySqlValueGenerationStrategy.IdentityColumn),
                    DisplayName = table.Column<string>(type: "longtext", nullable: false)
                        .Annotation("MySql:CharSet", "utf8mb4"),
                    InstantDelete = table.Column<bool>(type: "tinyint(1)", nullable: false),
                    AutomaticCardEdit = table.Column<bool>(type: "tinyint(1)", nullable: false),
                    AutomaticDeadlineReminder = table.Column<bool>(type: "tinyint(1)", nullable: false),
                    ProfilePictureFileName = table.Column<string>(type: "longtext", nullable: true)
                        .Annotation("MySql:CharSet", "utf8mb4"),
                    LastVisitedBoardId = table.Column<int>(type: "int", nullable: true),
                    CreatedAt = table.Column<DateTime>(type: "datetime(6)", nullable: false),
                    UserName = table.Column<string>(type: "varchar(256)", maxLength: 256, nullable: true)
                        .Annotation("MySql:CharSet", "utf8mb4"),
                    NormalizedUserName = table.Column<string>(type: "varchar(256)", maxLength: 256, nullable: true)
                        .Annotation("MySql:CharSet", "utf8mb4"),
                    Email = table.Column<string>(type: "varchar(256)", maxLength: 256, nullable: true)
                        .Annotation("MySql:CharSet", "utf8mb4"),
                    NormalizedEmail = table.Column<string>(type: "varchar(256)", maxLength: 256, nullable: true)
                        .Annotation("MySql:CharSet", "utf8mb4"),
                    EmailConfirmed = table.Column<bool>(type: "tinyint(1)", nullable: false),
                    PasswordHash = table.Column<string>(type: "longtext", nullable: true)
                        .Annotation("MySql:CharSet", "utf8mb4"),
                    SecurityStamp = table.Column<string>(type: "longtext", nullable: true)
                        .Annotation("MySql:CharSet", "utf8mb4"),
                    ConcurrencyStamp = table.Column<string>(type: "longtext", nullable: true)
                        .Annotation("MySql:CharSet", "utf8mb4"),
                    PhoneNumber = table.Column<string>(type: "longtext", nullable: true)
                        .Annotation("MySql:CharSet", "utf8mb4"),
                    PhoneNumberConfirmed = table.Column<bool>(type: "tinyint(1)", nullable: false),
                    TwoFactorEnabled = table.Column<bool>(type: "tinyint(1)", nullable: false),
                    LockoutEnd = table.Column<DateTimeOffset>(type: "datetime(6)", nullable: true),
                    LockoutEnabled = table.Column<bool>(type: "tinyint(1)", nullable: false),
                    AccessFailedCount = table.Column<int>(type: "int", nullable: false)
                },
                constraints: table =>
                {
                    table.PrimaryKey("PK_AspNetUsers", x => x.Id);
                })
                .Annotation("MySql:CharSet", "utf8mb4");

            migrationBuilder.CreateTable(
                name: "DataProtectionKeys",
                columns: table => new
                {
                    Id = table.Column<int>(type: "int", nullable: false)
                        .Annotation("MySql:ValueGenerationStrategy", MySqlValueGenerationStrategy.IdentityColumn),
                    FriendlyName = table.Column<string>(type: "longtext", nullable: true)
                        .Annotation("MySql:CharSet", "utf8mb4"),
                    Xml = table.Column<string>(type: "longtext", nullable: true)
                        .Annotation("MySql:CharSet", "utf8mb4")
                },
                constraints: table =>
                {
                    table.PrimaryKey("PK_DataProtectionKeys", x => x.Id);
                })
                .Annotation("MySql:CharSet", "utf8mb4");

            migrationBuilder.CreateTable(
                name: "Projects",
                columns: table => new
                {
                    Id = table.Column<int>(type: "int", nullable: false)
                        .Annotation("MySql:ValueGenerationStrategy", MySqlValueGenerationStrategy.IdentityColumn),
                    Name = table.Column<string>(type: "longtext", nullable: false)
                        .Annotation("MySql:CharSet", "utf8mb4"),
                    CreatedAt = table.Column<DateTime>(type: "datetime(6)", nullable: false)
                },
                constraints: table =>
                {
                    table.PrimaryKey("PK_Projects", x => x.Id);
                })
                .Annotation("MySql:CharSet", "utf8mb4");

            migrationBuilder.CreateTable(
                name: "AspNetRoleClaims",
                columns: table => new
                {
                    Id = table.Column<int>(type: "int", nullable: false)
                        .Annotation("MySql:ValueGenerationStrategy", MySqlValueGenerationStrategy.IdentityColumn),
                    RoleId = table.Column<int>(type: "int", nullable: false),
                    ClaimType = table.Column<string>(type: "longtext", nullable: true)
                        .Annotation("MySql:CharSet", "utf8mb4"),
                    ClaimValue = table.Column<string>(type: "longtext", nullable: true)
                        .Annotation("MySql:CharSet", "utf8mb4")
                },
                constraints: table =>
                {
                    table.PrimaryKey("PK_AspNetRoleClaims", x => x.Id);
                    table.ForeignKey(
                        name: "FK_AspNetRoleClaims_AspNetRoles_RoleId",
                        column: x => x.RoleId,
                        principalTable: "AspNetRoles",
                        principalColumn: "Id",
                        onDelete: ReferentialAction.Cascade);
                })
                .Annotation("MySql:CharSet", "utf8mb4");

            migrationBuilder.CreateTable(
                name: "AspNetUserClaims",
                columns: table => new
                {
                    Id = table.Column<int>(type: "int", nullable: false)
                        .Annotation("MySql:ValueGenerationStrategy", MySqlValueGenerationStrategy.IdentityColumn),
                    UserId = table.Column<int>(type: "int", nullable: false),
                    ClaimType = table.Column<string>(type: "longtext", nullable: true)
                        .Annotation("MySql:CharSet", "utf8mb4"),
                    ClaimValue = table.Column<string>(type: "longtext", nullable: true)
                        .Annotation("MySql:CharSet", "utf8mb4")
                },
                constraints: table =>
                {
                    table.PrimaryKey("PK_AspNetUserClaims", x => x.Id);
                    table.ForeignKey(
                        name: "FK_AspNetUserClaims_AspNetUsers_UserId",
                        column: x => x.UserId,
                        principalTable: "AspNetUsers",
                        principalColumn: "Id",
                        onDelete: ReferentialAction.Cascade);
                })
                .Annotation("MySql:CharSet", "utf8mb4");

            migrationBuilder.CreateTable(
                name: "AspNetUserLogins",
                columns: table => new
                {
                    LoginProvider = table.Column<string>(type: "varchar(255)", nullable: false)
                        .Annotation("MySql:CharSet", "utf8mb4"),
                    ProviderKey = table.Column<string>(type: "varchar(255)", nullable: false)
                        .Annotation("MySql:CharSet", "utf8mb4"),
                    ProviderDisplayName = table.Column<string>(type: "longtext", nullable: true)
                        .Annotation("MySql:CharSet", "utf8mb4"),
                    UserId = table.Column<int>(type: "int", nullable: false)
                },
                constraints: table =>
                {
                    table.PrimaryKey("PK_AspNetUserLogins", x => new { x.LoginProvider, x.ProviderKey });
                    table.ForeignKey(
                        name: "FK_AspNetUserLogins_AspNetUsers_UserId",
                        column: x => x.UserId,
                        principalTable: "AspNetUsers",
                        principalColumn: "Id",
                        onDelete: ReferentialAction.Cascade);
                })
                .Annotation("MySql:CharSet", "utf8mb4");

            migrationBuilder.CreateTable(
                name: "AspNetUserRoles",
                columns: table => new
                {
                    UserId = table.Column<int>(type: "int", nullable: false),
                    RoleId = table.Column<int>(type: "int", nullable: false)
                },
                constraints: table =>
                {
                    table.PrimaryKey("PK_AspNetUserRoles", x => new { x.UserId, x.RoleId });
                    table.ForeignKey(
                        name: "FK_AspNetUserRoles_AspNetRoles_RoleId",
                        column: x => x.RoleId,
                        principalTable: "AspNetRoles",
                        principalColumn: "Id",
                        onDelete: ReferentialAction.Cascade);
                    table.ForeignKey(
                        name: "FK_AspNetUserRoles_AspNetUsers_UserId",
                        column: x => x.UserId,
                        principalTable: "AspNetUsers",
                        principalColumn: "Id",
                        onDelete: ReferentialAction.Cascade);
                })
                .Annotation("MySql:CharSet", "utf8mb4");

            migrationBuilder.CreateTable(
                name: "AspNetUserTokens",
                columns: table => new
                {
                    UserId = table.Column<int>(type: "int", nullable: false),
                    LoginProvider = table.Column<string>(type: "varchar(255)", nullable: false)
                        .Annotation("MySql:CharSet", "utf8mb4"),
                    Name = table.Column<string>(type: "varchar(255)", nullable: false)
                        .Annotation("MySql:CharSet", "utf8mb4"),
                    Value = table.Column<string>(type: "longtext", nullable: true)
                        .Annotation("MySql:CharSet", "utf8mb4")
                },
                constraints: table =>
                {
                    table.PrimaryKey("PK_AspNetUserTokens", x => new { x.UserId, x.LoginProvider, x.Name });
                    table.ForeignKey(
                        name: "FK_AspNetUserTokens_AspNetUsers_UserId",
                        column: x => x.UserId,
                        principalTable: "AspNetUsers",
                        principalColumn: "Id",
                        onDelete: ReferentialAction.Cascade);
                })
                .Annotation("MySql:CharSet", "utf8mb4");

            migrationBuilder.CreateTable(
                name: "Codes",
                columns: table => new
                {
                    Id = table.Column<int>(type: "int", nullable: false)
                        .Annotation("MySql:ValueGenerationStrategy", MySqlValueGenerationStrategy.IdentityColumn),
                    Value = table.Column<string>(type: "longtext", nullable: false)
                        .Annotation("MySql:CharSet", "utf8mb4"),
                    CodePurpose = table.Column<int>(type: "int", nullable: false),
                    UserId = table.Column<int>(type: "int", nullable: false),
                    CreatedAt = table.Column<DateTime>(type: "datetime(6)", nullable: false)
                },
                constraints: table =>
                {
                    table.PrimaryKey("PK_Codes", x => x.Id);
                    table.ForeignKey(
                        name: "FK_Codes_AspNetUsers_UserId",
                        column: x => x.UserId,
                        principalTable: "AspNetUsers",
                        principalColumn: "Id",
                        onDelete: ReferentialAction.Cascade);
                })
                .Annotation("MySql:CharSet", "utf8mb4");

            migrationBuilder.CreateTable(
                name: "Boards",
                columns: table => new
                {
                    Id = table.Column<int>(type: "int", nullable: false)
                        .Annotation("MySql:ValueGenerationStrategy", MySqlValueGenerationStrategy.IdentityColumn),
                    Name = table.Column<string>(type: "longtext", nullable: false)
                        .Annotation("MySql:CharSet", "utf8mb4"),
                    Description = table.Column<string>(type: "longtext", nullable: false)
                        .Annotation("MySql:CharSet", "utf8mb4"),
                    Code = table.Column<string>(type: "varchar(5)", maxLength: 5, nullable: false)
                        .Annotation("MySql:CharSet", "utf8mb4"),
                    ProjectId = table.Column<int>(type: "int", nullable: false),
                    IsFavorite = table.Column<bool>(type: "tinyint(1)", nullable: false),
                    CreatedAt = table.Column<DateTime>(type: "datetime(6)", nullable: false)
                },
                constraints: table =>
                {
                    table.PrimaryKey("PK_Boards", x => x.Id);
                    table.ForeignKey(
                        name: "FK_Boards_Projects_ProjectId",
                        column: x => x.ProjectId,
                        principalTable: "Projects",
                        principalColumn: "Id",
                        onDelete: ReferentialAction.Cascade);
                })
                .Annotation("MySql:CharSet", "utf8mb4");

            migrationBuilder.CreateTable(
                name: "ProjectMemberships",
                columns: table => new
                {
                    Id = table.Column<int>(type: "int", nullable: false)
                        .Annotation("MySql:ValueGenerationStrategy", MySqlValueGenerationStrategy.IdentityColumn),
                    UserId = table.Column<int>(type: "int", nullable: false),
                    ProjectId = table.Column<int>(type: "int", nullable: false),
                    IsAdmin = table.Column<bool>(type: "tinyint(1)", nullable: false),
                    AddedAt = table.Column<DateTime>(type: "datetime(6)", nullable: false),
                    CreatedAt = table.Column<DateTime>(type: "datetime(6)", nullable: false)
                },
                constraints: table =>
                {
                    table.PrimaryKey("PK_ProjectMemberships", x => x.Id);
                    table.ForeignKey(
                        name: "FK_ProjectMemberships_AspNetUsers_UserId",
                        column: x => x.UserId,
                        principalTable: "AspNetUsers",
                        principalColumn: "Id",
                        onDelete: ReferentialAction.Cascade);
                    table.ForeignKey(
                        name: "FK_ProjectMemberships_Projects_ProjectId",
                        column: x => x.ProjectId,
                        principalTable: "Projects",
                        principalColumn: "Id",
                        onDelete: ReferentialAction.Cascade);
                })
                .Annotation("MySql:CharSet", "utf8mb4");

            migrationBuilder.CreateTable(
                name: "BoardMemberships",
                columns: table => new
                {
                    Id = table.Column<int>(type: "int", nullable: false)
                        .Annotation("MySql:ValueGenerationStrategy", MySqlValueGenerationStrategy.IdentityColumn),
                    UserId = table.Column<int>(type: "int", nullable: false),
                    BoardId = table.Column<int>(type: "int", nullable: false),
                    IsAdmin = table.Column<bool>(type: "tinyint(1)", nullable: false),
                    AddedAt = table.Column<DateTime>(type: "datetime(6)", nullable: false),
                    CreatedAt = table.Column<DateTime>(type: "datetime(6)", nullable: false)
                },
                constraints: table =>
                {
                    table.PrimaryKey("PK_BoardMemberships", x => x.Id);
                    table.ForeignKey(
                        name: "FK_BoardMemberships_AspNetUsers_UserId",
                        column: x => x.UserId,
                        principalTable: "AspNetUsers",
                        principalColumn: "Id",
                        onDelete: ReferentialAction.Cascade);
                    table.ForeignKey(
                        name: "FK_BoardMemberships_Boards_BoardId",
                        column: x => x.BoardId,
                        principalTable: "Boards",
                        principalColumn: "Id",
                        onDelete: ReferentialAction.Cascade);
                })
                .Annotation("MySql:CharSet", "utf8mb4");

            migrationBuilder.CreateTable(
                name: "Columns",
                columns: table => new
                {
                    Id = table.Column<int>(type: "int", nullable: false)
                        .Annotation("MySql:ValueGenerationStrategy", MySqlValueGenerationStrategy.IdentityColumn),
                    Name = table.Column<string>(type: "longtext", nullable: false)
                        .Annotation("MySql:CharSet", "utf8mb4"),
                    BoardId = table.Column<int>(type: "int", nullable: false),
                    Index = table.Column<int>(type: "int", nullable: false),
                    MaxCards = table.Column<int>(type: "int", nullable: false),
                    Finished = table.Column<bool>(type: "tinyint(1)", nullable: false),
                    Collapsed = table.Column<bool>(type: "tinyint(1)", nullable: false),
                    OrderRule = table.Column<int>(type: "int", nullable: false),
                    CreatedAt = table.Column<DateTime>(type: "datetime(6)", nullable: false)
                },
                constraints: table =>
                {
                    table.PrimaryKey("PK_Columns", x => x.Id);
                    table.ForeignKey(
                        name: "FK_Columns_Boards_BoardId",
                        column: x => x.BoardId,
                        principalTable: "Boards",
                        principalColumn: "Id",
                        onDelete: ReferentialAction.Cascade);
                })
                .Annotation("MySql:CharSet", "utf8mb4");

            migrationBuilder.CreateTable(
                name: "Labels",
                columns: table => new
                {
                    Id = table.Column<int>(type: "int", nullable: false)
                        .Annotation("MySql:ValueGenerationStrategy", MySqlValueGenerationStrategy.IdentityColumn),
                    Text = table.Column<string>(type: "longtext", nullable: false)
                        .Annotation("MySql:CharSet", "utf8mb4"),
                    TextColor = table.Column<int>(type: "int", nullable: false),
                    BackgroundColor = table.Column<int>(type: "int", nullable: false),
                    BoardId = table.Column<int>(type: "int", nullable: false),
                    CreatedAt = table.Column<DateTime>(type: "datetime(6)", nullable: false)
                },
                constraints: table =>
                {
                    table.PrimaryKey("PK_Labels", x => x.Id);
                    table.ForeignKey(
                        name: "FK_Labels_Boards_BoardId",
                        column: x => x.BoardId,
                        principalTable: "Boards",
                        principalColumn: "Id",
                        onDelete: ReferentialAction.Cascade);
                })
                .Annotation("MySql:CharSet", "utf8mb4");

            migrationBuilder.CreateTable(
                name: "LastVisits",
                columns: table => new
                {
                    Id = table.Column<int>(type: "int", nullable: false)
                        .Annotation("MySql:ValueGenerationStrategy", MySqlValueGenerationStrategy.IdentityColumn),
                    BoardId = table.Column<int>(type: "int", nullable: false),
                    VisitTime = table.Column<DateTime>(type: "datetime(6)", nullable: false),
                    UserId = table.Column<int>(type: "int", nullable: false),
                    CreatedAt = table.Column<DateTime>(type: "datetime(6)", nullable: false)
                },
                constraints: table =>
                {
                    table.PrimaryKey("PK_LastVisits", x => x.Id);
                    table.ForeignKey(
                        name: "FK_LastVisits_AspNetUsers_UserId",
                        column: x => x.UserId,
                        principalTable: "AspNetUsers",
                        principalColumn: "Id",
                        onDelete: ReferentialAction.Cascade);
                    table.ForeignKey(
                        name: "FK_LastVisits_Boards_BoardId",
                        column: x => x.BoardId,
                        principalTable: "Boards",
                        principalColumn: "Id",
                        onDelete: ReferentialAction.Cascade);
                })
                .Annotation("MySql:CharSet", "utf8mb4");

            migrationBuilder.CreateTable(
                name: "Cards",
                columns: table => new
                {
                    Id = table.Column<int>(type: "int", nullable: false)
                        .Annotation("MySql:ValueGenerationStrategy", MySqlValueGenerationStrategy.IdentityColumn),
                    Text = table.Column<string>(type: "longtext", nullable: false)
                        .Annotation("MySql:CharSet", "utf8mb4"),
                    Description = table.Column<string>(type: "longtext", nullable: false)
                        .Annotation("MySql:CharSet", "utf8mb4"),
                    Number = table.Column<int>(type: "int", nullable: false),
                    Index = table.Column<int>(type: "int", nullable: false),
                    ColumnId = table.Column<int>(type: "int", nullable: false),
                    Priority = table.Column<int>(type: "int", nullable: false),
                    Deadline = table.Column<DateTime>(type: "datetime(6)", nullable: true),
                    DeadlineProcessed = table.Column<bool>(type: "tinyint(1)", nullable: false),
                    Blocked = table.Column<bool>(type: "tinyint(1)", nullable: false),
                    CreatedById = table.Column<int>(type: "int", nullable: false),
                    CreatedAt = table.Column<DateTime>(type: "datetime(6)", nullable: false)
                },
                constraints: table =>
                {
                    table.PrimaryKey("PK_Cards", x => x.Id);
                    table.ForeignKey(
                        name: "FK_Cards_AspNetUsers_CreatedById",
                        column: x => x.CreatedById,
                        principalTable: "AspNetUsers",
                        principalColumn: "Id",
                        onDelete: ReferentialAction.Cascade);
                    table.ForeignKey(
                        name: "FK_Cards_Columns_ColumnId",
                        column: x => x.ColumnId,
                        principalTable: "Columns",
                        principalColumn: "Id",
                        onDelete: ReferentialAction.Cascade);
                })
                .Annotation("MySql:CharSet", "utf8mb4");

            migrationBuilder.CreateTable(
                name: "Activities",
                columns: table => new
                {
                    Id = table.Column<int>(type: "int", nullable: false)
                        .Annotation("MySql:ValueGenerationStrategy", MySqlValueGenerationStrategy.IdentityColumn),
                    UserId = table.Column<int>(type: "int", nullable: false),
                    Text = table.Column<string>(type: "longtext", nullable: false)
                        .Annotation("MySql:CharSet", "utf8mb4"),
                    At = table.Column<DateTime>(type: "datetime(6)", nullable: false),
                    CardId = table.Column<int>(type: "int", nullable: false),
                    CreatedAt = table.Column<DateTime>(type: "datetime(6)", nullable: false)
                },
                constraints: table =>
                {
                    table.PrimaryKey("PK_Activities", x => x.Id);
                    table.ForeignKey(
                        name: "FK_Activities_AspNetUsers_UserId",
                        column: x => x.UserId,
                        principalTable: "AspNetUsers",
                        principalColumn: "Id",
                        onDelete: ReferentialAction.Cascade);
                    table.ForeignKey(
                        name: "FK_Activities_Cards_CardId",
                        column: x => x.CardId,
                        principalTable: "Cards",
                        principalColumn: "Id",
                        onDelete: ReferentialAction.Cascade);
                })
                .Annotation("MySql:CharSet", "utf8mb4");

            migrationBuilder.CreateTable(
                name: "Attachments",
                columns: table => new
                {
                    Id = table.Column<int>(type: "int", nullable: false)
                        .Annotation("MySql:ValueGenerationStrategy", MySqlValueGenerationStrategy.IdentityColumn),
                    FileName = table.Column<string>(type: "longtext", nullable: false)
                        .Annotation("MySql:CharSet", "utf8mb4"),
                    OriginalName = table.Column<string>(type: "longtext", nullable: false)
                        .Annotation("MySql:CharSet", "utf8mb4"),
                    CardId = table.Column<int>(type: "int", nullable: false),
                    CreatedAt = table.Column<DateTime>(type: "datetime(6)", nullable: false)
                },
                constraints: table =>
                {
                    table.PrimaryKey("PK_Attachments", x => x.Id);
                    table.ForeignKey(
                        name: "FK_Attachments_Cards_CardId",
                        column: x => x.CardId,
                        principalTable: "Cards",
                        principalColumn: "Id",
                        onDelete: ReferentialAction.Cascade);
                })
                .Annotation("MySql:CharSet", "utf8mb4");

            migrationBuilder.CreateTable(
                name: "CardLabel",
                columns: table => new
                {
                    LabelsId = table.Column<int>(type: "int", nullable: false),
                    OnCardsId = table.Column<int>(type: "int", nullable: false)
                },
                constraints: table =>
                {
                    table.PrimaryKey("PK_CardLabel", x => new { x.LabelsId, x.OnCardsId });
                    table.ForeignKey(
                        name: "FK_CardLabel_Cards_OnCardsId",
                        column: x => x.OnCardsId,
                        principalTable: "Cards",
                        principalColumn: "Id",
                        onDelete: ReferentialAction.Cascade);
                    table.ForeignKey(
                        name: "FK_CardLabel_Labels_LabelsId",
                        column: x => x.LabelsId,
                        principalTable: "Labels",
                        principalColumn: "Id",
                        onDelete: ReferentialAction.Cascade);
                })
                .Annotation("MySql:CharSet", "utf8mb4");

            migrationBuilder.CreateTable(
                name: "CardLinks",
                columns: table => new
                {
                    Id = table.Column<int>(type: "int", nullable: false)
                        .Annotation("MySql:ValueGenerationStrategy", MySqlValueGenerationStrategy.IdentityColumn),
                    CardOneId = table.Column<int>(type: "int", nullable: false),
                    CardTwoId = table.Column<int>(type: "int", nullable: false),
                    Category = table.Column<string>(type: "longtext", nullable: false)
                        .Annotation("MySql:CharSet", "utf8mb4"),
                    CreatedAt = table.Column<DateTime>(type: "datetime(6)", nullable: false)
                },
                constraints: table =>
                {
                    table.PrimaryKey("PK_CardLinks", x => x.Id);
                    table.ForeignKey(
                        name: "FK_CardLinks_Cards_CardOneId",
                        column: x => x.CardOneId,
                        principalTable: "Cards",
                        principalColumn: "Id",
                        onDelete: ReferentialAction.Cascade);
                    table.ForeignKey(
                        name: "FK_CardLinks_Cards_CardTwoId",
                        column: x => x.CardTwoId,
                        principalTable: "Cards",
                        principalColumn: "Id",
                        onDelete: ReferentialAction.Cascade);
                })
                .Annotation("MySql:CharSet", "utf8mb4");

            migrationBuilder.CreateTable(
                name: "CardUser",
                columns: table => new
                {
                    AssignedToId = table.Column<int>(type: "int", nullable: false),
                    AssigneesId = table.Column<int>(type: "int", nullable: false)
                },
                constraints: table =>
                {
                    table.PrimaryKey("PK_CardUser", x => new { x.AssignedToId, x.AssigneesId });
                    table.ForeignKey(
                        name: "FK_CardUser_AspNetUsers_AssigneesId",
                        column: x => x.AssigneesId,
                        principalTable: "AspNetUsers",
                        principalColumn: "Id",
                        onDelete: ReferentialAction.Cascade);
                    table.ForeignKey(
                        name: "FK_CardUser_Cards_AssignedToId",
                        column: x => x.AssignedToId,
                        principalTable: "Cards",
                        principalColumn: "Id",
                        onDelete: ReferentialAction.Cascade);
                })
                .Annotation("MySql:CharSet", "utf8mb4");

            migrationBuilder.CreateTable(
                name: "Comments",
                columns: table => new
                {
                    Id = table.Column<int>(type: "int", nullable: false)
                        .Annotation("MySql:ValueGenerationStrategy", MySqlValueGenerationStrategy.IdentityColumn),
                    Text = table.Column<string>(type: "longtext", nullable: false)
                        .Annotation("MySql:CharSet", "utf8mb4"),
                    CreatedById = table.Column<int>(type: "int", nullable: false),
                    CardId = table.Column<int>(type: "int", nullable: false),
                    CreatedAt = table.Column<DateTime>(type: "datetime(6)", nullable: false)
                },
                constraints: table =>
                {
                    table.PrimaryKey("PK_Comments", x => x.Id);
                    table.ForeignKey(
                        name: "FK_Comments_AspNetUsers_CreatedById",
                        column: x => x.CreatedById,
                        principalTable: "AspNetUsers",
                        principalColumn: "Id",
                        onDelete: ReferentialAction.Cascade);
                    table.ForeignKey(
                        name: "FK_Comments_Cards_CardId",
                        column: x => x.CardId,
                        principalTable: "Cards",
                        principalColumn: "Id",
                        onDelete: ReferentialAction.Cascade);
                })
                .Annotation("MySql:CharSet", "utf8mb4");

            migrationBuilder.CreateTable(
                name: "Reminders",
                columns: table => new
                {
                    Id = table.Column<int>(type: "int", nullable: false)
                        .Annotation("MySql:ValueGenerationStrategy", MySqlValueGenerationStrategy.IdentityColumn),
                    At = table.Column<DateTime>(type: "datetime(6)", nullable: false),
                    CardId = table.Column<int>(type: "int", nullable: false),
                    CreatedAt = table.Column<DateTime>(type: "datetime(6)", nullable: false)
                },
                constraints: table =>
                {
                    table.PrimaryKey("PK_Reminders", x => x.Id);
                    table.ForeignKey(
                        name: "FK_Reminders_Cards_CardId",
                        column: x => x.CardId,
                        principalTable: "Cards",
                        principalColumn: "Id",
                        onDelete: ReferentialAction.Cascade);
                })
                .Annotation("MySql:CharSet", "utf8mb4");

            migrationBuilder.CreateTable(
                name: "Subtasks",
                columns: table => new
                {
                    Id = table.Column<int>(type: "int", nullable: false)
                        .Annotation("MySql:ValueGenerationStrategy", MySqlValueGenerationStrategy.IdentityColumn),
                    Index = table.Column<int>(type: "int", nullable: false),
                    Text = table.Column<string>(type: "longtext", nullable: false)
                        .Annotation("MySql:CharSet", "utf8mb4"),
                    Completed = table.Column<bool>(type: "tinyint(1)", nullable: false),
                    CardId = table.Column<int>(type: "int", nullable: false),
                    CreatedAt = table.Column<DateTime>(type: "datetime(6)", nullable: false)
                },
                constraints: table =>
                {
                    table.PrimaryKey("PK_Subtasks", x => x.Id);
                    table.ForeignKey(
                        name: "FK_Subtasks_Cards_CardId",
                        column: x => x.CardId,
                        principalTable: "Cards",
                        principalColumn: "Id",
                        onDelete: ReferentialAction.Cascade);
                })
                .Annotation("MySql:CharSet", "utf8mb4");

            migrationBuilder.CreateTable(
                name: "TimeRecords",
                columns: table => new
                {
                    Id = table.Column<int>(type: "int", nullable: false)
                        .Annotation("MySql:ValueGenerationStrategy", MySqlValueGenerationStrategy.IdentityColumn),
                    CardId = table.Column<int>(type: "int", nullable: false),
                    UserId = table.Column<int>(type: "int", nullable: false),
                    StartedAt = table.Column<DateTime>(type: "datetime(6)", nullable: false),
                    EndedAt = table.Column<DateTime>(type: "datetime(6)", nullable: true),
                    CreatedAt = table.Column<DateTime>(type: "datetime(6)", nullable: false)
                },
                constraints: table =>
                {
                    table.PrimaryKey("PK_TimeRecords", x => x.Id);
                    table.ForeignKey(
                        name: "FK_TimeRecords_AspNetUsers_UserId",
                        column: x => x.UserId,
                        principalTable: "AspNetUsers",
                        principalColumn: "Id",
                        onDelete: ReferentialAction.Cascade);
                    table.ForeignKey(
                        name: "FK_TimeRecords_Cards_CardId",
                        column: x => x.CardId,
                        principalTable: "Cards",
                        principalColumn: "Id",
                        onDelete: ReferentialAction.Cascade);
                })
                .Annotation("MySql:CharSet", "utf8mb4");

            migrationBuilder.CreateIndex(
                name: "IX_Activities_CardId",
                table: "Activities",
                column: "CardId");

            migrationBuilder.CreateIndex(
                name: "IX_Activities_UserId",
                table: "Activities",
                column: "UserId");

            migrationBuilder.CreateIndex(
                name: "IX_AspNetRoleClaims_RoleId",
                table: "AspNetRoleClaims",
                column: "RoleId");

            migrationBuilder.CreateIndex(
                name: "RoleNameIndex",
                table: "AspNetRoles",
                column: "NormalizedName",
                unique: true);

            migrationBuilder.CreateIndex(
                name: "IX_AspNetUserClaims_UserId",
                table: "AspNetUserClaims",
                column: "UserId");

            migrationBuilder.CreateIndex(
                name: "IX_AspNetUserLogins_UserId",
                table: "AspNetUserLogins",
                column: "UserId");

            migrationBuilder.CreateIndex(
                name: "IX_AspNetUserRoles_RoleId",
                table: "AspNetUserRoles",
                column: "RoleId");

            migrationBuilder.CreateIndex(
                name: "EmailIndex",
                table: "AspNetUsers",
                column: "NormalizedEmail");

            migrationBuilder.CreateIndex(
                name: "UserNameIndex",
                table: "AspNetUsers",
                column: "NormalizedUserName",
                unique: true);

            migrationBuilder.CreateIndex(
                name: "IX_Attachments_CardId",
                table: "Attachments",
                column: "CardId");

            migrationBuilder.CreateIndex(
                name: "IX_BoardMemberships_BoardId",
                table: "BoardMemberships",
                column: "BoardId");

            migrationBuilder.CreateIndex(
                name: "IX_BoardMemberships_UserId",
                table: "BoardMemberships",
                column: "UserId");

            migrationBuilder.CreateIndex(
                name: "IX_Boards_ProjectId",
                table: "Boards",
                column: "ProjectId");

            migrationBuilder.CreateIndex(
                name: "IX_CardLabel_OnCardsId",
                table: "CardLabel",
                column: "OnCardsId");

            migrationBuilder.CreateIndex(
                name: "IX_CardLinks_CardOneId",
                table: "CardLinks",
                column: "CardOneId");

            migrationBuilder.CreateIndex(
                name: "IX_CardLinks_CardTwoId",
                table: "CardLinks",
                column: "CardTwoId");

            migrationBuilder.CreateIndex(
                name: "IX_Cards_ColumnId",
                table: "Cards",
                column: "ColumnId");

            migrationBuilder.CreateIndex(
                name: "IX_Cards_CreatedById",
                table: "Cards",
                column: "CreatedById");

            migrationBuilder.CreateIndex(
                name: "IX_CardUser_AssigneesId",
                table: "CardUser",
                column: "AssigneesId");

            migrationBuilder.CreateIndex(
                name: "IX_Codes_UserId",
                table: "Codes",
                column: "UserId",
                unique: true);

            migrationBuilder.CreateIndex(
                name: "IX_Columns_BoardId",
                table: "Columns",
                column: "BoardId");

            migrationBuilder.CreateIndex(
                name: "IX_Comments_CardId",
                table: "Comments",
                column: "CardId");

            migrationBuilder.CreateIndex(
                name: "IX_Comments_CreatedById",
                table: "Comments",
                column: "CreatedById");

            migrationBuilder.CreateIndex(
                name: "IX_Labels_BoardId",
                table: "Labels",
                column: "BoardId");

            migrationBuilder.CreateIndex(
                name: "IX_LastVisits_BoardId",
                table: "LastVisits",
                column: "BoardId");

            migrationBuilder.CreateIndex(
                name: "IX_LastVisits_UserId",
                table: "LastVisits",
                column: "UserId");

            migrationBuilder.CreateIndex(
                name: "IX_ProjectMemberships_ProjectId",
                table: "ProjectMemberships",
                column: "ProjectId");

            migrationBuilder.CreateIndex(
                name: "IX_ProjectMemberships_UserId",
                table: "ProjectMemberships",
                column: "UserId");

            migrationBuilder.CreateIndex(
                name: "IX_Reminders_CardId",
                table: "Reminders",
                column: "CardId");

            migrationBuilder.CreateIndex(
                name: "IX_Subtasks_CardId",
                table: "Subtasks",
                column: "CardId");

            migrationBuilder.CreateIndex(
                name: "IX_TimeRecords_CardId",
                table: "TimeRecords",
                column: "CardId");

            migrationBuilder.CreateIndex(
                name: "IX_TimeRecords_UserId",
                table: "TimeRecords",
                column: "UserId");
        }

        /// <inheritdoc />
        protected override void Down(MigrationBuilder migrationBuilder)
        {
            migrationBuilder.DropTable(
                name: "Activities");

            migrationBuilder.DropTable(
                name: "AspNetRoleClaims");

            migrationBuilder.DropTable(
                name: "AspNetUserClaims");

            migrationBuilder.DropTable(
                name: "AspNetUserLogins");

            migrationBuilder.DropTable(
                name: "AspNetUserRoles");

            migrationBuilder.DropTable(
                name: "AspNetUserTokens");

            migrationBuilder.DropTable(
                name: "Attachments");

            migrationBuilder.DropTable(
                name: "BoardMemberships");

            migrationBuilder.DropTable(
                name: "CardLabel");

            migrationBuilder.DropTable(
                name: "CardLinks");

            migrationBuilder.DropTable(
                name: "CardUser");

            migrationBuilder.DropTable(
                name: "Codes");

            migrationBuilder.DropTable(
                name: "Comments");

            migrationBuilder.DropTable(
                name: "DataProtectionKeys");

            migrationBuilder.DropTable(
                name: "LastVisits");

            migrationBuilder.DropTable(
                name: "ProjectMemberships");

            migrationBuilder.DropTable(
                name: "Reminders");

            migrationBuilder.DropTable(
                name: "Subtasks");

            migrationBuilder.DropTable(
                name: "TimeRecords");

            migrationBuilder.DropTable(
                name: "AspNetRoles");

            migrationBuilder.DropTable(
                name: "Labels");

            migrationBuilder.DropTable(
                name: "Cards");

            migrationBuilder.DropTable(
                name: "AspNetUsers");

            migrationBuilder.DropTable(
                name: "Columns");

            migrationBuilder.DropTable(
                name: "Boards");

            migrationBuilder.DropTable(
                name: "Projects");
        }
    }
}


================================================
FILE: Ticky.Internal/Migrations/20250527093505_Favorites.Designer.cs
================================================
// <auto-generated />
using System;
using Microsoft.EntityFrameworkCore;
using Microsoft.EntityFrameworkCore.Infrastructure;
using Microsoft.EntityFrameworkCore.Metadata;
using Microsoft.EntityFrameworkCore.Migrations;
using Microsoft.EntityFrameworkCore.Storage.ValueConversion;
using Ticky.Internal.Data;

#nullable disable

namespace Ticky.Internal.Migrations
{
    [DbContext(typeof(DataContext))]
    [Migration("20250527093505_Favorites")]
    partial class Favorites
    {
        /// <inheritdoc />
        protected override void BuildTargetModel(ModelBuilder modelBuilder)
        {
#pragma warning disable 612, 618
            modelBuilder
                .HasAnnotation("ProductVersion", "9.0.5")
                .HasAnnotation("Relational:MaxIdentifierLength", 64);

            MySqlModelBuilderExtensions.AutoIncrementColumns(modelBuilder);

            modelBuilder.Entity("CardLabel", b =>
                {
                    b.Property<int>("LabelsId")
                        .HasColumnType("int");

                    b.Property<int>("OnCardsId")
                        .HasColumnType("int");

                    b.HasKey("LabelsId", "OnCardsId");

                    b.HasIndex("OnCardsId");

                    b.ToTable("CardLabel");
                });

            modelBuilder.Entity("CardUser", b =>
                {
                    b.Property<int>("AssignedToId")
                        .HasColumnType("int");

                    b.Property<int>("AssigneesId")
                        .HasColumnType("int");

                    b.HasKey("AssignedToId", "AssigneesId");

                    b.HasIndex("AssigneesId");

                    b.ToTable("CardUser");
                });

            modelBuilder.Entity("Microsoft.AspNetCore.DataProtection.EntityFrameworkCore.DataProtectionKey", b =>
                {
                    b.Property<int>("Id")
                        .ValueGeneratedOnAdd()
                        .HasColumnType("int");

                    MySqlPropertyBuilderExtensions.UseMySqlIdentityColumn(b.Property<int>("Id"));

                    b.Property<string>("FriendlyName")
                        .HasColumnType("longtext");

                    b.Property<string>("Xml")
                        .HasColumnType("longtext");

                    b.HasKey("Id");

                    b.ToTable("DataProtectionKeys");
                });

            modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityRole<int>", b =>
                {
                    b.Property<int>("Id")
                        .ValueGeneratedOnAdd()
                        .HasColumnType("int");

                    MySqlPropertyBuilderExtensions.UseMySqlIdentityColumn(b.Property<int>("Id"));

                    b.Property<string>("ConcurrencyStamp")
                        .IsConcurrencyToken()
                        .HasColumnType("longtext");

                    b.Property<string>("Name")
                        .HasMaxLength(256)
                        .HasColumnType("varchar(256)");

                    b.Property<string>("NormalizedName")
                        .HasMaxLength(256)
                        .HasColumnType("varchar(256)");

                    b.HasKey("Id");

                    b.HasIndex("NormalizedName")
                        .IsUnique()
                        .HasDatabaseName("RoleNameIndex");

                    b.ToTable("AspNetRoles", (string)null);
                });

            modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityRoleClaim<int>", b =>
                {
                    b.Property<int>("Id")
                        .ValueGeneratedOnAdd()
                        .HasColumnType("int");

                    MySqlPropertyBuilderExtensions.UseMySqlIdentityColumn(b.Property<int>("Id"));

                    b.Property<string>("ClaimType")
                        .HasColumnType("longtext");

                    b.Property<string>("ClaimValue")
                        .HasColumnType("longtext");

                    b.Property<int>("RoleId")
                        .HasColumnType("int");

                    b.HasKey("Id");

                    b.HasIndex("RoleId");

                    b.ToTable("AspNetRoleClaims", (string)null);
                });

            modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserClaim<int>", b =>
                {
                    b.Property<int>("Id")
                        .ValueGeneratedOnAdd()
                        .HasColumnType("int");

                    MySqlPropertyBuilderExtensions.UseMySqlIdentityColumn(b.Property<int>("Id"));

                    b.Property<string>("ClaimType")
                        .HasColumnType("longtext");

                    b.Property<string>("ClaimValue")
                        .HasColumnType("longtext");

                    b.Property<int>("UserId")
                        .HasColumnType("int");

                    b.HasKey("Id");

                    b.HasIndex("UserId");

                    b.ToTable("AspNetUserClaims", (string)null);
                });

            modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserLogin<int>", b =>
                {
                    b.Property<string>("LoginProvider")
                        .HasColumnType("varchar(255)");

                    b.Property<string>("ProviderKey")
                        .HasColumnType("varchar(255)");

                    b.Property<string>("ProviderDisplayName")
                        .HasColumnType("longtext");

                    b.Property<int>("UserId")
                        .HasColumnType("int");

                    b.HasKey("LoginProvider", "ProviderKey");

                    b.HasIndex("UserId");

                    b.ToTable("AspNetUserLogins", (string)null);
                });

            modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserRole<int>", b =>
                {
                    b.Property<int>("UserId")
                        .HasColumnType("int");

                    b.Property<int>("RoleId")
                        .HasColumnType("int");

                    b.HasKey("UserId", "RoleId");

                    b.HasIndex("RoleId");

                    b.ToTable("AspNetUserRoles", (string)null);
                });

            modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserToken<int>", b =>
                {
                    b.Property<int>("UserId")
                        .HasColumnType("int");

                    b.Property<string>("LoginProvider")
                        .HasColumnType("varchar(255)");

                    b.Property<string>("Name")
                        .HasColumnType("varchar(255)");

                    b.Property<string>("Value")
                        .HasColumnType("longtext");

                    b.HasKey("UserId", "LoginProvider", "Name");

                    b.ToTable("AspNetUserTokens", (string)null);
                });

            modelBuilder.Entity("Ticky.Base.Entities.Activity", b =>
                {
                    b.Property<int>("Id")
                        .ValueGeneratedOnAdd()
                        .HasColumnType("int");

                    MySqlPropertyBuilderExtensions.UseMySqlIdentityColumn(b.Property<int>("Id"));

                    b.Property<DateTime>("At")
                        .HasColumnType("datetime(6)");

                    b.Property<int>("CardId")
                        .HasColumnType("int");

                    b.Property<DateTime>("CreatedAt")
                        .HasColumnType("datetime(6)");

                    b.Property<string>("Text")
                        .IsRequired()
                        .HasColumnType("longtext");

                    b.Property<int>("UserId")
                        .HasColumnType("int");

                    b.HasKey("Id");

                    b.HasIndex("CardId");

                    b.HasIndex("UserId");

                    b.ToTable("Activities");
                });

            modelBuilder.Entity("Ticky.Base.Entities.Attachment", b =>
                {
                    b.Property<int>("Id")
                        .ValueGeneratedOnAdd()
                        .HasColumnType("int");

                    MySqlPropertyBuilderExtensions.UseMySqlIdentityColumn(b.Property<int>("Id"));

                    b.Property<int>("CardId")
                        .HasColumnType("int");

                    b.Property<DateTime>("CreatedAt")
                        .HasColumnType("datetime(6)");

                    b.Property<string>("FileName")
                        .IsRequired()
                        .HasColumnType("longtext");

                    b.Property<string>("OriginalName")
                        .IsRequired()
                        .HasColumnType("longtext");

                    b.HasKey("Id");

                    b.HasIndex("CardId");

                    b.ToTable("Attachments");
                });

            modelBuilder.Entity("Ticky.Base.Entities.Board", b =>
                {
                    b.Property<int>("Id")
                        .ValueGeneratedOnAdd()
                        .HasColumnType("int");

                    MySqlPropertyBuilderExtensions.UseMySqlIdentityColumn(b.Property<int>("Id"));

                    b.Property<string>("Code")
                        .IsRequired()
                        .HasMaxLength(5)
                        .HasColumnType("varchar(5)");

                    b.Property<DateTime>("CreatedAt")
                        .HasColumnType("datetime(6)");

                    b.Property<string>("Description")
                        .IsRequired()
                        .HasColumnType("longtext");

                    b.Property<string>("Name")
                        .IsRequired()
                        .HasColumnType("longtext");

                    b.Property<int>("ProjectId")
                        .HasColumnType("int");

                    b.HasKey("Id");

                    b.HasIndex("ProjectId");

                    b.ToTable("Boards");
                });

            modelBuilder.Entity("Ticky.Base.Entities.BoardMembership", b =>
                {
                    b.Property<int>("Id")
                        .ValueGeneratedOnAdd()
                        .HasColumnType("int");

                    MySqlPropertyBuilderExtensions.UseMySqlIdentityColumn(b.Property<int>("Id"));

                    b.Property<DateTime>("AddedAt")
                        .HasColumnType("datetime(6)");

                    b.Property<int>("BoardId")
                        .HasColumnType("int");

                    b.Property<DateTime>("CreatedAt")
                        .HasColumnType("datetime(6)");

                    b.Property<bool>("IsAdmin")
                        .HasColumnType("tinyint(1)");

                    b.Property<int>("UserId")
                        .HasColumnType("int");

                    b.HasKey("Id");

                    b.HasIndex("BoardId");

                    b.HasIndex("UserId");

                    b.ToTable("BoardMemberships");
                });

            modelBuilder.Entity("Ticky.Base.Entities.Card", b =>
                {
                    b.Property<int>("Id")
                        .ValueGeneratedOnAdd()
                        .HasColumnType("int");

                    MySqlPropertyBuilderExtensions.UseMySqlIdentityColumn(b.Property<int>("Id"));

                    b.Property<bool>("Blocked")
                        .HasColumnType("tinyint(1)");

                    b.Property<int>("ColumnId")
                        .HasColumnType("int");

                    b.Property<DateTime>("CreatedAt")
                        .HasColumnType("datetime(6)");

                    b.Property<int>("CreatedById")
              
Download .txt
gitextract_dmiie922/

├── .gitattributes
├── .github/
│   ├── FUNDING.yml
│   ├── ISSUE_TEMPLATE/
│   │   ├── bug_report.md
│   │   └── feature_request.md
│   └── workflows/
│       ├── build.yml
│       ├── docker-release.yml
│       └── docker-unstable.yml
├── .gitignore
├── .vscode/
│   └── settings.json
├── CONTRIBUTING.md
├── Dockerfile
├── LICENSE.txt
├── README.md
├── Ticky.Base/
│   ├── Constants.cs
│   ├── Converters/
│   │   ├── ColorToInt32Converter.cs
│   │   └── StringToTimeSpanConverter.cs
│   ├── DTOs/
│   │   ├── InformationDTO.cs
│   │   ├── Notification.cs
│   │   ├── Trello/
│   │   │   ├── TrelloCardDTO.cs
│   │   │   ├── TrelloCheckItemDTO.cs
│   │   │   ├── TrelloChecklistDTO.cs
│   │   │   ├── TrelloLabelDTO.cs
│   │   │   ├── TrelloListDTO.cs
│   │   │   ├── TrelloMemberDTO.cs
│   │   │   └── TrelloPreferencesDTO.cs
│   │   └── TrelloImportDTO.cs
│   ├── Entities/
│   │   ├── Abstractions/
│   │   │   ├── AbstractDbEntity.cs
│   │   │   ├── IAssignable.cs
│   │   │   ├── IDbEntry.cs
│   │   │   ├── IDeletable.cs
│   │   │   └── IOrderable.cs
│   │   ├── Activity.cs
│   │   ├── Attachment.cs
│   │   ├── Board.cs
│   │   ├── BoardMembership.cs
│   │   ├── Card.cs
│   │   ├── CardLink.cs
│   │   ├── Code.cs
│   │   ├── Column.cs
│   │   ├── Comment.cs
│   │   ├── Favorite.cs
│   │   ├── Label.cs
│   │   ├── LastVisit.cs
│   │   ├── Owned/
│   │   │   └── RepeatInfo.cs
│   │   ├── Project.cs
│   │   ├── ProjectMembership.cs
│   │   ├── Reminder.cs
│   │   ├── Subtask.cs
│   │   ├── TimeRecord.cs
│   │   └── User.cs
│   ├── Enums/
│   │   ├── CardPlacement.cs
│   │   ├── CardPriority.cs
│   │   ├── CodePurpose.cs
│   │   ├── DeadlineColor.cs
│   │   ├── ImportSource.cs
│   │   ├── ImportType.cs
│   │   ├── NotificationType.cs
│   │   ├── OperationType.cs
│   │   ├── OrderRule.cs
│   │   ├── RepeatType.cs
│   │   └── TrelloArchivedHandlingType.cs
│   ├── GlobalUsings.cs
│   ├── Models/
│   │   ├── BoardPreferencesModel.cs
│   │   ├── CloneBoardModel.cs
│   │   ├── CreateCardModel.cs
│   │   ├── CredentialsModel.cs
│   │   ├── DeadlineModel.cs
│   │   ├── FilterCardsModel.cs
│   │   ├── ImportModel.cs
│   │   ├── LabelModel.cs
│   │   ├── LinkCardsModel.cs
│   │   ├── ReminderModel.cs
│   │   ├── RepeatCardModel.cs
│   │   ├── SnoozeCardModel.cs
│   │   ├── SubtaskModel.cs
│   │   ├── TimeRecordModel.cs
│   │   └── UserModel.cs
│   ├── Ticky.Base.csproj
│   └── Validation/
│       ├── IsValidTimeSpan.cs
│       └── RequiredIf.cs
├── Ticky.Internal/
│   ├── Data/
│   │   ├── DataContext.cs
│   │   ├── DataMigrator.cs
│   │   └── DataSeeder.cs
│   ├── GlobalUsings.cs
│   ├── Helpers/
│   │   ├── AttachmentHelper.cs
│   │   ├── IndexHelper.cs
│   │   ├── StringHelper.cs
│   │   └── TimeHelper.cs
│   ├── Migrations/
│   │   ├── 20250523175138_Initial.Designer.cs
│   │   ├── 20250523175138_Initial.cs
│   │   ├── 20250527093505_Favorites.Designer.cs
│   │   ├── 20250527093505_Favorites.cs
│   │   ├── 20250606115441_ForceCredentialsChange.Designer.cs
│   │   ├── 20250606115441_ForceCredentialsChange.cs
│   │   ├── 20250615181842_TextToNameCard.Designer.cs
│   │   ├── 20250615181842_TextToNameCard.cs
│   │   ├── 20250617140549_SnoozeCards.Designer.cs
│   │   ├── 20250617140549_SnoozeCards.cs
│   │   ├── 20250618090806_AtRemoval.Designer.cs
│   │   ├── 20250618090806_AtRemoval.cs
│   │   ├── 20250618114219_SelfAssign.Designer.cs
│   │   ├── 20250618114219_SelfAssign.cs
│   │   ├── 20250703124731_DisableBoardAnimations.Designer.cs
│   │   ├── 20250703124731_DisableBoardAnimations.cs
│   │   ├── 20250709130027_RepeatingCards.Designer.cs
│   │   ├── 20250709130027_RepeatingCards.cs
│   │   ├── 20250826115734_NewCardPlacement.Designer.cs
│   │   ├── 20250826115734_NewCardPlacement.cs
│   │   ├── 20251005111419_BlockToFlagged.Designer.cs
│   │   ├── 20251005111419_BlockToFlagged.cs
│   │   ├── 20251005174743_SubtaskAssignee.Designer.cs
│   │   ├── 20251005174743_SubtaskAssignee.cs
│   │   ├── 20251011134337_Information.Designer.cs
│   │   ├── 20251011134337_Information.cs
│   │   └── DataContextModelSnapshot.cs
│   ├── Services/
│   │   ├── AvatarService.cs
│   │   ├── CardNumberingService.cs
│   │   ├── CodeService.cs
│   │   ├── EmailService.cs
│   │   ├── Hosted/
│   │   │   ├── AbstractHostedService.cs
│   │   │   ├── CleanupHostedService.cs
│   │   │   ├── ReminderHostedService.cs
│   │   │   ├── RepeatHostedService.cs
│   │   │   └── SnoozeHostedService.cs
│   │   ├── InformationService.cs
│   │   ├── SearchService.cs
│   │   └── TrelloImportService.cs
│   └── Ticky.Internal.csproj
├── Ticky.Units/
│   ├── CardTest.cs
│   ├── Constants.cs
│   ├── GlobalUsings.cs
│   └── Ticky.Units.csproj
├── Ticky.Web/
│   ├── Components/
│   │   ├── App.razor
│   │   ├── Dialogs/
│   │   │   ├── AbstractModal.razor
│   │   │   ├── AddLabelModal.razor
│   │   │   ├── AddOrEditRepeatModal.razor
│   │   │   ├── AddReminderModal.razor
│   │   │   ├── AddSubtaskModal.razor
│   │   │   ├── AddTimeRecordModal.razor
│   │   │   ├── AddUserModal.razor
│   │   │   ├── AssigneesModal.razor
│   │   │   ├── CloneBoardModal.razor
│   │   │   ├── CreateBoardModal.razor
│   │   │   ├── CreateColumnModal.razor
│   │   │   ├── CreateProjectModal.razor
│   │   │   ├── DeleteConfirmationDialog.razor
│   │   │   ├── EditBoardMembershipsModal.razor
│   │   │   ├── EditBoardModal.razor
│   │   │   ├── EditCardModal.razor
│   │   │   ├── EditColumnModal.razor
│   │   │   ├── EditDeadlineModal.razor
│   │   │   ├── EditLabelModal.razor
│   │   │   ├── EditProjectMembershipsModal.razor
│   │   │   ├── EditProjectModal.razor
│   │   │   ├── EditSubtaskModal.razor
│   │   │   ├── EditTimeRecordModal.razor
│   │   │   ├── EditUserModal.razor
│   │   │   ├── FilterCardsModal.razor
│   │   │   ├── ImportModal.razor
│   │   │   ├── InformationModal.razor
│   │   │   ├── LabelModal.razor
│   │   │   ├── LinkCardsModal.razor
│   │   │   ├── PriorityModal.razor
│   │   │   ├── SearchModal.razor
│   │   │   ├── SnoozeCardModal.razor
│   │   │   └── UserInfoModal.razor
│   │   ├── Elements/
│   │   │   ├── ActionModal.razor
│   │   │   ├── BoardCard.razor
│   │   │   ├── CardView.razor
│   │   │   ├── ColumnView.razor
│   │   │   ├── DisabledBadge.razor
│   │   │   ├── Dropdown.razor
│   │   │   ├── LabelView.razor
│   │   │   ├── Modal.razor
│   │   │   ├── Notifications.razor
│   │   │   ├── PriorityLabel.razor
│   │   │   ├── Sortable/
│   │   │   │   ├── SortableList.razor
│   │   │   │   ├── SortableList.razor.cs
│   │   │   │   ├── SortableList.razor.css
│   │   │   │   └── SortableList.razor.js
│   │   │   ├── Spinner.razor
│   │   │   ├── SubtaskView.razor
│   │   │   ├── TimeSelect.razor
│   │   │   └── Tooltip.razor
│   │   ├── Layout/
│   │   │   ├── MainLayout.razor
│   │   │   └── NavMenu.razor
│   │   ├── Pages/
│   │   │   ├── Abstractions/
│   │   │   │   └── NotifiableBase.razor
│   │   │   ├── AdminPanel.razor
│   │   │   ├── Auth/
│   │   │   │   ├── ChangePassword.cshtml
│   │   │   │   ├── ChangePassword.cshtml.cs
│   │   │   │   ├── ConfirmMail.cshtml
│   │   │   │   ├── ConfirmMail.cshtml.cs
│   │   │   │   ├── ForgotPassword.cshtml
│   │   │   │   ├── ForgotPassword.cshtml.cs
│   │   │   │   ├── Login.cshtml
│   │   │   │   ├── Login.cshtml.cs
│   │   │   │   ├── Logout.cshtml
│   │   │   │   ├── Logout.cshtml.cs
│   │   │   │   ├── MailConfirmed.cshtml
│   │   │   │   ├── MailConfirmed.cshtml.cs
│   │   │   │   ├── PasswordChanged.cshtml
│   │   │   │   ├── PasswordChanged.cshtml.cs
│   │   │   │   ├── Register.cshtml
│   │   │   │   ├── Register.cshtml.cs
│   │   │   │   ├── _AuthLayout.cshtml
│   │   │   │   ├── _ViewImports.cshtml
│   │   │   │   └── _ViewStart.cshtml
│   │   │   ├── BoardSettings.razor
│   │   │   ├── BoardView.razor
│   │   │   ├── Error.razor
│   │   │   ├── Home.razor
│   │   │   └── UserSettings.razor
│   │   ├── Routes.razor
│   │   └── _Imports.razor
│   ├── Controllers/
│   │   ├── AttachmentsController.cs
│   │   └── HealthController.cs
│   ├── GlobalUsings.cs
│   ├── Hubs/
│   │   └── UpdateHub.cs
│   ├── Program.cs
│   ├── Properties/
│   │   └── launchSettings.json
│   ├── Ticky.Web.csproj
│   ├── app.css
│   ├── appsettings.Development.json
│   ├── appsettings.json
│   ├── package.json
│   ├── tailwind.config.js
│   ├── tailwind.extension.json
│   └── wwwroot/
│       ├── css/
│       │   └── app.css
│       ├── emails/
│       │   ├── DeadlineReminder.html
│       │   ├── ForgottenPassword.html
│       │   ├── Reminder.html
│       │   └── VerifyEmail.html
│       ├── fonts/
│       │   ├── Noto-Sans.css
│       │   └── Poppins.css
│       ├── information.json
│       └── js/
│           ├── darkTheme.js
│           ├── downloadFile.js
│           ├── focusTrap.js
│           ├── main.js
│           └── screenHeight.js
└── Ticky.sln
Download .txt
SYMBOL INDEX (308 symbols across 130 files)

FILE: Ticky.Base/Constants.cs
  class Constants (line 3) | public static class Constants
    class CascadingParameters (line 15) | public static class CascadingParameters
    class Defaults (line 21) | public static class Defaults
    class Emails (line 27) | public static class Emails
      class Mappings (line 31) | public static class Mappings
    class Hubs (line 52) | public static class Hubs
    class Limits (line 57) | public static class Limits
    class Mappings (line 69) | public static class Mappings
    class Policies (line 78) | public static class Policies
    class Roles (line 83) | public static class Roles
    class StorageKeys (line 88) | public static class StorageKeys

FILE: Ticky.Base/Converters/ColorToInt32Converter.cs
  class ColorToInt32Converter (line 5) | public class ColorToInt32Converter : ValueConverter<Color, int>
    method ColorToInt32Converter (line 7) | public ColorToInt32Converter()

FILE: Ticky.Base/Converters/StringToTimeSpanConverter.cs
  class StringToTimeSpanConverter (line 3) | public static class StringToTimeSpanConverter
    method ConvertToTimeSpan (line 5) | public static TimeSpan ConvertToTimeSpan(this string str)
    method ConvertToString (line 24) | public static string ConvertToString(this TimeSpan time)

FILE: Ticky.Base/DTOs/InformationDTO.cs
  class InformationDTO (line 3) | public class InformationDTO

FILE: Ticky.Base/DTOs/Notification.cs
  type Notification (line 3) | public record Notification(string text, NotificationType type = Notifica...

FILE: Ticky.Base/DTOs/Trello/TrelloCardDTO.cs
  class TrelloCardDTO (line 3) | public class TrelloCardDTO

FILE: Ticky.Base/DTOs/Trello/TrelloCheckItemDTO.cs
  class TrelloCheckItemDTO (line 3) | public class TrelloCheckItemDTO

FILE: Ticky.Base/DTOs/Trello/TrelloChecklistDTO.cs
  class TrelloChecklistDTO (line 3) | public class TrelloChecklistDTO

FILE: Ticky.Base/DTOs/Trello/TrelloLabelDTO.cs
  class TrelloLabelDTO (line 3) | public class TrelloLabelDTO

FILE: Ticky.Base/DTOs/Trello/TrelloListDTO.cs
  class TrelloListDTO (line 3) | public class TrelloListDTO

FILE: Ticky.Base/DTOs/Trello/TrelloMemberDTO.cs
  class TrelloMemberDTO (line 3) | public class TrelloMemberDTO

FILE: Ticky.Base/DTOs/Trello/TrelloPreferencesDTO.cs
  class TrelloPreferencesDTO (line 3) | public class TrelloPreferencesDTO

FILE: Ticky.Base/DTOs/TrelloImportDTO.cs
  class TrelloImportDTO (line 5) | public class TrelloImportDTO

FILE: Ticky.Base/Entities/Abstractions/AbstractDbEntity.cs
  class AbstractDbEntity (line 3) | public abstract class AbstractDbEntity : IDbEntry

FILE: Ticky.Base/Entities/Abstractions/IAssignable.cs
  type IAssignable (line 3) | public interface IAssignable

FILE: Ticky.Base/Entities/Abstractions/IDbEntry.cs
  type IDbEntry (line 3) | public interface IDbEntry

FILE: Ticky.Base/Entities/Abstractions/IDeletable.cs
  type IDeletable (line 3) | public interface IDeletable : IDbEntry

FILE: Ticky.Base/Entities/Abstractions/IOrderable.cs
  type IOrderable (line 3) | public interface IOrderable

FILE: Ticky.Base/Entities/Activity.cs
  class Activity (line 3) | public class Activity : AbstractDbEntity

FILE: Ticky.Base/Entities/Attachment.cs
  class Attachment (line 3) | public class Attachment : AbstractDbEntity

FILE: Ticky.Base/Entities/Board.cs
  class Board (line 3) | public class Board : AbstractDbEntity, IDeletable
    method VerifyAccess (line 23) | public bool VerifyAccess(User user) =>

FILE: Ticky.Base/Entities/BoardMembership.cs
  class BoardMembership (line 3) | public class BoardMembership : AbstractDbEntity

FILE: Ticky.Base/Entities/Card.cs
  class Card (line 5) | public class Card : AbstractDbEntity, IOrderable, IDeletable, IAssignable
    method GetTotalTime (line 34) | public TimeSpan GetTotalTime() =>
    method CalculateNextRepeat (line 37) | public DateTime CalculateNextRepeat(DateTime from)

FILE: Ticky.Base/Entities/CardLink.cs
  class CardLink (line 3) | public class CardLink : AbstractDbEntity

FILE: Ticky.Base/Entities/Code.cs
  class Code (line 3) | public class Code : AbstractDbEntity

FILE: Ticky.Base/Entities/Column.cs
  class Column (line 3) | public class Column : AbstractDbEntity, IOrderable, IDeletable

FILE: Ticky.Base/Entities/Comment.cs
  class Comment (line 3) | public class Comment : AbstractDbEntity

FILE: Ticky.Base/Entities/Favorite.cs
  class Favorite (line 3) | public class Favorite : AbstractDbEntity

FILE: Ticky.Base/Entities/Label.cs
  class Label (line 3) | public class Label : AbstractDbEntity, IDeletable

FILE: Ticky.Base/Entities/LastVisit.cs
  class LastVisit (line 3) | public class LastVisit : AbstractDbEntity

FILE: Ticky.Base/Entities/Owned/RepeatInfo.cs
  class RepeatInfo (line 3) | [Owned]
    method GetRepeatString (line 20) | public string GetRepeatString() =>

FILE: Ticky.Base/Entities/Project.cs
  class Project (line 3) | public class Project : AbstractDbEntity, IDeletable

FILE: Ticky.Base/Entities/ProjectMembership.cs
  class ProjectMembership (line 3) | public class ProjectMembership : AbstractDbEntity

FILE: Ticky.Base/Entities/Reminder.cs
  class Reminder (line 3) | public class Reminder : AbstractDbEntity

FILE: Ticky.Base/Entities/Subtask.cs
  class Subtask (line 3) | public class Subtask : AbstractDbEntity, IOrderable, IAssignable

FILE: Ticky.Base/Entities/TimeRecord.cs
  class TimeRecord (line 3) | public class TimeRecord : AbstractDbEntity
    method GetTotalTime (line 17) | public TimeSpan GetTotalTime() =>

FILE: Ticky.Base/Entities/User.cs
  class User (line 3) | public class User : IdentityUser<int>, IDbEntry, IDeletable

FILE: Ticky.Base/Enums/CardPlacement.cs
  type CardPlacement (line 3) | public enum CardPlacement

FILE: Ticky.Base/Enums/CardPriority.cs
  type CardPriority (line 3) | public enum CardPriority

FILE: Ticky.Base/Enums/CodePurpose.cs
  type CodePurpose (line 3) | public enum CodePurpose

FILE: Ticky.Base/Enums/DeadlineColor.cs
  type DeadlineColor (line 3) | public enum DeadlineColor

FILE: Ticky.Base/Enums/ImportSource.cs
  type ImportSource (line 3) | public enum ImportSource

FILE: Ticky.Base/Enums/NotificationType.cs
  type NotificationType (line 3) | public enum NotificationType

FILE: Ticky.Base/Enums/OperationType.cs
  type OperationType (line 3) | public enum OperationType

FILE: Ticky.Base/Enums/OrderRule.cs
  type OrderRule (line 3) | public enum OrderRule

FILE: Ticky.Base/Enums/RepeatType.cs
  type RepeatType (line 3) | public enum RepeatType

FILE: Ticky.Base/Enums/TrelloArchivedHandlingType.cs
  type TrelloArchivedHandlingType (line 3) | public enum TrelloArchivedHandlingType

FILE: Ticky.Base/Models/BoardPreferencesModel.cs
  class BoardPreferencesModel (line 3) | public class BoardPreferencesModel

FILE: Ticky.Base/Models/CloneBoardModel.cs
  class CloneBoardModel (line 3) | public class CloneBoardModel

FILE: Ticky.Base/Models/CreateCardModel.cs
  class CreateCardModel (line 3) | public class CreateCardModel

FILE: Ticky.Base/Models/CredentialsModel.cs
  class CredentialsModel (line 3) | public class CredentialsModel

FILE: Ticky.Base/Models/DeadlineModel.cs
  class DeadlineModel (line 3) | public class DeadlineModel

FILE: Ticky.Base/Models/FilterCardsModel.cs
  class FilterCardsModel (line 3) | public class FilterCardsModel
    method IsAnyFilterApplied (line 19) | public bool IsAnyFilterApplied()
    method ClearFilters (line 28) | public void ClearFilters()

FILE: Ticky.Base/Models/ImportModel.cs
  class ImportModel (line 3) | public class ImportModel

FILE: Ticky.Base/Models/LabelModel.cs
  class LabelModel (line 3) | public class LabelModel

FILE: Ticky.Base/Models/LinkCardsModel.cs
  class LinkCardsModel (line 3) | public class LinkCardsModel

FILE: Ticky.Base/Models/ReminderModel.cs
  class ReminderModel (line 3) | public class ReminderModel

FILE: Ticky.Base/Models/RepeatCardModel.cs
  class RepeatCardModel (line 5) | public class RepeatCardModel
    method GetRelevantSelectedValue (line 55) | public string? GetRelevantSelectedValue() =>
    method SetRelevantSelectedValue (line 63) | public void SetRelevantSelectedValue(string? value)

FILE: Ticky.Base/Models/SnoozeCardModel.cs
  class SnoozeCardModel (line 3) | public class SnoozeCardModel

FILE: Ticky.Base/Models/SubtaskModel.cs
  class SubtaskModel (line 3) | public class SubtaskModel

FILE: Ticky.Base/Models/TimeRecordModel.cs
  class TimeRecordModel (line 3) | public class TimeRecordModel

FILE: Ticky.Base/Models/UserModel.cs
  class UserModel (line 3) | public class UserModel

FILE: Ticky.Base/Validation/IsValidTimeSpan.cs
  class IsValidTimeSpan (line 3) | public class IsValidTimeSpan() : ValidationAttribute
    method IsValid (line 7) | protected override ValidationResult? IsValid(object? value, Validation...

FILE: Ticky.Base/Validation/RequiredIf.cs
  class RequiredIfAttribute (line 3) | public class RequiredIfAttribute : ValidationAttribute
    method RequiredIfAttribute (line 8) | public RequiredIfAttribute(string propertyName, params object[] allowe...
    method FormatErrorMessage (line 14) | public override string FormatErrorMessage(string name)
    method IsValid (line 20) | protected override ValidationResult? IsValid(object? value, Validation...

FILE: Ticky.Internal/Data/DataContext.cs
  class DataContext (line 7) | public class DataContext
    method DataContext (line 30) | public DataContext(DbContextOptions<DataContext> options)
    method OnModelCreating (line 33) | protected override void OnModelCreating(ModelBuilder modelBuilder)

FILE: Ticky.Internal/Data/DataMigrator.cs
  class DataMigrator (line 3) | public class DataMigrator
    method Seed (line 5) | public static async Task Seed(IServiceProvider serviceProvider)

FILE: Ticky.Internal/Data/DataSeeder.cs
  class DataSeeder (line 7) | public class DataSeeder
    method Seed (line 9) | public static async Task Seed(IServiceProvider serviceProvider)

FILE: Ticky.Internal/Helpers/AttachmentHelper.cs
  class AttachmentHelper (line 3) | public static class AttachmentHelper
    method GetFileTypeFromAttachment (line 5) | public static string GetFileTypeFromAttachment(Attachment attachment)
    method GetImageNameFromAttachment (line 31) | public static string GetImageNameFromAttachment(Attachment attachment)

FILE: Ticky.Internal/Helpers/IndexHelper.cs
  class IndexHelper (line 3) | public static class IndexHelper
    method GetNextIndex (line 5) | public static int GetNextIndex<T>(this List<T> current)
    method FixIndices (line 16) | public static void FixIndices<T>(this List<T> current)
    method ChangeOrderOfItem (line 25) | public static void ChangeOrderOfItem<T>(this List<T> current, int curr...

FILE: Ticky.Internal/Helpers/StringHelper.cs
  class StringHelper (line 5) | public static class StringHelper
    method ToFriendlyName (line 7) | public static string ToFriendlyName(this string str)

FILE: Ticky.Internal/Helpers/TimeHelper.cs
  class TimeHelper (line 3) | public static class TimeHelper
    method ToElapsedString (line 5) | public static string ToElapsedString(this DateTime dateTime)
    method ToElapsedString (line 29) | public static string ToElapsedString(this TimeSpan timeSpan, bool cutO...
    method ToShortString (line 46) | public static string ToShortString(this DateTime dateTime) =>

FILE: Ticky.Internal/Migrations/20250523175138_Initial.Designer.cs
  class Initial (line 13) | [DbContext(typeof(DataContext))]
    method BuildTargetModel (line 18) | protected override void BuildTargetModel(ModelBuilder modelBuilder)

FILE: Ticky.Internal/Migrations/20250523175138_Initial.cs
  class Initial (line 10) | public partial class Initial : Migration
    method Up (line 13) | protected override void Up(MigrationBuilder migrationBuilder)
    method Down (line 875) | protected override void Down(MigrationBuilder migrationBuilder)

FILE: Ticky.Internal/Migrations/20250527093505_Favorites.Designer.cs
  class Favorites (line 14) | [DbContext(typeof(DataContext))]
    method BuildTargetModel (line 19) | protected override void BuildTargetModel(ModelBuilder modelBuilder)

FILE: Ticky.Internal/Migrations/20250527093505_Favorites.cs
  class Favorites (line 10) | public partial class Favorites : Migration
    method Up (line 13) | protected override void Up(MigrationBuilder migrationBuilder)
    method Down (line 248) | protected override void Down(MigrationBuilder migrationBuilder)

FILE: Ticky.Internal/Migrations/20250606115441_ForceCredentialsChange.Designer.cs
  class ForceCredentialsChange (line 14) | [DbContext(typeof(DataContext))]
    method BuildTargetModel (line 19) | protected override void BuildTargetModel(ModelBuilder modelBuilder)

FILE: Ticky.Internal/Migrations/20250606115441_ForceCredentialsChange.cs
  class ForceCredentialsChange (line 8) | public partial class ForceCredentialsChange : Migration
    method Up (line 11) | protected override void Up(MigrationBuilder migrationBuilder)
    method Down (line 22) | protected override void Down(MigrationBuilder migrationBuilder)

FILE: Ticky.Internal/Migrations/20250615181842_TextToNameCard.Designer.cs
  class TextToNameCard (line 14) | [DbContext(typeof(DataContext))]
    method BuildTargetModel (line 19) | protected override void BuildTargetModel(ModelBuilder modelBuilder)

FILE: Ticky.Internal/Migrations/20250615181842_TextToNameCard.cs
  class TextToNameCard (line 8) | public partial class TextToNameCard : Migration
    method Up (line 11) | protected override void Up(MigrationBuilder migrationBuilder)
    method Down (line 25) | protected override void Down(MigrationBuilder migrationBuilder)

FILE: Ticky.Internal/Migrations/20250617140549_SnoozeCards.Designer.cs
  class SnoozeCards (line 14) | [DbContext(typeof(DataContext))]
    method BuildTargetModel (line 19) | protected override void BuildTargetModel(ModelBuilder modelBuilder)

FILE: Ticky.Internal/Migrations/20250617140549_SnoozeCards.cs
  class SnoozeCards (line 9) | public partial class SnoozeCards : Migration
    method Up (line 12) | protected override void Up(MigrationBuilder migrationBuilder)
    method Down (line 22) | protected override void Down(MigrationBuilder migrationBuilder)

FILE: Ticky.Internal/Migrations/20250618090806_AtRemoval.Designer.cs
  class AtRemoval (line 14) | [DbContext(typeof(DataContext))]
    method BuildTargetModel (line 19) | protected override void BuildTargetModel(ModelBuilder modelBuilder)

FILE: Ticky.Internal/Migrations/20250618090806_AtRemoval.cs
  class AtRemoval (line 9) | public partial class AtRemoval : Migration
    method Up (line 12) | protected override void Up(MigrationBuilder migrationBuilder)
    method Down (line 20) | protected override void Down(MigrationBuilder migrationBuilder)

FILE: Ticky.Internal/Migrations/20250618114219_SelfAssign.Designer.cs
  class SelfAssign (line 14) | [DbContext(typeof(DataContext))]
    method BuildTargetModel (line 19) | protected override void BuildTargetModel(ModelBuilder modelBuilder)

FILE: Ticky.Internal/Migrations/20250618114219_SelfAssign.cs
  class SelfAssign (line 8) | public partial class SelfAssign : Migration
    method Up (line 11) | protected override void Up(MigrationBuilder migrationBuilder)
    method Down (line 22) | protected override void Down(MigrationBuilder migrationBuilder)

FILE: Ticky.Internal/Migrations/20250703124731_DisableBoardAnimations.Designer.cs
  class DisableBoardAnimations (line 14) | [DbContext(typeof(DataContext))]
    method BuildTargetModel (line 19) | protected override void BuildTargetModel(ModelBuilder modelBuilder)

FILE: Ticky.Internal/Migrations/20250703124731_DisableBoardAnimations.cs
  class DisableBoardAnimations (line 8) | public partial class DisableBoardAnimations : Migration
    method Up (line 11) | protected override void Up(MigrationBuilder migrationBuilder)
    method Down (line 22) | protected override void Down(MigrationBuilder migrationBuilder)

FILE: Ticky.Internal/Migrations/20250709130027_RepeatingCards.Designer.cs
  class RepeatingCards (line 14) | [DbContext(typeof(DataContext))]
    method BuildTargetModel (line 19) | protected override void BuildTargetModel(ModelBuilder modelBuilder)

FILE: Ticky.Internal/Migrations/20250709130027_RepeatingCards.cs
  class RepeatingCards (line 9) | public partial class RepeatingCards : Migration
    method Up (line 12) | protected override void Up(MigrationBuilder migrationBuilder)
    method Down (line 59) | protected override void Down(MigrationBuilder migrationBuilder)

FILE: Ticky.Internal/Migrations/20250826115734_NewCardPlacement.Designer.cs
  class NewCardPlacement (line 14) | [DbContext(typeof(DataContext))]
    method BuildTargetModel (line 19) | protected override void BuildTargetModel(ModelBuilder modelBuilder)

FILE: Ticky.Internal/Migrations/20250826115734_NewCardPlacement.cs
  class NewCardPlacement (line 8) | public partial class NewCardPlacement : Migration
    method Up (line 11) | protected override void Up(MigrationBuilder migrationBuilder)
    method Down (line 22) | protected override void Down(MigrationBuilder migrationBuilder)

FILE: Ticky.Internal/Migrations/20251005111419_BlockToFlagged.Designer.cs
  class BlockToFlagged (line 14) | [DbContext(typeof(DataContext))]
    method BuildTargetModel (line 19) | protected override void BuildTargetModel(ModelBuilder modelBuilder)

FILE: Ticky.Internal/Migrations/20251005111419_BlockToFlagged.cs
  class BlockToFlagged (line 8) | public partial class BlockToFlagged : Migration
    method Up (line 11) | protected override void Up(MigrationBuilder migrationBuilder)
    method Down (line 20) | protected override void Down(MigrationBuilder migrationBuilder)

FILE: Ticky.Internal/Migrations/20251005174743_SubtaskAssignee.Designer.cs
  class SubtaskAssignee (line 14) | [DbContext(typeof(DataContext))]
    method BuildTargetModel (line 19) | protected override void BuildTargetModel(ModelBuilder modelBuilder)

FILE: Ticky.Internal/Migrations/20251005174743_SubtaskAssignee.cs
  class SubtaskAssignee (line 8) | public partial class SubtaskAssignee : Migration
    method Up (line 11) | protected override void Up(MigrationBuilder migrationBuilder)
    method Down (line 45) | protected override void Down(MigrationBuilder migrationBuilder)

FILE: Ticky.Internal/Migrations/20251011134337_Information.Designer.cs
  class Information (line 14) | [DbContext(typeof(DataContext))]
    method BuildTargetModel (line 19) | protected override void BuildTargetModel(ModelBuilder modelBuilder)

FILE: Ticky.Internal/Migrations/20251011134337_Information.cs
  class Information (line 9) | public partial class Information : Migration
    method Up (line 12) | protected override void Up(MigrationBuilder migrationBuilder)
    method Down (line 28) | protected override void Down(MigrationBuilder migrationBuilder)

FILE: Ticky.Internal/Migrations/DataContextModelSnapshot.cs
  class DataContextModelSnapshot (line 13) | [DbContext(typeof(DataContext))]
    method BuildModel (line 16) | protected override void BuildModel(ModelBuilder modelBuilder)

FILE: Ticky.Internal/Services/AvatarService.cs
  class AvatarService (line 3) | public class AvatarService
    method AvatarService (line 7) | public AvatarService(HttpClient httpClient)
    method FetchAvatarAsync (line 12) | public async Task<string> FetchAvatarAsync(string name)

FILE: Ticky.Internal/Services/CardNumberingService.cs
  class CardNumberingService (line 3) | public class CardNumberingService
    method CardNumberingService (line 7) | public CardNumberingService(IDbContextFactory<DataContext> dbContextFa...
    method GetNextNumberAsync (line 12) | public async Task<int> GetNextNumberAsync(int boardId)

FILE: Ticky.Internal/Services/CodeService.cs
  class CodeService (line 5) | public class CodeService
    method CodeService (line 9) | public CodeService(DataContext dataContext)
    method CreateCodeAsync (line 14) | public async Task<Code> CreateCodeAsync(User account, CodePurpose code...
    method GetUnusedCodeAsync (line 31) | private async Task<string> GetUnusedCodeAsync()

FILE: Ticky.Internal/Services/EmailService.cs
  class EmailService (line 7) | public class EmailService(IEmailService emailService, ILogger<EmailServi...
    method SendVerificationEmailAsync (line 27) | public async Task SendVerificationEmailAsync(string emailAddress, Code...
    method SendReminderEmailAsync (line 39) | public async Task SendReminderEmailAsync(string emailAddress, Reminder...
    method SendDeadlineReminderEmailAsync (line 100) | public async Task SendDeadlineReminderEmailAsync(string emailAddress, ...
    method SendForgottenPasswordCodeEmailAsync (line 150) | public async Task SendForgottenPasswordCodeEmailAsync(string emailAddr...
    method SendDevityEmailAsync (line 162) | private async Task SendDevityEmailAsync(DevityEmail devityEmail)

FILE: Ticky.Internal/Services/Hosted/AbstractHostedService.cs
  class AbstractHostedService (line 5) | public abstract class AbstractHostedService<T> : IHostedService
    method AbstractHostedService (line 14) | protected AbstractHostedService(
    method StartAsync (line 33) | public Task StartAsync(CancellationToken cancellationToken)
    method StopAsync (line 44) | public Task StopAsync(CancellationToken cancellationToken)
    method DoWork (line 56) | private async void DoWork(object? state)
    method GetNextRunDateTime (line 77) | private string GetNextRunDateTime(DateTime startedAt)
    method ChangeTimer (line 88) | protected void ChangeTimer(TimeSpan untilStart, TimeSpan frequency)
    method OnStart (line 96) | protected virtual void OnStart() { }
    method OnStop (line 98) | protected virtual void OnStop() { }
    method OnRun (line 100) | protected virtual Task OnRun()
    method Dispose (line 105) | public void Dispose()

FILE: Ticky.Internal/Services/Hosted/CleanupHostedService.cs
  class CleanupHostedService (line 3) | public class CleanupHostedService(IServiceScopeFactory serviceScopeFactory)
    method OnRun (line 6) | protected override async Task OnRun()
    method CleanCodes (line 13) | private async Task CleanCodes()
    method DeleteUnusedProfilePictures (line 46) | private async Task DeleteUnusedProfilePictures()
    method DeleteUnlinkedAttachments (line 80) | private async Task DeleteUnlinkedAttachments()

FILE: Ticky.Internal/Services/Hosted/ReminderHostedService.cs
  class ReminderHostedService (line 3) | public class ReminderHostedService(IServiceScopeFactory serviceScopeFact...
    method OnRun (line 8) | protected override async Task OnRun()

FILE: Ticky.Internal/Services/Hosted/RepeatHostedService.cs
  class RepeatHostedService (line 3) | public class RepeatHostedService(IServiceScopeFactory serviceScopeFactor...
    method OnRun (line 8) | protected override async Task OnRun()

FILE: Ticky.Internal/Services/Hosted/SnoozeHostedService.cs
  class SnoozeHostedService (line 3) | public class SnoozeHostedService(IServiceScopeFactory serviceScopeFactor...
    method OnRun (line 8) | protected override async Task OnRun()

FILE: Ticky.Internal/Services/InformationService.cs
  class InformationService (line 5) | public class InformationService
    method InformationService (line 9) | public InformationService()

FILE: Ticky.Internal/Services/SearchService.cs
  class SearchService (line 3) | public class SearchService
    method SearchService (line 7) | public SearchService(IDbContextFactory<DataContext> dbContextFactory)
    method SearchAsync (line 12) | public async Task<List<Card>> SearchAsync(

FILE: Ticky.Internal/Services/TrelloImportService.cs
  class TrelloImportService (line 6) | public class TrelloImportService
    method TrelloImportService (line 10) | public TrelloImportService(IDbContextFactory<DataContext> dbContextFac...
    method ImportTrelloBoardAsync (line 15) | public async Task ImportTrelloBoardAsync(
    method GetLabelColorsFromName (line 202) | private (Color textColor, Color backgroundColor) GetLabelColorsFromNam...

FILE: Ticky.Units/CardTest.cs
  class CardTest (line 3) | public class CardTest
    method Setup (line 7) | [SetUp]
    method CalculateNextRepeat_Daily_SameDay (line 27) | [Test]
    method CalculateNextRepeat_Daily_NextDay (line 44) | [Test]
    method CalculateNextRepeat_WeekDays_SameDay (line 61) | [Test]
    method CalculateNextRepeat_WeekDays_NextDay (line 79) | [Test]
    method CalculateNextRepeat_MonthDayNumber_SameDay (line 97) | [Test]
    method CalculateNextRepeat_MonthDayNumber_NextDay (line 115) | [Test]
    method CalculateNextRepeat_EveryXthDay_TimeBefore (line 133) | [Test]
    method CalculateNextRepeat_EveryXthWeek_TimeBefore (line 151) | [Test]
    method CalculateNextRepeat_EveryXthMonth_TimeBefore (line 169) | [Test]
    method CalculateNextRepeat_EveryXthYear_TimeBefore (line 187) | [Test]
    method CalculateNextRepeat_EveryXthDay_TimeAfter (line 205) | [Test]
    method CalculateNextRepeat_EveryXthWeek_TimeAfter (line 223) | [Test]
    method CalculateNextRepeat_EveryXthMonth_TimeAfter (line 241) | [Test]
    method CalculateNextRepeat_EveryXthYear_TimeAfter (line 259) | [Test]

FILE: Ticky.Units/Constants.cs
  class Constants (line 3) | public static class Constants

FILE: Ticky.Web/Components/Elements/Sortable/SortableList.razor.cs
  class SortableList (line 8) | public partial class SortableList<T>
    method Dispose (line 78) | public void Dispose() => selfReference?.Dispose();
    method OnRemoveJS (line 80) | [JSInvokable]
    method OnUpdateJS (line 93) | [JSInvokable]
    method OnExceededLimitJS (line 99) | [JSInvokable]
    method OnAfterRenderAsync (line 102) | protected override async Task OnAfterRenderAsync(bool firstRender)

FILE: Ticky.Web/Components/Elements/Sortable/SortableList.razor.js
  function init (line 1) | function init(id, group, pull, put, sort, handle, filter, component, for...

FILE: Ticky.Web/Components/Pages/Auth/ChangePassword.cshtml.cs
  class ChangePasswordModel (line 3) | public class ChangePasswordModel : PageModel
    method ChangePasswordModel (line 11) | public ChangePasswordModel(DataContext dataContext, UserManager<User> ...
    method OnGet (line 17) | public IActionResult OnGet()
    method OnPostAsync (line 22) | public async Task<IActionResult> OnPostAsync()
    class InputModel (line 51) | public class InputModel

FILE: Ticky.Web/Components/Pages/Auth/ConfirmMail.cshtml.cs
  class ConfirmMailModel (line 3) | public class ConfirmMailModel : PageModel
    method ConfirmMailModel (line 11) | public ConfirmMailModel(DataContext dataContext, SignInManager<User> s...
    method OnGet (line 17) | public IActionResult OnGet(string email)
    method OnPostAsync (line 23) | public async Task<IActionResult> OnPostAsync()
    class InputModel (line 57) | public class InputModel

FILE: Ticky.Web/Components/Pages/Auth/ForgotPassword.cshtml.cs
  class ForgotPasswordModel (line 3) | public class ForgotPasswordModel : PageModel
    method ForgotPasswordModel (line 12) | public ForgotPasswordModel(
    method OnGet (line 23) | public IActionResult OnGet()
    method OnPostAsync (line 28) | public async Task<IActionResult> OnPostAsync()
    class InputModel (line 54) | public class InputModel

FILE: Ticky.Web/Components/Pages/Auth/Login.cshtml.cs
  class LoginModel (line 6) | public class LoginModel : PageModel
    method LoginModel (line 14) | public LoginModel(SignInManager<User> signInManager)
    method OnGet (line 19) | public IActionResult OnGet(string? returnUrl = null)
    method OnPostAsync (line 25) | public async Task<IActionResult> OnPostAsync(string? returnUrl = null)
    class InputModel (line 48) | public class InputModel

FILE: Ticky.Web/Components/Pages/Auth/Logout.cshtml.cs
  class LogoutModel (line 6) | public class LogoutModel : PageModel
    method OnGet (line 8) | public void OnGet() { }

FILE: Ticky.Web/Components/Pages/Auth/MailConfirmed.cshtml.cs
  class MailConfirmedModel (line 3) | public class MailConfirmedModel : PageModel
    method OnGet (line 5) | public IActionResult OnGet()

FILE: Ticky.Web/Components/Pages/Auth/PasswordChanged.cshtml.cs
  class PasswordChangedModel (line 3) | public class PasswordChangedModel : PageModel
    method OnGet (line 5) | public IActionResult OnGet()

FILE: Ticky.Web/Components/Pages/Auth/Register.cshtml.cs
  class RegisterModel (line 3) | public class RegisterModel : PageModel
    method RegisterModel (line 14) | public RegisterModel(
    method OnGet (line 29) | public IActionResult OnGet()
    method OnPostAsync (line 34) | public async Task<IActionResult> OnPostAsync()
    class InputModel (line 83) | public class InputModel

FILE: Ticky.Web/Controllers/AttachmentsController.cs
  class AttachmentsController (line 5) | [Authorize]
    method AttachmentsController (line 13) | public AttachmentsController(
    method Download (line 22) | [HttpGet("download/{*fileName}")]
    method Upload (line 61) | [HttpPost("upload")]

FILE: Ticky.Web/Controllers/HealthController.cs
  class HealthController (line 3) | [Route("api/[controller]")]
    method Get (line 7) | [HttpGet]

FILE: Ticky.Web/Hubs/UpdateHub.cs
  class UpdateHub (line 3) | [Authorize]
    method UpdateHub (line 8) | public UpdateHub(IDbContextFactory<DataContext> dbContextFactory)
    method IsUserInBoard (line 13) | private async Task<bool> IsUserInBoard(int boardId)
    method BoardChange (line 41) | public async Task BoardChange(int boardId, Guid pageId)
    method SubscribeBoard (line 46) | public async Task SubscribeBoard(int boardId)

FILE: Ticky.Web/wwwroot/js/darkTheme.js
  function switchTheme (line 14) | function switchTheme() {

FILE: Ticky.Web/wwwroot/js/downloadFile.js
  function triggerFileDownload (line 1) | function triggerFileDownload(fileName, url) {
  function downloadJsonText (line 9) | function downloadJsonText(fileName, jsonText) {

FILE: Ticky.Web/wwwroot/js/focusTrap.js
  function trapFocusInElement (line 2) | function trapFocusInElement(element) {

FILE: Ticky.Web/wwwroot/js/main.js
  constant OPEN_OFFSET (line 1) | const OPEN_OFFSET = 5;
  function positionDropdown (line 3) | function positionDropdown(dropdownElement, left, top, width = null) {
  function triggerConfetti (line 55) | function triggerConfetti(x, y) {
  function resetLastClosed (line 89) | function resetLastClosed() {
  function closeDropdowns (line 96) | function closeDropdowns() {
  function openDropDownOnElementId (line 116) | function openDropDownOnElementId(dropdownElement, triggerElementId) {
  function openDropDownOnElementPosition (line 120) | function openDropDownOnElementPosition(dropdownElement, triggerElement) {
  function onDropdownTriggerClicked (line 149) | function onDropdownTriggerClicked(clientX, clientY, dropdownElement) {
  function copyText (line 164) | async function copyText(text) {
  function attachCardPasteHandler (line 203) | function attachCardPasteHandler(dotNetReference, targetCardId) {
  function detachCardPasteHandler (line 240) | function detachCardPasteHandler() {
  function getElementScrollTop (line 246) | function getElementScrollTop(element) {
  function setElementScrollTop (line 255) | function setElementScrollTop(element, value) {

FILE: Ticky.Web/wwwroot/js/screenHeight.js
  function setViewportHeight (line 1) | function setViewportHeight() {
Condensed preview — 241 files, each showing path, character count, and a content snippet. Download the .json file or copy for the full structured content (1,399K chars).
[
  {
    "path": ".gitattributes",
    "chars": 2518,
    "preview": "###############################################################################\n# Set default behavior to automatically "
  },
  {
    "path": ".github/FUNDING.yml",
    "chars": 814,
    "preview": "# These are supported funding model platforms\n\ngithub: [dkorecko]\npatreon: # Replace with a single Patreon username\nopen"
  },
  {
    "path": ".github/ISSUE_TEMPLATE/bug_report.md",
    "chars": 380,
    "preview": "---\nname: Bug report\nabout: Create a report to help us improve\ntitle: ''\nlabels: bug\nassignees: ''\n\n---\n\n**Describe the "
  },
  {
    "path": ".github/ISSUE_TEMPLATE/feature_request.md",
    "chars": 359,
    "preview": "---\nname: Feature request\nabout: Suggest an idea for this project\ntitle: \"[Feature]\"\nlabels: enhancement\nassignees: ''\n\n"
  },
  {
    "path": ".github/workflows/build.yml",
    "chars": 563,
    "preview": "name: Build app\n\non:\n  pull_request:\n\njobs:\n  build:\n    runs-on: ubuntu-latest\n    permissions:\n      contents: read\n  "
  },
  {
    "path": ".github/workflows/docker-release.yml",
    "chars": 1275,
    "preview": "name: Build and Publish Docker image to GHCR\n\non:\n  release:\n    types: [published]\n\njobs:\n  build-and-push:\n    runs-on"
  },
  {
    "path": ".github/workflows/docker-unstable.yml",
    "chars": 2536,
    "preview": "name: Build and publish unstable Docker image\n\non:\n  push:\n    branches:\n      - main\n  workflow_dispatch:\n    inputs:\n "
  },
  {
    "path": ".gitignore",
    "chars": 6256,
    "preview": "## Ignore Visual Studio temporary files, build results, and\n## files generated by popular Visual Studio add-ons.\n##\n## G"
  },
  {
    "path": ".vscode/settings.json",
    "chars": 43,
    "preview": "{\n  \"kiroAgent.configureMCP\": \"Disabled\"\n}\n"
  },
  {
    "path": "CONTRIBUTING.md",
    "chars": 3088,
    "preview": "# Contributing to Ticky\n\nThank you for considering contributing to Ticky! This document outlines the process for contrib"
  },
  {
    "path": "Dockerfile",
    "chars": 1352,
    "preview": "# Declare ARGs for build platform and target architecture for clarity\nARG BUILDPLATFORM\n\nFROM mcr.microsoft.com/dotnet/a"
  },
  {
    "path": "LICENSE.txt",
    "chars": 1069,
    "preview": "MIT License\n\nCopyright (c) [year] [fullname]\n\nPermission is hereby granted, free of charge, to any person obtaining a co"
  },
  {
    "path": "README.md",
    "chars": 11678,
    "preview": "<p align=\"center\">\n    <img src=\"images/ticky_logo.png\" width=\"300\" title=\"Ticky logo\">\n</p>\n\n# Ticky\n\nTicky is a modern"
  },
  {
    "path": "Ticky.Base/Constants.cs",
    "chars": 4876,
    "preview": "namespace Ticky.Base\n{\n    public static class Constants\n    {\n        public static bool SMTP_ENABLED = true;\n        p"
  },
  {
    "path": "Ticky.Base/Converters/ColorToInt32Converter.cs",
    "chars": 259,
    "preview": "using Microsoft.EntityFrameworkCore.Storage.ValueConversion;\n\nnamespace Ticky.Base.Converters;\n\npublic class ColorToInt3"
  },
  {
    "path": "Ticky.Base/Converters/StringToTimeSpanConverter.cs",
    "chars": 1225,
    "preview": "namespace Ticky.Base.Converters;\n\npublic static class StringToTimeSpanConverter\n{\n    public static TimeSpan ConvertToTi"
  },
  {
    "path": "Ticky.Base/DTOs/InformationDTO.cs",
    "chars": 199,
    "preview": "namespace Ticky.Base.DTOs;\n\npublic class InformationDTO\n{\n    public required string Title { get; set; }\n\n    public req"
  },
  {
    "path": "Ticky.Base/DTOs/Notification.cs",
    "chars": 222,
    "preview": "namespace Ticky.Base.DTOs;\n\npublic record Notification(string text, NotificationType type = NotificationType.Success)\n{\n"
  },
  {
    "path": "Ticky.Base/DTOs/Trello/TrelloCardDTO.cs",
    "chars": 1206,
    "preview": "namespace Ticky.Base.DTOs.Trello;\n\npublic class TrelloCardDTO\n{\n    [JsonPropertyName(\"id\")]\n    public required string "
  },
  {
    "path": "Ticky.Base/DTOs/Trello/TrelloCheckItemDTO.cs",
    "chars": 394,
    "preview": "namespace Ticky.Base.DTOs.Trello;\n\npublic class TrelloCheckItemDTO\n{\n    [JsonPropertyName(\"id\")]\n    public required st"
  },
  {
    "path": "Ticky.Base/DTOs/Trello/TrelloChecklistDTO.cs",
    "chars": 412,
    "preview": "namespace Ticky.Base.DTOs.Trello;\n\npublic class TrelloChecklistDTO\n{\n    [JsonPropertyName(\"id\")]\n    public required st"
  },
  {
    "path": "Ticky.Base/DTOs/Trello/TrelloLabelDTO.cs",
    "chars": 336,
    "preview": "namespace Ticky.Base.DTOs.Trello\n{\n    public class TrelloLabelDTO\n    {\n        [JsonPropertyName(\"id\")]\n        public"
  },
  {
    "path": "Ticky.Base/DTOs/Trello/TrelloListDTO.cs",
    "chars": 365,
    "preview": "namespace Ticky.Base.DTOs.Trello;\n\npublic class TrelloListDTO\n{\n    [JsonPropertyName(\"id\")]\n    public required string "
  },
  {
    "path": "Ticky.Base/DTOs/Trello/TrelloMemberDTO.cs",
    "chars": 313,
    "preview": "namespace Ticky.Base.DTOs.Trello;\n\npublic class TrelloMemberDTO\n{\n    [JsonPropertyName(\"id\")]\n    public required strin"
  },
  {
    "path": "Ticky.Base/DTOs/Trello/TrelloPreferencesDTO.cs",
    "chars": 156,
    "preview": "namespace Ticky.Base.DTOs.Trello;\n\npublic class TrelloPreferencesDTO\n{\n    [JsonPropertyName(\"selfJoin\")]\n    public req"
  },
  {
    "path": "Ticky.Base/DTOs/TrelloImportDTO.cs",
    "chars": 925,
    "preview": "using Ticky.Base.DTOs.Trello;\n\nnamespace Ticky.Base.DTOs;\n\npublic class TrelloImportDTO\n{\n    [JsonPropertyName(\"name\")]"
  },
  {
    "path": "Ticky.Base/Entities/Abstractions/AbstractDbEntity.cs",
    "chars": 193,
    "preview": "namespace Ticky.Base.Entities.Abstractions;\n\npublic abstract class AbstractDbEntity : IDbEntry\n{\n    public int Id { get"
  },
  {
    "path": "Ticky.Base/Entities/Abstractions/IAssignable.cs",
    "chars": 119,
    "preview": "namespace Ticky.Base.Entities.Abstractions;\n\npublic interface IAssignable\n{\n    public List<User> Assignees { get; }\n}\n"
  },
  {
    "path": "Ticky.Base/Entities/Abstractions/IDbEntry.cs",
    "chars": 147,
    "preview": "namespace Ticky.Base.Entities.Abstractions;\n\npublic interface IDbEntry\n{\n    public int Id { get; set; }\n\n    public Dat"
  },
  {
    "path": "Ticky.Base/Entities/Abstractions/IDeletable.cs",
    "chars": 120,
    "preview": "namespace Ticky.Base.Entities.Abstractions;\n\npublic interface IDeletable : IDbEntry\n{\n    public string Name { get; }\n}\n"
  },
  {
    "path": "Ticky.Base/Entities/Abstractions/IOrderable.cs",
    "chars": 112,
    "preview": "namespace Ticky.Base.Entities.Abstractions;\n\npublic interface IOrderable\n{\n    public int Index { get; set; }\n}\n"
  },
  {
    "path": "Ticky.Base/Entities/Activity.cs",
    "chars": 317,
    "preview": "namespace Ticky.Base.Entities;\n\npublic class Activity : AbstractDbEntity\n{\n    public required int UserId { get; set; }\n"
  },
  {
    "path": "Ticky.Base/Entities/Attachment.cs",
    "chars": 283,
    "preview": "namespace Ticky.Base.Entities;\n\npublic class Attachment : AbstractDbEntity\n{\n    public required string FileName { get; "
  },
  {
    "path": "Ticky.Base/Entities/Board.cs",
    "chars": 1088,
    "preview": "namespace Ticky.Base.Entities;\n\npublic class Board : AbstractDbEntity, IDeletable\n{\n    [Required(AllowEmptyStrings = fa"
  },
  {
    "path": "Ticky.Base/Entities/BoardMembership.cs",
    "chars": 382,
    "preview": "namespace Ticky.Base.Entities;\n\npublic class BoardMembership : AbstractDbEntity\n{\n    public required int UserId { get; "
  },
  {
    "path": "Ticky.Base/Entities/Card.cs",
    "chars": 3467,
    "preview": "using Ticky.Base.Entities.Owned;\n\nnamespace Ticky.Base.Entities;\n\npublic class Card : AbstractDbEntity, IOrderable, IDel"
  },
  {
    "path": "Ticky.Base/Entities/CardLink.cs",
    "chars": 337,
    "preview": "namespace Ticky.Base.Entities;\n\npublic class CardLink : AbstractDbEntity\n{\n    public required int CardOneId { get; set;"
  },
  {
    "path": "Ticky.Base/Entities/Code.cs",
    "chars": 281,
    "preview": "namespace Ticky.Base.Entities;\n\npublic class Code : AbstractDbEntity\n{\n    public required string Value { get; set; }\n\n "
  },
  {
    "path": "Ticky.Base/Entities/Column.cs",
    "chars": 888,
    "preview": "namespace Ticky.Base.Entities;\n\npublic class Column : AbstractDbEntity, IOrderable, IDeletable\n{\n    public required str"
  },
  {
    "path": "Ticky.Base/Entities/Comment.cs",
    "chars": 326,
    "preview": "namespace Ticky.Base.Entities;\n\npublic class Comment : AbstractDbEntity\n{\n    public required string Text { get; set; }\n"
  },
  {
    "path": "Ticky.Base/Entities/Favorite.cs",
    "chars": 277,
    "preview": "namespace Ticky.Base.Entities;\n\npublic class Favorite : AbstractDbEntity\n{\n    public required int UserId { get; set; }\n"
  },
  {
    "path": "Ticky.Base/Entities/Label.cs",
    "chars": 439,
    "preview": "namespace Ticky.Base.Entities\n{\n    public class Label : AbstractDbEntity, IDeletable\n    {\n        public required stri"
  },
  {
    "path": "Ticky.Base/Entities/LastVisit.cs",
    "chars": 330,
    "preview": "namespace Ticky.Base.Entities;\n\npublic class LastVisit : AbstractDbEntity\n{\n    public required int BoardId { get; set; "
  },
  {
    "path": "Ticky.Base/Entities/Owned/RepeatInfo.cs",
    "chars": 1044,
    "preview": "namespace Ticky.Base.Entities.Owned;\n\n[Owned]\npublic class RepeatInfo\n{\n    public required RepeatType Type { get; set; "
  },
  {
    "path": "Ticky.Base/Entities/Project.cs",
    "chars": 311,
    "preview": "namespace Ticky.Base.Entities;\n\npublic class Project : AbstractDbEntity, IDeletable\n{\n    [Required(AllowEmptyStrings = "
  },
  {
    "path": "Ticky.Base/Entities/ProjectMembership.cs",
    "chars": 390,
    "preview": "namespace Ticky.Base.Entities;\n\npublic class ProjectMembership : AbstractDbEntity\n{\n    public required int UserId { get"
  },
  {
    "path": "Ticky.Base/Entities/Reminder.cs",
    "chars": 220,
    "preview": "namespace Ticky.Base.Entities;\n\npublic class Reminder : AbstractDbEntity\n{\n    public required DateTime At { get; set; }"
  },
  {
    "path": "Ticky.Base/Entities/Subtask.cs",
    "chars": 388,
    "preview": "namespace Ticky.Base.Entities;\n\npublic class Subtask : AbstractDbEntity, IOrderable, IAssignable\n{\n    public required i"
  },
  {
    "path": "Ticky.Base/Entities/TimeRecord.cs",
    "chars": 460,
    "preview": "namespace Ticky.Base.Entities;\n\npublic class TimeRecord : AbstractDbEntity\n{\n    public required int CardId { get; set; "
  },
  {
    "path": "Ticky.Base/Entities/User.cs",
    "chars": 1508,
    "preview": "namespace Ticky.Base.Entities;\n\npublic class User : IdentityUser<int>, IDbEntry, IDeletable\n{\n    [NotMapped]\n    public"
  },
  {
    "path": "Ticky.Base/Enums/CardPlacement.cs",
    "chars": 87,
    "preview": "namespace Ticky.Base.Enums;\n\npublic enum CardPlacement\n{\n    Top = 0,\n    Bottom = 1\n}\n"
  },
  {
    "path": "Ticky.Base/Enums/CardPriority.cs",
    "chars": 105,
    "preview": "namespace Ticky.Base.Enums;\n\npublic enum CardPriority\n{\n    Normal,\n    Medium,\n    High,\n    Critical\n}\n"
  },
  {
    "path": "Ticky.Base/Enums/CodePurpose.cs",
    "chars": 96,
    "preview": "namespace Ticky.Base.Enums;\n\npublic enum CodePurpose\n{\n    NewAccount,\n    ForgottenPassword\n}\n"
  },
  {
    "path": "Ticky.Base/Enums/DeadlineColor.cs",
    "chars": 103,
    "preview": "namespace Ticky.Base.Enums;\n\npublic enum DeadlineColor\n{\n    Default,\n    Yellow,\n    Red,\n    Green\n}\n"
  },
  {
    "path": "Ticky.Base/Enums/ImportSource.cs",
    "chars": 69,
    "preview": "namespace Ticky.Base.Enums;\n\npublic enum ImportSource\n{\n    Trello\n}\n"
  },
  {
    "path": "Ticky.Base/Enums/ImportType.cs",
    "chars": 0,
    "preview": ""
  },
  {
    "path": "Ticky.Base/Enums/NotificationType.cs",
    "chars": 84,
    "preview": "namespace Ticky.Base.Enums;\n\npublic enum NotificationType\n{\n    Success,\n    Fail\n}\n"
  },
  {
    "path": "Ticky.Base/Enums/OperationType.cs",
    "chars": 264,
    "preview": "namespace Ticky.Base.Enums;\n\npublic enum OperationType\n{\n    [Display(Name = \"added\")]\n    Added,\n\n    [Display(Name = \""
  },
  {
    "path": "Ticky.Base/Enums/OrderRule.cs",
    "chars": 565,
    "preview": "namespace Ticky.Base.Enums;\n\npublic enum OrderRule\n{\n    [Display(Name = \"No automatic ordering\")]\n    None = 0,\n\n    [D"
  },
  {
    "path": "Ticky.Base/Enums/RepeatType.cs",
    "chars": 648,
    "preview": "namespace Ticky.Base.Enums;\n\npublic enum RepeatType\n{\n    [Display(Name = \"Daily\")]\n    Daily = 1,\n\n    [Display(Name = "
  },
  {
    "path": "Ticky.Base/Enums/TrelloArchivedHandlingType.cs",
    "chars": 183,
    "preview": "namespace Ticky.Base.Enums;\n\npublic enum TrelloArchivedHandlingType\n{\n    [Display(Name = \"Do not add to the board\")]\n  "
  },
  {
    "path": "Ticky.Base/GlobalUsings.cs",
    "chars": 485,
    "preview": "global using Devity.Extensions;\nglobal using System.ComponentModel.DataAnnotations;\nglobal using System.ComponentModel.D"
  },
  {
    "path": "Ticky.Base/Models/BoardPreferencesModel.cs",
    "chars": 118,
    "preview": "namespace Ticky.Base.Models;\n\npublic class BoardPreferencesModel\n{\n    public bool ShowStats { get; set; } = false;\n}\n"
  },
  {
    "path": "Ticky.Base/Models/CloneBoardModel.cs",
    "chars": 429,
    "preview": "namespace Ticky.Base.Models;\n\npublic class CloneBoardModel\n{\n    [Display(Name = \"Target project\")]\n    [Required]\n    p"
  },
  {
    "path": "Ticky.Base/Models/CreateCardModel.cs",
    "chars": 116,
    "preview": "namespace Ticky.Base.Models;\n\npublic class CreateCardModel\n{\n    public string Text { get; set; } = string.Empty;\n}\n"
  },
  {
    "path": "Ticky.Base/Models/CredentialsModel.cs",
    "chars": 872,
    "preview": "namespace Ticky.Base.Models;\n\npublic class CredentialsModel\n{\n    [Display(Name = \"Current e-mail address (username)\")]\n"
  },
  {
    "path": "Ticky.Base/Models/DeadlineModel.cs",
    "chars": 419,
    "preview": "namespace Ticky.Base.Models;\n\npublic class DeadlineModel\n{\n    [Required(AllowEmptyStrings = false)]\n    [DataType(DataT"
  },
  {
    "path": "Ticky.Base/Models/FilterCardsModel.cs",
    "chars": 878,
    "preview": "namespace Ticky.Base.Models;\n\npublic class FilterCardsModel\n{\n    public string Text { get; set; } = string.Empty;\n\n    "
  },
  {
    "path": "Ticky.Base/Models/ImportModel.cs",
    "chars": 708,
    "preview": "namespace Ticky.Base.Models;\n\npublic class ImportModel\n{\n    public ImportSource Source { get; set; } = ImportSource.Tre"
  },
  {
    "path": "Ticky.Base/Models/LabelModel.cs",
    "chars": 441,
    "preview": "namespace Ticky.Base.Models;\n\npublic class LabelModel\n{\n    [Required(AllowEmptyStrings = false)]\n    [Display(Name = \""
  },
  {
    "path": "Ticky.Base/Models/LinkCardsModel.cs",
    "chars": 186,
    "preview": "namespace Ticky.Base.Models;\n\npublic class LinkCardsModel\n{\n    [Required(AllowEmptyStrings = false)]\n    [Display(Name "
  },
  {
    "path": "Ticky.Base/Models/ReminderModel.cs",
    "chars": 490,
    "preview": "namespace Ticky.Base.Models;\n\npublic class ReminderModel\n{\n    [Required(AllowEmptyStrings = false)]\n    [DataType(DataT"
  },
  {
    "path": "Ticky.Base/Models/RepeatCardModel.cs",
    "chars": 2358,
    "preview": "using System.ComponentModel;\n\nnamespace Ticky.Base.Models;\n\npublic class RepeatCardModel\n{\n    public RepeatType Type { "
  },
  {
    "path": "Ticky.Base/Models/SnoozeCardModel.cs",
    "chars": 421,
    "preview": "namespace Ticky.Base.Models;\n\npublic class SnoozeCardModel\n{\n    [Required(AllowEmptyStrings = false)]\n    [DataType(Dat"
  },
  {
    "path": "Ticky.Base/Models/SubtaskModel.cs",
    "chars": 193,
    "preview": "namespace Ticky.Base.Models;\n\npublic class SubtaskModel\n{\n    [Required(AllowEmptyStrings = false)]\n    [Display(Name ="
  },
  {
    "path": "Ticky.Base/Models/TimeRecordModel.cs",
    "chars": 226,
    "preview": "namespace Ticky.Base.Models;\n\npublic class TimeRecordModel\n{\n    [Display(Name = \"Spent time (0h 0m 0s)\")]\n    [Required"
  },
  {
    "path": "Ticky.Base/Models/UserModel.cs",
    "chars": 619,
    "preview": "namespace Ticky.Base.Models;\n\npublic class UserModel\n{\n    [Display(Name = \"Full name\")]\n    [Required]\n    public strin"
  },
  {
    "path": "Ticky.Base/Ticky.Base.csproj",
    "chars": 414,
    "preview": "<Project Sdk=\"Microsoft.NET.Sdk\">\n\n  <PropertyGroup>\n    <TargetFramework>net9.0</TargetFramework>\n    <ImplicitUsings>e"
  },
  {
    "path": "Ticky.Base/Validation/IsValidTimeSpan.cs",
    "chars": 873,
    "preview": "namespace Ticky.Base.Validation;\n\npublic class IsValidTimeSpan() : ValidationAttribute\n{\n    private const string ERROR_"
  },
  {
    "path": "Ticky.Base/Validation/RequiredIf.cs",
    "chars": 1786,
    "preview": "namespace Ticky.Base.Validation;\n\npublic class RequiredIfAttribute : ValidationAttribute\n{\n    private readonly string _"
  },
  {
    "path": "Ticky.Internal/Data/DataContext.cs",
    "chars": 8408,
    "preview": "using Microsoft.AspNetCore.DataProtection.EntityFrameworkCore;\nusing Microsoft.AspNetCore.Identity;\nusing Ticky.Base.Con"
  },
  {
    "path": "Ticky.Internal/Data/DataMigrator.cs",
    "chars": 1081,
    "preview": "namespace Ticky.Internal.Data;\n\npublic class DataMigrator\n{\n    public static async Task Seed(IServiceProvider servicePr"
  },
  {
    "path": "Ticky.Internal/Data/DataSeeder.cs",
    "chars": 5567,
    "preview": "using System.Drawing;\nusing System.Security.Claims;\nusing Microsoft.AspNetCore.Identity;\n\nnamespace Ticky.Internal.Data;"
  },
  {
    "path": "Ticky.Internal/GlobalUsings.cs",
    "chars": 514,
    "preview": "global using Devity.Extensions;\nglobal using Microsoft.AspNetCore.Identity.EntityFrameworkCore;\nglobal using Microsoft.E"
  },
  {
    "path": "Ticky.Internal/Helpers/AttachmentHelper.cs",
    "chars": 2033,
    "preview": "namespace Ticky.Internal.Helpers;\n\npublic static class AttachmentHelper\n{\n    public static string GetFileTypeFromAttac"
  },
  {
    "path": "Ticky.Internal/Helpers/IndexHelper.cs",
    "chars": 1181,
    "preview": "namespace Ticky.Internal.Helpers;\n\npublic static class IndexHelper\n{\n    public static int GetNextIndex<T>(this List<T> "
  },
  {
    "path": "Ticky.Internal/Helpers/StringHelper.cs",
    "chars": 505,
    "preview": "using System.Text;\n\nnamespace Ticky.Internal.Helpers;\n\npublic static class StringHelper\n{\n    public static string ToFri"
  },
  {
    "path": "Ticky.Internal/Helpers/TimeHelper.cs",
    "chars": 1740,
    "preview": "namespace Ticky.Internal.Helpers;\n\npublic static class TimeHelper\n{\n    public static string ToElapsedString(this DateTi"
  },
  {
    "path": "Ticky.Internal/Migrations/20250523175138_Initial.Designer.cs",
    "chars": 38503,
    "preview": "// <auto-generated />\nusing System;\nusing Microsoft.EntityFrameworkCore;\nusing Microsoft.EntityFrameworkCore.Infrastruc"
  },
  {
    "path": "Ticky.Internal/Migrations/20250523175138_Initial.cs",
    "chars": 45265,
    "preview": "using System;\nusing Microsoft.EntityFrameworkCore.Metadata;\nusing Microsoft.EntityFrameworkCore.Migrations;\n\n#nullable "
  },
  {
    "path": "Ticky.Internal/Migrations/20250527093505_Favorites.Designer.cs",
    "chars": 42224,
    "preview": "// <auto-generated />\nusing System;\nusing Microsoft.EntityFrameworkCore;\nusing Microsoft.EntityFrameworkCore.Infrastruc"
  },
  {
    "path": "Ticky.Internal/Migrations/20250527093505_Favorites.cs",
    "chars": 17617,
    "preview": "using System;\nusing Microsoft.EntityFrameworkCore.Metadata;\nusing Microsoft.EntityFrameworkCore.Migrations;\n\n#nullable "
  },
  {
    "path": "Ticky.Internal/Migrations/20250606115441_ForceCredentialsChange.Designer.cs",
    "chars": 42365,
    "preview": "// <auto-generated />\nusing System;\nusing Microsoft.EntityFrameworkCore;\nusing Microsoft.EntityFrameworkCore.Infrastruc"
  },
  {
    "path": "Ticky.Internal/Migrations/20250606115441_ForceCredentialsChange.cs",
    "chars": 802,
    "preview": "using Microsoft.EntityFrameworkCore.Migrations;\n\n#nullable disable\n\nnamespace Ticky.Internal.Migrations\n{\n    /// <inhe"
  },
  {
    "path": "Ticky.Internal/Migrations/20250615181842_TextToNameCard.Designer.cs",
    "chars": 42349,
    "preview": "// <auto-generated />\nusing System;\nusing Microsoft.EntityFrameworkCore;\nusing Microsoft.EntityFrameworkCore.Infrastruc"
  },
  {
    "path": "Ticky.Internal/Migrations/20250615181842_TextToNameCard.cs",
    "chars": 993,
    "preview": "using Microsoft.EntityFrameworkCore.Migrations;\n\n#nullable disable\n\nnamespace Ticky.Internal.Migrations\n{\n    /// <inhe"
  },
  {
    "path": "Ticky.Internal/Migrations/20250617140549_SnoozeCards.Designer.cs",
    "chars": 42457,
    "preview": "// <auto-generated />\nusing System;\nusing Microsoft.EntityFrameworkCore;\nusing Microsoft.EntityFrameworkCore.Infrastruc"
  },
  {
    "path": "Ticky.Internal/Migrations/20250617140549_SnoozeCards.cs",
    "chars": 746,
    "preview": "using System;\nusing Microsoft.EntityFrameworkCore.Migrations;\n\n#nullable disable\n\nnamespace Ticky.Internal.Migrations\n{"
  },
  {
    "path": "Ticky.Internal/Migrations/20250618090806_AtRemoval.Designer.cs",
    "chars": 42350,
    "preview": "// <auto-generated />\nusing System;\nusing Microsoft.EntityFrameworkCore;\nusing Microsoft.EntityFrameworkCore.Infrastruc"
  },
  {
    "path": "Ticky.Internal/Migrations/20250618090806_AtRemoval.cs",
    "chars": 826,
    "preview": "using System;\nusing Microsoft.EntityFrameworkCore.Migrations;\n\n#nullable disable\n\nnamespace Ticky.Internal.Migrations\n{"
  },
  {
    "path": "Ticky.Internal/Migrations/20250618114219_SelfAssign.Designer.cs",
    "chars": 42463,
    "preview": "// <auto-generated />\nusing System;\nusing Microsoft.EntityFrameworkCore;\nusing Microsoft.EntityFrameworkCore.Infrastruc"
  },
  {
    "path": "Ticky.Internal/Migrations/20250618114219_SelfAssign.cs",
    "chars": 782,
    "preview": "using Microsoft.EntityFrameworkCore.Migrations;\n\n#nullable disable\n\nnamespace Ticky.Internal.Migrations\n{\n    /// <inhe"
  },
  {
    "path": "Ticky.Internal/Migrations/20250703124731_DisableBoardAnimations.Designer.cs",
    "chars": 42607,
    "preview": "// <auto-generated />\nusing System;\nusing Microsoft.EntityFrameworkCore;\nusing Microsoft.EntityFrameworkCore.Infrastruc"
  },
  {
    "path": "Ticky.Internal/Migrations/20250703124731_DisableBoardAnimations.cs",
    "chars": 802,
    "preview": "using Microsoft.EntityFrameworkCore.Migrations;\n\n#nullable disable\n\nnamespace Ticky.Internal.Migrations\n{\n    /// <inhe"
  },
  {
    "path": "Ticky.Internal/Migrations/20250709130027_RepeatingCards.Designer.cs",
    "chars": 43923,
    "preview": "// <auto-generated />\nusing System;\nusing Microsoft.EntityFrameworkCore;\nusing Microsoft.EntityFrameworkCore.Infrastruc"
  },
  {
    "path": "Ticky.Internal/Migrations/20250709130027_RepeatingCards.cs",
    "chars": 2672,
    "preview": "using System;\nusing Microsoft.EntityFrameworkCore.Migrations;\n\n#nullable disable\n\nnamespace Ticky.Internal.Migrations\n{"
  },
  {
    "path": "Ticky.Internal/Migrations/20250826115734_NewCardPlacement.Designer.cs",
    "chars": 44031,
    "preview": "// <auto-generated />\nusing System;\nusing Microsoft.EntityFrameworkCore;\nusing Microsoft.EntityFrameworkCore.Infrastruc"
  },
  {
    "path": "Ticky.Internal/Migrations/20250826115734_NewCardPlacement.cs",
    "chars": 770,
    "preview": "using Microsoft.EntityFrameworkCore.Migrations;\n\n#nullable disable\n\nnamespace Ticky.Internal.Migrations\n{\n    /// <inhe"
  },
  {
    "path": "Ticky.Internal/Migrations/20251005111419_BlockToFlagged.Designer.cs",
    "chars": 44027,
    "preview": "// <auto-generated />\nusing System;\nusing Microsoft.EntityFrameworkCore;\nusing Microsoft.EntityFrameworkCore.Infrastruc"
  },
  {
    "path": "Ticky.Internal/Migrations/20251005111419_BlockToFlagged.cs",
    "chars": 723,
    "preview": "using Microsoft.EntityFrameworkCore.Migrations;\n\n#nullable disable\n\nnamespace Ticky.Internal.Migrations\n{\n    /// <inhe"
  },
  {
    "path": "Ticky.Internal/Migrations/20251005174743_SubtaskAssignee.Designer.cs",
    "chars": 45064,
    "preview": "// <auto-generated />\nusing System;\nusing Microsoft.EntityFrameworkCore;\nusing Microsoft.EntityFrameworkCore.Infrastruc"
  },
  {
    "path": "Ticky.Internal/Migrations/20251005174743_SubtaskAssignee.cs",
    "chars": 1875,
    "preview": "using Microsoft.EntityFrameworkCore.Migrations;\n\n#nullable disable\n\nnamespace Ticky.Internal.Migrations\n{\n    /// <inhe"
  },
  {
    "path": "Ticky.Internal/Migrations/20251011134337_Information.Designer.cs",
    "chars": 45290,
    "preview": "// <auto-generated />\nusing System;\nusing Microsoft.EntityFrameworkCore;\nusing Microsoft.EntityFrameworkCore.Infrastruc"
  },
  {
    "path": "Ticky.Internal/Migrations/20251011134337_Information.cs",
    "chars": 1099,
    "preview": "using System;\nusing Microsoft.EntityFrameworkCore.Migrations;\n\n#nullable disable\n\nnamespace Ticky.Internal.Migrations\n{"
  },
  {
    "path": "Ticky.Internal/Migrations/DataContextModelSnapshot.cs",
    "chars": 45192,
    "preview": "// <auto-generated />\nusing System;\nusing Microsoft.EntityFrameworkCore;\nusing Microsoft.EntityFrameworkCore.Infrastruc"
  },
  {
    "path": "Ticky.Internal/Services/AvatarService.cs",
    "chars": 1423,
    "preview": "namespace Ticky.Internal.Services\n{\n    public class AvatarService\n    {\n        private readonly HttpClient _httpClient"
  },
  {
    "path": "Ticky.Internal/Services/CardNumberingService.cs",
    "chars": 652,
    "preview": "namespace Ticky.Internal.Services;\n\npublic class CardNumberingService\n{\n    private readonly IDbContextFactory<DataCont"
  },
  {
    "path": "Ticky.Internal/Services/CodeService.cs",
    "chars": 1376,
    "preview": "using Microsoft.EntityFrameworkCore;\n\nnamespace Ticky.Internal.Services;\n\npublic class CodeService\n{\n    private readon"
  },
  {
    "path": "Ticky.Internal/Services/EmailService.cs",
    "chars": 7887,
    "preview": "using Devity.Extensions.Templates;\nusing Devity.Mailing;\nusing Devity.NETCore.MailKit.Core;\n\nnamespace Ticky.Internal.Se"
  },
  {
    "path": "Ticky.Internal/Services/Hosted/AbstractHostedService.cs",
    "chars": 3108,
    "preview": "using Microsoft.Extensions.Hosting;\n\nnamespace Ticky.Internal.Services.Hosted;\n\npublic abstract class AbstractHostedServ"
  },
  {
    "path": "Ticky.Internal/Services/Hosted/CleanupHostedService.cs",
    "chars": 3577,
    "preview": "namespace Ticky.Internal.Services.Hosted;\n\npublic class CleanupHostedService(IServiceScopeFactory serviceScopeFactory)\n "
  },
  {
    "path": "Ticky.Internal/Services/Hosted/ReminderHostedService.cs",
    "chars": 3527,
    "preview": "namespace Ticky.Internal.Services.Hosted;\n\npublic class ReminderHostedService(IServiceScopeFactory serviceScopeFactory)\n"
  },
  {
    "path": "Ticky.Internal/Services/Hosted/RepeatHostedService.cs",
    "chars": 7146,
    "preview": "namespace Ticky.Internal.Services.Hosted;\n\npublic class RepeatHostedService(IServiceScopeFactory serviceScopeFactory) : "
  },
  {
    "path": "Ticky.Internal/Services/Hosted/SnoozeHostedService.cs",
    "chars": 1047,
    "preview": "namespace Ticky.Internal.Services.Hosted;\n\npublic class SnoozeHostedService(IServiceScopeFactory serviceScopeFactory) : "
  },
  {
    "path": "Ticky.Internal/Services/InformationService.cs",
    "chars": 703,
    "preview": "using System.Text.Json;\n\nnamespace Ticky.Internal.Services;\n\npublic class InformationService\n{\n    public IReadOnlyList<"
  },
  {
    "path": "Ticky.Internal/Services/SearchService.cs",
    "chars": 1349,
    "preview": "namespace Ticky.Internal.Services;\n\npublic class SearchService\n{\n    private readonly IDbContextFactory<DataContext> _db"
  },
  {
    "path": "Ticky.Internal/Services/TrelloImportService.cs",
    "chars": 10543,
    "preview": "using System.Drawing;\nusing Ticky.Base.Models;\n\nnamespace Ticky.Internal.Services;\n\npublic class TrelloImportService\n{\n "
  },
  {
    "path": "Ticky.Internal/Ticky.Internal.csproj",
    "chars": 1640,
    "preview": "<Project Sdk=\"Microsoft.NET.Sdk\">\n\n  <PropertyGroup>\n    <TargetFramework>net9.0</TargetFramework>\n    <ImplicitUsings>e"
  },
  {
    "path": "Ticky.Units/CardTest.cs",
    "chars": 8377,
    "preview": "namespace Ticky.Units;\n\npublic class CardTest\n{\n    private Card? _card;\n\n    [SetUp]\n    public void Setup()\n    {\n    "
  },
  {
    "path": "Ticky.Units/Constants.cs",
    "chars": 104,
    "preview": "namespace Ticky.Units;\n\npublic static class Constants\n{\n    public const string NAME = \"Random Name\";\n}\n"
  },
  {
    "path": "Ticky.Units/GlobalUsings.cs",
    "chars": 161,
    "preview": "global using Moq;\nglobal using Ticky.Base.Entities;\nglobal using Ticky.Base.Entities.Owned;\nglobal using Ticky.Base.Enum"
  },
  {
    "path": "Ticky.Units/Ticky.Units.csproj",
    "chars": 948,
    "preview": "<Project Sdk=\"Microsoft.NET.Sdk\">\n\n  <PropertyGroup>\n    <TargetFramework>net9.0</TargetFramework>\n    <LangVersion>lat"
  },
  {
    "path": "Ticky.Web/Components/App.razor",
    "chars": 2019,
    "preview": "<!DOCTYPE html>\n<html class=\"text-app-text\" lang=\"en\" data-theme=\"\">\n\n<head>\n    <meta charset=\"utf-8\" />\n    <meta name"
  },
  {
    "path": "Ticky.Web/Components/Dialogs/AbstractModal.razor",
    "chars": 828,
    "preview": "@implements IDisposable\n\n@code {\n    protected object? FocusElement { get; set; }\n\n    protected Modal? Modal;\n\n    publ"
  },
  {
    "path": "Ticky.Web/Components/Dialogs/AddLabelModal.razor",
    "chars": 4022,
    "preview": "@inherits AbstractModal\n@inject IDbContextFactory<DataContext> _dbContextFactory\n\n<Modal @ref=Modal Title=\"Add label\" On"
  },
  {
    "path": "Ticky.Web/Components/Dialogs/AddOrEditRepeatModal.razor",
    "chars": 7348,
    "preview": "@inject IDbContextFactory<DataContext> _dbContextFactory\n\n<ActionModal @ref=_modalRef Title=\"Repeat card [BETA]\">\n    @i"
  },
  {
    "path": "Ticky.Web/Components/Dialogs/AddReminderModal.razor",
    "chars": 2584,
    "preview": "@inject IDbContextFactory<DataContext> _dbContextFactory\n\n<ActionModal @ref=_modalRef Title=\"Add reminder\">\n    @if(_car"
  },
  {
    "path": "Ticky.Web/Components/Dialogs/AddSubtaskModal.razor",
    "chars": 2131,
    "preview": "@inject IDbContextFactory<DataContext> _dbContextFactory\n\n<ActionModal @ref=_modalRef Title=\"Add subtask\">\n    @if(_card"
  },
  {
    "path": "Ticky.Web/Components/Dialogs/AddTimeRecordModal.razor",
    "chars": 2304,
    "preview": "@inject IDbContextFactory<DataContext> _dbContextFactory\n\n<ActionModal @ref=_modalRef Title=\"Add time record\">\n    @if(_"
  },
  {
    "path": "Ticky.Web/Components/Dialogs/AddUserModal.razor",
    "chars": 3000,
    "preview": "@inherits AbstractModal\n@inject IDbContextFactory<DataContext> _dbContextFactory\n@inject UserManager<User> _userManager\n"
  },
  {
    "path": "Ticky.Web/Components/Dialogs/AssigneesModal.razor",
    "chars": 4675,
    "preview": "@inject IDbContextFactory<DataContext> _dbContextFactory\n\n<ActionModal @ref=_modalRef Title=\"Assignees\">\n    @if(_assign"
  },
  {
    "path": "Ticky.Web/Components/Dialogs/CloneBoardModal.razor",
    "chars": 6685,
    "preview": "@inject IDbContextFactory<DataContext> _dbContextFactory\n@inherits AbstractModal\n\n<Modal @ref=\"Modal\" Title=\"Clone board"
  },
  {
    "path": "Ticky.Web/Components/Dialogs/CreateBoardModal.razor",
    "chars": 3345,
    "preview": "@inherits AbstractModal\n@inject IDbContextFactory<DataContext> _dbContextFactory\n\n<Modal @ref=\"Modal\" Title=\"New board\" "
  },
  {
    "path": "Ticky.Web/Components/Dialogs/CreateColumnModal.razor",
    "chars": 2832,
    "preview": "@inherits AbstractModal\n@inject IDbContextFactory<DataContext> _dbContextFactory\n\n<Modal @ref=\"Modal\" Title=\"New column\""
  },
  {
    "path": "Ticky.Web/Components/Dialogs/CreateProjectModal.razor",
    "chars": 1870,
    "preview": "@inherits AbstractModal\n@inject IDbContextFactory<DataContext> _dbContextFactory\n\n<Modal @ref=\"Modal\" Title=\"New project"
  },
  {
    "path": "Ticky.Web/Components/Dialogs/DeleteConfirmationDialog.razor",
    "chars": 1996,
    "preview": "@inject IDbContextFactory<DataContext> _dbContextFactory\n@typeparam T where T : class, IDbEntry, IDeletable\n\n<Modal @ref"
  },
  {
    "path": "Ticky.Web/Components/Dialogs/EditBoardMembershipsModal.razor",
    "chars": 8484,
    "preview": "@inherits AbstractModal\n@inject IDbContextFactory<DataContext> _dbContextFactory\n\n<Modal @ref=\"Modal\" Title=@($\"Edit mem"
  },
  {
    "path": "Ticky.Web/Components/Dialogs/EditBoardModal.razor",
    "chars": 2436,
    "preview": "@inherits AbstractModal\n@inject IDbContextFactory<DataContext> _dbContextFactory\n\n<Modal @ref=\"Modal\" Title=\"Edit board\""
  },
  {
    "path": "Ticky.Web/Components/Dialogs/EditCardModal.razor",
    "chars": 55919,
    "preview": "@inherits AbstractModal\n@implements IDisposable\n@inject IDbContextFactory<DataContext> _dbContextFactory\n@inject IJSRun"
  },
  {
    "path": "Ticky.Web/Components/Dialogs/EditColumnModal.razor",
    "chars": 3063,
    "preview": "@inherits AbstractModal\n@inject IDbContextFactory<DataContext> _dbContextFactory\n\n<Modal @ref=\"Modal\" Title=\"Edit column"
  },
  {
    "path": "Ticky.Web/Components/Dialogs/EditDeadlineModal.razor",
    "chars": 2650,
    "preview": "@inject IDbContextFactory<DataContext> _dbContextFactory\n\n<ActionModal @ref=_modalRef Title=\"Set deadline\">\n    @if(_car"
  },
  {
    "path": "Ticky.Web/Components/Dialogs/EditLabelModal.razor",
    "chars": 4365,
    "preview": "@inherits AbstractModal\n@inject IDbContextFactory<DataContext> _dbContextFactory\n\n<Modal @ref=Modal Title=\"Edit label\" O"
  },
  {
    "path": "Ticky.Web/Components/Dialogs/EditProjectMembershipsModal.razor",
    "chars": 6727,
    "preview": "@inherits AbstractModal\n@inject IDbContextFactory<DataContext> _dbContextFactory\n\n<Modal @ref=\"Modal\" Title=@($\"Edit mem"
  },
  {
    "path": "Ticky.Web/Components/Dialogs/EditProjectModal.razor",
    "chars": 1819,
    "preview": "@inherits AbstractModal\n@inject IDbContextFactory<DataContext> _dbContextFactory\n\n<Modal @ref=\"Modal\" Title=\"Edit projec"
  },
  {
    "path": "Ticky.Web/Components/Dialogs/EditSubtaskModal.razor",
    "chars": 2026,
    "preview": "@inject IDbContextFactory<DataContext> _dbContextFactory\n\n<ActionModal @ref=_modalRef Title=\"Edit subtask\">\n    @if(_sub"
  },
  {
    "path": "Ticky.Web/Components/Dialogs/EditTimeRecordModal.razor",
    "chars": 2465,
    "preview": "@inject IDbContextFactory<DataContext> _dbContextFactory\n\n<ActionModal @ref=_modalRef Title=\"Edit time record\">\n    @if("
  },
  {
    "path": "Ticky.Web/Components/Dialogs/EditUserModal.razor",
    "chars": 3447,
    "preview": "@inherits AbstractModal\n@inject IDbContextFactory<DataContext> _dbContextFactory\n@inject UserManager<User> _userManager\n"
  },
  {
    "path": "Ticky.Web/Components/Dialogs/FilterCardsModal.razor",
    "chars": 7178,
    "preview": "@implements IDisposable\n\n<ActionModal @ref=_modalRef Title=\"Filter cards\">\n    <div class=\"form p-2\">\n        <div class"
  },
  {
    "path": "Ticky.Web/Components/Dialogs/ImportModal.razor",
    "chars": 6945,
    "preview": "@using System.Text.Json\n@inherits AbstractModal\n@inject ILogger<ImportModal> _logger\n@inject TrelloImportService _trello"
  },
  {
    "path": "Ticky.Web/Components/Dialogs/InformationModal.razor",
    "chars": 1965,
    "preview": "@inherits AbstractModal\n@inject IDbContextFactory<DataContext> _dbContextFactory\n@inject InformationService _information"
  },
  {
    "path": "Ticky.Web/Components/Dialogs/LabelModal.razor",
    "chars": 2827,
    "preview": "@inject IDbContextFactory<DataContext> _dbContextFactory\n\n<ActionModal @ref=_modalRef Title=\"Labels\">\n    @if(_card is n"
  },
  {
    "path": "Ticky.Web/Components/Dialogs/LinkCardsModal.razor",
    "chars": 4781,
    "preview": "@inject IDbContextFactory<DataContext> _dbContextFactory\n@inject IJSRuntime _js\n\n<ActionModal @ref=_modalRef Title=\"Link"
  },
  {
    "path": "Ticky.Web/Components/Dialogs/PriorityModal.razor",
    "chars": 2750,
    "preview": "@inject IDbContextFactory<DataContext> _dbContextFactory\n\n<ActionModal @ref=_modalRef Title=\"Priority\">\n    @if(_card is"
  },
  {
    "path": "Ticky.Web/Components/Dialogs/SearchModal.razor",
    "chars": 3204,
    "preview": "@inject IDbContextFactory<DataContext> _dbContextFactory\n@inject NavigationManager _navigationManager\n@inherits Abstract"
  },
  {
    "path": "Ticky.Web/Components/Dialogs/SnoozeCardModal.razor",
    "chars": 2634,
    "preview": "@inject IDbContextFactory<DataContext> _dbContextFactory\n\n<ActionModal @ref=_modalRef Title=\"Snooze card\">\n    @if(_card"
  },
  {
    "path": "Ticky.Web/Components/Dialogs/UserInfoModal.razor",
    "chars": 2395,
    "preview": "@inject IDbContextFactory<DataContext> _dbContextFactory\n\n<ActionModal @ref=_modalRef Title=\"User information\">\n    @if("
  },
  {
    "path": "Ticky.Web/Components/Elements/ActionModal.razor",
    "chars": 1487,
    "preview": "@inject IJSRuntime _js\n\n<div @ref=\"_element\" class=\"dropdown dropdown-animate absolute top-0 left-0 z-20 hidden overflo"
  },
  {
    "path": "Ticky.Web/Components/Elements/BoardCard.razor",
    "chars": 4336,
    "preview": "@inject NavigationManager _navigationManager\n@inject IDbContextFactory<DataContext> _dbContextFactory\n\n<EditBoardModal @"
  },
  {
    "path": "Ticky.Web/Components/Elements/CardView.razor",
    "chars": 5472,
    "preview": "@inject IDbContextFactory<DataContext> _dbContextFactory\n\n<div id=\"@Card.Id\" data-blocked=\"@Card.Flagged.ToString()\" cla"
  },
  {
    "path": "Ticky.Web/Components/Elements/ColumnView.razor",
    "chars": 17996,
    "preview": "@inject IDbContextFactory<DataContext> _dbContextFactory\n@inject IJSRuntime _js\n@inject CardNumberingService _cardNumber"
  },
  {
    "path": "Ticky.Web/Components/Elements/DisabledBadge.razor",
    "chars": 80,
    "preview": "<div class=\"label !cursor-help bg-gray-400 text-label-text\">\n    Disabled\n</div>"
  },
  {
    "path": "Ticky.Web/Components/Elements/Dropdown.razor",
    "chars": 1250,
    "preview": "@inject IJSRuntime _js\n\n<div @ref=\"_element\" @onclick=\"OpenMenu\" @onclick:stopPropagation=\"true\">\n\t@ChildContent\n</div>"
  },
  {
    "path": "Ticky.Web/Components/Elements/LabelView.razor",
    "chars": 350,
    "preview": "<div class=\"label\" \n    style=\"background-color: rgba(@Label.BackgroundColor.R, @Label.BackgroundColor.G, @Label.Backgro"
  },
  {
    "path": "Ticky.Web/Components/Elements/Modal.razor",
    "chars": 3227,
    "preview": "@if(_triggeredShow)\n{\n\t<div data-active=\"@_shown.ToString()\" class='h-visible-screen fixed top-0 left-0 @(ForceOverlay ?"
  },
  {
    "path": "Ticky.Web/Components/Elements/Notifications.razor",
    "chars": 1431,
    "preview": "<div class=\"fixed right-5 bottom-5 z-20 flex flex-col gap-10 transition-all\">\n    @foreach(var notification in _notific"
  },
  {
    "path": "Ticky.Web/Components/Elements/PriorityLabel.razor",
    "chars": 744,
    "preview": "@if(Priority.Equals(CardPriority.Normal)) {\n    <div class=\"priority bg-sky-500 text-label-text\">\n        <i class=\"fa f"
  },
  {
    "path": "Ticky.Web/Components/Elements/Sortable/SortableList.razor",
    "chars": 508,
    "preview": "@using System.Collections.Generic\n@using System.Diagnostics.CodeAnalysis\n@using Microsoft.JSInterop\n\n@inject IJSRuntime "
  },
  {
    "path": "Ticky.Web/Components/Elements/Sortable/SortableList.razor.cs",
    "chars": 3785,
    "preview": "using System.Diagnostics.CodeAnalysis;\nusing System.Linq.Expressions;\nusing Microsoft.AspNetCore.Components;\nusing Micro"
  },
  {
    "path": "Ticky.Web/Components/Elements/Sortable/SortableList.razor.css",
    "chars": 302,
    "preview": "/* \n  you need the ::deep identifier if you are using scoped styles like this\n  because scoped styles are only applied t"
  },
  {
    "path": "Ticky.Web/Components/Elements/Sortable/SortableList.razor.js",
    "chars": 4520,
    "preview": "export function init(id, group, pull, put, sort, handle, filter, component, forceFallback, direction, animation) {\n    c"
  },
  {
    "path": "Ticky.Web/Components/Elements/Spinner.razor",
    "chars": 246,
    "preview": "<div data-small='@Small.ToString()' class=\"element-spinner-wrapper @Class\">\n    <div class=\"element-spinner\">\n    </div>"
  },
  {
    "path": "Ticky.Web/Components/Elements/SubtaskView.razor",
    "chars": 4715,
    "preview": "@using Microsoft.JSInterop\n@using Microsoft.EntityFrameworkCore\n@inject IDbContextFactory<DataContext> _dbContextFactory"
  },
  {
    "path": "Ticky.Web/Components/Elements/TimeSelect.razor",
    "chars": 1402,
    "preview": "@using Microsoft.AspNetCore.Components\n@using Microsoft.AspNetCore.Components.Forms\n\n<InputSelect class=\"form-control\" T"
  },
  {
    "path": "Ticky.Web/Components/Elements/Tooltip.razor",
    "chars": 252,
    "preview": "<div class=\"tooltip-wrapper\">\n    <span class=\"tooltip-span\">@Text</span>\n    @ChildContent\n</div>\n\n@code {\n    [Paramet"
  },
  {
    "path": "Ticky.Web/Components/Layout/MainLayout.razor",
    "chars": 3497,
    "preview": "@inherits LayoutComponentBase\n@inject IDbContextFactory<DataContext> _dbContextFactory\n@inject NavigationManager _naviga"
  },
  {
    "path": "Ticky.Web/Components/Layout/NavMenu.razor",
    "chars": 6853,
    "preview": "@implements IDisposable\n@inject IDbContextFactory<DataContext> _dbContextFactory\n@inject IJSRuntime _js\n@inject Navigati"
  },
  {
    "path": "Ticky.Web/Components/Pages/Abstractions/NotifiableBase.razor",
    "chars": 522,
    "preview": "@code {\n    [CascadingParameter(Name = Constants.CascadingParameters.MainLayout)]\n    protected MainLayout MainLayout { "
  },
  {
    "path": "Ticky.Web/Components/Pages/AdminPanel.razor",
    "chars": 4079,
    "preview": "@page \"/admin\"\n@inject IDbContextFactory<DataContext> _dbContextFactory\n@inject ILogger<BoardSettings> _logger\n@inherits"
  },
  {
    "path": "Ticky.Web/Components/Pages/Auth/ChangePassword.cshtml",
    "chars": 1833,
    "preview": "@page\n@model ChangePasswordModel\n\n<main class=\"auth-bg\">\n    <section class=\"auth-card\">\n        <div class=\"flex flex-c"
  },
  {
    "path": "Ticky.Web/Components/Pages/Auth/ChangePassword.cshtml.cs",
    "chars": 2236,
    "preview": "namespace Ticky.Web.Pages.Auth;\n\npublic class ChangePasswordModel : PageModel\n{\n    private readonly DataContext _dataC"
  },
  {
    "path": "Ticky.Web/Components/Pages/Auth/ConfirmMail.cshtml",
    "chars": 1218,
    "preview": "@page\n@model ConfirmMailModel\n\n<main class=\"auth-bg\">\n    <section class=\"auth-card\">\n        <div class=\"flex flex-col"
  },
  {
    "path": "Ticky.Web/Components/Pages/Auth/ConfirmMail.cshtml.cs",
    "chars": 2037,
    "preview": "namespace Ticky.Web.Pages.Auth;\n\npublic class ConfirmMailModel : PageModel\n{\n    private readonly DataContext _dataConte"
  },
  {
    "path": "Ticky.Web/Components/Pages/Auth/ForgotPassword.cshtml",
    "chars": 1470,
    "preview": "@page\n@model ForgotPasswordModel\n\n<main class=\"auth-bg\">\n    <section class=\"auth-card\">\n        <div class=\"flex flex-"
  },
  {
    "path": "Ticky.Web/Components/Pages/Auth/ForgotPassword.cshtml.cs",
    "chars": 1738,
    "preview": "namespace Ticky.Web.Pages.Auth;\n\npublic class ForgotPasswordModel : PageModel\n{\n    private readonly DataContext _dataCo"
  },
  {
    "path": "Ticky.Web/Components/Pages/Auth/Login.cshtml",
    "chars": 1734,
    "preview": "@page\n@model LoginModel\n\n<main class=\"auth-bg\">\n    <section class=\"auth-card\">\n        <div class=\"flex flex-col gap-1"
  },
  {
    "path": "Ticky.Web/Components/Pages/Auth/Login.cshtml.cs",
    "chars": 1684,
    "preview": "using System.ComponentModel.DataAnnotations;\nusing Microsoft.AspNetCore.Identity;\n\nnamespace Ticky.Web.Pages.Auth;\n\npub"
  },
  {
    "path": "Ticky.Web/Components/Pages/Auth/Logout.cshtml",
    "chars": 330,
    "preview": "@page\n@attribute [IgnoreAntiforgeryToken]\n@inject SignInManager<User> SignInManager\n\n@functions {\n    public async Task<"
  },
  {
    "path": "Ticky.Web/Components/Pages/Auth/Logout.cshtml.cs",
    "chars": 196,
    "preview": "using Microsoft.AspNetCore.Mvc;\nusing Microsoft.AspNetCore.Mvc.RazorPages;\n\nnamespace Ticky.Web.Pages.Auth\n{\n    public "
  },
  {
    "path": "Ticky.Web/Components/Pages/Auth/MailConfirmed.cshtml",
    "chars": 465,
    "preview": "@page\n@model MailConfirmedModel\n\n<main class=\"auth-bg\">\n    <section class=\"auth-card justify-center items-center text-c"
  },
  {
    "path": "Ticky.Web/Components/Pages/Auth/MailConfirmed.cshtml.cs",
    "chars": 149,
    "preview": "namespace Ticky.Web.Pages.Auth;\n\npublic class MailConfirmedModel : PageModel\n{\n    public IActionResult OnGet()\n    {\n  "
  }
]

// ... and 41 more files (download for full content)

About this extraction

This page contains the full source code of the dkorecko/Ticky GitHub repository, extracted and formatted as plain text for AI agents and large language models (LLMs). The extraction includes 241 files (1.3 MB), approximately 274.8k tokens, and a symbol index with 308 extracted functions, classes, methods, constants, and types. Use this with OpenClaw, Claude, ChatGPT, Cursor, Windsurf, or any other AI tool that accepts text input. You can copy the full output to your clipboard or download it as a .txt file.

Extracted by GitExtract — free GitHub repo to text converter for AI. Built by Nikandr Surkov.

Copied to clipboard!