ci: add pull request check Gitea actions #73
| @@ -5,5 +5,5 @@ | ||||
| /node_modules/ | ||||
| /static/ | ||||
| /target/ | ||||
| /docker-compose-dev.yml | ||||
| /docker-compose-prod.yml | ||||
| /docker-compose-dev.yaml | ||||
| /docker-compose-prod.yaml | ||||
|   | ||||
							
								
								
									
										4
									
								
								.github/config/markdownlint.yaml
									
									
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										4
									
								
								.github/config/markdownlint.yaml
									
									
									
									
										vendored
									
									
										Normal file
									
								
							| @@ -0,0 +1,4 @@ | ||||
| --- | ||||
| # no-duplicate-heading | ||||
| MD024: | ||||
|   siblings_only: true | ||||
							
								
								
									
										4
									
								
								.github/config/yamllint.yaml
									
									
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										4
									
								
								.github/config/yamllint.yaml
									
									
									
									
										vendored
									
									
										Normal file
									
								
							| @@ -0,0 +1,4 @@ | ||||
| rules: | ||||
|   comments: | ||||
|     # https://github.com/redhat-developer/vscode-yaml/issues/433 | ||||
|     min-spaces-from-content: 1 | ||||
							
								
								
									
										1670
									
								
								.github/dependencies/checkov-check/requirements.txt
									
									
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										1670
									
								
								.github/dependencies/checkov-check/requirements.txt
									
									
									
									
										vendored
									
									
										Normal file
									
								
							
										
											
												File diff suppressed because it is too large
												Load Diff
											
										
									
								
							
							
								
								
									
										41
									
								
								.github/workflows/actionlint-check.yaml
									
									
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										41
									
								
								.github/workflows/actionlint-check.yaml
									
									
									
									
										vendored
									
									
										Normal file
									
								
							| @@ -0,0 +1,41 @@ | ||||
| --- | ||||
| name: actionlint check | ||||
|  | ||||
| on: # yamllint disable-line rule:truthy | ||||
|   pull_request: | ||||
|     types: [opened, edited, reopened, synchronize] | ||||
|  | ||||
| permissions: {} | ||||
|  | ||||
| jobs: | ||||
|   build: | ||||
|     name: actionlint check | ||||
|     runs-on: ubuntu-22.04 | ||||
|     permissions: | ||||
|       contents: read | ||||
|       pull-requests: read | ||||
|       packages: read | ||||
|       statuses: write | ||||
|     steps: | ||||
|       - name: actionlint installation | ||||
|         working-directory: /bin | ||||
|         run: | | ||||
|           set -e | ||||
|  | ||||
|           INSTALL_SCRIPT_URL="https://raw.githubusercontent.com/rhysd/actionlint/e11169d0656294827d65370a3c76a2325406da85/scripts/download-actionlint.bash" | ||||
|           ACTIONLINT_VERSION="1.7.5" | ||||
|  | ||||
|           INSTALL_SCRIPT_CHECKSUM="99ab9f1d97c31c9a051e6902305f7ea9f48e7e7e1b0ee41f64aa831c86655168  download-actionlint.bash" | ||||
|           ACTIONLINT_CHECKSUM="3d74253aa0cf645e6224fd53f2d56776998c7c05a0d3c12307463285515898f8  actionlint" | ||||
|  | ||||
|           wget -O download-actionlint.bash "$INSTALL_SCRIPT_URL" | ||||
|           echo "$INSTALL_SCRIPT_CHECKSUM" | sha256sum --check | ||||
|  | ||||
|           bash download-actionlint.bash "$ACTIONLINT_VERSION" | ||||
|           echo "$ACTIONLINT_CHECKSUM" | sha256sum --check | ||||
|       - name: code checkout | ||||
|         uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 | ||||
|         with: | ||||
|           fetch-depth: 0 | ||||
|       - name: actionlint check | ||||
|         run: actionlint | ||||
							
								
								
									
										33
									
								
								.github/workflows/checkov-check.yaml
									
									
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										33
									
								
								.github/workflows/checkov-check.yaml
									
									
									
									
										vendored
									
									
										Normal file
									
								
							| @@ -0,0 +1,33 @@ | ||||
| --- | ||||
| name: checkov check | ||||
|  | ||||
| on: # yamllint disable-line rule:truthy | ||||
|   pull_request: | ||||
|     types: [opened, edited, reopened, synchronize] | ||||
|  | ||||
| permissions: {} | ||||
|  | ||||
| jobs: | ||||
|   build: | ||||
|     name: checkov check | ||||
|     runs-on: ubuntu-22.04 | ||||
|     permissions: | ||||
|       contents: read | ||||
|       pull-requests: read | ||||
|       packages: read | ||||
|       statuses: write | ||||
|     steps: | ||||
|       - name: Python installation | ||||
|         uses: actions/setup-python@0b93645e9fea7318ecaed2b359559ac225c90a2b | ||||
|         with: | ||||
|           python-version: "3.13.1" | ||||
|       - name: Rust toolchain installation | ||||
|         uses: dtolnay/rust-toolchain@0579bb9e1907e560c2f263f705f93655a44a07e5 | ||||
|       - name: code checkout | ||||
|         uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 | ||||
|         with: | ||||
|           fetch-depth: 0 | ||||
|       - name: checkov installation | ||||
|         run: pip install --no-cache-dir --require-hashes -r .github/dependencies/checkov-check/requirements.txt | ||||
|       - name: checkov check | ||||
|         run: checkov --directory . | ||||
							
								
								
									
										29
									
								
								.github/workflows/conventional-commit-messages-check.yaml
									
									
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										29
									
								
								.github/workflows/conventional-commit-messages-check.yaml
									
									
									
									
										vendored
									
									
										Normal file
									
								
							| @@ -0,0 +1,29 @@ | ||||
| --- | ||||
| name: conventional commit messages check | ||||
|  | ||||
| on: # yamllint disable-line rule:truthy | ||||
|   pull_request: | ||||
|     types: [opened, edited, reopened, synchronize] | ||||
|  | ||||
| permissions: {} | ||||
|  | ||||
| jobs: | ||||
|   build: | ||||
|     name: conventional commit messages check | ||||
|     runs-on: ubuntu-22.04 | ||||
|     permissions: | ||||
|       contents: read | ||||
|       pull-requests: read | ||||
|       packages: read | ||||
|       statuses: write | ||||
|     steps: | ||||
|       - name: code checkout | ||||
|         uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 | ||||
|         with: | ||||
|           fetch-depth: 0 | ||||
|       - name: conventional commit messages check | ||||
|         uses: davidglezz/action-conventional-commits-check@ea8ae44ab9c7b79c1da0bd811a6ee6c5768ce476 | ||||
|         with: | ||||
|           target-branch: ${{ github.event.pull_request.base.ref }} | ||||
|           current-branch: ${{ github.event.pull_request.head.ref }} | ||||
|           pattern: "^(build|chore|ci|docs|feat|fix|hotfix|perf|refactor|revert|style|test){1}(\\([[:alnum:]._-]+\\))?(!)?: ([[:alnum:]])+([[:space:][:print:]]*)$" | ||||
							
								
								
									
										24
									
								
								.github/workflows/conventional-commits.yml
									
									
									
									
										vendored
									
									
								
							
							
						
						
									
										24
									
								
								.github/workflows/conventional-commits.yml
									
									
									
									
										vendored
									
									
								
							| @@ -1,24 +0,0 @@ | ||||
| name: conventional commits | ||||
|  | ||||
| on: | ||||
|   pull_request: | ||||
|     types: [ opened, synchronize, reopened, edited ] | ||||
|  | ||||
| permissions: { } | ||||
|  | ||||
| jobs: | ||||
|   build: | ||||
|     name: conventional commits | ||||
|     runs-on: ubuntu-22.04 | ||||
|     permissions: | ||||
|       contents: read | ||||
|       pull-requests: read | ||||
|       packages: read | ||||
|       statuses: write | ||||
|     steps: | ||||
|       - name: checkout code | ||||
|         uses: actions/checkout@v3 | ||||
|       - name: conventional commits check | ||||
|         uses: webiny/action-conventional-commits@v1.3.0 | ||||
|         with: | ||||
|           GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} | ||||
							
								
								
									
										24
									
								
								.github/workflows/conventional-pull-request-title-check.yaml
									
									
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										24
									
								
								.github/workflows/conventional-pull-request-title-check.yaml
									
									
									
									
										vendored
									
									
										Normal file
									
								
							| @@ -0,0 +1,24 @@ | ||||
| --- | ||||
| name: conventional pull request title check | ||||
|  | ||||
| on: # yamllint disable-line rule:truthy | ||||
|   pull_request: | ||||
|     types: [opened, edited, reopened, synchronize] | ||||
|  | ||||
| permissions: {} | ||||
|  | ||||
| jobs: | ||||
|   build: | ||||
|     name: conventional pull request title check | ||||
|     runs-on: ubuntu-22.04 | ||||
|     permissions: | ||||
|       contents: read | ||||
|       pull-requests: read | ||||
|       packages: read | ||||
|       statuses: write | ||||
|     steps: | ||||
|       - name: conventional pull request title check | ||||
|         uses: ytanikin/PRConventionalCommits@b628c5a234cc32513014b7bfdd1e47b532124d98 | ||||
|         with: | ||||
|           task_types: '["build","chore","ci","docs","feat","fix","hotfix","perf","refactor","revert","style","test"]' | ||||
|           add_label: "false" | ||||
							
								
								
									
										23
									
								
								.github/workflows/conventional-pull-requests.yml
									
									
									
									
										vendored
									
									
								
							
							
						
						
									
										23
									
								
								.github/workflows/conventional-pull-requests.yml
									
									
									
									
										vendored
									
									
								
							| @@ -1,23 +0,0 @@ | ||||
| name: conventional pull requests | ||||
|  | ||||
| on: | ||||
|   pull_request: | ||||
|     types: [ opened, synchronize, reopened, edited ] | ||||
|  | ||||
| permissions: { } | ||||
|  | ||||
| jobs: | ||||
|   build: | ||||
|     name: conventional pull requests | ||||
|     runs-on: ubuntu-22.04 | ||||
|     permissions: | ||||
|       contents: read | ||||
|       pull-requests: read | ||||
|       packages: read | ||||
|       statuses: write | ||||
|     steps: | ||||
|       - name: conventional pull requests check | ||||
|         uses: ytanikin/PRConventionalCommits@1.1.0 | ||||
|         with: | ||||
|           task_types: '["build","chore","ci","docs","feat","fix","perf","refactor","revert","style","test"]' | ||||
|           add_label: 'false' | ||||
							
								
								
									
										41
									
								
								.github/workflows/dotenv-linter-check.yaml
									
									
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										41
									
								
								.github/workflows/dotenv-linter-check.yaml
									
									
									
									
										vendored
									
									
										Normal file
									
								
							| @@ -0,0 +1,41 @@ | ||||
| --- | ||||
| name: dotenv-linter check | ||||
|  | ||||
| on: # yamllint disable-line rule:truthy | ||||
|   pull_request: | ||||
|     types: [opened, edited, reopened, synchronize] | ||||
|  | ||||
| permissions: {} | ||||
|  | ||||
| jobs: | ||||
|   build: | ||||
|     name: dotenv-linter check | ||||
|     runs-on: ubuntu-22.04 | ||||
|     permissions: | ||||
|       contents: read | ||||
|       pull-requests: read | ||||
|       packages: read | ||||
|       statuses: write | ||||
|     steps: | ||||
|       - name: dotenv-linter installation | ||||
|         working-directory: /bin | ||||
|         run: | | ||||
|           set -e | ||||
|  | ||||
|           INSTALL_SCRIPT_URL="https://raw.githubusercontent.com/dotenv-linter/dotenv-linter/ced5b9b28d9a1a309fe900cb992d69dba91205dc/install.sh" | ||||
|           DOTENV_LINTER_VERSION="v3.3.0" | ||||
|  | ||||
|           INSTALL_SCRIPT_CHECKSUM="3b883cbc2bc3b48b6acd794802326a50fcbfeff7d5cd61e457c54fd6072bd809  dotenv-linter-install.sh" | ||||
|           DOTENV_LINTER_CHECKSUM="4bf3efb743a1e3383ab3407ff48c6147dd527dac35b736e224a26425df00a2ee  dotenv-linter" | ||||
|  | ||||
|           wget -O dotenv-linter-install.sh "$INSTALL_SCRIPT_URL" | ||||
|           echo "$INSTALL_SCRIPT_CHECKSUM" | sha256sum --check | ||||
|  | ||||
|           sh dotenv-linter-install.sh -- -b . "$DOTENV_LINTER_VERSION" | ||||
|           echo "$DOTENV_LINTER_CHECKSUM" | sha256sum --check | ||||
|       - name: code checkout | ||||
|         uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 | ||||
|         with: | ||||
|           fetch-depth: 0 | ||||
|       - name: dotenv-linter check | ||||
|         run: dotenv-linter | ||||
							
								
								
									
										38
									
								
								.github/workflows/gitleaks-check.yaml
									
									
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										38
									
								
								.github/workflows/gitleaks-check.yaml
									
									
									
									
										vendored
									
									
										Normal file
									
								
							| @@ -0,0 +1,38 @@ | ||||
| --- | ||||
| name: GitLeaks check | ||||
|  | ||||
| on: # yamllint disable-line rule:truthy | ||||
|   pull_request: | ||||
|     types: [opened, edited, reopened, synchronize] | ||||
|  | ||||
| permissions: {} | ||||
|  | ||||
| jobs: | ||||
|   build: | ||||
|     name: GitLeaks check | ||||
|     runs-on: ubuntu-22.04 | ||||
|     permissions: | ||||
|       contents: read | ||||
|       pull-requests: read | ||||
|       packages: read | ||||
|       statuses: write | ||||
|     steps: | ||||
|       - name: GitLeaks installation | ||||
|         working-directory: /bin | ||||
|         run: | | ||||
|           set -e | ||||
|  | ||||
|           VERSION="v8.22.0" | ||||
|           CHECKSUM="3f95fef7e361adafed2b1bb9c591ba3bc6b595b4f296b346257301b7bf04be15  gitleaks.tar.gz" | ||||
|  | ||||
|           wget -O "gitleaks.tar.gz" "https://github.com/gitleaks/gitleaks/releases/download/$VERSION/gitleaks_8.22.0_linux_arm64.tar.gz" | ||||
|           echo "$CHECKSUM" | sha256sum --check | ||||
|  | ||||
|           tar xzf gitleaks.tar.gz | ||||
|           chmod +x gitleaks | ||||
|       - name: code checkout | ||||
|         uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 | ||||
|         with: | ||||
|           fetch-depth: 0 | ||||
|       - name: GitLeaks check | ||||
|         run: gitleaks git && gitleaks dir | ||||
							
								
								
									
										43
									
								
								.github/workflows/hadolint-check.yaml
									
									
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										43
									
								
								.github/workflows/hadolint-check.yaml
									
									
									
									
										vendored
									
									
										Normal file
									
								
							| @@ -0,0 +1,43 @@ | ||||
| --- | ||||
| name: hadolint check | ||||
|  | ||||
| on: # yamllint disable-line rule:truthy | ||||
|   pull_request: | ||||
|     types: [opened, edited, reopened, synchronize] | ||||
|  | ||||
| permissions: {} | ||||
|  | ||||
| jobs: | ||||
|   build: | ||||
|     name: hadolint check | ||||
|     runs-on: ubuntu-22.04 | ||||
|     permissions: | ||||
|       contents: read | ||||
|       pull-requests: read | ||||
|       packages: read | ||||
|       statuses: write | ||||
|     steps: | ||||
|       - name: hadolint installation | ||||
|         working-directory: /bin | ||||
|         run: | | ||||
|           set -e | ||||
|  | ||||
|           VERSION="v2.12.0" | ||||
|           CHECKSUM="5798551bf19f33951881f15eb238f90aef023f11e7ec7e9f4c37961cb87c5df6 hadolint" | ||||
|  | ||||
|           wget -O hadolint "https://github.com/hadolint/hadolint/releases/download/$VERSION/hadolint-Linux-arm64" | ||||
|           echo "$CHECKSUM" | sha256sum --check | ||||
|  | ||||
|           chmod +x hadolint | ||||
|       - name: code checkout | ||||
|         uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 | ||||
|         with: | ||||
|           fetch-depth: 0 | ||||
|       - name: hadolint check | ||||
|         run: | | ||||
|           STATUS=0 | ||||
|           while read -r file; do | ||||
|             echo "Linting $file..." | ||||
|             hadolint "$file" || STATUS=1 | ||||
|           done < <(find . -name "Dockerfile") | ||||
|           exit $STATUS | ||||
							
								
								
									
										27
									
								
								.github/workflows/htmlhint-check.yaml
									
									
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										27
									
								
								.github/workflows/htmlhint-check.yaml
									
									
									
									
										vendored
									
									
										Normal file
									
								
							| @@ -0,0 +1,27 @@ | ||||
| --- | ||||
| name: htmlhint check | ||||
|  | ||||
| on: # yamllint disable-line rule:truthy | ||||
|   pull_request: | ||||
|     types: [opened, edited, reopened, synchronize] | ||||
|  | ||||
| permissions: {} | ||||
|  | ||||
| jobs: | ||||
|   build: | ||||
|     name: htmlhint check | ||||
|     runs-on: ubuntu-22.04 | ||||
|     permissions: | ||||
|       contents: read | ||||
|       pull-requests: read | ||||
|       packages: read | ||||
|       statuses: write | ||||
|     steps: | ||||
|       - name: code checkout | ||||
|         uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 | ||||
|         with: | ||||
|           fetch-depth: 0 | ||||
|       - name: dependencies installation | ||||
|         run: npm install | ||||
|       - name: htmlhint check | ||||
|         run: npx htmlhint "**/*.html" | ||||
							
								
								
									
										43
									
								
								.github/workflows/lint.yml
									
									
									
									
										vendored
									
									
								
							
							
						
						
									
										43
									
								
								.github/workflows/lint.yml
									
									
									
									
										vendored
									
									
								
							| @@ -1,43 +0,0 @@ | ||||
| name: lint | ||||
|  | ||||
| on: | ||||
|   pull_request: | ||||
|     types: [ opened, synchronize, reopened, edited ] | ||||
|  | ||||
| permissions: { } | ||||
|  | ||||
| jobs: | ||||
|   build: | ||||
|     name: lint | ||||
|     runs-on: ubuntu-latest | ||||
|     permissions: | ||||
|       contents: read | ||||
|       pull-requests: read | ||||
|       packages: read | ||||
|       statuses: write | ||||
|     steps: | ||||
|       - name: checkout code | ||||
|         uses: actions/checkout@v4 | ||||
|         with: | ||||
|           # Super-linter needs the full git history to get the list of files that changed across commits | ||||
|           fetch-depth: 0 | ||||
|       - name: Super-linter check | ||||
|         uses: super-linter/super-linter@v7.0.0 # x-release-please-version | ||||
|         env: | ||||
|           # to report GitHub Actions status checks | ||||
|           GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} | ||||
|           VALIDATE_ALL_CODEBASE: false | ||||
|           VALIDATE_JAVASCRIPT_PRETTIER: false | ||||
|           VALIDATE_JAVASCRIPT_STANDARD: false | ||||
|           VALIDATE_JSCPD: false | ||||
|           VALIDATE_RUST_2015: false | ||||
|           VALIDATE_RUST_2018: false | ||||
|           VALIDATE_SQLFLUFF: false | ||||
|           FIX_CSS_PRETTIER: true | ||||
|           FIX_HTML_PRETTIER: true | ||||
|           FIX_JAVASCRIPT_ES: true | ||||
|           FIX_JSON_PRETTIER: true | ||||
|           FIX_RUST_2021: true | ||||
|           FIX_RUST_CLIPPY: true | ||||
|           FIX_TYPESCRIPT_ES: true | ||||
|           FIX_YAML_PRETTIER: true | ||||
							
								
								
									
										27
									
								
								.github/workflows/markdownlint-check.yaml
									
									
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										27
									
								
								.github/workflows/markdownlint-check.yaml
									
									
									
									
										vendored
									
									
										Normal file
									
								
							| @@ -0,0 +1,27 @@ | ||||
| --- | ||||
| name: markdownlint check | ||||
|  | ||||
| on: # yamllint disable-line rule:truthy | ||||
|   pull_request: | ||||
|     types: [opened, edited, reopened, synchronize] | ||||
|  | ||||
| permissions: {} | ||||
|  | ||||
| jobs: | ||||
|   build: | ||||
|     name: markdownlint check | ||||
|     runs-on: ubuntu-22.04 | ||||
|     permissions: | ||||
|       contents: read | ||||
|       pull-requests: read | ||||
|       packages: read | ||||
|       statuses: write | ||||
|     steps: | ||||
|       - name: code checkout | ||||
|         uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 | ||||
|         with: | ||||
|           fetch-depth: 0 | ||||
|       - name: dependencies installation | ||||
|         run: npm install | ||||
|       - name: markdownlint check | ||||
|         run: npx markdownlint --config .github/config/markdownlint.yaml "**/*.md" --ignore node_modules | ||||
							
								
								
									
										27
									
								
								.github/workflows/prettier-check.yaml
									
									
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										27
									
								
								.github/workflows/prettier-check.yaml
									
									
									
									
										vendored
									
									
										Normal file
									
								
							| @@ -0,0 +1,27 @@ | ||||
| --- | ||||
| name: Prettier check | ||||
|  | ||||
| on: # yamllint disable-line rule:truthy | ||||
|   pull_request: | ||||
|     types: [opened, edited, reopened, synchronize] | ||||
|  | ||||
| permissions: {} | ||||
|  | ||||
| jobs: | ||||
|   build: | ||||
|     name: Prettier check | ||||
|     runs-on: ubuntu-22.04 | ||||
|     permissions: | ||||
|       contents: read | ||||
|       pull-requests: read | ||||
|       packages: read | ||||
|       statuses: write | ||||
|     steps: | ||||
|       - name: code checkout | ||||
|         uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 | ||||
|         with: | ||||
|           fetch-depth: 0 | ||||
|       - name: dependencies installation | ||||
|         run: npm install | ||||
|       - name: Prettier check | ||||
|         run: npx prettier . --check | ||||
							
								
								
									
										43
									
								
								.github/workflows/rust-check.yaml
									
									
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										43
									
								
								.github/workflows/rust-check.yaml
									
									
									
									
										vendored
									
									
										Normal file
									
								
							| @@ -0,0 +1,43 @@ | ||||
| --- | ||||
| name: Rust check | ||||
|  | ||||
| on: # yamllint disable-line rule:truthy | ||||
|   pull_request: | ||||
|     types: [opened, edited, reopened, synchronize] | ||||
|  | ||||
| permissions: {} | ||||
|  | ||||
| jobs: | ||||
|   build: | ||||
|     name: Rust check | ||||
|     runs-on: ubuntu-22.04 | ||||
|     permissions: | ||||
|       contents: read | ||||
|       pull-requests: read | ||||
|       packages: read | ||||
|       statuses: write | ||||
|     steps: | ||||
|       - name: dependencies installation | ||||
|         run: > | ||||
|           apt-get update && apt-get install -y | ||||
|           libgtk-3-dev=3.24.33-1ubuntu2.2 | ||||
|           libjavascriptcoregtk-4.1-dev=2.46.4-0ubuntu0.22.04.1 | ||||
|           libsoup-3.0-dev=3.0.7-0ubuntu1 | ||||
|           libwebkit2gtk-4.1-dev=2.46.4-0ubuntu0.22.04.1 | ||||
|           libxdo-dev=1:3.20160805.1-4 | ||||
|       - name: Rust toolchain installation | ||||
|         uses: dtolnay/rust-toolchain@0579bb9e1907e560c2f263f705f93655a44a07e5 | ||||
|         with: | ||||
|           components: clippy, rustfmt | ||||
|       - name: code checkout | ||||
|         uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 | ||||
|         with: | ||||
|           fetch-depth: 0 | ||||
|       - name: npm build | ||||
|         run: npm install && npm run build | ||||
|       - name: rustfmt check | ||||
|         run: cargo fmt --all --check | ||||
|       - name: Clippy check | ||||
|         run: cargo clippy --all-targets --all-features -- --deny warnings | ||||
|       - name: test check | ||||
|         run: cargo test --all --all-targets --all-features | ||||
							
								
								
									
										28
									
								
								.github/workflows/shellcheck-check.yaml
									
									
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										28
									
								
								.github/workflows/shellcheck-check.yaml
									
									
									
									
										vendored
									
									
										Normal file
									
								
							| @@ -0,0 +1,28 @@ | ||||
| --- | ||||
| name: ShellCheck check | ||||
|  | ||||
| on: # yamllint disable-line rule:truthy | ||||
|   pull_request: | ||||
|     types: [opened, edited, reopened, synchronize] | ||||
|  | ||||
| permissions: {} | ||||
|  | ||||
| jobs: | ||||
|   build: | ||||
|     name: ShellCheck check | ||||
|     runs-on: ubuntu-22.04 | ||||
|     permissions: | ||||
|       contents: read | ||||
|       pull-requests: read | ||||
|       packages: read | ||||
|       statuses: write | ||||
|     steps: | ||||
|       - name: ShellCheck installation | ||||
|         run: apt-get update && apt-get install -y shellcheck=0.8.0-2 | ||||
|       - name: code checkout | ||||
|         uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 | ||||
|         with: | ||||
|           fetch-depth: 0 | ||||
|       - name: ShellCheck check | ||||
|         # https://www.shellcheck.net/wiki/Recursiveness | ||||
|         run: find . -type f -name "*.sh" -print0 | xargs -0 shellcheck | ||||
							
								
								
									
										27
									
								
								.github/workflows/stylelint-check.yaml
									
									
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										27
									
								
								.github/workflows/stylelint-check.yaml
									
									
									
									
										vendored
									
									
										Normal file
									
								
							| @@ -0,0 +1,27 @@ | ||||
| --- | ||||
| name: Stylelint check | ||||
|  | ||||
| on: # yamllint disable-line rule:truthy | ||||
|   pull_request: | ||||
|     types: [opened, edited, reopened, synchronize] | ||||
|  | ||||
| permissions: {} | ||||
|  | ||||
| jobs: | ||||
|   build: | ||||
|     name: Stylelint check | ||||
|     runs-on: ubuntu-22.04 | ||||
|     permissions: | ||||
|       contents: read | ||||
|       pull-requests: read | ||||
|       packages: read | ||||
|       statuses: write | ||||
|     steps: | ||||
|       - name: code checkout | ||||
|         uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 | ||||
|         with: | ||||
|           fetch-depth: 0 | ||||
|       - name: dependencies installation | ||||
|         run: npm install | ||||
|       - name: Stylelint check | ||||
|         run: npx stylelint "**/*.css" | ||||
							
								
								
									
										27
									
								
								.github/workflows/yamllint-check.yaml
									
									
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										27
									
								
								.github/workflows/yamllint-check.yaml
									
									
									
									
										vendored
									
									
										Normal file
									
								
							| @@ -0,0 +1,27 @@ | ||||
| --- | ||||
| name: yamllint check | ||||
|  | ||||
| on: # yamllint disable-line rule:truthy | ||||
|   pull_request: | ||||
|     types: [opened, edited, reopened, synchronize] | ||||
|  | ||||
| permissions: {} | ||||
|  | ||||
| jobs: | ||||
|   build: | ||||
|     name: yamllint check | ||||
|     runs-on: ubuntu-22.04 | ||||
|     permissions: | ||||
|       contents: read | ||||
|       pull-requests: read | ||||
|       packages: read | ||||
|       statuses: write | ||||
|     steps: | ||||
|       - name: yamllint installation | ||||
|         run: apt-get update && apt-get install -y yamllint=1.26.3-1 | ||||
|       - name: code checkout | ||||
|         uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 | ||||
|         with: | ||||
|           fetch-depth: 0 | ||||
|       - name: yamllint check | ||||
|         run: yamllint -c .github/config/yamllint.yaml . | ||||
							
								
								
									
										2
									
								
								.idea/dataSources.local.xml
									
									
									
										generated
									
									
									
								
							
							
						
						
									
										2
									
								
								.idea/dataSources.local.xml
									
									
									
										generated
									
									
									
								
							| @@ -1,6 +1,6 @@ | ||||
| <?xml version="1.0" encoding="UTF-8"?> | ||||
| <project version="4"> | ||||
|   <component name="dataSourceStorageLocal" created-in="RR-243.21565.245"> | ||||
|   <component name="dataSourceStorageLocal" created-in="RR-243.22562.230"> | ||||
|     <data-source name="todo_baggins@localhost" uuid="1658668c-c2b8-426d-a22f-16fbad9eff0b"> | ||||
|       <database-info product="PostgreSQL" version="16.4 (Debian 16.4-1.pgdg120+1)" jdbc-version="4.2" driver-name="PostgreSQL JDBC Driver" driver-version="42.6.0" dbms="POSTGRES" exact-version="16.4" exact-driver-version="42.6"> | ||||
|         <identifier-quote-string>"</identifier-quote-string> | ||||
|   | ||||
							
								
								
									
										8
									
								
								.idea/prettier.xml
									
									
									
										generated
									
									
									
										Normal file
									
								
							
							
						
						
									
										8
									
								
								.idea/prettier.xml
									
									
									
										generated
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,8 @@ | ||||
| <?xml version="1.0" encoding="UTF-8"?> | ||||
| <project version="4"> | ||||
|   <component name="PrettierConfiguration"> | ||||
|     <option name="myConfigurationMode" value="AUTOMATIC" /> | ||||
|     <option name="myRunOnSave" value="true" /> | ||||
|     <option name="myFilesPattern" value="**/*.{html,css,scss,js,ts,cjs,cts,mjs,mts,json,yaml}" /> | ||||
|   </component> | ||||
| </project> | ||||
							
								
								
									
										2
									
								
								.idea/runConfigurations/dev.xml
									
									
									
										generated
									
									
									
								
							
							
						
						
									
										2
									
								
								.idea/runConfigurations/dev.xml
									
									
									
										generated
									
									
									
								
							| @@ -3,7 +3,7 @@ | ||||
|     <deployment type="docker-compose.yml"> | ||||
|       <settings> | ||||
|         <option name="envFilePath" value="" /> | ||||
|         <option name="sourceFilePath" value="docker-compose-dev.yml" /> | ||||
|         <option name="sourceFilePath" value="docker-compose-dev.yaml" /> | ||||
|       </settings> | ||||
|     </deployment> | ||||
|     <method v="2" /> | ||||
|   | ||||
							
								
								
									
										1
									
								
								.prettierrc
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										1
									
								
								.prettierrc
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1 @@ | ||||
| {} | ||||
							
								
								
									
										3
									
								
								.stylelintrc.json
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										3
									
								
								.stylelintrc.json
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,3 @@ | ||||
| { | ||||
|   "extends": ["stylelint-config-standard"] | ||||
| } | ||||
							
								
								
									
										536
									
								
								Cargo.lock
									
									
									
										generated
									
									
									
								
							
							
						
						
									
										536
									
								
								Cargo.lock
									
									
									
										generated
									
									
									
								
							| @@ -23,6 +23,18 @@ version = "2.0.0" | ||||
| source = "registry+https://github.com/rust-lang/crates.io-index" | ||||
| checksum = "512761e0bb2578dd7380c6baaa0f4ce03e84f95e960231d1dec8bf4d7d6e2627" | ||||
|  | ||||
| [[package]] | ||||
| name = "ahash" | ||||
| version = "0.8.11" | ||||
| source = "registry+https://github.com/rust-lang/crates.io-index" | ||||
| checksum = "e89da841a80418a9b391ebaea17f5c112ffaaa96f621d2c285b5174da76b9011" | ||||
| dependencies = [ | ||||
|  "cfg-if", | ||||
|  "once_cell", | ||||
|  "version_check", | ||||
|  "zerocopy", | ||||
| ] | ||||
|  | ||||
| [[package]] | ||||
| name = "aho-corasick" | ||||
| version = "1.1.3" | ||||
| @@ -32,6 +44,12 @@ dependencies = [ | ||||
|  "memchr", | ||||
| ] | ||||
|  | ||||
| [[package]] | ||||
| name = "allocator-api2" | ||||
| version = "0.2.21" | ||||
| source = "registry+https://github.com/rust-lang/crates.io-index" | ||||
| checksum = "683d7910e743518b0e34f1186f92494becacb047c7b6bf616c96772180fef923" | ||||
|  | ||||
| [[package]] | ||||
| name = "android-tzdata" | ||||
| version = "0.1.1" | ||||
| @@ -64,6 +82,12 @@ dependencies = [ | ||||
|  "zbus", | ||||
| ] | ||||
|  | ||||
| [[package]] | ||||
| name = "askama_escape" | ||||
| version = "0.10.3" | ||||
| source = "registry+https://github.com/rust-lang/crates.io-index" | ||||
| checksum = "619743e34b5ba4e9703bba34deac3427c72507c7159f5fd030aea8cac0cfe341" | ||||
|  | ||||
| [[package]] | ||||
| name = "async-broadcast" | ||||
| version = "0.7.1" | ||||
| @@ -312,6 +336,77 @@ version = "1.3.0" | ||||
| source = "registry+https://github.com/rust-lang/crates.io-index" | ||||
| checksum = "0c4b4d0bd25bd0b74681c0ad21497610ce1b7c91b1022cd21c80c6fbdd9476b0" | ||||
|  | ||||
| [[package]] | ||||
| name = "axum" | ||||
| version = "0.7.9" | ||||
| source = "registry+https://github.com/rust-lang/crates.io-index" | ||||
| checksum = "edca88bc138befd0323b20752846e6587272d3b03b0343c8ea28a6f819e6e71f" | ||||
| dependencies = [ | ||||
|  "async-trait", | ||||
|  "axum-core", | ||||
|  "axum-macros", | ||||
|  "base64", | ||||
|  "bytes", | ||||
|  "futures-util", | ||||
|  "http", | ||||
|  "http-body", | ||||
|  "http-body-util", | ||||
|  "hyper", | ||||
|  "hyper-util", | ||||
|  "itoa 1.0.11", | ||||
|  "matchit", | ||||
|  "memchr", | ||||
|  "mime", | ||||
|  "multer", | ||||
|  "percent-encoding", | ||||
|  "pin-project-lite", | ||||
|  "rustversion", | ||||
|  "serde", | ||||
|  "serde_json", | ||||
|  "serde_path_to_error", | ||||
|  "serde_urlencoded", | ||||
|  "sha1", | ||||
|  "sync_wrapper", | ||||
|  "tokio", | ||||
|  "tokio-tungstenite", | ||||
|  "tower 0.5.2", | ||||
|  "tower-layer", | ||||
|  "tower-service", | ||||
|  "tracing", | ||||
| ] | ||||
|  | ||||
| [[package]] | ||||
| name = "axum-core" | ||||
| version = "0.4.5" | ||||
| source = "registry+https://github.com/rust-lang/crates.io-index" | ||||
| checksum = "09f2bd6146b97ae3359fa0cc6d6b376d9539582c7b4220f041a33ec24c226199" | ||||
| dependencies = [ | ||||
|  "async-trait", | ||||
|  "bytes", | ||||
|  "futures-util", | ||||
|  "http", | ||||
|  "http-body", | ||||
|  "http-body-util", | ||||
|  "mime", | ||||
|  "pin-project-lite", | ||||
|  "rustversion", | ||||
|  "sync_wrapper", | ||||
|  "tower-layer", | ||||
|  "tower-service", | ||||
|  "tracing", | ||||
| ] | ||||
|  | ||||
| [[package]] | ||||
| name = "axum-macros" | ||||
| version = "0.4.2" | ||||
| source = "registry+https://github.com/rust-lang/crates.io-index" | ||||
| checksum = "57d123550fa8d071b7255cb0cc04dc302baa6c8c4a79f55701552684d8399bce" | ||||
| dependencies = [ | ||||
|  "proc-macro2", | ||||
|  "quote", | ||||
|  "syn 2.0.74", | ||||
| ] | ||||
|  | ||||
| [[package]] | ||||
| name = "backtrace" | ||||
| version = "0.3.73" | ||||
| @@ -544,7 +639,7 @@ dependencies = [ | ||||
|  "cocoa-foundation 0.1.2", | ||||
|  "core-foundation 0.9.4", | ||||
|  "core-graphics 0.23.2", | ||||
|  "foreign-types", | ||||
|  "foreign-types 0.5.0", | ||||
|  "libc", | ||||
|  "objc", | ||||
| ] | ||||
| @@ -560,7 +655,7 @@ dependencies = [ | ||||
|  "cocoa-foundation 0.2.0", | ||||
|  "core-foundation 0.10.0", | ||||
|  "core-graphics 0.24.0", | ||||
|  "foreign-types", | ||||
|  "foreign-types 0.5.0", | ||||
|  "libc", | ||||
|  "objc", | ||||
| ] | ||||
| @@ -713,7 +808,7 @@ dependencies = [ | ||||
|  "bitflags 1.3.2", | ||||
|  "core-foundation 0.9.4", | ||||
|  "core-graphics-types 0.1.3", | ||||
|  "foreign-types", | ||||
|  "foreign-types 0.5.0", | ||||
|  "libc", | ||||
| ] | ||||
|  | ||||
| @@ -726,7 +821,7 @@ dependencies = [ | ||||
|  "bitflags 2.6.0", | ||||
|  "core-foundation 0.10.0", | ||||
|  "core-graphics-types 0.2.0", | ||||
|  "foreign-types", | ||||
|  "foreign-types 0.5.0", | ||||
|  "libc", | ||||
| ] | ||||
|  | ||||
| @@ -991,10 +1086,12 @@ dependencies = [ | ||||
|  "dioxus-history", | ||||
|  "dioxus-hooks", | ||||
|  "dioxus-html", | ||||
|  "dioxus-liveview", | ||||
|  "dioxus-logger", | ||||
|  "dioxus-mobile", | ||||
|  "dioxus-router", | ||||
|  "dioxus-signals", | ||||
|  "dioxus-ssr", | ||||
|  "dioxus-web", | ||||
|  "manganis", | ||||
|  "serde", | ||||
| @@ -1123,7 +1220,7 @@ dependencies = [ | ||||
|  "serde", | ||||
|  "serde_json", | ||||
|  "tracing", | ||||
|  "tungstenite", | ||||
|  "tungstenite 0.23.0", | ||||
|  "warnings", | ||||
| ] | ||||
|  | ||||
| @@ -1162,23 +1259,41 @@ version = "0.6.0" | ||||
| source = "registry+https://github.com/rust-lang/crates.io-index" | ||||
| checksum = "ffe83189fc0139a34a4cc195aa3eb917f5b6a3317bf3f4183d9e8f1e110a9bab" | ||||
| dependencies = [ | ||||
|  "async-trait", | ||||
|  "axum", | ||||
|  "base64", | ||||
|  "bytes", | ||||
|  "ciborium", | ||||
|  "dioxus-cli-config", | ||||
|  "dioxus-desktop", | ||||
|  "dioxus-devtools", | ||||
|  "dioxus-history", | ||||
|  "dioxus-interpreter-js", | ||||
|  "dioxus-isrg", | ||||
|  "dioxus-lib", | ||||
|  "dioxus-mobile", | ||||
|  "dioxus-ssr", | ||||
|  "dioxus-web", | ||||
|  "dioxus_server_macro", | ||||
|  "futures-channel", | ||||
|  "futures-util", | ||||
|  "generational-box", | ||||
|  "http", | ||||
|  "hyper", | ||||
|  "once_cell", | ||||
|  "parking_lot", | ||||
|  "pin-project", | ||||
|  "serde", | ||||
|  "server_fn", | ||||
|  "thiserror", | ||||
|  "tokio", | ||||
|  "tokio-stream", | ||||
|  "tokio-util", | ||||
|  "tower 0.4.13", | ||||
|  "tower-http", | ||||
|  "tower-layer", | ||||
|  "tracing", | ||||
|  "tracing-futures", | ||||
|  "web-sys", | ||||
| ] | ||||
|  | ||||
| @@ -1277,6 +1392,20 @@ dependencies = [ | ||||
|  "web-sys", | ||||
| ] | ||||
|  | ||||
| [[package]] | ||||
| name = "dioxus-isrg" | ||||
| version = "0.6.0" | ||||
| source = "registry+https://github.com/rust-lang/crates.io-index" | ||||
| checksum = "8b9777b9a71c9ecede300e52d217b800c5f884654b5af2da2a534c7f8bded7d0" | ||||
| dependencies = [ | ||||
|  "chrono", | ||||
|  "http", | ||||
|  "lru", | ||||
|  "rustc-hash", | ||||
|  "thiserror", | ||||
|  "tracing", | ||||
| ] | ||||
|  | ||||
| [[package]] | ||||
| name = "dioxus-lib" | ||||
| version = "0.6.0" | ||||
| @@ -1294,6 +1423,34 @@ dependencies = [ | ||||
|  "dioxus-signals", | ||||
| ] | ||||
|  | ||||
| [[package]] | ||||
| name = "dioxus-liveview" | ||||
| version = "0.6.0" | ||||
| source = "registry+https://github.com/rust-lang/crates.io-index" | ||||
| checksum = "0df966bf26040e5875908caa4e59bb2152f3532cca97937a34dd1057168c023f" | ||||
| dependencies = [ | ||||
|  "axum", | ||||
|  "dioxus-cli-config", | ||||
|  "dioxus-core", | ||||
|  "dioxus-devtools", | ||||
|  "dioxus-document", | ||||
|  "dioxus-history", | ||||
|  "dioxus-html", | ||||
|  "dioxus-interpreter-js", | ||||
|  "futures-channel", | ||||
|  "futures-util", | ||||
|  "generational-box", | ||||
|  "rustc-hash", | ||||
|  "serde", | ||||
|  "serde_json", | ||||
|  "slab", | ||||
|  "thiserror", | ||||
|  "tokio", | ||||
|  "tokio-stream", | ||||
|  "tokio-util", | ||||
|  "tracing", | ||||
| ] | ||||
|  | ||||
| [[package]] | ||||
| name = "dioxus-logger" | ||||
| version = "0.6.0" | ||||
| @@ -1389,6 +1546,18 @@ dependencies = [ | ||||
|  "warnings", | ||||
| ] | ||||
|  | ||||
| [[package]] | ||||
| name = "dioxus-ssr" | ||||
| version = "0.6.0" | ||||
| source = "registry+https://github.com/rust-lang/crates.io-index" | ||||
| checksum = "e6cd3d309a60d654bafcf49b32b96fa72da6d9155582eba9b5c608c07ab0c850" | ||||
| dependencies = [ | ||||
|  "askama_escape", | ||||
|  "dioxus-core", | ||||
|  "dioxus-core-types", | ||||
|  "rustc-hash", | ||||
| ] | ||||
|  | ||||
| [[package]] | ||||
| name = "dioxus-web" | ||||
| version = "0.6.0" | ||||
| @@ -1547,6 +1716,15 @@ version = "1.13.0" | ||||
| source = "registry+https://github.com/rust-lang/crates.io-index" | ||||
| checksum = "60b1af1c220855b6ceac025d3f6ecdd2b7c4894bfe9cd9bda4fbb4bc7c0d4cf0" | ||||
|  | ||||
| [[package]] | ||||
| name = "encoding_rs" | ||||
| version = "0.8.35" | ||||
| source = "registry+https://github.com/rust-lang/crates.io-index" | ||||
| checksum = "75030f3c4f45dafd7586dd6780965a8c7e8e285a5ecb86713e63a79c5b2766f3" | ||||
| dependencies = [ | ||||
|  "cfg-if", | ||||
| ] | ||||
|  | ||||
| [[package]] | ||||
| name = "endi" | ||||
| version = "1.1.0" | ||||
| @@ -1755,6 +1933,21 @@ version = "1.0.7" | ||||
| source = "registry+https://github.com/rust-lang/crates.io-index" | ||||
| checksum = "3f9eec918d3f24069decb9af1554cad7c880e2da24a9afd88aca000531ab82c1" | ||||
|  | ||||
| [[package]] | ||||
| name = "foldhash" | ||||
| version = "0.1.3" | ||||
| source = "registry+https://github.com/rust-lang/crates.io-index" | ||||
| checksum = "f81ec6369c545a7d40e4589b5597581fa1c441fe1cce96dd1de43159910a36a2" | ||||
|  | ||||
| [[package]] | ||||
| name = "foreign-types" | ||||
| version = "0.3.2" | ||||
| source = "registry+https://github.com/rust-lang/crates.io-index" | ||||
| checksum = "f6f339eb8adc052cd2ca78910fda869aefa38d22d5cb648e6485e4d3fc06f3b1" | ||||
| dependencies = [ | ||||
|  "foreign-types-shared 0.1.1", | ||||
| ] | ||||
|  | ||||
| [[package]] | ||||
| name = "foreign-types" | ||||
| version = "0.5.0" | ||||
| @@ -1762,7 +1955,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" | ||||
| checksum = "d737d9aa519fb7b749cbc3b962edcf310a8dd1f4b67c91c4f83975dbdd17d965" | ||||
| dependencies = [ | ||||
|  "foreign-types-macros", | ||||
|  "foreign-types-shared", | ||||
|  "foreign-types-shared 0.3.1", | ||||
| ] | ||||
|  | ||||
| [[package]] | ||||
| @@ -1776,6 +1969,12 @@ dependencies = [ | ||||
|  "syn 2.0.74", | ||||
| ] | ||||
|  | ||||
| [[package]] | ||||
| name = "foreign-types-shared" | ||||
| version = "0.1.1" | ||||
| source = "registry+https://github.com/rust-lang/crates.io-index" | ||||
| checksum = "00b0228411908ca8685dba7fc2cdd70ec9990a6e753e89b6ac91a84c40fbaf4b" | ||||
|  | ||||
| [[package]] | ||||
| name = "foreign-types-shared" | ||||
| version = "0.3.1" | ||||
| @@ -2300,6 +2499,21 @@ name = "hashbrown" | ||||
| version = "0.14.5" | ||||
| source = "registry+https://github.com/rust-lang/crates.io-index" | ||||
| checksum = "e5274423e17b7c9fc20b6e7e208532f9b19825d82dfd615708b70edd83df41f1" | ||||
| dependencies = [ | ||||
|  "ahash", | ||||
|  "allocator-api2", | ||||
| ] | ||||
|  | ||||
| [[package]] | ||||
| name = "hashbrown" | ||||
| version = "0.15.2" | ||||
| source = "registry+https://github.com/rust-lang/crates.io-index" | ||||
| checksum = "bf151400ff0baff5465007dd2f3e717f3fe502074ca563069ce3a6629d07b289" | ||||
| dependencies = [ | ||||
|  "allocator-api2", | ||||
|  "equivalent", | ||||
|  "foldhash", | ||||
| ] | ||||
|  | ||||
| [[package]] | ||||
| name = "heck" | ||||
| @@ -2388,12 +2602,24 @@ dependencies = [ | ||||
|  "pin-project-lite", | ||||
| ] | ||||
|  | ||||
| [[package]] | ||||
| name = "http-range-header" | ||||
| version = "0.4.2" | ||||
| source = "registry+https://github.com/rust-lang/crates.io-index" | ||||
| checksum = "9171a2ea8a68358193d15dd5d70c1c10a2afc3e7e4c5bc92bc9f025cebd7359c" | ||||
|  | ||||
| [[package]] | ||||
| name = "httparse" | ||||
| version = "1.9.4" | ||||
| source = "registry+https://github.com/rust-lang/crates.io-index" | ||||
| checksum = "0fcc0b4a115bf80b728eb8ea024ad5bd707b615bfed49e0665b6e0f86fd082d9" | ||||
|  | ||||
| [[package]] | ||||
| name = "httpdate" | ||||
| version = "1.0.3" | ||||
| source = "registry+https://github.com/rust-lang/crates.io-index" | ||||
| checksum = "df3b46402a9d5adb4c86a0cf463f42e19994e3ee891101b1841f30a545cb49a9" | ||||
|  | ||||
| [[package]] | ||||
| name = "hyper" | ||||
| version = "1.4.1" | ||||
| @@ -2406,6 +2632,7 @@ dependencies = [ | ||||
|  "http", | ||||
|  "http-body", | ||||
|  "httparse", | ||||
|  "httpdate", | ||||
|  "itoa 1.0.11", | ||||
|  "pin-project-lite", | ||||
|  "smallvec", | ||||
| @@ -2413,6 +2640,22 @@ dependencies = [ | ||||
|  "want", | ||||
| ] | ||||
|  | ||||
| [[package]] | ||||
| name = "hyper-tls" | ||||
| version = "0.6.0" | ||||
| source = "registry+https://github.com/rust-lang/crates.io-index" | ||||
| checksum = "70206fc6890eaca9fde8a0bf71caa2ddfc9fe045ac9e5c70df101a7dbde866e0" | ||||
| dependencies = [ | ||||
|  "bytes", | ||||
|  "http-body-util", | ||||
|  "hyper", | ||||
|  "hyper-util", | ||||
|  "native-tls", | ||||
|  "tokio", | ||||
|  "tokio-native-tls", | ||||
|  "tower-service", | ||||
| ] | ||||
|  | ||||
| [[package]] | ||||
| name = "hyper-util" | ||||
| version = "0.1.7" | ||||
| @@ -2428,7 +2671,7 @@ dependencies = [ | ||||
|  "pin-project-lite", | ||||
|  "socket2 0.5.7", | ||||
|  "tokio", | ||||
|  "tower", | ||||
|  "tower 0.4.13", | ||||
|  "tower-service", | ||||
|  "tracing", | ||||
| ] | ||||
| @@ -2673,6 +2916,12 @@ dependencies = [ | ||||
|  "unic-langid", | ||||
| ] | ||||
|  | ||||
| [[package]] | ||||
| name = "inventory" | ||||
| version = "0.3.15" | ||||
| source = "registry+https://github.com/rust-lang/crates.io-index" | ||||
| checksum = "f958d3d68f4167080a18141e10381e7634563984a537f2a49a30fd8e53ac5767" | ||||
|  | ||||
| [[package]] | ||||
| name = "io-lifetimes" | ||||
| version = "1.0.11" | ||||
| @@ -2913,6 +3162,15 @@ version = "0.1.0" | ||||
| source = "registry+https://github.com/rust-lang/crates.io-index" | ||||
| checksum = "b3bd0dd2cd90571056fdb71f6275fada10131182f84899f4b2a916e565d81d86" | ||||
|  | ||||
| [[package]] | ||||
| name = "lru" | ||||
| version = "0.12.5" | ||||
| source = "registry+https://github.com/rust-lang/crates.io-index" | ||||
| checksum = "234cf4f4a04dc1f57e24b96cc0cd600cf2af460d4161ac5ecdd0af8e1f3b2a38" | ||||
| dependencies = [ | ||||
|  "hashbrown 0.15.2", | ||||
| ] | ||||
|  | ||||
| [[package]] | ||||
| name = "mac" | ||||
| version = "0.1.1" | ||||
| @@ -2983,6 +3241,12 @@ version = "0.1.10" | ||||
| source = "registry+https://github.com/rust-lang/crates.io-index" | ||||
| checksum = "2532096657941c2fea9c289d370a250971c689d4f143798ff67113ec042024a5" | ||||
|  | ||||
| [[package]] | ||||
| name = "matchit" | ||||
| version = "0.7.3" | ||||
| source = "registry+https://github.com/rust-lang/crates.io-index" | ||||
| checksum = "0e7465ac9959cc2b1404e8e2367b43684a6d13790fe23056cc8c6c5a6b7bcb94" | ||||
|  | ||||
| [[package]] | ||||
| name = "memchr" | ||||
| version = "2.7.4" | ||||
| @@ -3104,6 +3368,40 @@ dependencies = [ | ||||
|  "windows-sys 0.59.0", | ||||
| ] | ||||
|  | ||||
| [[package]] | ||||
| name = "multer" | ||||
| version = "3.1.0" | ||||
| source = "registry+https://github.com/rust-lang/crates.io-index" | ||||
| checksum = "83e87776546dc87511aa5ee218730c92b666d7264ab6ed41f9d215af9cd5224b" | ||||
| dependencies = [ | ||||
|  "bytes", | ||||
|  "encoding_rs", | ||||
|  "futures-util", | ||||
|  "http", | ||||
|  "httparse", | ||||
|  "memchr", | ||||
|  "mime", | ||||
|  "spin", | ||||
|  "version_check", | ||||
| ] | ||||
|  | ||||
| [[package]] | ||||
| name = "native-tls" | ||||
| version = "0.2.12" | ||||
| source = "registry+https://github.com/rust-lang/crates.io-index" | ||||
| checksum = "a8614eb2c83d59d1c8cc974dd3f920198647674a0a035e1af1fa58707e317466" | ||||
| dependencies = [ | ||||
|  "libc", | ||||
|  "log", | ||||
|  "openssl", | ||||
|  "openssl-probe", | ||||
|  "openssl-sys", | ||||
|  "schannel", | ||||
|  "security-framework", | ||||
|  "security-framework-sys", | ||||
|  "tempfile", | ||||
| ] | ||||
|  | ||||
| [[package]] | ||||
| name = "ndk" | ||||
| version = "0.9.0" | ||||
| @@ -3347,6 +3645,50 @@ version = "1.19.0" | ||||
| source = "registry+https://github.com/rust-lang/crates.io-index" | ||||
| checksum = "3fdb12b2476b595f9358c5161aa467c2438859caa136dec86c26fdd2efe17b92" | ||||
|  | ||||
| [[package]] | ||||
| name = "openssl" | ||||
| version = "0.10.68" | ||||
| source = "registry+https://github.com/rust-lang/crates.io-index" | ||||
| checksum = "6174bc48f102d208783c2c84bf931bb75927a617866870de8a4ea85597f871f5" | ||||
| dependencies = [ | ||||
|  "bitflags 2.6.0", | ||||
|  "cfg-if", | ||||
|  "foreign-types 0.3.2", | ||||
|  "libc", | ||||
|  "once_cell", | ||||
|  "openssl-macros", | ||||
|  "openssl-sys", | ||||
| ] | ||||
|  | ||||
| [[package]] | ||||
| name = "openssl-macros" | ||||
| version = "0.1.1" | ||||
| source = "registry+https://github.com/rust-lang/crates.io-index" | ||||
| checksum = "a948666b637a0f465e8564c73e89d4dde00d72d4d473cc972f390fc3dcee7d9c" | ||||
| dependencies = [ | ||||
|  "proc-macro2", | ||||
|  "quote", | ||||
|  "syn 2.0.74", | ||||
| ] | ||||
|  | ||||
| [[package]] | ||||
| name = "openssl-probe" | ||||
| version = "0.1.5" | ||||
| source = "registry+https://github.com/rust-lang/crates.io-index" | ||||
| checksum = "ff011a302c396a5197692431fc1948019154afc178baf7d8e37367442a4601cf" | ||||
|  | ||||
| [[package]] | ||||
| name = "openssl-sys" | ||||
| version = "0.9.104" | ||||
| source = "registry+https://github.com/rust-lang/crates.io-index" | ||||
| checksum = "45abf306cbf99debc8195b66b7346498d7b10c210de50418b5ccd7ceba08c741" | ||||
| dependencies = [ | ||||
|  "cc", | ||||
|  "libc", | ||||
|  "pkg-config", | ||||
|  "vcpkg", | ||||
| ] | ||||
|  | ||||
| [[package]] | ||||
| name = "option-ext" | ||||
| version = "0.2.0" | ||||
| @@ -3908,20 +4250,24 @@ dependencies = [ | ||||
|  "http-body", | ||||
|  "http-body-util", | ||||
|  "hyper", | ||||
|  "hyper-tls", | ||||
|  "hyper-util", | ||||
|  "ipnet", | ||||
|  "js-sys", | ||||
|  "log", | ||||
|  "mime", | ||||
|  "mime_guess", | ||||
|  "native-tls", | ||||
|  "once_cell", | ||||
|  "percent-encoding", | ||||
|  "pin-project-lite", | ||||
|  "rustls-pemfile", | ||||
|  "serde", | ||||
|  "serde_json", | ||||
|  "serde_urlencoded", | ||||
|  "sync_wrapper", | ||||
|  "tokio", | ||||
|  "tokio-native-tls", | ||||
|  "tokio-util", | ||||
|  "tower-service", | ||||
|  "url", | ||||
| @@ -4003,6 +4349,21 @@ dependencies = [ | ||||
|  "windows-sys 0.52.0", | ||||
| ] | ||||
|  | ||||
| [[package]] | ||||
| name = "rustls-pemfile" | ||||
| version = "2.2.0" | ||||
| source = "registry+https://github.com/rust-lang/crates.io-index" | ||||
| checksum = "dce314e5fee3f39953d46bb63bb8a46d40c2f8fb7cc5a3b6cab2bde9721d6e50" | ||||
| dependencies = [ | ||||
|  "rustls-pki-types", | ||||
| ] | ||||
|  | ||||
| [[package]] | ||||
| name = "rustls-pki-types" | ||||
| version = "1.10.1" | ||||
| source = "registry+https://github.com/rust-lang/crates.io-index" | ||||
| checksum = "d2bf47e6ff922db3825eb750c4e2ff784c6ff8fb9e13046ef6a1d1c5401b0b37" | ||||
|  | ||||
| [[package]] | ||||
| name = "rustversion" | ||||
| version = "1.0.17" | ||||
| @@ -4024,12 +4385,44 @@ dependencies = [ | ||||
|  "winapi-util", | ||||
| ] | ||||
|  | ||||
| [[package]] | ||||
| name = "schannel" | ||||
| version = "0.1.27" | ||||
| source = "registry+https://github.com/rust-lang/crates.io-index" | ||||
| checksum = "1f29ebaa345f945cec9fbbc532eb307f0fdad8161f281b6369539c8d84876b3d" | ||||
| dependencies = [ | ||||
|  "windows-sys 0.59.0", | ||||
| ] | ||||
|  | ||||
| [[package]] | ||||
| name = "scopeguard" | ||||
| version = "1.2.0" | ||||
| source = "registry+https://github.com/rust-lang/crates.io-index" | ||||
| checksum = "94143f37725109f92c262ed2cf5e59bce7498c01bcc1502d7b9afe439a4e9f49" | ||||
|  | ||||
| [[package]] | ||||
| name = "security-framework" | ||||
| version = "2.11.1" | ||||
| source = "registry+https://github.com/rust-lang/crates.io-index" | ||||
| checksum = "897b2245f0b511c87893af39b033e5ca9cce68824c4d7e7630b5a1d339658d02" | ||||
| dependencies = [ | ||||
|  "bitflags 2.6.0", | ||||
|  "core-foundation 0.9.4", | ||||
|  "core-foundation-sys", | ||||
|  "libc", | ||||
|  "security-framework-sys", | ||||
| ] | ||||
|  | ||||
| [[package]] | ||||
| name = "security-framework-sys" | ||||
| version = "2.13.0" | ||||
| source = "registry+https://github.com/rust-lang/crates.io-index" | ||||
| checksum = "1863fd3768cd83c56a7f60faa4dc0d403f1b6df0a38c3c25f44b7894e45370d5" | ||||
| dependencies = [ | ||||
|  "core-foundation-sys", | ||||
|  "libc", | ||||
| ] | ||||
|  | ||||
| [[package]] | ||||
| name = "selectors" | ||||
| version = "0.22.0" | ||||
| @@ -4123,6 +4516,16 @@ dependencies = [ | ||||
|  "serde", | ||||
| ] | ||||
|  | ||||
| [[package]] | ||||
| name = "serde_path_to_error" | ||||
| version = "0.1.16" | ||||
| source = "registry+https://github.com/rust-lang/crates.io-index" | ||||
| checksum = "af99884400da37c88f5e9146b7f1fd0fbcae8f6eec4e9da38b67d05486f814a6" | ||||
| dependencies = [ | ||||
|  "itoa 1.0.11", | ||||
|  "serde", | ||||
| ] | ||||
|  | ||||
| [[package]] | ||||
| name = "serde_qs" | ||||
| version = "0.12.0" | ||||
| @@ -4202,12 +4605,16 @@ version = "0.6.14" | ||||
| source = "registry+https://github.com/rust-lang/crates.io-index" | ||||
| checksum = "024b400db1aca5bd4188714f7bbbf7a2e1962b9a12a80b2a21e937e509086963" | ||||
| dependencies = [ | ||||
|  "axum", | ||||
|  "bytes", | ||||
|  "const_format", | ||||
|  "dashmap", | ||||
|  "futures", | ||||
|  "gloo-net", | ||||
|  "http", | ||||
|  "http-body-util", | ||||
|  "hyper", | ||||
|  "inventory", | ||||
|  "js-sys", | ||||
|  "once_cell", | ||||
|  "reqwest", | ||||
| @@ -4217,6 +4624,8 @@ dependencies = [ | ||||
|  "serde_qs", | ||||
|  "server_fn_macro_default", | ||||
|  "thiserror", | ||||
|  "tower 0.4.13", | ||||
|  "tower-layer", | ||||
|  "url", | ||||
|  "wasm-bindgen", | ||||
|  "wasm-bindgen-futures", | ||||
| @@ -4427,6 +4836,12 @@ dependencies = [ | ||||
|  "system-deps", | ||||
| ] | ||||
|  | ||||
| [[package]] | ||||
| name = "spin" | ||||
| version = "0.9.8" | ||||
| source = "registry+https://github.com/rust-lang/crates.io-index" | ||||
| checksum = "6980e8d7511241f8acf4aebddbb1ff938df5eebe98691418c4468d0b72a96a67" | ||||
|  | ||||
| [[package]] | ||||
| name = "stable_deref_trait" | ||||
| version = "1.2.0" | ||||
| @@ -4757,6 +5172,40 @@ dependencies = [ | ||||
|  "syn 2.0.74", | ||||
| ] | ||||
|  | ||||
| [[package]] | ||||
| name = "tokio-native-tls" | ||||
| version = "0.3.1" | ||||
| source = "registry+https://github.com/rust-lang/crates.io-index" | ||||
| checksum = "bbae76ab933c85776efabc971569dd6119c580d8f5d448769dec1764bf796ef2" | ||||
| dependencies = [ | ||||
|  "native-tls", | ||||
|  "tokio", | ||||
| ] | ||||
|  | ||||
| [[package]] | ||||
| name = "tokio-stream" | ||||
| version = "0.1.17" | ||||
| source = "registry+https://github.com/rust-lang/crates.io-index" | ||||
| checksum = "eca58d7bba4a75707817a2c44174253f9236b2d5fbd055602e9d5c07c139a047" | ||||
| dependencies = [ | ||||
|  "futures-core", | ||||
|  "pin-project-lite", | ||||
|  "tokio", | ||||
|  "tokio-util", | ||||
| ] | ||||
|  | ||||
| [[package]] | ||||
| name = "tokio-tungstenite" | ||||
| version = "0.24.0" | ||||
| source = "registry+https://github.com/rust-lang/crates.io-index" | ||||
| checksum = "edc5f74e248dc973e0dbb7b74c7e0d6fcc301c694ff50049504004ef4d0cdcd9" | ||||
| dependencies = [ | ||||
|  "futures-util", | ||||
|  "log", | ||||
|  "tokio", | ||||
|  "tungstenite 0.24.0", | ||||
| ] | ||||
|  | ||||
| [[package]] | ||||
| name = "tokio-util" | ||||
| version = "0.7.11" | ||||
| @@ -4766,6 +5215,8 @@ dependencies = [ | ||||
|  "bytes", | ||||
|  "futures-core", | ||||
|  "futures-sink", | ||||
|  "futures-util", | ||||
|  "hashbrown 0.14.5", | ||||
|  "pin-project-lite", | ||||
|  "tokio", | ||||
| ] | ||||
| @@ -4828,6 +5279,48 @@ dependencies = [ | ||||
|  "tokio", | ||||
|  "tower-layer", | ||||
|  "tower-service", | ||||
|  "tracing", | ||||
| ] | ||||
|  | ||||
| [[package]] | ||||
| name = "tower" | ||||
| version = "0.5.2" | ||||
| source = "registry+https://github.com/rust-lang/crates.io-index" | ||||
| checksum = "d039ad9159c98b70ecfd540b2573b97f7f52c3e8d9f8ad57a24b916a536975f9" | ||||
| dependencies = [ | ||||
|  "futures-core", | ||||
|  "futures-util", | ||||
|  "pin-project-lite", | ||||
|  "sync_wrapper", | ||||
|  "tokio", | ||||
|  "tower-layer", | ||||
|  "tower-service", | ||||
|  "tracing", | ||||
| ] | ||||
|  | ||||
| [[package]] | ||||
| name = "tower-http" | ||||
| version = "0.5.2" | ||||
| source = "registry+https://github.com/rust-lang/crates.io-index" | ||||
| checksum = "1e9cd434a998747dd2c4276bc96ee2e0c7a2eadf3cae88e52be55a05fa9053f5" | ||||
| dependencies = [ | ||||
|  "bitflags 2.6.0", | ||||
|  "bytes", | ||||
|  "futures-util", | ||||
|  "http", | ||||
|  "http-body", | ||||
|  "http-body-util", | ||||
|  "http-range-header", | ||||
|  "httpdate", | ||||
|  "mime", | ||||
|  "mime_guess", | ||||
|  "percent-encoding", | ||||
|  "pin-project-lite", | ||||
|  "tokio", | ||||
|  "tokio-util", | ||||
|  "tower-layer", | ||||
|  "tower-service", | ||||
|  "tracing", | ||||
| ] | ||||
|  | ||||
| [[package]] | ||||
| @@ -4848,6 +5341,7 @@ version = "0.1.40" | ||||
| source = "registry+https://github.com/rust-lang/crates.io-index" | ||||
| checksum = "c3523ab5a71916ccf420eebdf5521fcef02141234bbc0b8a49f2fdc4544364ef" | ||||
| dependencies = [ | ||||
|  "log", | ||||
|  "pin-project-lite", | ||||
|  "tracing-attributes", | ||||
|  "tracing-core", | ||||
| @@ -4873,6 +5367,16 @@ dependencies = [ | ||||
|  "once_cell", | ||||
| ] | ||||
|  | ||||
| [[package]] | ||||
| name = "tracing-futures" | ||||
| version = "0.2.5" | ||||
| source = "registry+https://github.com/rust-lang/crates.io-index" | ||||
| checksum = "97d095ae15e245a057c8e8451bab9b3ee1e1f68e9ba2b4fbc18d0ac5237835f2" | ||||
| dependencies = [ | ||||
|  "pin-project", | ||||
|  "tracing", | ||||
| ] | ||||
|  | ||||
| [[package]] | ||||
| name = "tracing-subscriber" | ||||
| version = "0.3.18" | ||||
| @@ -4939,6 +5443,24 @@ dependencies = [ | ||||
|  "utf-8", | ||||
| ] | ||||
|  | ||||
| [[package]] | ||||
| name = "tungstenite" | ||||
| version = "0.24.0" | ||||
| source = "registry+https://github.com/rust-lang/crates.io-index" | ||||
| checksum = "18e5b8366ee7a95b16d32197d0b2604b43a0be89dc5fac9f8e96ccafbaedda8a" | ||||
| dependencies = [ | ||||
|  "byteorder", | ||||
|  "bytes", | ||||
|  "data-encoding", | ||||
|  "http", | ||||
|  "httparse", | ||||
|  "log", | ||||
|  "rand 0.8.5", | ||||
|  "sha1", | ||||
|  "thiserror", | ||||
|  "utf-8", | ||||
| ] | ||||
|  | ||||
| [[package]] | ||||
| name = "type-map" | ||||
| version = "0.5.0" | ||||
|   | ||||
							
								
								
									
										42
									
								
								Cargo.toml
									
									
									
									
									
								
							
							
						
						
									
										42
									
								
								Cargo.toml
									
									
									
									
									
								
							| @@ -7,30 +7,44 @@ edition = "2021" | ||||
| # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html | ||||
|  | ||||
| [dependencies] | ||||
| dioxus = { version = "0.6.0", features = ["fullstack", "router"] } | ||||
| dioxus-query = "0.6.0" | ||||
| dioxus-i18n = "0.3.0" | ||||
|  | ||||
| async-std = "1.12.0" | ||||
| chrono = { version = "0.4.38", features = ["serde", "unstable-locales"] } | ||||
| diesel = { version = "2.2.2", features = ["chrono", "postgres", "postgres_backend", "serde_json"] } | ||||
| diesel_migrations = { version = "2.2.0", features = ["postgres"] } | ||||
| dotenvy = "0.15.7" | ||||
| feruca = "0.10.0" | ||||
| serde = "1.0.208" | ||||
| serde_json = "1.0.125" | ||||
| dioxus = { version = "0.6.0", features = ["fullstack", "router"] } | ||||
| feruca = { version = "0.10.0" } | ||||
| serde = { version = "1.0.208" } | ||||
| serde_json = { version = "1.0.125" } | ||||
| serde_with = { version = "3.9.0", features = ["chrono_0_4"] } | ||||
| time = "0.3.36" | ||||
| tracing = "0.1.40" | ||||
| unic-langid-impl = { version = "0.9.5", features = ["serde"] } | ||||
| validator = { version = "0.19.0", features = ["derive"] } | ||||
| voca_rs = "1.15.2" | ||||
|  | ||||
| diesel = { version = "2.2.2", features = ["chrono", "postgres", "postgres_backend", "serde_json"], optional = true } | ||||
| diesel_migrations = { version = "2.2.0", features = ["postgres"], optional = true } | ||||
| dotenvy = { version = "0.15.7", optional = true } | ||||
| time = { version = "0.3.36", optional = true } | ||||
|  | ||||
| async-std = { version = "1.12.0", optional = true } | ||||
| dioxus-i18n = { version = "0.3.0", optional = true } | ||||
| dioxus-query = { version = "0.6.0", optional = true } | ||||
| voca_rs = { version = "1.15.2", optional = true } | ||||
|  | ||||
| [features] | ||||
| default = ["web"] | ||||
| desktop = ["dioxus/desktop"] | ||||
| mobile = ["dioxus/mobile"] | ||||
| web = ["dioxus/web"] | ||||
| web = [ | ||||
|     "dioxus/web", | ||||
|     "dep:async-std", | ||||
|     "dep:dioxus-query", | ||||
|     "dep:dioxus-i18n", | ||||
|     "dep:voca_rs" | ||||
| ] | ||||
| server = [ | ||||
|     "dioxus/server", | ||||
|     "dep:diesel", | ||||
|     "dep:diesel_migrations", | ||||
|     "dep:dotenvy", | ||||
|     "dep:time", | ||||
| ] | ||||
|  | ||||
| [profile] | ||||
|  | ||||
|   | ||||
| @@ -1,17 +1,17 @@ | ||||
| @layer base { | ||||
|     @font-face { | ||||
|         font-family: Inter; | ||||
|         font-style: normal; | ||||
|         font-weight: 100 900; | ||||
|         font-display: swap; | ||||
|         src: url("/assets/fonts/inter_variable.woff2") format("woff2"); | ||||
|     } | ||||
|   @font-face { | ||||
|     font-family: Inter; | ||||
|     font-style: normal; | ||||
|     font-weight: 100 900; | ||||
|     font-display: swap; | ||||
|     src: url("/assets/fonts/inter_variable.woff2") format("woff2"); | ||||
|   } | ||||
|  | ||||
|     @font-face { | ||||
|         font-family: Inter; | ||||
|         font-style: italic; | ||||
|         font-weight: 100 900; | ||||
|         font-display: swap; | ||||
|         src: url("/assets/fonts/inter_variable_italic.woff2") format("woff2"); | ||||
|     } | ||||
|   @font-face { | ||||
|     font-family: Inter; | ||||
|     font-style: italic; | ||||
|     font-weight: 100 900; | ||||
|     font-display: swap; | ||||
|     src: url("/assets/fonts/inter_variable_italic.woff2") format("woff2"); | ||||
|   } | ||||
| } | ||||
|   | ||||
| @@ -1,10 +1,10 @@ | ||||
| input::-webkit-outer-spin-button, | ||||
| input::-webkit-inner-spin-button { | ||||
|     display: none; | ||||
|     appearance: none; | ||||
|     margin: 0; | ||||
|   display: none; | ||||
|   appearance: none; | ||||
|   margin: 0; | ||||
| } | ||||
|  | ||||
| input[type="number"] { | ||||
|     appearance:textfield; | ||||
|   appearance: textfield; | ||||
| } | ||||
|   | ||||
| @@ -1,63 +1,63 @@ | ||||
| input[type="range"], | ||||
| input[type="range"]::-webkit-slider-runnable-track, | ||||
| input[type="range"]::-webkit-slider-thumb { | ||||
|     appearance: none; | ||||
|   appearance: none; | ||||
| } | ||||
|  | ||||
| input[type="range"] { | ||||
|     background: transparent; | ||||
|   background: transparent; | ||||
| } | ||||
|  | ||||
| input[type="range"]::-moz-range-thumb { | ||||
|     width: 1.25rem; | ||||
|     height: 1.25rem; | ||||
|     background: rgba(228 228 231); | ||||
|     border: 0; | ||||
|     border-radius: 0.5rem; | ||||
|   width: 1.25rem; | ||||
|   height: 1.25rem; | ||||
|   background: rgba(228 228 231); | ||||
|   border: 0; | ||||
|   border-radius: 0.5rem; | ||||
| } | ||||
|  | ||||
| input[type="range"]::-moz-range-progress { | ||||
|     background: #525259; | ||||
|     height: 0.5rem; | ||||
|     border-radius: 0.25rem; | ||||
|   background: #525259; | ||||
|   height: 0.5rem; | ||||
|   border-radius: 0.25rem; | ||||
| } | ||||
|  | ||||
| input[type="range"]::-moz-range-track { | ||||
|     background: rgba(39 39 42 / 50%); | ||||
|     height: 0.5rem; | ||||
|     border-radius: 0.25rem; | ||||
|   background: rgba(39 39 42 / 50%); | ||||
|   height: 0.5rem; | ||||
|   border-radius: 0.25rem; | ||||
| } | ||||
|  | ||||
| input[type="range"].input-range-reverse::-moz-range-progress { | ||||
|     background: #2d2d31; | ||||
|     height: 0.5rem; | ||||
|     border-radius: 0.25rem; | ||||
|   background: #2d2d31; | ||||
|   height: 0.5rem; | ||||
|   border-radius: 0.25rem; | ||||
| } | ||||
|  | ||||
| input[type="range"].input-range-reverse::-moz-range-track { | ||||
|     background: rgba(113 113 122 / 50%); | ||||
|     height: 0.5rem; | ||||
|     border-radius: 0.25rem; | ||||
|   background: rgba(113 113 122 / 50%); | ||||
|   height: 0.5rem; | ||||
|   border-radius: 0.25rem; | ||||
| } | ||||
|  | ||||
| input[type="range"]::-webkit-slider-thumb { | ||||
|     width: 1.25rem; | ||||
|     height: 1.25rem; | ||||
|     background: rgba(228 228 231); | ||||
|     border: 0; | ||||
|     border-radius: 0.5rem; | ||||
|     position: relative; | ||||
|     top: -0.4rem; | ||||
|   width: 1.25rem; | ||||
|   height: 1.25rem; | ||||
|   background: rgba(228 228 231); | ||||
|   border: 0; | ||||
|   border-radius: 0.5rem; | ||||
|   position: relative; | ||||
|   top: -0.4rem; | ||||
| } | ||||
|  | ||||
| input[type="range"]::-webkit-slider-runnable-track { | ||||
|     background: rgba(39 39 42 / 50%); | ||||
|     height: 0.5rem; | ||||
|     border-radius: 0.25rem; | ||||
|   background: rgba(39 39 42 / 50%); | ||||
|   height: 0.5rem; | ||||
|   border-radius: 0.25rem; | ||||
| } | ||||
|  | ||||
| input[type="range"].input-range-reverse::-webkit-slider-runnable-track { | ||||
|     background: rgba(39 39 42 / 50%); | ||||
|     height: 0.5rem; | ||||
|     border-radius: 0.25rem; | ||||
|   background: rgba(39 39 42 / 50%); | ||||
|   height: 0.5rem; | ||||
|   border-radius: 0.25rem; | ||||
| } | ||||
|   | ||||
| @@ -1,3 +1,4 @@ | ||||
| --- | ||||
| services: | ||||
|   app: | ||||
|     build: | ||||
| @@ -14,13 +15,13 @@ services: | ||||
|       - ./package.json:/srv/app/package.json | ||||
|       - ./package-lock.json:/srv/app/package-lock.json | ||||
|     restart: always | ||||
|     ports: [ "8000:8000" ] | ||||
|     depends_on: [ "db" ] | ||||
|     ports: ["8000:8000"] | ||||
|     depends_on: ["db"] | ||||
| 
 | ||||
|   db: | ||||
|     image: postgres:16.4-bookworm | ||||
|     volumes: [ "db_data:/var/lib/postgresql/data" ] | ||||
|     ports: [ "5432:5432" ] | ||||
|     volumes: ["db_data:/var/lib/postgresql/data"] | ||||
|     ports: ["5432:5432"] | ||||
|     environment: | ||||
|       POSTGRES_DB: todo_baggins | ||||
|       POSTGRES_USER: app | ||||
| @@ -1,3 +1,4 @@ | ||||
| --- | ||||
| services: | ||||
|   app: | ||||
|     build: | ||||
| @@ -6,11 +7,11 @@ services: | ||||
|       - default | ||||
|       - web-server-network | ||||
|     restart: always | ||||
|     depends_on: [ "db" ] | ||||
|     depends_on: ["db"] | ||||
| 
 | ||||
|   db: | ||||
|     image: postgres:16.4-bookworm | ||||
|     volumes: [ "db_data:/var/lib/postgresql/data" ] | ||||
|     volumes: ["db_data:/var/lib/postgresql/data"] | ||||
|     networks: | ||||
|       - default | ||||
|     environment: | ||||
| @@ -1,8 +1,12 @@ | ||||
| FROM rust:1.83-bookworm | ||||
| FROM rust@sha256:a45bf1f5d9af0a23b26703b3500d70af1abff7f984a7abef5a104b42c02a292b | ||||
|  | ||||
| RUN rustup target add wasm32-unknown-unknown && \ | ||||
|     cargo install dioxus-cli diesel_cli && \ | ||||
|     apt-get update && apt-get install -y nodejs=18.19.0+dfsg-6~deb12u2 npm=9.2.0~ds1-1 supervisor=4.2.5-1 | ||||
| RUN cargo install dioxus-cli diesel_cli \ | ||||
|  && apt-get update && apt-get install -y --no-install-recommends \ | ||||
|     nodejs=18.19.0+dfsg-6~deb12u2 \ | ||||
|     npm=9.2.0~ds1-1 \ | ||||
|     supervisor=4.2.5-1 \ | ||||
|  && apt-get clean \ | ||||
|  && rm -rf /var/lib/apt/lists/* | ||||
|  | ||||
| COPY . /srv/app | ||||
| WORKDIR /srv/app | ||||
| @@ -11,10 +15,10 @@ RUN npm install | ||||
|  | ||||
| COPY docker/dev/app/supervisord.conf /etc/supervisor/conf.d/supervisord.conf | ||||
|  | ||||
| RUN chown -R 1000:1000 /srv/app && \ | ||||
|     chown -R 1000:1000 /usr/local/cargo && \ | ||||
|     mkdir -p /.local/share/dioxus && \ | ||||
|     chown -R 1000:1000 /.local/share/dioxus | ||||
| RUN chown -R 1000:1000 /srv/app \ | ||||
|  && chown -R 1000:1000 /usr/local/cargo \ | ||||
|  && mkdir -p /.local/share/dioxus \ | ||||
|  && chown -R 1000:1000 /.local/share/dioxus | ||||
|  | ||||
| HEALTHCHECK CMD curl --fail -H "Accept: text/html" http://localhost:8000 || exit 1 | ||||
|  | ||||
|   | ||||
| @@ -1,19 +1,27 @@ | ||||
| FROM rust:1.83-bookworm AS builder | ||||
| FROM rust@sha256:a45bf1f5d9af0a23b26703b3500d70af1abff7f984a7abef5a104b42c02a292b AS builder | ||||
|  | ||||
| RUN rustup target add wasm32-unknown-unknown && \ | ||||
|     cargo install dioxus-cli diesel_cli && \ | ||||
|     apt-get update && apt-get install -y nodejs=18.19.0+dfsg-6~deb12u2 npm=9.2.0~ds1-1 supervisor=4.2.5-1 | ||||
| RUN rustup target add wasm32-unknown-unknown \ | ||||
|  && cargo install dioxus-cli diesel_cli \ | ||||
|  && apt-get update && apt-get install -y --no-install-recommends \ | ||||
|     nodejs=18.19.0+dfsg-6~deb12u2 \ | ||||
|     npm=9.2.0~ds1-1 \ | ||||
|     supervisor=4.2.5-1 \ | ||||
|  && apt-get clean \ | ||||
|  && rm -rf /var/lib/apt/lists/* | ||||
|  | ||||
| COPY . /srv/app | ||||
| WORKDIR /srv/app | ||||
|  | ||||
| RUN npm install && \ | ||||
|     npm run build && \ | ||||
|     dx build --release | ||||
| RUN npm install \ | ||||
|  && npm run build \ | ||||
|  && dx build --release | ||||
|  | ||||
| FROM debian:bookworm-slim AS runner | ||||
| FROM debian@sha256:b877a1a3fdf02469440f1768cf69c9771338a875b7add5e80c45b756c92ac20a AS runner | ||||
|  | ||||
| RUN apt-get update && apt-get install -y libpq5=15.8-0+deb12u1 | ||||
| RUN apt-get update \ | ||||
|  && apt-get install -y --no-install-recommends libpq5=15.8-0+deb12u1 \ | ||||
|  && apt-get clean \ | ||||
|  && rm -rf /var/lib/apt/lists/* | ||||
|  | ||||
| COPY --from=builder /srv/app/target/dx/todo-baggins/release/web /srv/app | ||||
| COPY .env /srv/app/.env | ||||
|   | ||||
							
								
								
									
										1655
									
								
								package-lock.json
									
									
									
										generated
									
									
									
								
							
							
						
						
									
										1655
									
								
								package-lock.json
									
									
									
										generated
									
									
									
								
							
										
											
												File diff suppressed because it is too large
												Load Diff
											
										
									
								
							| @@ -1,9 +1,16 @@ | ||||
| { | ||||
|   "dependencies": { | ||||
|     "tailwindcss": "^3.4.10" | ||||
|     "tailwindcss": "3.4.10" | ||||
|   }, | ||||
|   "scripts": { | ||||
|     "build": "tailwindcss -c src/styles/tailwind.config.js -i src/styles/tailwind.css -o assets/styles/tailwind_output.css", | ||||
|     "watch": "npm run build -- --watch" | ||||
|   }, | ||||
|   "devDependencies": { | ||||
|     "htmlhint": "1.1.4", | ||||
|     "markdownlint-cli": "0.43.0", | ||||
|     "prettier": "3.4.2", | ||||
|     "stylelint": "16.12.0", | ||||
|     "stylelint-config-standard": "36.0.1" | ||||
|   } | ||||
| } | ||||
|   | ||||
| @@ -25,7 +25,7 @@ pub(crate) fn BottomPanel(display_form: Signal<bool>) -> Element { | ||||
|                 // Necessary for a smooth – not instant – height transition. | ||||
|                 async_std::task::sleep(std::time::Duration::from_millis(500)).await; | ||||
|                 /* The check is necessary for the situation when the user expands the panel while | ||||
|                    it is being closed. */ | ||||
|                 it is being closed. */ | ||||
|                 if !display_form() { | ||||
|                     expanded.set(false); | ||||
|                 } | ||||
|   | ||||
| @@ -4,7 +4,10 @@ use dioxus::dioxus_core::Element; | ||||
| use dioxus::prelude::*; | ||||
|  | ||||
| #[component] | ||||
| pub(crate) fn CategoryInput(selected_category: Signal<Category>, class: Option<&'static str>) -> Element { | ||||
| pub(crate) fn CategoryInput( | ||||
|     selected_category: Signal<Category>, | ||||
|     class: Option<&'static str>, | ||||
| ) -> Element { | ||||
|     rsx! { | ||||
|         div { | ||||
|             class: format!("flex flex-row gap-2 {}", class.unwrap_or("")), | ||||
|   | ||||
| @@ -4,6 +4,5 @@ use dioxus::prelude::*; | ||||
|  | ||||
| #[component] | ||||
| pub(crate) fn Home() -> Element { | ||||
|     rsx! { | ||||
|     } | ||||
|     rsx! {} | ||||
| } | ||||
|   | ||||
| @@ -11,17 +11,14 @@ use dioxus::prelude::*; | ||||
| #[component] | ||||
| pub(crate) fn Layout() -> Element { | ||||
|     let mut display_form = use_signal(|| false); | ||||
|     let project_being_edited = use_context_provider::<Signal<Option<Project>>>( | ||||
|         || Signal::new(None) | ||||
|     ); | ||||
|     let task_being_edited = use_context_provider::<Signal<Option<Task>>>( | ||||
|         || Signal::new(None) | ||||
|     ); | ||||
|      | ||||
|     let project_being_edited = | ||||
|         use_context_provider::<Signal<Option<Project>>>(|| Signal::new(None)); | ||||
|     let task_being_edited = use_context_provider::<Signal<Option<Task>>>(|| Signal::new(None)); | ||||
|  | ||||
|     use_effect(move || { | ||||
|         display_form.set(project_being_edited().is_some() || task_being_edited().is_some()); | ||||
|     }); | ||||
|      | ||||
|  | ||||
|     rsx! { | ||||
|         Outlet::<Route> {} | ||||
|         StickyBottom { | ||||
|   | ||||
| @@ -1,16 +1,16 @@ | ||||
| pub(crate) mod app; | ||||
| pub(crate) mod bottom_panel; | ||||
| pub(crate) mod category_input; | ||||
| pub(crate) mod form_open_button; | ||||
| pub(crate) mod home; | ||||
| pub(crate) mod layout; | ||||
| pub(crate) mod navigation; | ||||
| pub(crate) mod navigation_item; | ||||
| pub(crate) mod pages; | ||||
| pub(crate) mod project_form; | ||||
| pub(crate) mod reoccurrence_input; | ||||
| pub(crate) mod sticky_bottom; | ||||
| pub(crate) mod subtasks_form; | ||||
| pub(crate) mod task_form; | ||||
| pub(crate) mod task_list; | ||||
| pub(crate) mod pages; | ||||
| pub(crate) mod navigation; | ||||
| pub(crate) mod form_open_button; | ||||
| pub(crate) mod bottom_panel; | ||||
| pub(crate) mod sticky_bottom; | ||||
| pub(crate) mod category_input; | ||||
| pub(crate) mod reoccurrence_input; | ||||
| pub(crate) mod layout; | ||||
| pub(crate) mod navigation_item; | ||||
| pub(crate) mod subtasks_form; | ||||
| pub(crate) mod task_list_item; | ||||
|   | ||||
| @@ -1,5 +1,5 @@ | ||||
| use dioxus::prelude::*; | ||||
| use crate::route::Route; | ||||
| use dioxus::prelude::*; | ||||
|  | ||||
| #[component] | ||||
| pub(crate) fn NavigationItem(route: Route, children: Element) -> Element { | ||||
|   | ||||
| @@ -1,8 +1,8 @@ | ||||
| use crate::components::pages::category_page::CategoryPage; | ||||
| use crate::models::category::Category; | ||||
| use dioxus::core_macro::rsx; | ||||
| use dioxus::dioxus_core::Element; | ||||
| use dioxus::prelude::*; | ||||
| use crate::components::pages::category_page::CategoryPage; | ||||
|  | ||||
| #[component] | ||||
| pub(crate) fn CategoryDonePage() -> Element { | ||||
|   | ||||
| @@ -1,8 +1,8 @@ | ||||
| use crate::components::pages::category_page::CategoryPage; | ||||
| use crate::models::category::Category; | ||||
| use dioxus::core_macro::rsx; | ||||
| use dioxus::dioxus_core::Element; | ||||
| use dioxus::prelude::*; | ||||
| use crate::components::pages::category_page::CategoryPage; | ||||
|  | ||||
| #[component] | ||||
| pub(crate) fn CategoryInboxPage() -> Element { | ||||
|   | ||||
| @@ -1,8 +1,8 @@ | ||||
| use crate::components::pages::category_page::CategoryPage; | ||||
| use crate::models::category::Category; | ||||
| use dioxus::core_macro::rsx; | ||||
| use dioxus::dioxus_core::Element; | ||||
| use dioxus::prelude::*; | ||||
| use crate::components::pages::category_page::CategoryPage; | ||||
|  | ||||
| #[component] | ||||
| pub(crate) fn CategoryLongTermPage() -> Element { | ||||
|   | ||||
| @@ -1,8 +1,8 @@ | ||||
| use crate::components::pages::category_page::CategoryPage; | ||||
| use crate::models::category::Category; | ||||
| use dioxus::core_macro::rsx; | ||||
| use dioxus::dioxus_core::Element; | ||||
| use dioxus::prelude::*; | ||||
| use crate::components::pages::category_page::CategoryPage; | ||||
|  | ||||
| #[component] | ||||
| pub(crate) fn CategoryNextStepsPage() -> Element { | ||||
|   | ||||
| @@ -28,6 +28,6 @@ pub(crate) fn CategoryPage(category: Category) -> Element { | ||||
|                 "Errors occurred: {errors:?}" | ||||
|             } | ||||
|         }, | ||||
|         value => panic!("Unexpected query result: {value:?}") | ||||
|         value => panic!("Unexpected query result: {value:?}"), | ||||
|     } | ||||
| } | ||||
|   | ||||
| @@ -1,8 +1,8 @@ | ||||
| use crate::components::pages::category_page::CategoryPage; | ||||
| use crate::models::category::Category; | ||||
| use dioxus::core_macro::rsx; | ||||
| use dioxus::dioxus_core::Element; | ||||
| use dioxus::prelude::*; | ||||
| use crate::components::pages::category_page::CategoryPage; | ||||
|  | ||||
| #[component] | ||||
| pub(crate) fn CategorySomedayMaybePage() -> Element { | ||||
|   | ||||
| @@ -25,7 +25,7 @@ pub(crate) fn CategoryTodayPage() -> Element { | ||||
|  | ||||
|     let long_term_tasks_query = use_tasks_with_subtasks_in_category_query(Category::LongTerm); | ||||
|     let long_term_tasks_query_result = long_term_tasks_query.result(); | ||||
|      | ||||
|  | ||||
|     rsx! { | ||||
|         div { | ||||
|             class: "pt-4 flex flex-col gap-8", | ||||
|   | ||||
| @@ -1,8 +1,8 @@ | ||||
| use crate::components::pages::category_page::CategoryPage; | ||||
| use crate::models::category::Category; | ||||
| use dioxus::core_macro::rsx; | ||||
| use dioxus::dioxus_core::Element; | ||||
| use dioxus::prelude::*; | ||||
| use crate::components::pages::category_page::CategoryPage; | ||||
|  | ||||
| #[component] | ||||
| pub(crate) fn CategoryTrashPage() -> Element { | ||||
|   | ||||
| @@ -1,8 +1,8 @@ | ||||
| use crate::components::pages::category_page::CategoryPage; | ||||
| use crate::models::category::Category; | ||||
| use dioxus::core_macro::rsx; | ||||
| use dioxus::dioxus_core::Element; | ||||
| use dioxus::prelude::*; | ||||
| use crate::components::pages::category_page::CategoryPage; | ||||
|  | ||||
| #[component] | ||||
| pub(crate) fn CategoryWaitingForPage() -> Element { | ||||
|   | ||||
| @@ -1,12 +1,12 @@ | ||||
| pub(crate) mod category_inbox_page; | ||||
| pub(crate) mod category_calendar_page; | ||||
| pub(crate) mod category_today_page; | ||||
| pub(crate) mod category_waiting_for_page; | ||||
| pub(crate) mod category_done_page; | ||||
| pub(crate) mod category_inbox_page; | ||||
| pub(crate) mod category_long_term_page; | ||||
| pub(crate) mod category_next_steps_page; | ||||
| pub(crate) mod category_page; | ||||
| pub(crate) mod category_someday_maybe_page; | ||||
| pub(crate) mod category_done_page; | ||||
| pub(crate) mod category_today_page; | ||||
| pub(crate) mod category_trash_page; | ||||
| pub(crate) mod category_waiting_for_page; | ||||
| pub(crate) mod not_found_page; | ||||
| pub(crate) mod projects_page; | ||||
| pub(crate) mod category_page; | ||||
|   | ||||
| @@ -1,8 +1,8 @@ | ||||
| use dioxus::prelude::*; | ||||
| use dioxus_query::prelude::QueryResult; | ||||
| use crate::models::project::Project; | ||||
| use crate::query::projects::use_projects_query; | ||||
| use crate::query::QueryValue; | ||||
| use dioxus::prelude::*; | ||||
| use dioxus_query::prelude::QueryResult; | ||||
|  | ||||
| #[component] | ||||
| pub(crate) fn ProjectsPage() -> Element { | ||||
|   | ||||
| @@ -7,8 +7,10 @@ use dioxus::prelude::*; | ||||
| use dioxus_query::prelude::use_query_client; | ||||
|  | ||||
| #[component] | ||||
| pub(crate) fn ProjectForm(project: Option<Project>, on_successful_submit: EventHandler<()>) | ||||
|                           -> Element { | ||||
| pub(crate) fn ProjectForm( | ||||
|     project: Option<Project>, | ||||
|     on_successful_submit: EventHandler<()>, | ||||
| ) -> Element { | ||||
|     let query_client = use_query_client::<QueryValue, QueryErrors, QueryKey>(); | ||||
|     let project_for_submit = project.clone(); | ||||
|  | ||||
|   | ||||
| @@ -6,7 +6,7 @@ use dioxus::prelude::*; | ||||
| #[component] | ||||
| pub(crate) fn ReoccurrenceIntervalInput( | ||||
|     reoccurrence_interval: Signal<Option<ReoccurrenceInterval>>, | ||||
|     class_buttons: Option<&'static str> | ||||
|     class_buttons: Option<&'static str>, | ||||
| ) -> Element { | ||||
|     rsx! { | ||||
|         button { | ||||
|   | ||||
| @@ -4,6 +4,7 @@ use crate::components::subtasks_form::SubtasksForm; | ||||
| use crate::models::category::{CalendarTime, Category, Reoccurrence}; | ||||
| use crate::models::task::NewTask; | ||||
| use crate::models::task::Task; | ||||
| use crate::query::projects::use_projects_query; | ||||
| use crate::query::{QueryErrors, QueryKey, QueryValue}; | ||||
| use crate::route::Route; | ||||
| use crate::server::tasks::{create_task, delete_task, edit_task}; | ||||
| @@ -13,7 +14,6 @@ use dioxus::dioxus_core::Element; | ||||
| use dioxus::prelude::*; | ||||
| use dioxus_i18n::t; | ||||
| use dioxus_query::prelude::{use_query_client, QueryResult}; | ||||
| use crate::query::projects::use_projects_query; | ||||
|  | ||||
| const REMINDER_OFFSETS: [Option<Duration>; 17] = [ | ||||
|     None, | ||||
| @@ -40,7 +40,8 @@ pub(crate) fn TaskForm(task: Option<Task>, on_successful_submit: EventHandler<() | ||||
|     let projects_query = use_projects_query(); | ||||
|  | ||||
|     let route = use_route::<Route>(); | ||||
|     let selected_category = use_signal(|| if let Some(task) = &task { | ||||
|     let selected_category = use_signal(|| { | ||||
|         if let Some(task) = &task { | ||||
|             task.category().clone() | ||||
|         } else { | ||||
|             match route { | ||||
| @@ -56,26 +57,41 @@ pub(crate) fn TaskForm(task: Option<Task>, on_successful_submit: EventHandler<() | ||||
|                 _ => Category::Inbox, | ||||
|             } | ||||
|         } | ||||
|     ); | ||||
|     let category_calendar_reoccurrence_interval = use_signal(|| task.as_ref().and_then(|task| | ||||
|         if let Category::Calendar { reoccurrence: Some(reoccurrence), .. } = task.category() { | ||||
|             Some(reoccurrence.interval().clone()) | ||||
|         } else { | ||||
|             None | ||||
|         } | ||||
|     )); | ||||
|     let mut category_calendar_has_time = use_signal(|| task.as_ref().is_some_and( | ||||
|         |task| matches!(*task.category(), Category::Calendar { time: Some(_), .. })) | ||||
|     ); | ||||
|     let mut category_calendar_reminder_offset_index = use_signal(|| task.as_ref().and_then(|task| | ||||
|         if let Category::Calendar { time: Some(time), .. } = task.category() { | ||||
|             REMINDER_OFFSETS.iter().position(|&reminder_offset| | ||||
|                 reminder_offset == time.reminder_offset() | ||||
|             ) | ||||
|         } else { | ||||
|             None | ||||
|         } | ||||
|     ).unwrap_or(REMINDER_OFFSETS.len() - 1)); | ||||
|     }); | ||||
|     let category_calendar_reoccurrence_interval = use_signal(|| { | ||||
|         task.as_ref().and_then(|task| { | ||||
|             if let Category::Calendar { | ||||
|                 reoccurrence: Some(reoccurrence), | ||||
|                 .. | ||||
|             } = task.category() | ||||
|             { | ||||
|                 Some(reoccurrence.interval().clone()) | ||||
|             } else { | ||||
|                 None | ||||
|             } | ||||
|         }) | ||||
|     }); | ||||
|     let mut category_calendar_has_time = use_signal(|| { | ||||
|         task.as_ref().is_some_and(|task| { | ||||
|             matches!(*task.category(), Category::Calendar { time: Some(_), .. }) | ||||
|         }) | ||||
|     }); | ||||
|     let mut category_calendar_reminder_offset_index = use_signal(|| { | ||||
|         task.as_ref() | ||||
|             .and_then(|task| { | ||||
|                 if let Category::Calendar { | ||||
|                     time: Some(time), .. | ||||
|                 } = task.category() | ||||
|                 { | ||||
|                     REMINDER_OFFSETS | ||||
|                         .iter() | ||||
|                         .position(|&reminder_offset| reminder_offset == time.reminder_offset()) | ||||
|                 } else { | ||||
|                     None | ||||
|                 } | ||||
|             }) | ||||
|             .unwrap_or(REMINDER_OFFSETS.len() - 1) | ||||
|     }); | ||||
|  | ||||
|     let query_client = use_query_client::<QueryValue, QueryErrors, QueryKey>(); | ||||
|     let task_for_submit = task.clone(); | ||||
|   | ||||
| @@ -1,13 +1,12 @@ | ||||
| use crate::components::task_list_item::TaskListItem; | ||||
| use crate::models::category::Category; | ||||
| use crate::models::task::{Task, TaskWithSubtasks}; | ||||
| use crate::query::{QueryErrors, QueryKey, QueryValue}; | ||||
| use crate::server::tasks::complete_task; | ||||
| use dioxus::core_macro::rsx; | ||||
| use dioxus::dioxus_core::Element; | ||||
| use dioxus::prelude::*; | ||||
| use dioxus_query::prelude::use_query_client; | ||||
| use tracing::info; | ||||
| use crate::components::task_list_item::TaskListItem; | ||||
| use crate::query::{QueryErrors, QueryKey, QueryValue}; | ||||
| use crate::server::tasks::complete_task; | ||||
|  | ||||
| #[component] | ||||
| pub(crate) fn TaskList(tasks: Vec<TaskWithSubtasks>, class: Option<&'static str>) -> Element { | ||||
| @@ -15,7 +14,7 @@ pub(crate) fn TaskList(tasks: Vec<TaskWithSubtasks>, class: Option<&'static str> | ||||
|     let mut task_being_edited = use_context::<Signal<Option<Task>>>(); | ||||
|  | ||||
|     tasks.sort(); | ||||
|      | ||||
|  | ||||
|     rsx! { | ||||
|         div { | ||||
|             class: format!("flex flex-col {}", class.unwrap_or("")), | ||||
|   | ||||
| @@ -1,4 +1,3 @@ | ||||
| use dioxus_i18n::prelude::i18n; | ||||
| use crate::internationalization::LocaleFromLanguageIdentifier; | ||||
| use crate::models::category::Category; | ||||
| use crate::models::task::TaskWithSubtasks; | ||||
| @@ -6,6 +5,7 @@ use chrono::{Datelike, Local}; | ||||
| use dioxus::core_macro::rsx; | ||||
| use dioxus::dioxus_core::Element; | ||||
| use dioxus::prelude::*; | ||||
| use dioxus_i18n::prelude::i18n; | ||||
| use dioxus_i18n::t; | ||||
| use voca_rs::Voca; | ||||
|  | ||||
|   | ||||
| @@ -7,9 +7,10 @@ pub enum Error { | ||||
|     ServerInternal, | ||||
| } | ||||
|  | ||||
| #[cfg(feature = "server")] | ||||
| impl From<diesel::result::Error> for Error { | ||||
|     fn from(_: diesel::result::Error) -> Self {  | ||||
|         Self::ServerInternal  | ||||
|     fn from(_: diesel::result::Error) -> Self { | ||||
|         Self::ServerInternal | ||||
|     } | ||||
| } | ||||
|  | ||||
|   | ||||
| @@ -1,7 +1,7 @@ | ||||
| use std::fmt::Display; | ||||
| use std::str::FromStr; | ||||
| use serde::Deserialize; | ||||
| use serde_with::serde_derive::Serialize; | ||||
| use std::fmt::Display; | ||||
| use std::str::FromStr; | ||||
|  | ||||
| #[derive(Serialize, Deserialize, Clone, Debug)] | ||||
| pub struct ErrorVec<T> { | ||||
|   | ||||
| @@ -1,5 +1,5 @@ | ||||
| pub(crate) mod error; | ||||
| pub(crate) mod error_vec; | ||||
| pub(crate) mod project_error; | ||||
| pub(crate) mod task_error; | ||||
| pub(crate) mod subtask_error; | ||||
| pub(crate) mod task_error; | ||||
|   | ||||
| @@ -13,7 +13,8 @@ pub enum ProjectError { | ||||
|  | ||||
| impl From<ValidationErrors> for ErrorVec<ProjectError> { | ||||
|     fn from(validation_errors: ValidationErrors) -> Self { | ||||
|         validation_errors.errors() | ||||
|         validation_errors | ||||
|             .errors() | ||||
|             .iter() | ||||
|             .flat_map(|(&field, error_kind)| match field { | ||||
|                 "title" => match error_kind { | ||||
| @@ -34,6 +35,7 @@ impl From<ValidationErrors> for ErrorVec<ProjectError> { | ||||
|     } | ||||
| } | ||||
|  | ||||
| #[cfg(feature = "server")] | ||||
| impl From<diesel::result::Error> for ProjectError { | ||||
|     fn from(_: diesel::result::Error) -> Self { | ||||
|         Self::Error(Error::ServerInternal) | ||||
|   | ||||
| @@ -14,7 +14,8 @@ pub enum SubtaskError { | ||||
|  | ||||
| impl From<ValidationErrors> for ErrorVec<SubtaskError> { | ||||
|     fn from(validation_errors: ValidationErrors) -> Self { | ||||
|         validation_errors.errors() | ||||
|         validation_errors | ||||
|             .errors() | ||||
|             .iter() | ||||
|             .flat_map(|(&field, error_kind)| match field { | ||||
|                 "title" => match error_kind { | ||||
| @@ -35,28 +36,29 @@ impl From<ValidationErrors> for ErrorVec<SubtaskError> { | ||||
|     } | ||||
| } | ||||
|  | ||||
| #[cfg(feature = "server")] | ||||
| impl From<diesel::result::Error> for SubtaskError { | ||||
|     fn from(diesel_error: diesel::result::Error) -> Self { | ||||
|         match diesel_error { | ||||
|             diesel::result::Error::DatabaseError( | ||||
|                 diesel::result::DatabaseErrorKind::ForeignKeyViolation, info | ||||
|             ) => { | ||||
|                 match info.constraint_name() { | ||||
|                     Some("subtasks_task_id_fkey") => Self::TaskNotFound, | ||||
|                     _ => Self::Error(Error::ServerInternal) | ||||
|                 } | ||||
|             } | ||||
|             _ => { | ||||
|                 Self::Error(Error::ServerInternal) | ||||
|             } | ||||
|                 diesel::result::DatabaseErrorKind::ForeignKeyViolation, | ||||
|                 info, | ||||
|             ) => match info.constraint_name() { | ||||
|                 Some("subtasks_task_id_fkey") => Self::TaskNotFound, | ||||
|                 _ => Self::Error(Error::ServerInternal), | ||||
|             }, | ||||
|             _ => Self::Error(Error::ServerInternal), | ||||
|         } | ||||
|     } | ||||
| } | ||||
|  | ||||
| impl From<ErrorVec<Error>> for ErrorVec<SubtaskError> { | ||||
|     fn from(error_vec: ErrorVec<Error>) -> Self { | ||||
|         Vec::from(error_vec).into_iter() | ||||
|             .map(SubtaskError::Error).collect::<Vec<SubtaskError>>().into() | ||||
|         Vec::from(error_vec) | ||||
|             .into_iter() | ||||
|             .map(SubtaskError::Error) | ||||
|             .collect::<Vec<SubtaskError>>() | ||||
|             .into() | ||||
|     } | ||||
| } | ||||
|  | ||||
|   | ||||
| @@ -14,7 +14,8 @@ pub enum TaskError { | ||||
|  | ||||
| impl From<ValidationErrors> for ErrorVec<TaskError> { | ||||
|     fn from(validation_errors: ValidationErrors) -> Self { | ||||
|         validation_errors.errors() | ||||
|         validation_errors | ||||
|             .errors() | ||||
|             .iter() | ||||
|             .flat_map(|(&field, error_kind)| match field { | ||||
|                 "title" => match error_kind { | ||||
| @@ -35,20 +36,18 @@ impl From<ValidationErrors> for ErrorVec<TaskError> { | ||||
|     } | ||||
| } | ||||
|  | ||||
| #[cfg(feature = "server")] | ||||
| impl From<diesel::result::Error> for TaskError { | ||||
|     fn from(diesel_error: diesel::result::Error) -> Self { | ||||
|         match diesel_error { | ||||
|             diesel::result::Error::DatabaseError( | ||||
|                 diesel::result::DatabaseErrorKind::ForeignKeyViolation, info | ||||
|             ) => { | ||||
|                 match info.constraint_name() { | ||||
|                     Some("tasks_project_id_fkey") => Self::ProjectNotFound, | ||||
|                     _ => Self::Error(Error::ServerInternal) | ||||
|                 } | ||||
|             } | ||||
|             _ => { | ||||
|                 Self::Error(Error::ServerInternal) | ||||
|             } | ||||
|                 diesel::result::DatabaseErrorKind::ForeignKeyViolation, | ||||
|                 info, | ||||
|             ) => match info.constraint_name() { | ||||
|                 Some("tasks_project_id_fkey") => Self::ProjectNotFound, | ||||
|                 _ => Self::Error(Error::ServerInternal), | ||||
|             }, | ||||
|             _ => Self::Error(Error::ServerInternal), | ||||
|         } | ||||
|     } | ||||
| } | ||||
|   | ||||
| @@ -1,15 +1,15 @@ | ||||
| use std::ops::Deref; | ||||
| use std::sync::Mutex; | ||||
| use chrono::Locale; | ||||
| use dioxus::fullstack::once_cell::sync::Lazy; | ||||
| use feruca::Collator; | ||||
| use std::ops::Deref; | ||||
| use std::sync::Mutex; | ||||
| use unic_langid_impl::LanguageIdentifier; | ||||
|  | ||||
| pub(crate) static COLLATOR: Lazy<Mutex<Collator>> = Lazy::new(|| Mutex::new(Collator::default())); | ||||
|  | ||||
| pub(crate) struct LocaleFromLanguageIdentifier<'a>(&'a LanguageIdentifier); | ||||
|  | ||||
| impl<'a> Deref for LocaleFromLanguageIdentifier<'a> { | ||||
| impl Deref for LocaleFromLanguageIdentifier<'_> { | ||||
|     type Target = LanguageIdentifier; | ||||
|  | ||||
|     fn deref(&self) -> &Self::Target { | ||||
| @@ -17,9 +17,13 @@ impl<'a> Deref for LocaleFromLanguageIdentifier<'a> { | ||||
|     } | ||||
| } | ||||
|  | ||||
| impl<'a> From<LocaleFromLanguageIdentifier<'a>> for Locale { | ||||
| impl From<LocaleFromLanguageIdentifier<'_>> for Locale { | ||||
|     fn from(language_identifier: LocaleFromLanguageIdentifier) -> Self { | ||||
|         language_identifier.to_string().replace("-", "_").parse().unwrap() | ||||
|         language_identifier | ||||
|             .to_string() | ||||
|             .replace("-", "_") | ||||
|             .parse() | ||||
|             .unwrap() | ||||
|     } | ||||
| } | ||||
|  | ||||
|   | ||||
| @@ -1,13 +1,15 @@ | ||||
| mod components; | ||||
| mod errors; | ||||
| mod internationalization; | ||||
| #[cfg(feature = "server")] | ||||
| mod migrations; | ||||
| mod models; | ||||
| mod query; | ||||
| mod route; | ||||
| #[cfg(feature = "server")] | ||||
| mod schema; | ||||
| mod server; | ||||
| mod query; | ||||
| mod utils; | ||||
| mod internationalization; | ||||
| mod migrations; | ||||
|  | ||||
| use components::app::App; | ||||
| use dioxus::prelude::*; | ||||
|   | ||||
| @@ -1,19 +1,28 @@ | ||||
| use std::hash::Hash; | ||||
| #[cfg(feature = "server")] | ||||
| use crate::schema::tasks; | ||||
| use chrono::{Duration, NaiveDate, NaiveTime}; | ||||
| #[cfg(feature = "server")] | ||||
| use diesel::deserialize::FromSql; | ||||
| #[cfg(feature = "server")] | ||||
| use diesel::pg::{Pg, PgValue}; | ||||
| #[cfg(feature = "server")] | ||||
| use diesel::serialize::{Output, ToSql}; | ||||
| #[cfg(feature = "server")] | ||||
| use diesel::sql_types::{Bool, Jsonb}; | ||||
| #[cfg(feature = "server")] | ||||
| use diesel::{AsExpression, BoxableExpression, FromSqlRow, PgJsonbExpressionMethods}; | ||||
| use serde::{Deserialize, Serialize}; | ||||
| #[cfg(feature = "server")] | ||||
| use serde_json::json; | ||||
| use serde_with::DurationSeconds; | ||||
| use std::hash::Hash; | ||||
| #[cfg(feature = "server")] | ||||
| use std::io::Write; | ||||
|  | ||||
| #[serde_with::serde_as] | ||||
| #[derive(AsExpression, FromSqlRow, Serialize, Deserialize, Clone, Debug)] | ||||
| #[diesel(sql_type = Jsonb)] | ||||
| #[derive(Serialize, Deserialize, Clone, Debug)] | ||||
| #[cfg_attr(feature = "server", derive(AsExpression, FromSqlRow))] | ||||
| #[cfg_attr(feature = "server", diesel(sql_type = Jsonb))] | ||||
| pub enum Category { | ||||
|     Inbox, | ||||
|     SomedayMaybe, | ||||
| @@ -29,8 +38,9 @@ pub enum Category { | ||||
|     Trash, | ||||
| } | ||||
|  | ||||
| #[cfg(feature = "server")] | ||||
| impl Category { | ||||
|     pub fn eq_sql_predicate(&self) -> Box<dyn BoxableExpression<tasks::table, Pg, SqlType=Bool>> { | ||||
|     pub fn eq_sql_predicate(&self) -> Box<dyn BoxableExpression<tasks::table, Pg, SqlType = Bool>> { | ||||
|         use crate::schema::tasks::dsl::*; | ||||
|  | ||||
|         match self { | ||||
| @@ -60,6 +70,7 @@ impl PartialEq for Category { | ||||
|  | ||||
| impl Eq for Category {} | ||||
|  | ||||
| #[cfg(feature = "server")] | ||||
| impl ToSql<Jsonb, Pg> for Category { | ||||
|     fn to_sql<'b>(&'b self, out: &mut Output<'b, '_, Pg>) -> diesel::serialize::Result { | ||||
|         let json = serde_json::to_string(self)?; | ||||
| @@ -72,6 +83,7 @@ impl ToSql<Jsonb, Pg> for Category { | ||||
|     } | ||||
| } | ||||
|  | ||||
| #[cfg(feature = "server")] | ||||
| impl FromSql<Jsonb, Pg> for Category { | ||||
|     fn from_sql(bytes: PgValue) -> diesel::deserialize::Result<Self> { | ||||
|         let bytes = bytes.as_bytes(); | ||||
| @@ -99,13 +111,17 @@ pub struct Reoccurrence { | ||||
|  | ||||
| impl Reoccurrence { | ||||
|     pub fn new(start_date: NaiveDate, interval: ReoccurrenceInterval, length: u32) -> Self { | ||||
|         Self { start_date, interval, length } | ||||
|         Self { | ||||
|             start_date, | ||||
|             interval, | ||||
|             length, | ||||
|         } | ||||
|     } | ||||
|      | ||||
|  | ||||
|     pub fn start_date(&self) -> NaiveDate { | ||||
|         self.start_date | ||||
|     } | ||||
|      | ||||
|  | ||||
|     pub fn interval(&self) -> &ReoccurrenceInterval { | ||||
|         &self.interval | ||||
|     } | ||||
| @@ -125,9 +141,12 @@ pub struct CalendarTime { | ||||
|  | ||||
| impl CalendarTime { | ||||
|     pub fn new(time: NaiveTime, reminder_offset: Option<Duration>) -> Self { | ||||
|         Self { time, reminder_offset } | ||||
|         Self { | ||||
|             time, | ||||
|             reminder_offset, | ||||
|         } | ||||
|     } | ||||
|      | ||||
|  | ||||
|     pub fn time(&self) -> NaiveTime { | ||||
|         self.time | ||||
|     } | ||||
|   | ||||
| @@ -1,4 +1,4 @@ | ||||
| pub(crate) mod project; | ||||
| pub(crate) mod category; | ||||
| pub(crate) mod task; | ||||
| pub(crate) mod project; | ||||
| pub(crate) mod subtask; | ||||
| pub(crate) mod task; | ||||
|   | ||||
| @@ -1,17 +1,22 @@ | ||||
| use std::cmp::Ordering; | ||||
| use chrono::NaiveDateTime; | ||||
| use crate::internationalization::COLLATOR; | ||||
| #[cfg(feature = "server")] | ||||
| use crate::schema::projects; | ||||
| use chrono::NaiveDateTime; | ||||
| #[cfg(feature = "server")] | ||||
| use diesel::prelude::*; | ||||
| use serde::{Deserialize, Serialize}; | ||||
| use std::cmp::Ordering; | ||||
| use validator::Validate; | ||||
| use crate::internationalization::COLLATOR; | ||||
|  | ||||
| const TITLE_LENGTH_MIN: u64 = 1; | ||||
| const TITLE_LENGTH_MAX: u64 = 255; | ||||
|  | ||||
| #[derive(Queryable, Selectable, Identifiable, Serialize, Deserialize, PartialEq, Clone, Debug)] | ||||
| #[diesel(table_name = crate::schema::projects)] | ||||
| #[diesel(check_for_backend(diesel::pg::Pg))] | ||||
| #[derive(Serialize, Deserialize, PartialEq, Clone, Debug)] | ||||
| #[cfg_attr(feature = "server", derive(Queryable, Selectable, Identifiable))] | ||||
| #[cfg_attr( | ||||
|     feature = "server", | ||||
|     diesel(table_name = crate::schema::projects, check_for_backend(diesel::pg::Pg)) | ||||
| )] | ||||
| pub struct Project { | ||||
|     id: i32, | ||||
|     title: String, | ||||
| @@ -19,6 +24,7 @@ pub struct Project { | ||||
|     updated_at: NaiveDateTime, | ||||
| } | ||||
|  | ||||
| #[allow(dead_code)] | ||||
| impl Project { | ||||
|     pub fn id(&self) -> i32 { | ||||
|         self.id | ||||
| @@ -27,11 +33,11 @@ impl Project { | ||||
|     pub fn title(&self) -> &str { | ||||
|         &self.title | ||||
|     } | ||||
|      | ||||
|  | ||||
|     pub fn created_at(&self) -> NaiveDateTime { | ||||
|         self.created_at | ||||
|     } | ||||
|      | ||||
|  | ||||
|     pub fn updated_at(&self) -> NaiveDateTime { | ||||
|         self.updated_at | ||||
|     } | ||||
| @@ -47,14 +53,22 @@ impl PartialOrd<Self> for Project { | ||||
|  | ||||
| impl Ord for Project { | ||||
|     fn cmp(&self, other: &Self) -> Ordering { | ||||
|         COLLATOR.lock().unwrap().collate(self.title(), other.title()) | ||||
|         COLLATOR | ||||
|             .lock() | ||||
|             .unwrap() | ||||
|             .collate(self.title(), other.title()) | ||||
|     } | ||||
| } | ||||
|  | ||||
| #[derive(Insertable, Serialize, Deserialize, Validate, Clone, Debug)] | ||||
| #[diesel(table_name = projects)] | ||||
| #[derive(Serialize, Deserialize, Validate, Clone, Debug)] | ||||
| #[cfg_attr(feature = "server", derive(Insertable))] | ||||
| #[cfg_attr(feature = "server", diesel(table_name = projects))] | ||||
| pub struct NewProject { | ||||
|     #[validate(length(min = "TITLE_LENGTH_MIN", max = "TITLE_LENGTH_MAX", code = "title_length"))] | ||||
|     #[validate(length( | ||||
|         min = "TITLE_LENGTH_MIN", | ||||
|         max = "TITLE_LENGTH_MAX", | ||||
|         code = "title_length" | ||||
|     ))] | ||||
|     pub title: String, | ||||
| } | ||||
|  | ||||
|   | ||||
| @@ -1,19 +1,30 @@ | ||||
| use std::cmp::Ordering; | ||||
| #[cfg(feature = "server")] | ||||
| use crate::models::task::Task; | ||||
| #[cfg(feature = "server")] | ||||
| use crate::schema::subtasks; | ||||
| use chrono::NaiveDateTime; | ||||
| #[cfg(feature = "server")] | ||||
| use diesel::prelude::*; | ||||
| use serde::{Deserialize, Serialize}; | ||||
| use std::cmp::Ordering; | ||||
| use validator::Validate; | ||||
|  | ||||
| const TITLE_LENGTH_MIN: u64 = 1; | ||||
| const TITLE_LENGTH_MAX: u64 = 255; | ||||
|  | ||||
| #[derive(Queryable, Selectable, Identifiable, Associations, Serialize, Deserialize, PartialEq, | ||||
|     Clone, Debug)] | ||||
| #[diesel(belongs_to(Task, foreign_key = task_id))] | ||||
| #[diesel(table_name = subtasks)] | ||||
| #[diesel(check_for_backend(diesel::pg::Pg))] | ||||
| #[derive(Serialize, Deserialize, PartialEq, Clone, Debug)] | ||||
| #[cfg_attr( | ||||
|     feature = "server", | ||||
|     derive(Queryable, Selectable, Identifiable, Associations) | ||||
| )] | ||||
| #[cfg_attr( | ||||
|     feature = "server",  | ||||
|     diesel( | ||||
|         table_name = subtasks, | ||||
|         belongs_to(Task, foreign_key = task_id), | ||||
|         check_for_backend(diesel::pg::Pg) | ||||
|     ) | ||||
| )] | ||||
| pub struct Subtask { | ||||
|     id: i32, | ||||
|     task_id: i32, | ||||
| @@ -59,23 +70,33 @@ impl PartialOrd<Self> for Subtask { | ||||
|  | ||||
| impl Ord for Subtask { | ||||
|     fn cmp(&self, other: &Self) -> Ordering { | ||||
|         self.is_completed().cmp(&other.is_completed()) | ||||
|         self.is_completed() | ||||
|             .cmp(&other.is_completed()) | ||||
|             .then(self.created_at().cmp(&other.created_at())) | ||||
|     } | ||||
| } | ||||
|  | ||||
| #[derive(Insertable, Serialize, Deserialize, Validate, Clone, Debug)] | ||||
| #[diesel(table_name = subtasks)] | ||||
| #[derive(Serialize, Deserialize, Validate, Clone, Debug)] | ||||
| #[cfg_attr(feature = "server", derive(Insertable))] | ||||
| #[cfg_attr(feature = "server", diesel(table_name = subtasks))] | ||||
| pub struct NewSubtask { | ||||
|     pub task_id: i32, | ||||
|     #[validate(length(min = "TITLE_LENGTH_MIN", max = "TITLE_LENGTH_MAX", code = "title_length"))] | ||||
|     #[validate(length( | ||||
|         min = "TITLE_LENGTH_MIN", | ||||
|         max = "TITLE_LENGTH_MAX", | ||||
|         code = "title_length" | ||||
|     ))] | ||||
|     pub title: String, | ||||
|     pub is_completed: bool, | ||||
| } | ||||
|  | ||||
| impl NewSubtask { | ||||
|     pub fn new(task_id: i32, title: String, is_completed: bool) -> Self { | ||||
|         Self { task_id, title, is_completed } | ||||
|         Self { | ||||
|             task_id, | ||||
|             title, | ||||
|             is_completed, | ||||
|         } | ||||
|     } | ||||
| } | ||||
|  | ||||
|   | ||||
| @@ -1,8 +1,10 @@ | ||||
| use crate::models::category::Category; | ||||
| use crate::models::subtask::Subtask; | ||||
| #[cfg(feature = "server")] | ||||
| use crate::schema::tasks; | ||||
| use crate::utils::reverse_ord_option::ReverseOrdOption; | ||||
| use chrono::NaiveDateTime; | ||||
| #[cfg(feature = "server")] | ||||
| use diesel::prelude::*; | ||||
| use serde::{Deserialize, Serialize}; | ||||
| use std::cmp::Ordering; | ||||
| @@ -11,9 +13,9 @@ use validator::Validate; | ||||
| const TITLE_LENGTH_MIN: u64 = 1; | ||||
| const TITLE_LENGTH_MAX: u64 = 255; | ||||
|  | ||||
| #[derive(Queryable, Selectable, Identifiable, Serialize, Deserialize, PartialEq, Clone, Debug)] | ||||
| #[diesel(table_name = tasks)] | ||||
| #[diesel(check_for_backend(diesel::pg::Pg))] | ||||
| #[derive(Serialize, Deserialize, PartialEq, Clone, Debug)] | ||||
| #[cfg_attr(feature = "server", derive(Queryable, Selectable, Identifiable))] | ||||
| #[cfg_attr(feature = "server", diesel(table_name = tasks, check_for_backend(diesel::pg::Pg)))] | ||||
| pub struct Task { | ||||
|     id: i32, | ||||
|     title: String, | ||||
| @@ -67,23 +69,39 @@ impl Ord for Task { | ||||
|         match (&self.category, &other.category) { | ||||
|             (Category::Inbox, Category::Inbox) => self.created_at.cmp(&other.created_at), | ||||
|             ( | ||||
|                 Category::Calendar { date: self_date, time: self_time, .. }, | ||||
|                 Category::Calendar { date: other_date, time: other_time, .. } | ||||
|             ) => self_date.cmp(other_date) | ||||
|                 .then(ReverseOrdOption::from( | ||||
|                     &self_time.as_ref().map(|calendar_time| calendar_time.time()) | ||||
|                 ).cmp(&ReverseOrdOption::from( | ||||
|                     &other_time.as_ref().map(|calendar_time| calendar_time.time()) | ||||
|                 ))) | ||||
|                 .then(ReverseOrdOption::from(&self.deadline()).cmp( | ||||
|                     &ReverseOrdOption::from(&other.deadline()) | ||||
|                 )) | ||||
|                 Category::Calendar { | ||||
|                     date: self_date, | ||||
|                     time: self_time, | ||||
|                     .. | ||||
|                 }, | ||||
|                 Category::Calendar { | ||||
|                     date: other_date, | ||||
|                     time: other_time, | ||||
|                     .. | ||||
|                 }, | ||||
|             ) => self_date | ||||
|                 .cmp(other_date) | ||||
|                 .then( | ||||
|                     ReverseOrdOption::from( | ||||
|                         &self_time.as_ref().map(|calendar_time| calendar_time.time()), | ||||
|                     ) | ||||
|                     .cmp(&ReverseOrdOption::from( | ||||
|                         &other_time | ||||
|                             .as_ref() | ||||
|                             .map(|calendar_time| calendar_time.time()), | ||||
|                     )), | ||||
|                 ) | ||||
|                 .then( | ||||
|                     ReverseOrdOption::from(&self.deadline()) | ||||
|                         .cmp(&ReverseOrdOption::from(&other.deadline())), | ||||
|                 ) | ||||
|                 .then(self.created_at.cmp(&other.created_at)), | ||||
|             (Category::Done, Category::Done) | (Category::Trash, Category::Trash) => { | ||||
|                 self.updated_at.cmp(&other.updated_at).reverse() | ||||
|             } | ||||
|             (_, _) => ReverseOrdOption::from(&self.deadline()) | ||||
|                 .cmp(&ReverseOrdOption::from(&other.deadline())) | ||||
|                 .then(self.created_at.cmp(&other.created_at)), | ||||
|             (Category::Done, Category::Done) | (Category::Trash, Category::Trash) | ||||
|             => self.updated_at.cmp(&other.updated_at).reverse(), | ||||
|             (_, _) => ReverseOrdOption::from(&self.deadline()).cmp( | ||||
|                 &ReverseOrdOption::from(&other.deadline()) | ||||
|             ).then(self.created_at.cmp(&other.created_at)), | ||||
|         } | ||||
|     } | ||||
| } | ||||
| @@ -122,10 +140,15 @@ impl Ord for TaskWithSubtasks { | ||||
|     } | ||||
| } | ||||
|  | ||||
| #[derive(Insertable, Serialize, Deserialize, Validate, Clone, Debug)] | ||||
| #[diesel(table_name = tasks)] | ||||
| #[derive(Serialize, Deserialize, Validate, Clone, Debug)] | ||||
| #[cfg_attr(feature = "server", derive(Insertable))] | ||||
| #[cfg_attr(feature = "server", diesel(table_name = tasks))] | ||||
| pub struct NewTask { | ||||
|     #[validate(length(min = "TITLE_LENGTH_MIN", max = "TITLE_LENGTH_MAX", code = "title_length"))] | ||||
|     #[validate(length( | ||||
|         min = "TITLE_LENGTH_MIN", | ||||
|         max = "TITLE_LENGTH_MAX", | ||||
|         code = "title_length" | ||||
|     ))] | ||||
|     pub title: String, | ||||
|     pub deadline: Option<chrono::NaiveDate>, | ||||
|     pub category: Category, | ||||
| @@ -139,7 +162,12 @@ impl NewTask { | ||||
|         category: Category, | ||||
|         project_id: Option<i32>, | ||||
|     ) -> Self { | ||||
|         Self { title, deadline, category, project_id } | ||||
|         Self { | ||||
|             title, | ||||
|             deadline, | ||||
|             category, | ||||
|             project_id, | ||||
|         } | ||||
|     } | ||||
| } | ||||
|  | ||||
|   | ||||
| @@ -5,10 +5,11 @@ use crate::models::project::Project; | ||||
| use crate::models::subtask::Subtask; | ||||
| use crate::models::task::{Task, TaskWithSubtasks}; | ||||
|  | ||||
| pub(crate) mod tasks; | ||||
| pub(crate) mod projects; | ||||
| pub(crate) mod subtasks; | ||||
| pub(crate) mod tasks; | ||||
|  | ||||
| #[allow(dead_code)] | ||||
| #[derive(PartialEq, Debug)] | ||||
| pub(crate) enum QueryValue { | ||||
|     Projects(Vec<Project>), | ||||
| @@ -17,6 +18,7 @@ pub(crate) enum QueryValue { | ||||
|     Subtasks(Vec<Subtask>), | ||||
| } | ||||
|  | ||||
| #[allow(dead_code)] | ||||
| #[derive(Debug)] | ||||
| pub(crate) enum QueryErrors { | ||||
|     Error(ErrorVec<Error>), | ||||
|   | ||||
| @@ -12,8 +12,9 @@ async fn fetch_projects(keys: Vec<QueryKey>) -> QueryResult<QueryValue, QueryErr | ||||
|         match get_projects().await { | ||||
|             Ok(projects) => Ok(QueryValue::Projects(projects)), | ||||
|             Err(ServerFnError::WrappedServerError(errors)) => Err(QueryErrors::Error(errors)), | ||||
|             Err(error) => panic!("Unexpected error: {:?}", error) | ||||
|         }.into() | ||||
|             Err(error) => panic!("Unexpected error: {:?}", error), | ||||
|         } | ||||
|         .into() | ||||
|     } else { | ||||
|         panic!("Unexpected query keys: {:?}", keys); | ||||
|     } | ||||
|   | ||||
| @@ -3,9 +3,13 @@ use crate::server::subtasks::get_subtasks_of_task; | ||||
| use dioxus::prelude::ServerFnError; | ||||
| use dioxus_query::prelude::{use_get_query, QueryResult, UseQuery}; | ||||
|  | ||||
| pub(crate) fn use_subtasks_of_task_query(task_id: i32) | ||||
|                                          -> UseQuery<QueryValue, QueryErrors, QueryKey> { | ||||
|     use_get_query([QueryKey::SubtasksOfTaskId(task_id)], fetch_subtasks_of_task) | ||||
| pub(crate) fn use_subtasks_of_task_query( | ||||
|     task_id: i32, | ||||
| ) -> UseQuery<QueryValue, QueryErrors, QueryKey> { | ||||
|     use_get_query( | ||||
|         [QueryKey::SubtasksOfTaskId(task_id)], | ||||
|         fetch_subtasks_of_task, | ||||
|     ) | ||||
| } | ||||
|  | ||||
| async fn fetch_subtasks_of_task(keys: Vec<QueryKey>) -> QueryResult<QueryValue, QueryErrors> { | ||||
| @@ -13,8 +17,9 @@ async fn fetch_subtasks_of_task(keys: Vec<QueryKey>) -> QueryResult<QueryValue, | ||||
|         match get_subtasks_of_task(*task_id).await { | ||||
|             Ok(subtasks) => Ok(QueryValue::Subtasks(subtasks)), | ||||
|             Err(ServerFnError::WrappedServerError(errors)) => Err(QueryErrors::Error(errors)), | ||||
|             Err(error) => panic!("Unexpected error: {:?}", error) | ||||
|         }.into() | ||||
|             Err(error) => panic!("Unexpected error: {:?}", error), | ||||
|         } | ||||
|         .into() | ||||
|     } else { | ||||
|         panic!("Unexpected query keys: {:?}", keys); | ||||
|     } | ||||
|   | ||||
| @@ -1,14 +1,17 @@ | ||||
| use dioxus::prelude::ServerFnError; | ||||
| use dioxus_query::prelude::{use_get_query, QueryResult, UseQuery}; | ||||
| use crate::models::category::Category; | ||||
| use crate::query::{QueryErrors, QueryKey, QueryValue}; | ||||
| use crate::server::tasks::{get_tasks_in_category, get_tasks_with_subtasks_in_category}; | ||||
| use dioxus::prelude::ServerFnError; | ||||
| use dioxus_query::prelude::{use_get_query, QueryResult, UseQuery}; | ||||
|  | ||||
|  | ||||
|  | ||||
| pub(crate) fn use_tasks_in_category_query(category: Category) | ||||
|                                           -> UseQuery<QueryValue, QueryErrors, QueryKey> { | ||||
|     use_get_query([QueryKey::TasksInCategory(category), QueryKey::Tasks], fetch_tasks_in_category) | ||||
| #[allow(dead_code)] | ||||
| pub(crate) fn use_tasks_in_category_query( | ||||
|     category: Category, | ||||
| ) -> UseQuery<QueryValue, QueryErrors, QueryKey> { | ||||
|     use_get_query( | ||||
|         [QueryKey::TasksInCategory(category), QueryKey::Tasks], | ||||
|         fetch_tasks_in_category, | ||||
|     ) | ||||
| } | ||||
|  | ||||
| async fn fetch_tasks_in_category(keys: Vec<QueryKey>) -> QueryResult<QueryValue, QueryErrors> { | ||||
| @@ -16,34 +19,37 @@ async fn fetch_tasks_in_category(keys: Vec<QueryKey>) -> QueryResult<QueryValue, | ||||
|         match get_tasks_in_category(category.clone()).await { | ||||
|             Ok(tasks) => Ok(QueryValue::Tasks(tasks)), | ||||
|             Err(ServerFnError::WrappedServerError(errors)) => Err(QueryErrors::Error(errors)), | ||||
|             Err(error) => panic!("Unexpected error: {:?}", error) | ||||
|         }.into() | ||||
|             Err(error) => panic!("Unexpected error: {:?}", error), | ||||
|         } | ||||
|         .into() | ||||
|     } else { | ||||
|         panic!("Unexpected query keys: {:?}", keys); | ||||
|     } | ||||
| } | ||||
|  | ||||
| pub(crate) fn use_tasks_with_subtasks_in_category_query(category: Category) | ||||
|                                           -> UseQuery<QueryValue, QueryErrors, QueryKey> { | ||||
| pub(crate) fn use_tasks_with_subtasks_in_category_query( | ||||
|     category: Category, | ||||
| ) -> UseQuery<QueryValue, QueryErrors, QueryKey> { | ||||
|     use_get_query( | ||||
|         [ | ||||
|             QueryKey::TasksWithSubtasksInCategory( | ||||
|             category.clone()), | ||||
|             QueryKey::TasksWithSubtasksInCategory(category.clone()), | ||||
|             QueryKey::TasksInCategory(category), | ||||
|             QueryKey::Tasks | ||||
|             QueryKey::Tasks, | ||||
|         ], | ||||
|         fetch_tasks_with_subtasks_in_category | ||||
|         fetch_tasks_with_subtasks_in_category, | ||||
|     ) | ||||
| } | ||||
|  | ||||
| async fn fetch_tasks_with_subtasks_in_category(keys: Vec<QueryKey>) | ||||
|     -> QueryResult<QueryValue, QueryErrors> { | ||||
| async fn fetch_tasks_with_subtasks_in_category( | ||||
|     keys: Vec<QueryKey>, | ||||
| ) -> QueryResult<QueryValue, QueryErrors> { | ||||
|     if let Some(QueryKey::TasksWithSubtasksInCategory(category)) = keys.first() { | ||||
|         match get_tasks_with_subtasks_in_category(category.clone()).await { | ||||
|             Ok(tasks) => Ok(QueryValue::TasksWithSubtasks(tasks)), | ||||
|             Err(ServerFnError::WrappedServerError(errors)) => Err(QueryErrors::Error(errors)), | ||||
|             Err(error) => panic!("Unexpected error: {:?}", error) | ||||
|         }.into() | ||||
|             Err(error) => panic!("Unexpected error: {:?}", error), | ||||
|         } | ||||
|         .into() | ||||
|     } else { | ||||
|         panic!("Unexpected query keys: {:?}", keys); | ||||
|     } | ||||
|   | ||||
| @@ -1,15 +1,15 @@ | ||||
| use crate::components::layout::Layout; | ||||
| use crate::components::pages::category_calendar_page::CategoryCalendarPage; | ||||
| use crate::components::pages::category_done_page::CategoryDonePage; | ||||
| use crate::components::pages::category_inbox_page::CategoryInboxPage; | ||||
| use crate::components::pages::category_long_term_page::CategoryLongTermPage; | ||||
| use crate::components::pages::category_next_steps_page::CategoryNextStepsPage; | ||||
| use crate::components::pages::category_someday_maybe_page::CategorySomedayMaybePage; | ||||
| use crate::components::pages::category_today_page::CategoryTodayPage; | ||||
| use crate::components::pages::category_trash_page::CategoryTrashPage; | ||||
| use crate::components::pages::category_waiting_for_page::CategoryWaitingForPage; | ||||
| use crate::components::pages::category_someday_maybe_page::CategorySomedayMaybePage; | ||||
| use crate::components::pages::category_done_page::CategoryDonePage; | ||||
| use crate::components::pages::category_calendar_page::CategoryCalendarPage; | ||||
| use crate::components::pages::category_long_term_page::CategoryLongTermPage; | ||||
| use crate::components::pages::projects_page::ProjectsPage; | ||||
| use crate::components::pages::not_found_page::NotFoundPage; | ||||
| use crate::components::layout::Layout; | ||||
| use crate::components::pages::projects_page::ProjectsPage; | ||||
| use dioxus::prelude::*; | ||||
|  | ||||
| // All variants have the same postfix because they have to match the component names. | ||||
|   | ||||
| @@ -35,8 +35,4 @@ diesel::table! { | ||||
| diesel::joinable!(subtasks -> tasks (task_id)); | ||||
| diesel::joinable!(tasks -> projects (project_id)); | ||||
|  | ||||
| diesel::allow_tables_to_appear_in_same_query!( | ||||
|     projects, | ||||
|     subtasks, | ||||
|     tasks, | ||||
| ); | ||||
| diesel::allow_tables_to_appear_in_same_query!(projects, subtasks, tasks,); | ||||
|   | ||||
| @@ -1,8 +1,9 @@ | ||||
| use std::env; | ||||
| use dioxus::prelude::ServerFnError; | ||||
| use unic_langid_impl::LanguageIdentifier; | ||||
| use dioxus::prelude::*; | ||||
| #[cfg(feature = "server")] | ||||
| use dotenvy::dotenv; | ||||
| use std::env; | ||||
| use unic_langid_impl::LanguageIdentifier; | ||||
|  | ||||
| #[server] | ||||
| pub(crate) async fn get_language_identifier() -> Result<LanguageIdentifier, ServerFnError> { | ||||
|   | ||||
| @@ -1,5 +1,6 @@ | ||||
| #[cfg(feature = "server")] | ||||
| pub(crate) mod database_connection; | ||||
| pub(crate) mod projects; | ||||
| pub(crate) mod tasks; | ||||
| pub(crate) mod subtasks; | ||||
| pub(crate) mod internationalization; | ||||
| pub(crate) mod projects; | ||||
| pub(crate) mod subtasks; | ||||
| pub(crate) mod tasks; | ||||
|   | ||||
| @@ -2,27 +2,32 @@ use crate::errors::error::Error; | ||||
| use crate::errors::error_vec::ErrorVec; | ||||
| use crate::errors::project_error::ProjectError; | ||||
| use crate::models::project::{NewProject, Project}; | ||||
| #[cfg(feature = "server")] | ||||
| use crate::server::database_connection::establish_database_connection; | ||||
| #[cfg(feature = "server")] | ||||
| use diesel::{ExpressionMethods, QueryDsl, RunQueryDsl, SelectableHelper}; | ||||
| use dioxus::prelude::*; | ||||
| #[cfg(feature = "server")] | ||||
| use validator::Validate; | ||||
|  | ||||
| #[server] | ||||
| pub(crate) async fn create_project(new_project: NewProject) | ||||
|                                    -> Result<Project, ServerFnError<ErrorVec<ProjectError>>> { | ||||
| pub(crate) async fn create_project( | ||||
|     new_project: NewProject, | ||||
| ) -> Result<Project, ServerFnError<ErrorVec<ProjectError>>> { | ||||
|     use crate::schema::projects; | ||||
|  | ||||
|     // TODO: replace with model sanitization (https://github.com/matous-volf/todo-baggins/issues/13) | ||||
|     let mut new_project = new_project; | ||||
|     new_project.title = new_project.title.trim().to_owned(); | ||||
|  | ||||
|     new_project.validate() | ||||
|     new_project | ||||
|         .validate() | ||||
|         .map_err::<ErrorVec<ProjectError>, _>(|errors| errors.into())?; | ||||
|  | ||||
|     let mut connection = establish_database_connection() | ||||
|         .map_err::<ErrorVec<ProjectError>, _>( | ||||
|             |_| vec![ProjectError::Error(Error::ServerInternal)].into() | ||||
|         )?; | ||||
|     let mut connection = | ||||
|         establish_database_connection().map_err::<ErrorVec<ProjectError>, _>(|_| { | ||||
|             vec![ProjectError::Error(Error::ServerInternal)].into() | ||||
|         })?; | ||||
|  | ||||
|     let new_project = diesel::insert_into(projects::table) | ||||
|         .values(&new_project) | ||||
| @@ -34,41 +39,39 @@ pub(crate) async fn create_project(new_project: NewProject) | ||||
| } | ||||
|  | ||||
| #[server] | ||||
| pub(crate) async fn get_projects() | ||||
|     -> Result<Vec<Project>, ServerFnError<ErrorVec<Error>>> { | ||||
| pub(crate) async fn get_projects() -> Result<Vec<Project>, ServerFnError<ErrorVec<Error>>> { | ||||
|     use crate::schema::projects::dsl::*; | ||||
|  | ||||
|     let mut connection = establish_database_connection() | ||||
|         .map_err::<ErrorVec<Error>, _>( | ||||
|             |_| vec![Error::ServerInternal].into() | ||||
|         )?; | ||||
|         .map_err::<ErrorVec<Error>, _>(|_| vec![Error::ServerInternal].into())?; | ||||
|  | ||||
|     let results = projects | ||||
|         .select(Project::as_select()) | ||||
|         .load::<Project>(&mut connection) | ||||
|         .map_err::<ErrorVec<Error>, _>( | ||||
|             |_| vec![Error::ServerInternal].into() | ||||
|         )?; | ||||
|         .map_err::<ErrorVec<Error>, _>(|_| vec![Error::ServerInternal].into())?; | ||||
|  | ||||
|     Ok(results) | ||||
| } | ||||
|  | ||||
| #[server] | ||||
| pub(crate) async fn edit_project(project_id: i32, new_project: NewProject) | ||||
|                                  -> Result<Project, ServerFnError<ErrorVec<ProjectError>>> { | ||||
| pub(crate) async fn edit_project( | ||||
|     project_id: i32, | ||||
|     new_project: NewProject, | ||||
| ) -> Result<Project, ServerFnError<ErrorVec<ProjectError>>> { | ||||
|     use crate::schema::projects::dsl::*; | ||||
|  | ||||
|     // TODO: replace with model sanitization (https://github.com/matous-volf/todo-baggins/issues/13) | ||||
|     let mut new_project = new_project; | ||||
|     new_project.title = new_project.title.trim().to_owned(); | ||||
|  | ||||
|     new_project.validate() | ||||
|     new_project | ||||
|         .validate() | ||||
|         .map_err::<ErrorVec<ProjectError>, _>(|errors| errors.into())?; | ||||
|  | ||||
|     let mut connection = establish_database_connection() | ||||
|         .map_err::<ErrorVec<ProjectError>, _>( | ||||
|             |_| vec![ProjectError::Error(Error::ServerInternal)].into() | ||||
|         )?; | ||||
|     let mut connection = | ||||
|         establish_database_connection().map_err::<ErrorVec<ProjectError>, _>(|_| { | ||||
|             vec![ProjectError::Error(Error::ServerInternal)].into() | ||||
|         })?; | ||||
|  | ||||
|     let updated_project = diesel::update(projects) | ||||
|         .filter(id.eq(project_id)) | ||||
| @@ -83,15 +86,14 @@ pub(crate) async fn edit_project(project_id: i32, new_project: NewProject) | ||||
| // TODO: Get rid of this suppression. | ||||
| //noinspection DuplicatedCode | ||||
| #[server] | ||||
| pub(crate) async fn delete_project(project_id: i32) | ||||
|                                    -> Result<(), ServerFnError<ErrorVec<Error>>> { | ||||
| pub(crate) async fn delete_project(project_id: i32) -> Result<(), ServerFnError<ErrorVec<Error>>> { | ||||
|     use crate::schema::projects::dsl::*; | ||||
|  | ||||
|     let mut connection = establish_database_connection() | ||||
|         .map_err::<ErrorVec<Error>, _>(|_| vec![Error::ServerInternal].into())?; | ||||
|  | ||||
|      | ||||
|     diesel::delete(projects.filter(id.eq(project_id))).execute(&mut connection) | ||||
|     diesel::delete(projects.filter(id.eq(project_id))) | ||||
|         .execute(&mut connection) | ||||
|         .map_err::<ErrorVec<Error>, _>(|error| vec![error.into()].into())?; | ||||
|  | ||||
|     Ok(()) | ||||
|   | ||||
| @@ -2,44 +2,52 @@ use crate::errors::error::Error; | ||||
| use crate::errors::error_vec::ErrorVec; | ||||
| use crate::errors::subtask_error::SubtaskError; | ||||
| use crate::models::subtask::{NewSubtask, Subtask}; | ||||
| #[cfg(feature = "server")] | ||||
| use crate::server::database_connection::establish_database_connection; | ||||
| #[cfg(feature = "server")] | ||||
| use crate::server::tasks::trigger_task_updated_at; | ||||
| #[cfg(feature = "server")] | ||||
| use diesel::{ExpressionMethods, QueryDsl, RunQueryDsl, SelectableHelper}; | ||||
| use dioxus::prelude::*; | ||||
| #[cfg(feature = "server")] | ||||
| use validator::Validate; | ||||
| use crate::server::tasks::trigger_task_updated_at; | ||||
|  | ||||
| #[server] | ||||
| pub(crate) async fn create_subtask(new_subtask: NewSubtask) | ||||
|                                    -> Result<Subtask, ServerFnError<ErrorVec<SubtaskError>>> { | ||||
| pub(crate) async fn create_subtask( | ||||
|     new_subtask: NewSubtask, | ||||
| ) -> Result<Subtask, ServerFnError<ErrorVec<SubtaskError>>> { | ||||
|     use crate::schema::subtasks; | ||||
|  | ||||
|     // TODO: replace with model sanitization (https://github.com/matous-volf/todo-baggins/issues/13) | ||||
|     let mut new_subtask = new_subtask; | ||||
|     new_subtask.title = new_subtask.title.trim().to_owned(); | ||||
|  | ||||
|     new_subtask.validate() | ||||
|     new_subtask | ||||
|         .validate() | ||||
|         .map_err::<ErrorVec<SubtaskError>, _>(|errors| errors.into())?; | ||||
|  | ||||
|     let mut connection = establish_database_connection() | ||||
|         .map_err::<ErrorVec<SubtaskError>, _>( | ||||
|             |_| vec![SubtaskError::Error(Error::ServerInternal)].into() | ||||
|         )?; | ||||
|     let mut connection = | ||||
|         establish_database_connection().map_err::<ErrorVec<SubtaskError>, _>(|_| { | ||||
|             vec![SubtaskError::Error(Error::ServerInternal)].into() | ||||
|         })?; | ||||
|  | ||||
|     let created_subtask = diesel::insert_into(subtasks::table) | ||||
|         .values(&new_subtask) | ||||
|         .returning(Subtask::as_returning()) | ||||
|         .get_result(&mut connection) | ||||
|         .map_err::<ErrorVec<SubtaskError>, _>(|error| vec![error.into()].into())?; | ||||
|      | ||||
|     trigger_task_updated_at(new_subtask.task_id).await | ||||
|  | ||||
|     trigger_task_updated_at(new_subtask.task_id) | ||||
|         .await | ||||
|         .map_err::<ErrorVec<SubtaskError>, _>(|error_vec| error_vec.into())?; | ||||
|  | ||||
|     Ok(created_subtask) | ||||
| } | ||||
|  | ||||
| #[server] | ||||
| pub(crate) async fn get_subtasks_of_task(filtered_task_id: i32) | ||||
|                                          -> Result<Vec<Subtask>, ServerFnError<ErrorVec<Error>>> { | ||||
| pub(crate) async fn get_subtasks_of_task( | ||||
|     filtered_task_id: i32, | ||||
| ) -> Result<Vec<Subtask>, ServerFnError<ErrorVec<Error>>> { | ||||
|     use crate::schema::subtasks::dsl::*; | ||||
|  | ||||
|     let mut connection = establish_database_connection() | ||||
| @@ -55,43 +63,46 @@ pub(crate) async fn get_subtasks_of_task(filtered_task_id: i32) | ||||
| } | ||||
|  | ||||
| #[server] | ||||
| pub(crate) async fn edit_subtask(subtask_id: i32, new_subtask: NewSubtask) | ||||
|                                  -> Result<Subtask, ServerFnError<ErrorVec<SubtaskError>>> { | ||||
| pub(crate) async fn edit_subtask( | ||||
|     subtask_id: i32, | ||||
|     new_subtask: NewSubtask, | ||||
| ) -> Result<Subtask, ServerFnError<ErrorVec<SubtaskError>>> { | ||||
|     use crate::schema::subtasks::dsl::*; | ||||
|  | ||||
|     // TODO: replace with model sanitization (https://github.com/matous-volf/todo-baggins/issues/13) | ||||
|     let mut new_subtask = new_subtask; | ||||
|     new_subtask.title = new_subtask.title.trim().to_owned(); | ||||
|  | ||||
|     new_subtask.validate() | ||||
|     new_subtask | ||||
|         .validate() | ||||
|         .map_err::<ErrorVec<SubtaskError>, _>(|errors| errors.into())?; | ||||
|  | ||||
|     let mut connection = establish_database_connection() | ||||
|         .map_err::<ErrorVec<SubtaskError>, _>( | ||||
|             |_| vec![SubtaskError::Error(Error::ServerInternal)].into() | ||||
|         )?; | ||||
|     let mut connection = | ||||
|         establish_database_connection().map_err::<ErrorVec<SubtaskError>, _>(|_| { | ||||
|             vec![SubtaskError::Error(Error::ServerInternal)].into() | ||||
|         })?; | ||||
|  | ||||
|     let updated_task = diesel::update(subtasks) | ||||
|         .filter(id.eq(subtask_id)) | ||||
|         .set(( | ||||
|             title.eq(new_subtask.title), | ||||
|             is_completed.eq(new_subtask.is_completed) | ||||
|             is_completed.eq(new_subtask.is_completed), | ||||
|         )) | ||||
|         .returning(Subtask::as_returning()) | ||||
|         .get_result(&mut connection) | ||||
|         .map_err::<ErrorVec<SubtaskError>, _>(|error| vec![error.into()].into())?; | ||||
|  | ||||
|     trigger_task_updated_at(new_subtask.task_id).await | ||||
|     trigger_task_updated_at(new_subtask.task_id) | ||||
|         .await | ||||
|         .map_err::<ErrorVec<SubtaskError>, _>(|error_vec| error_vec.into())?; | ||||
|      | ||||
|  | ||||
|     Ok(updated_task) | ||||
| } | ||||
|  | ||||
| #[server] | ||||
| pub(crate) async fn restore_subtasks_of_task(filtered_task_id: i32) -> Result< | ||||
|     Vec<Subtask>, | ||||
|     ServerFnError<ErrorVec<Error>> | ||||
| > { | ||||
| pub(crate) async fn restore_subtasks_of_task( | ||||
|     filtered_task_id: i32, | ||||
| ) -> Result<Vec<Subtask>, ServerFnError<ErrorVec<Error>>> { | ||||
|     use crate::schema::subtasks::dsl::*; | ||||
|  | ||||
|     let mut connection = establish_database_connection() | ||||
| @@ -110,8 +121,7 @@ pub(crate) async fn restore_subtasks_of_task(filtered_task_id: i32) -> Result< | ||||
| // TODO: Get rid of this suppression. | ||||
| //noinspection DuplicatedCode | ||||
| #[server] | ||||
| pub(crate) async fn delete_subtask(subtask_id: i32) | ||||
|                                    -> Result<(), ServerFnError<ErrorVec<Error>>> { | ||||
| pub(crate) async fn delete_subtask(subtask_id: i32) -> Result<(), ServerFnError<ErrorVec<Error>>> { | ||||
|     use crate::schema::subtasks::dsl::*; | ||||
|  | ||||
|     let mut connection = establish_database_connection() | ||||
|   | ||||
| @@ -1,34 +1,46 @@ | ||||
| use chrono::{Datelike, Days, Local, Months, NaiveDate}; | ||||
| use crate::errors::error::Error; | ||||
| use crate::errors::error_vec::ErrorVec; | ||||
| use crate::errors::task_error::TaskError; | ||||
| use crate::models::category::Category; | ||||
| #[cfg(feature = "server")] | ||||
| use crate::models::category::ReoccurrenceInterval; | ||||
| #[cfg(feature = "server")] | ||||
| use crate::models::subtask::Subtask; | ||||
| use crate::models::task::{NewTask, Task, TaskWithSubtasks}; | ||||
| #[cfg(feature = "server")] | ||||
| use crate::server::database_connection::establish_database_connection; | ||||
| #[cfg(feature = "server")] | ||||
| use crate::server::subtasks::restore_subtasks_of_task; | ||||
| #[cfg(feature = "server")] | ||||
| use chrono::{Datelike, Days, Local, Months, NaiveDate}; | ||||
| #[cfg(feature = "server")] | ||||
| use diesel::prelude::*; | ||||
| #[cfg(feature = "server")] | ||||
| use diesel::{ExpressionMethods, OptionalExtension, QueryDsl, RunQueryDsl, SelectableHelper}; | ||||
| use dioxus::prelude::*; | ||||
| use diesel::prelude::*; | ||||
| #[cfg(feature = "server")] | ||||
| use time::util::days_in_year_month; | ||||
| #[cfg(feature = "server")] | ||||
| use validator::Validate; | ||||
| use crate::errors::task_error::TaskError; | ||||
| use crate::models::category::{Category, ReoccurrenceInterval}; | ||||
| use crate::models::subtask::Subtask; | ||||
| use crate::server::subtasks::restore_subtasks_of_task; | ||||
|  | ||||
| #[server] | ||||
| pub(crate) async fn create_task(new_task: NewTask) | ||||
|                                 -> Result<Task, ServerFnError<ErrorVec<TaskError>>> { | ||||
| pub(crate) async fn create_task( | ||||
|     new_task: NewTask, | ||||
| ) -> Result<Task, ServerFnError<ErrorVec<TaskError>>> { | ||||
|     use crate::schema::tasks; | ||||
|      | ||||
|  | ||||
|     // TODO: replace with model sanitization (https://github.com/matous-volf/todo-baggins/issues/13) | ||||
|     let mut new_task = new_task; | ||||
|     new_task.title = new_task.title.trim().to_owned(); | ||||
|  | ||||
|     new_task.validate() | ||||
|     new_task | ||||
|         .validate() | ||||
|         .map_err::<ErrorVec<TaskError>, _>(|errors| errors.into())?; | ||||
|  | ||||
|     let mut connection = establish_database_connection() | ||||
|         .map_err::<ErrorVec<TaskError>, _>( | ||||
|             |_| vec![TaskError::Error(Error::ServerInternal)].into() | ||||
|         )?; | ||||
|     let mut connection = | ||||
|         establish_database_connection().map_err::<ErrorVec<TaskError>, _>(|_| { | ||||
|             vec![TaskError::Error(Error::ServerInternal)].into() | ||||
|         })?; | ||||
|  | ||||
|     let new_task = diesel::insert_into(tasks::table) | ||||
|         .values(&new_task) | ||||
| @@ -58,31 +70,27 @@ pub(crate) async fn get_task(task_id: i32) -> Result<Task, ServerFnError<ErrorVe | ||||
| } | ||||
|  | ||||
| #[server] | ||||
| pub(crate) async fn get_tasks_in_category(filtered_category: Category) | ||||
|                                           -> Result<Vec<Task>, ServerFnError<ErrorVec<Error>>> { | ||||
| pub(crate) async fn get_tasks_in_category( | ||||
|     filtered_category: Category, | ||||
| ) -> Result<Vec<Task>, ServerFnError<ErrorVec<Error>>> { | ||||
|     use crate::schema::tasks::dsl::*; | ||||
|  | ||||
|     let mut connection = establish_database_connection() | ||||
|         .map_err::<ErrorVec<Error>, _>( | ||||
|             |_| vec![Error::ServerInternal].into() | ||||
|         )?; | ||||
|         .map_err::<ErrorVec<Error>, _>(|_| vec![Error::ServerInternal].into())?; | ||||
|  | ||||
|     let results = tasks | ||||
|         .select(Task::as_select()) | ||||
|         .filter(filtered_category.eq_sql_predicate()) | ||||
|         .load::<Task>(&mut connection) | ||||
|         .map_err::<ErrorVec<Error>, _>( | ||||
|             |_| vec![Error::ServerInternal].into() | ||||
|         )?; | ||||
|         .map_err::<ErrorVec<Error>, _>(|_| vec![Error::ServerInternal].into())?; | ||||
|  | ||||
|     Ok(results) | ||||
| } | ||||
|  | ||||
| #[server] | ||||
| pub(crate) async fn get_tasks_with_subtasks_in_category(filtered_category: Category) -> Result< | ||||
|     Vec<TaskWithSubtasks>, | ||||
|     ServerFnError<ErrorVec<Error>> | ||||
| > { | ||||
| pub(crate) async fn get_tasks_with_subtasks_in_category( | ||||
|     filtered_category: Category, | ||||
| ) -> Result<Vec<TaskWithSubtasks>, ServerFnError<ErrorVec<Error>>> { | ||||
|     use crate::schema::tasks; | ||||
|  | ||||
|     let mut connection = establish_database_connection() | ||||
| @@ -90,7 +98,8 @@ pub(crate) async fn get_tasks_with_subtasks_in_category(filtered_category: Categ | ||||
|  | ||||
|     let tasks_in_category = tasks::table | ||||
|         .filter(filtered_category.eq_sql_predicate()) | ||||
|         .select(Task::as_select()).load(&mut connection) | ||||
|         .select(Task::as_select()) | ||||
|         .load(&mut connection) | ||||
|         .map_err::<ErrorVec<Error>, _>(|_| vec![Error::ServerInternal].into())?; | ||||
|  | ||||
|     let subtasks = Subtask::belonging_to(&tasks_in_category) | ||||
| @@ -109,21 +118,24 @@ pub(crate) async fn get_tasks_with_subtasks_in_category(filtered_category: Categ | ||||
| } | ||||
|  | ||||
| #[server] | ||||
| pub(crate) async fn edit_task(task_id: i32, mut new_task: NewTask) | ||||
|                               -> Result<Task, ServerFnError<ErrorVec<TaskError>>> { | ||||
| pub(crate) async fn edit_task( | ||||
|     task_id: i32, | ||||
|     mut new_task: NewTask, | ||||
| ) -> Result<Task, ServerFnError<ErrorVec<TaskError>>> { | ||||
|     use crate::schema::tasks::dsl::*; | ||||
|  | ||||
|     // TODO: replace with model sanitization (https://github.com/matous-volf/todo-baggins/issues/13) | ||||
|     let mut new_task = new_task; | ||||
|     new_task.title = new_task.title.trim().to_owned(); | ||||
|      | ||||
|     new_task.validate() | ||||
|  | ||||
|     new_task | ||||
|         .validate() | ||||
|         .map_err::<ErrorVec<TaskError>, _>(|errors| errors.into())?; | ||||
|  | ||||
|     let mut connection = establish_database_connection() | ||||
|         .map_err::<ErrorVec<TaskError>, _>( | ||||
|             |_| vec![TaskError::Error(Error::ServerInternal)].into() | ||||
|         )?; | ||||
|     let mut connection = | ||||
|         establish_database_connection().map_err::<ErrorVec<TaskError>, _>(|_| { | ||||
|             vec![TaskError::Error(Error::ServerInternal)].into() | ||||
|         })?; | ||||
|  | ||||
|     let updated_task = diesel::update(tasks) | ||||
|         .filter(id.eq(task_id)) | ||||
| @@ -149,15 +161,20 @@ pub(crate) async fn complete_task(task_id: i32) -> Result<Task, ServerFnError<Er | ||||
|         reoccurrence: Some(reoccurrence), | ||||
|         date, | ||||
|         .. | ||||
|     } = &mut new_task.category { | ||||
|     } = &mut new_task.category | ||||
|     { | ||||
|         match reoccurrence.interval() { | ||||
|             ReoccurrenceInterval::Day => *date = *date + Days::new(reoccurrence.length() as u64), | ||||
|             ReoccurrenceInterval::Month | ReoccurrenceInterval::Year => { | ||||
|                 *date = *date + Months::new( | ||||
|                     reoccurrence.length() * | ||||
|                         if *(reoccurrence.interval()) == ReoccurrenceInterval::Year | ||||
|                         { 12 } else { 1 } | ||||
|                 ); | ||||
|                 *date = *date | ||||
|                     + Months::new( | ||||
|                         reoccurrence.length() | ||||
|                             * if *(reoccurrence.interval()) == ReoccurrenceInterval::Year { | ||||
|                                 12 | ||||
|                             } else { | ||||
|                                 1 | ||||
|                             }, | ||||
|                     ); | ||||
|                 *date = NaiveDate::from_ymd_opt( | ||||
|                     date.year(), | ||||
|                     date.month(), | ||||
| @@ -165,7 +182,8 @@ pub(crate) async fn complete_task(task_id: i32) -> Result<Task, ServerFnError<Er | ||||
|                         date.year(), | ||||
|                         (date.month() as u8).try_into().unwrap(), | ||||
|                     ) as u32), | ||||
|                 ).unwrap() | ||||
|                 ) | ||||
|                 .unwrap() | ||||
|             } | ||||
|         } | ||||
|         restore_subtasks_of_task(task_id).await?; | ||||
| @@ -173,7 +191,8 @@ pub(crate) async fn complete_task(task_id: i32) -> Result<Task, ServerFnError<Er | ||||
|         new_task.category = Category::Done; | ||||
|     } | ||||
|  | ||||
|     let updated_task = edit_task(task_id, new_task).await | ||||
|     let updated_task = edit_task(task_id, new_task) | ||||
|         .await | ||||
|         .map_err::<ErrorVec<Error>, _>(|_| vec![Error::ServerInternal].into())?; | ||||
|  | ||||
|     Ok(updated_task) | ||||
| @@ -182,26 +201,25 @@ pub(crate) async fn complete_task(task_id: i32) -> Result<Task, ServerFnError<Er | ||||
| // TODO: Get rid of this suppression. | ||||
| //noinspection DuplicatedCode | ||||
| #[server] | ||||
| pub(crate) async fn delete_task(task_id: i32) | ||||
|                                 -> Result<(), ServerFnError<ErrorVec<Error>>> { | ||||
| pub(crate) async fn delete_task(task_id: i32) -> Result<(), ServerFnError<ErrorVec<Error>>> { | ||||
|     use crate::schema::tasks::dsl::*; | ||||
|  | ||||
|     let mut connection = establish_database_connection() | ||||
|         .map_err::<ErrorVec<Error>, _>(|_| vec![Error::ServerInternal].into())?; | ||||
|  | ||||
|     diesel::delete(tasks.filter(id.eq(task_id))).execute(&mut connection) | ||||
|     diesel::delete(tasks.filter(id.eq(task_id))) | ||||
|         .execute(&mut connection) | ||||
|         .map_err::<ErrorVec<Error>, _>(|error| vec![error.into()].into())?; | ||||
|  | ||||
|     Ok(()) | ||||
| } | ||||
|  | ||||
| #[cfg(feature = "server")] | ||||
| pub(crate) async fn trigger_task_updated_at(task_id: i32) -> Result<Task, ErrorVec<Error>> { | ||||
|     use crate::schema::tasks::dsl::*; | ||||
|      | ||||
|  | ||||
|     let mut connection = establish_database_connection() | ||||
|         .map_err::<ErrorVec<Error>, _>( | ||||
|             |_| vec![Error::ServerInternal].into() | ||||
|         )?; | ||||
|         .map_err::<ErrorVec<Error>, _>(|_| vec![Error::ServerInternal].into())?; | ||||
|  | ||||
|     let updated_task = diesel::update(tasks) | ||||
|         .filter(id.eq(task_id)) | ||||
|   | ||||
| @@ -1,12 +1,12 @@ | ||||
| /** @type {import('tailwindcss').Config} */ | ||||
| module.exports = { | ||||
|     mode: "all", | ||||
|     content: ["./src/**/*.{rs,html,css}", "./dist/**/*.html"], | ||||
|     theme: { | ||||
|         fontFamily: { | ||||
|             sans: ["Inter", "sans-serif"], | ||||
|         }, | ||||
|         extend: {}, | ||||
|   mode: "all", | ||||
|   content: ["./src/**/*.{rs,html,css}", "./dist/**/*.html"], | ||||
|   theme: { | ||||
|     fontFamily: { | ||||
|       sans: ["Inter", "sans-serif"], | ||||
|     }, | ||||
|     plugins: [], | ||||
|     extend: {}, | ||||
|   }, | ||||
|   plugins: [], | ||||
| }; | ||||
|   | ||||
| @@ -1,11 +1,11 @@ | ||||
| use std::cmp::Ordering; | ||||
| use std::ops::Deref; | ||||
| /* The default ordering of `Option`s is `None` being less than `Some`. The purpose of this struct is | ||||
|    to reverse that. */ | ||||
| to reverse that. */ | ||||
| #[derive(PartialEq)] | ||||
| pub(crate) struct ReverseOrdOption<'a, T>(&'a Option<T>); | ||||
|  | ||||
| impl<'a, T> Deref for ReverseOrdOption<'a, T> { | ||||
| impl<T> Deref for ReverseOrdOption<'_, T> { | ||||
|     type Target = Option<T>; | ||||
|  | ||||
|     fn deref(&self) -> &Self::Target { | ||||
| @@ -13,21 +13,21 @@ impl<'a, T> Deref for ReverseOrdOption<'a, T> { | ||||
|     } | ||||
| } | ||||
|  | ||||
| impl<'a, T: Ord> Eq for ReverseOrdOption<'a, T> {} | ||||
| impl<T: Ord> Eq for ReverseOrdOption<'_, T> {} | ||||
|  | ||||
| impl<'a, T: Ord> PartialOrd<Self> for ReverseOrdOption<'a, T> { | ||||
| impl<T: Ord> PartialOrd<Self> for ReverseOrdOption<'_, T> { | ||||
|     fn partial_cmp(&self, other: &Self) -> Option<Ordering> { | ||||
|         Some(self.cmp(other)) | ||||
|     } | ||||
| } | ||||
|  | ||||
| impl<'a, T: Ord> Ord for ReverseOrdOption<'a, T> { | ||||
| impl<T: Ord> Ord for ReverseOrdOption<'_, T> { | ||||
|     fn cmp(&self, other: &Self) -> Ordering { | ||||
|         match (self.as_ref(), other.as_ref()) { | ||||
|             (None, None) => Ordering::Equal, | ||||
|             (None, Some(_)) => Ordering::Greater, | ||||
|             (Some(_), None) => Ordering::Less, | ||||
|             (Some(self_time), Some(other_time)) => self_time.cmp(other_time) | ||||
|             (Some(self_time), Some(other_time)) => self_time.cmp(other_time), | ||||
|         } | ||||
|     } | ||||
| } | ||||
|   | ||||
		Reference in New Issue
	
	Block a user