Compare commits
10 Commits
77618e46ea
...
v2.0.0
| Author | SHA1 | Date | |
|---|---|---|---|
| 4f4047b097 | |||
| 0537cc09cf | |||
| ad1795ba01 | |||
| 95b3e979ad | |||
| a6ff689847 | |||
| 117f52f585 | |||
| 065861a549 | |||
| 787b4f3ea9 | |||
| 710bfa432d | |||
| 518b9e5bf8 |
@@ -1,127 +1,112 @@
|
||||
name: Publish Release
|
||||
on:
|
||||
push:
|
||||
tags:
|
||||
- "*"
|
||||
env:
|
||||
APP_NAME: ToDo App
|
||||
jobs:
|
||||
publish-tauri:
|
||||
permissions:
|
||||
contents: write
|
||||
strategy:
|
||||
fail-fast: false
|
||||
matrix:
|
||||
include:
|
||||
- platform: arch-latest
|
||||
output_dir: src-tauri/target/release/bundle/appimage/**.pkg.tar.zst
|
||||
##- platform: ubuntu-latest
|
||||
## output_dir_one: src-tauri/target/release/bundle/appimage/**.AppImage
|
||||
## output_dir_two: src-tauri/target/release/bundle/deb/**.deb
|
||||
- platform: windows-latest
|
||||
output_dir_one: src-tauri/target/release/bundle/msi/**.msi
|
||||
output_dir_two: src-tauri/target/release/bundle/nsis/**.exe
|
||||
#- platform.os: macos-latest
|
||||
# args: --target aarch64-apple-darwin
|
||||
# output_dir_one: src-tauri/target/aarch64-apple-darwin/release/bundle/macos/**.app.tar.gz
|
||||
# output_dir_two: src-tauri/target/aarch64-apple-darwin/release/bundle/dmg/**.dmg
|
||||
#- platform.os: macos-latest
|
||||
# args: --target x86_64-apple-darwin
|
||||
# output_dir_one: src-tauri/target/x86_64-apple-darwin/release/bundle/macos/**.app
|
||||
# output_dir_two: src-tauri/target/x86_64-apple-darwin/release/bundle/dmg/**.dmg
|
||||
runs-on: ${{ matrix.platform }}
|
||||
steps:
|
||||
- name: Preinstall Arch dependencies
|
||||
if: matrix.platform == 'arch-latest'
|
||||
run: |
|
||||
pacman -Syyu --noconfirm
|
||||
pacman -S nodejs git git-lfs --noconfirm
|
||||
|
||||
curl --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs | sh -s -- -y
|
||||
- name: Setup Node.js
|
||||
uses: actions/setup-node@v4
|
||||
- name: Checkout repository
|
||||
uses: actions/checkout@v4
|
||||
with:
|
||||
lfs: true
|
||||
token: ${{secrets.RELEASE_TOKEN}}
|
||||
- name: Check files (Linux)
|
||||
if: matrix.platform == 'ubuntu-latest' || matrix.platform == 'arch-latest'
|
||||
run: |
|
||||
touch src-tauri/src/config.rs
|
||||
echo 'pub(crate) static ENC_KEY: &str = "${{secrets.HASH_KEY}}";' > src-tauri/src/config.rs
|
||||
- name: Check files (Windows)
|
||||
if: matrix.platform == 'windows-latest'
|
||||
run: |
|
||||
New-Item -Path src-tauri/src -name "config.rs" -Value 'pub(crate) static ENC_KEY: &str = "${{secrets.HASH_KEY}}";'
|
||||
|
||||
- name: Install Rust stable
|
||||
uses: dtolnay/rust-toolchain@stable
|
||||
with:
|
||||
targets: ${{ matrix.platform == 'macos-latest' && 'aarch64-apple-darwin,x86_64-apple-darwin' || '' }}
|
||||
- name: Install Ubuntu dependencies
|
||||
if: matrix.platform == 'ubuntu-latest'
|
||||
run: |
|
||||
sudo apt update
|
||||
sudo apt-get install libgtk-3-dev -y
|
||||
sudo apt install libwebkit2gtk-4.0-dev \
|
||||
build-essential \
|
||||
curl \
|
||||
wget \
|
||||
file \
|
||||
libssl-dev \
|
||||
libgtk-3-dev \
|
||||
libayatana-appindicator3-dev \
|
||||
librsvg2-dev -y
|
||||
- name: Install Arch dependencies
|
||||
if: matrix.platform == 'arch-latest'
|
||||
run: |
|
||||
|
||||
useradd -m user
|
||||
su user
|
||||
git clone https://aur.archlinux.org/yay.git && cd yay && makepkg -si --noconfirm
|
||||
exit
|
||||
yay -S debtap --noconfirm
|
||||
- name: Install frontend
|
||||
run: |
|
||||
npm install
|
||||
- name: Build Tauri app
|
||||
uses: tauri-apps/tauri-action@v0
|
||||
env:
|
||||
GITHUB_TOKEN: ${{secrets.RELEASE_TOKEN}}
|
||||
with:
|
||||
releaseName: ${{ env.APP_NAME }} v__VERSION__
|
||||
releaseDraft: true
|
||||
prerelease: false
|
||||
args: ${{ matrix.args }}
|
||||
|
||||
- name: Build Arch installer
|
||||
if: matrix.platform == 'arch-latest'
|
||||
run: |
|
||||
cd src-tauri/target/release/bundle/deb
|
||||
debtap -Q **.deb
|
||||
- name: Publish Arch installer
|
||||
if: matrix.platform == 'arch-latest'
|
||||
uses: akkuman/gitea-release-action@v1
|
||||
with:
|
||||
tag_name: ${{gitea.ref_name}}
|
||||
server_url: 'http://192.168.0.80:3000'
|
||||
prerelease: false
|
||||
draft: true
|
||||
files: >-
|
||||
${{ matrix.output_dir }}
|
||||
token: ${{secrets.RELEASE_TOKEN}}
|
||||
|
||||
- name: Publish Tauri App
|
||||
if: matrix.platform == 'ubuntu-latest' || matrix.platform == 'windows-latest'
|
||||
uses: akkuman/gitea-release-action@v1
|
||||
with:
|
||||
tag_name: ${{gitea.ref_name}}
|
||||
server_url: 'http://192.168.0.80:3000'
|
||||
prerelease: false
|
||||
draft: true
|
||||
files: >-
|
||||
${{ matrix.output_dir_one }}
|
||||
|
||||
${{ matrix.output_dir_two }}
|
||||
name: Publish Release
|
||||
on:
|
||||
push:
|
||||
tags:
|
||||
- "*"
|
||||
env:
|
||||
APP_NAME: ToDo App
|
||||
jobs:
|
||||
publish-tauri:
|
||||
permissions:
|
||||
contents: write
|
||||
strategy:
|
||||
fail-fast: false
|
||||
matrix:
|
||||
include:
|
||||
- platform: arch-latest
|
||||
args: --bundles deb
|
||||
output_dir: src-tauri/target/release/bundle/deb/**.pkg.tar.zst
|
||||
- platform: ubuntu-latest
|
||||
output_dir_one: src-tauri/target/release/bundle/appimage/**.AppImage
|
||||
output_dir_two: src-tauri/target/release/bundle/deb/**.deb
|
||||
- platform: window-latest
|
||||
output_dir_one: src-tauri/target/release/bundle/msi/**.msi
|
||||
output_dir_two: src-tauri/target/release/bundle/nsis/**.exe
|
||||
#- platform.os: macos-latest
|
||||
# args: --target aarch64-apple-darwin
|
||||
# output_dir_one: src-tauri/target/aarch64-apple-darwin/release/bundle/macos/**.app.tar.gz
|
||||
# output_dir_two: src-tauri/target/aarch64-apple-darwin/release/bundle/dmg/**.dmg
|
||||
#- platform.os: macos-latest
|
||||
# args: --target x86_64-apple-darwin
|
||||
# output_dir_one: src-tauri/target/x86_64-apple-darwin/release/bundle/macos/**.app
|
||||
# output_dir_two: src-tauri/target/x86_64-apple-darwin/release/bundle/dmg/**.dmg
|
||||
runs-on: ${{ matrix.platform }}
|
||||
steps:
|
||||
- name: Setup Node.js
|
||||
uses: actions/setup-node@v4
|
||||
- name: Checkout repository
|
||||
uses: actions/checkout@v4
|
||||
with:
|
||||
lfs: true
|
||||
token: ${{secrets.RELEASE_TOKEN}}
|
||||
- name: Check files (Linux)
|
||||
if: matrix.platform == 'ubuntu-latest' || matrix.platform == 'arch-latest'
|
||||
run: |
|
||||
touch src-tauri/src/config.rs
|
||||
echo 'pub(crate) static ENC_KEY: &str = "${{secrets.HASH_KEY}}";' > src-tauri/src/config.rs
|
||||
- name: Check files (Windows)
|
||||
if: matrix.platform == 'window-latest'
|
||||
run: |
|
||||
New-Item -Path src-tauri/src -name "config.rs" -Value 'pub(crate) static ENC_KEY: &str = "${{secrets.HASH_KEY}}";'
|
||||
|
||||
- name: Install Rust stable
|
||||
uses: dtolnay/rust-toolchain@stable
|
||||
with:
|
||||
targets: ${{ matrix.platform == 'macos-latest' && 'aarch64-apple-darwin,x86_64-apple-darwin' || '' }}
|
||||
- name: Install Ubuntu dependencies
|
||||
if: matrix.platform == 'ubuntu-latest'
|
||||
run: |
|
||||
sudo apt update
|
||||
sudo apt-get install libgtk-3-dev -y
|
||||
sudo apt install libwebkit2gtk-4.0-dev \
|
||||
build-essential \
|
||||
curl \
|
||||
wget \
|
||||
file \
|
||||
libssl-dev \
|
||||
libgtk-3-dev \
|
||||
libayatana-appindicator3-dev \
|
||||
librsvg2-dev -y
|
||||
- name: Install frontend
|
||||
run: |
|
||||
npm install
|
||||
- name: Build Tauri app
|
||||
uses: tauri-apps/tauri-action@v0
|
||||
env:
|
||||
GITHUB_TOKEN: ${{secrets.RELEASE_TOKEN}}
|
||||
with:
|
||||
releaseName: ${{ env.APP_NAME }} v__VERSION__
|
||||
releaseDraft: true
|
||||
prerelease: false
|
||||
args: ${{ matrix.args }}
|
||||
|
||||
- name: Build Arch installer
|
||||
if: matrix.platform == 'arch-latest'
|
||||
run: |
|
||||
cd src-tauri/target/release/bundle/deb
|
||||
debtap -Q **.deb
|
||||
- name: Publish Arch installer
|
||||
if: matrix.platform == 'arch-latest'
|
||||
uses: akkuman/gitea-release-action@v1
|
||||
with:
|
||||
tag_name: ${{gitea.ref_name}}
|
||||
server_url: 'http://192.168.0.80:3000'
|
||||
prerelease: false
|
||||
draft: true
|
||||
files: >-
|
||||
${{ matrix.output_dir }}
|
||||
token: ${{secrets.RELEASE_TOKEN}}
|
||||
|
||||
- name: Publish Tauri App
|
||||
if: matrix.platform == 'ubuntu-latest' || matrix.platform == 'window-latest'
|
||||
uses: akkuman/gitea-release-action@v1
|
||||
with:
|
||||
tag_name: ${{gitea.ref_name}}
|
||||
server_url: 'http://192.168.0.80:3000'
|
||||
prerelease: false
|
||||
draft: true
|
||||
files: >-
|
||||
${{ matrix.output_dir_one }}
|
||||
|
||||
${{ matrix.output_dir_two }}
|
||||
token: ${{secrets.RELEASE_TOKEN}}
|
||||
@@ -1,2 +1,2 @@
|
||||
# ToDo Tauri
|
||||
# ToDo Tauri
|
||||
ToDo приложение созданное на Tauri
|
||||
@@ -1,12 +0,0 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="ru">
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
||||
<title>Api</title>
|
||||
</head>
|
||||
<body>
|
||||
<div id="app_api"></div>
|
||||
<script type="module" src="/src/api_window.js"></script>
|
||||
</body>
|
||||
</html>
|
||||
@@ -1,13 +1,13 @@
|
||||
<!doctype html>
|
||||
<html lang="ru">
|
||||
<head>
|
||||
<meta charset="UTF-8" />
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
||||
<title>ToDo App</title>
|
||||
</head>
|
||||
|
||||
<body>
|
||||
<div id="app"></div>
|
||||
<script type="module" src="/src/main.js"></script>
|
||||
</body>
|
||||
</html>
|
||||
<!doctype html>
|
||||
<html lang="ru">
|
||||
<head>
|
||||
<meta charset="UTF-8" />
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
||||
<title>ToDo App</title>
|
||||
</head>
|
||||
|
||||
<body>
|
||||
<div id="app"></div>
|
||||
<script type="module" src="/src/main.js"></script>
|
||||
</body>
|
||||
</html>
|
||||
|
||||
@@ -1,29 +1,29 @@
|
||||
{
|
||||
"name": "to-do-app",
|
||||
"private": true,
|
||||
"version": "1.4.0",
|
||||
"type": "module",
|
||||
"scripts": {
|
||||
"dev": "vite",
|
||||
"build": "vite build",
|
||||
"preview": "vite preview",
|
||||
"tauri": "tauri",
|
||||
"tauri dev": "tauri dev",
|
||||
"tauri build": "tauri build"
|
||||
},
|
||||
"dependencies": {
|
||||
"@tauri-apps/api": "^1",
|
||||
"@vueuse/core": "^10.10.0",
|
||||
"vue": "^3.3.4",
|
||||
"vue-router": "^4.3.3"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@iconify/vue": "^4.1.2",
|
||||
"@tauri-apps/cli": "^1",
|
||||
"@vitejs/plugin-vue": "^5.0.4",
|
||||
"autoprefixer": "^10.4.19",
|
||||
"postcss": "^8.4.38",
|
||||
"tailwindcss": "^3.4.4",
|
||||
"vite": "^5.0.0"
|
||||
}
|
||||
}
|
||||
{
|
||||
"name": "to-do-app",
|
||||
"private": true,
|
||||
"version": "2.0.0-beta",
|
||||
"type": "module",
|
||||
"scripts": {
|
||||
"dev": "vite",
|
||||
"build": "vite build",
|
||||
"preview": "vite preview",
|
||||
"tauri": "tauri",
|
||||
"tauri dev": "tauri dev",
|
||||
"tauri build": "tauri build"
|
||||
},
|
||||
"dependencies": {
|
||||
"@tauri-apps/api": "^2.0.1",
|
||||
"@tauri-apps/plugin-shell": "^2.0.0",
|
||||
"@vueuse/core": "^10.10.0",
|
||||
"vue": "^3.3.4",
|
||||
"vue-router": "^4.3.3"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@tauri-apps/cli": "^2.0.1",
|
||||
"@vitejs/plugin-vue": "^5.0.4",
|
||||
"autoprefixer": "^10.4.19",
|
||||
"postcss": "^8.4.38",
|
||||
"tailwindcss": "^3.4.4",
|
||||
"vite": "^5.0.0"
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
export default {
|
||||
plugins: {
|
||||
tailwindcss: {},
|
||||
autoprefixer: {},
|
||||
},
|
||||
}
|
||||
export default {
|
||||
plugins: {
|
||||
tailwindcss: {},
|
||||
autoprefixer: {},
|
||||
},
|
||||
}
|
||||
|
||||
@@ -0,0 +1 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" width="1em" height="1em" viewBox="0 0 1024 1024"><path fill="currentColor" d="m917.7 148.8l-42.4-42.4c-1.6-1.6-3.6-2.3-5.7-2.3s-4.1.8-5.7 2.3l-76.1 76.1a199.27 199.27 0 0 0-112.1-34.3c-51.2 0-102.4 19.5-141.5 58.6L432.3 308.7a8.03 8.03 0 0 0 0 11.3L704 591.7c1.6 1.6 3.6 2.3 5.7 2.3c2 0 4.1-.8 5.7-2.3l101.9-101.9c68.9-69 77-175.7 24.3-253.5l76.1-76.1c3.1-3.2 3.1-8.3 0-11.4M769.1 441.7l-59.4 59.4l-186.8-186.8l59.4-59.4c24.9-24.9 58.1-38.7 93.4-38.7s68.4 13.7 93.4 38.7c24.9 24.9 38.7 58.1 38.7 93.4s-13.8 68.4-38.7 93.4m-190.2 105a8.03 8.03 0 0 0-11.3 0L501 613.3L410.7 523l66.7-66.7c3.1-3.1 3.1-8.2 0-11.3L441 408.6a8.03 8.03 0 0 0-11.3 0L363 475.3l-43-43a7.85 7.85 0 0 0-5.7-2.3c-2 0-4.1.8-5.7 2.3L206.8 534.2c-68.9 69-77 175.7-24.3 253.5l-76.1 76.1a8.03 8.03 0 0 0 0 11.3l42.4 42.4c1.6 1.6 3.6 2.3 5.7 2.3s4.1-.8 5.7-2.3l76.1-76.1c33.7 22.9 72.9 34.3 112.1 34.3c51.2 0 102.4-19.5 141.5-58.6l101.9-101.9c3.1-3.1 3.1-8.2 0-11.3l-43-43l66.7-66.7c3.1-3.1 3.1-8.2 0-11.3zM441.7 769.1a131.32 131.32 0 0 1-93.4 38.7c-35.3 0-68.4-13.7-93.4-38.7a131.32 131.32 0 0 1-38.7-93.4c0-35.3 13.7-68.4 38.7-93.4l59.4-59.4l186.8 186.8z"/></svg>
|
||||
|
After Width: | Height: | Size: 1.1 KiB |
@@ -0,0 +1 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" width="1em" height="1em" viewBox="0 0 48 48"><g fill="none" stroke="currentColor" stroke-linecap="round" stroke-linejoin="round" stroke-width="4"><path d="M24 44c11.046 0 20-8.954 20-20S35.046 4 24 4S4 12.954 4 24s8.954 20 20 20" clip-rule="evenodd"/><path d="M4 24h40"/><path d="M24 44c4.418 0 8-8.954 8-20S28.418 4 24 4s-8 8.954-8 20s3.582 20 8 20" clip-rule="evenodd"/><path d="M9.858 10.142A19.94 19.94 0 0 0 24 16a19.94 19.94 0 0 0 14.142-5.858m0 27.716A19.94 19.94 0 0 0 24 32a19.94 19.94 0 0 0-14.142 5.858"/></g></svg>
|
||||
|
After Width: | Height: | Size: 566 B |
@@ -0,0 +1 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" width="1em" height="1em" viewBox="0 0 16 16"><circle cx="8" cy="8" r="4" fill="currentColor"/></svg>
|
||||
|
After Width: | Height: | Size: 140 B |
@@ -0,0 +1 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" width="1em" height="1em" viewBox="0 0 24 24"><path fill="#DEC427" d="M4.47 21h15.06c1.54 0 2.5-1.67 1.73-3L13.73 4.99c-.77-1.33-2.69-1.33-3.46 0L2.74 18c-.77 1.33.19 3 1.73 3M12 14c-.55 0-1-.45-1-1v-2c0-.55.45-1 1-1s1 .45 1 1v2c0 .55-.45 1-1 1m1 4h-2v-2h2z"/></svg>
|
||||
|
After Width: | Height: | Size: 305 B |
@@ -0,0 +1 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" width="1em" height="1em" viewBox="0 0 24 24"><path fill="currentColor" fill-rule="evenodd" d="M6 3a3 3 0 0 0-3 3v12a3 3 0 0 0 3 3h12a3 3 0 0 0 3-3V6a3 3 0 0 0-3-3zm4 5a1 1 0 0 1 1-1h6a1 1 0 1 1 0 2h-6a1 1 0 0 1-1-1m0 4a1 1 0 0 1 1-1h6a1 1 0 1 1 0 2h-6a1 1 0 0 1-1-1m0 4a1 1 0 0 1 1-1h6a1 1 0 1 1 0 2h-6a1 1 0 0 1-1-1M7 7a1 1 0 0 0 0 2h.001a1 1 0 0 0 0-2zm-1 5a1 1 0 0 1 1-1h.001a1 1 0 1 1 0 2H7a1 1 0 0 1-1-1m1 3a1 1 0 1 0 0 2h.001a1 1 0 1 0 0-2z" clip-rule="evenodd"/></svg>
|
||||
|
After Width: | Height: | Size: 515 B |
@@ -0,0 +1 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" width="1em" height="1em" viewBox="0 0 24 24"><path fill="currentColor" d="m8 18l-6-6l6-6l1.425 1.425l-4.6 4.6L9.4 16.6zm8 0l-1.425-1.425l4.6-4.6L14.6 7.4L16 6l6 6z"/></svg>
|
||||
|
After Width: | Height: | Size: 212 B |
@@ -0,0 +1 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" width="1em" height="1em" viewBox="0 0 24 24"><path fill="currentColor" d="M4 21V9l8-6l8 6v12h-6v-7h-4v7z"/></svg>
|
||||
|
After Width: | Height: | Size: 153 B |
@@ -0,0 +1 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" width="1em" height="1em" viewBox="0 0 24 24"><path fill="currentColor" d="M5 2a3 3 0 0 0-3 3v14a3 3 0 0 0 3 3h14a3 3 0 0 0 3-3V5a3 3 0 0 0-3-3zM4 5a1 1 0 0 1 1-1h14a1 1 0 0 1 1 1v14a1 1 0 0 1-1 1H5a1 1 0 0 1-1-1zm5.723 13L16.58 6h-2.303L7.42 18z"/></svg>
|
||||
|
After Width: | Height: | Size: 294 B |
@@ -0,0 +1 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" width="1em" height="1em" viewBox="0 0 24 24"><circle cx="18" cy="12" r="0" fill="currentColor"><animate attributeName="r" begin=".67" calcMode="spline" dur="1.5s" keySplines="0.2 0.2 0.4 0.8;0.2 0.2 0.4 0.8;0.2 0.2 0.4 0.8" repeatCount="indefinite" values="0;2;0;0"/></circle><circle cx="12" cy="12" r="0" fill="currentColor"><animate attributeName="r" begin=".33" calcMode="spline" dur="1.5s" keySplines="0.2 0.2 0.4 0.8;0.2 0.2 0.4 0.8;0.2 0.2 0.4 0.8" repeatCount="indefinite" values="0;2;0;0"/></circle><circle cx="6" cy="12" r="0" fill="currentColor"><animate attributeName="r" begin="0" calcMode="spline" dur="1.5s" keySplines="0.2 0.2 0.4 0.8;0.2 0.2 0.4 0.8;0.2 0.2 0.4 0.8" repeatCount="indefinite" values="0;2;0;0"/></circle></svg>
|
||||
|
After Width: | Height: | Size: 781 B |
@@ -0,0 +1 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" width="1em" height="1em" viewBox="0 0 24 24"><path fill="currentColor" d="m7.825 13l5.6 5.6L12 20l-8-8l8-8l1.425 1.4l-5.6 5.6H20v2z"/></svg>
|
||||
|
After Width: | Height: | Size: 180 B |
@@ -0,0 +1 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" width="1em" height="1em" viewBox="0 0 24 24"><path fill="currentColor" d="m9.25 22l-.4-3.2q-.325-.125-.612-.3t-.563-.375L4.7 19.375l-2.75-4.75l2.575-1.95Q4.5 12.5 4.5 12.338v-.675q0-.163.025-.338L1.95 9.375l2.75-4.75l2.975 1.25q.275-.2.575-.375t.6-.3l.4-3.2h5.5l.4 3.2q.325.125.613.3t.562.375l2.975-1.25l2.75 4.75l-2.575 1.95q.025.175.025.338v.674q0 .163-.05.338l2.575 1.95l-2.75 4.75l-2.95-1.25q-.275.2-.575.375t-.6.3l-.4 3.2zM11 20h1.975l.35-2.65q.775-.2 1.438-.587t1.212-.938l2.475 1.025l.975-1.7l-2.15-1.625q.125-.35.175-.737T17.5 12t-.05-.787t-.175-.738l2.15-1.625l-.975-1.7l-2.475 1.05q-.55-.575-1.212-.962t-1.438-.588L13 4h-1.975l-.35 2.65q-.775.2-1.437.588t-1.213.937L5.55 7.15l-.975 1.7l2.15 1.6q-.125.375-.175.75t-.05.8q0 .4.05.775t.175.75l-2.15 1.625l.975 1.7l2.475-1.05q.55.575 1.213.963t1.437.587zm1.05-4.5q1.45 0 2.475-1.025T15.55 12t-1.025-2.475T12.05 8.5q-1.475 0-2.488 1.025T8.55 12t1.013 2.475T12.05 15.5M12 12"/></svg>
|
||||
|
After Width: | Height: | Size: 977 B |
@@ -0,0 +1 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" width="1em" height="1em" viewBox="0 0 24 24"><path fill="currentColor" d="m7 21l-5-5l5-5l1.425 1.4l-2.6 2.6H21v2H5.825l2.6 2.6zm10-8l-1.425-1.4l2.6-2.6H3V7h15.175l-2.6-2.6L17 3l5 5z"/></svg>
|
||||
|
After Width: | Height: | Size: 230 B |
@@ -0,0 +1 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" width="1em" height="1em" viewBox="0 0 24 24"><g fill="none"><path d="m12.593 23.258l-.011.002l-.071.035l-.02.004l-.014-.004l-.071-.035q-.016-.005-.024.005l-.004.01l-.017.428l.005.02l.01.013l.104.074l.015.004l.012-.004l.104-.074l.012-.016l.004-.017l-.017-.427q-.004-.016-.017-.018m.265-.113l-.013.002l-.185.093l-.01.01l-.003.011l.018.43l.005.012l.008.007l.201.093q.019.005.029-.008l.004-.014l-.034-.614q-.005-.018-.02-.022m-.715.002a.02.02 0 0 0-.027.006l-.006.014l-.034.614q.001.018.017.024l.015-.002l.201-.093l.01-.008l.004-.011l.017-.43l-.003-.012l-.01-.01z"/><path fill="currentColor" d="M20 17.5a1.5 1.5 0 0 1 .144 2.993L20 20.5H4a1.5 1.5 0 0 1-.144-2.993L4 17.5zm0-7a1.5 1.5 0 0 1 0 3H4a1.5 1.5 0 0 1 0-3zm0-7a1.5 1.5 0 0 1 0 3H4a1.5 1.5 0 1 1 0-3z"/></g></svg>
|
||||
|
After Width: | Height: | Size: 806 B |
@@ -0,0 +1 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" width="1em" height="1em" viewBox="0 0 24 24"><g fill="none"><path d="m12.593 23.258l-.011.002l-.071.035l-.02.004l-.014-.004l-.071-.035q-.016-.005-.024.005l-.004.01l-.017.428l.005.02l.01.013l.104.074l.015.004l.012-.004l.104-.074l.012-.016l.004-.017l-.017-.427q-.004-.016-.017-.018m.265-.113l-.013.002l-.185.093l-.01.01l-.003.011l.018.43l.005.012l.008.007l.201.093q.019.005.029-.008l.004-.014l-.034-.614q-.005-.018-.02-.022m-.715.002a.02.02 0 0 0-.027.006l-.006.014l-.034.614q.001.018.017.024l.015-.002l.201-.093l.01-.008l.004-.011l.017-.43l-.003-.012l-.01-.01z"/><path fill="currentColor" d="M12 2c5.523 0 10 4.477 10 10s-4.477 10-10 10S2 17.523 2 12S6.477 2 12 2m0 2a8 8 0 1 0 0 16a8 8 0 0 0 0-16m0 12a1 1 0 1 1 0 2a1 1 0 0 1 0-2m0-9.5a3.625 3.625 0 0 1 1.348 6.99a.8.8 0 0 0-.305.201c-.044.05-.051.114-.05.18L13 14a1 1 0 0 1-1.993.117L11 14v-.25c0-1.153.93-1.845 1.604-2.116a1.626 1.626 0 1 0-2.229-1.509a1 1 0 1 1-2 0A3.625 3.625 0 0 1 12 6.5"/></g></svg>
|
||||
|
After Width: | Height: | Size: 997 B |
@@ -0,0 +1 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" width="1em" height="1em" viewBox="0 0 16 16"><path fill="#EF4444" fill-rule="evenodd" d="M6.701 2.25c.577-1 2.02-1 2.598 0l5.196 9a1.5 1.5 0 0 1-1.299 2.25H2.804a1.5 1.5 0 0 1-1.3-2.25zM8 4a.75.75 0 0 1 .75.75v3a.75.75 0 1 1-1.5 0v-3A.75.75 0 0 1 8 4m0 8a1 1 0 1 0 0-2a1 1 0 0 0 0 2" clip-rule="evenodd"/></svg>
|
||||
|
After Width: | Height: | Size: 351 B |
@@ -0,0 +1 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" width="1em" height="1em" viewBox="0 0 24 24"><path fill="currentColor" d="M6 19c0 1.1.9 2 2 2h8c1.1 0 2-.9 2-2V7H6zM19 4h-3.5l-1-1h-5l-1 1H5v2h14z"/></svg>
|
||||
|
After Width: | Height: | Size: 195 B |
@@ -0,0 +1 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" width="1em" height="1em" viewBox="0 0 24 24"><path fill="currentColor" d="M19 12.998h-6v6h-2v-6H5v-2h6v-6h2v6h6z"/></svg>
|
||||
|
After Width: | Height: | Size: 161 B |
@@ -0,0 +1 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" width="1em" height="1em" viewBox="0 0 24 24"><path fill="#22C55E" d="m9.55 18l-5.7-5.7l1.425-1.425L9.55 15.15l9.175-9.175L20.15 7.4z"/></svg>
|
||||
|
After Width: | Height: | Size: 181 B |
@@ -0,0 +1 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" width="1em" height="1em" viewBox="0 0 24 24"><path fill="currentColor" d="M20.71 7.04c.39-.39.39-1.04 0-1.41l-2.34-2.34c-.37-.39-1.02-.39-1.41 0l-1.84 1.83l3.75 3.75M3 17.25V21h3.75L17.81 9.93l-3.75-3.75z"/></svg>
|
||||
|
After Width: | Height: | Size: 253 B |
@@ -0,0 +1 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" width="1em" height="1em" viewBox="0 0 14 14"><path fill="#EA580C" fill-rule="evenodd" d="M13 1h-2.662a.5.5 0 0 0-.5.5v12a.5.5 0 0 0 .5.5h2.663a.5.5 0 0 0 .5-.5v-12A.5.5 0 0 0 13 1M3.163 8H.5a.5.5 0 0 0-.5.5v5a.5.5 0 0 0 .5.5h2.662a.5.5 0 0 0 .5-.5v-5a.5.5 0 0 0-.5-.5ZM5.42 5h2.663a.5.5 0 0 1 .5.5v8a.5.5 0 0 1-.5.5H5.419a.5.5 0 0 1-.5-.5v-8a.5.5 0 0 1 .5-.5Z" clip-rule="evenodd"/></svg>
|
||||
|
After Width: | Height: | Size: 428 B |
@@ -0,0 +1 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" width="1em" height="1em" viewBox="0 0 14 14"><path fill="#FDBA74" fill-rule="evenodd" d="M3.162 8H.5a.5.5 0 0 0-.5.5v5a.5.5 0 0 0 .5.5h2.662a.5.5 0 0 0 .5-.5v-5a.5.5 0 0 0-.5-.5m6.954 5.25a.75.75 0 0 1 .75-.75h2.375a.75.75 0 0 1 0 1.5h-2.375a.75.75 0 0 1-.75-.75m-4.303-.75a.75.75 0 0 0 0 1.5h2.374a.75.75 0 0 0 0-1.5z" clip-rule="evenodd"/></svg>
|
||||
|
After Width: | Height: | Size: 387 B |
@@ -0,0 +1 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" width="1em" height="1em" viewBox="0 0 14 14"><path fill="#F59E0B" fill-rule="evenodd" d="M8.331 5H5.67a.5.5 0 0 0-.5.5v8a.5.5 0 0 0 .5.5h2.66a.5.5 0 0 0 .5-.5v-8a.5.5 0 0 0-.5-.5ZM3.162 8H.5a.5.5 0 0 0-.5.5v5a.5.5 0 0 0 .5.5h2.662a.5.5 0 0 0 .5-.5v-5a.5.5 0 0 0-.5-.5m7.704 4.5a.75.75 0 0 0 0 1.5h2.375a.75.75 0 0 0 0-1.5z" clip-rule="evenodd"/></svg>
|
||||
|
After Width: | Height: | Size: 391 B |
@@ -0,0 +1 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" width="1em" height="1em" viewBox="0 0 14 14"><path fill="#6B7280" fill-rule="evenodd" d="M0 13.25a.75.75 0 0 1 .75-.75h2.375a.75.75 0 0 1 0 1.5H.75a.75.75 0 0 1-.75-.75m10.116 0a.75.75 0 0 1 .75-.75h2.375a.75.75 0 0 1 0 1.5h-2.375a.75.75 0 0 1-.75-.75m-4.303-.75a.75.75 0 0 0 0 1.5h2.374a.75.75 0 0 0 0-1.5z" clip-rule="evenodd"/></svg>
|
||||
|
After Width: | Height: | Size: 376 B |
@@ -0,0 +1 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" width="1em" height="1em" viewBox="0 0 24 24"><path fill="#FACC15" fill-rule="evenodd" d="M13 3a1 1 0 1 0-2 0v2a1 1 0 1 0 2 0zM6.343 4.929A1 1 0 0 0 4.93 6.343l1.414 1.414a1 1 0 0 0 1.414-1.414zm12.728 1.414a1 1 0 0 0-1.414-1.414l-1.414 1.414a1 1 0 0 0 1.414 1.414zM12 7a5 5 0 1 0 0 10a5 5 0 0 0 0-10m-9 4a1 1 0 1 0 0 2h2a1 1 0 1 0 0-2zm16 0a1 1 0 1 0 0 2h2a1 1 0 1 0 0-2zM7.757 17.657a1 1 0 1 0-1.414-1.414l-1.414 1.414a1 1 0 1 0 1.414 1.414zm9.9-1.414a1 1 0 0 0-1.414 1.414l1.414 1.414a1 1 0 0 0 1.414-1.414zM13 19a1 1 0 1 0-2 0v2a1 1 0 1 0 2 0z" clip-rule="evenodd"/></svg>
|
||||
|
After Width: | Height: | Size: 615 B |
@@ -0,0 +1 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" width="1em" height="1em" viewBox="0 0 24 24"><path fill="currentColor" d="M13.46 12L19 17.54V19h-1.46L12 13.46L6.46 19H5v-1.46L10.54 12L5 6.46V5h1.46L12 10.54L17.54 5H19v1.46z"/></svg>
|
||||
|
After Width: | Height: | Size: 224 B |
@@ -0,0 +1 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" width="1em" height="1em" viewBox="0 0 24 24"><path fill="currentColor" d="M4 4h16v16H4zm2 4v10h12V8z"/></svg>
|
||||
|
After Width: | Height: | Size: 149 B |
@@ -0,0 +1 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" width="1em" height="1em" viewBox="0 0 24 24"><path fill="currentColor" d="M20 14H4v-4h16"/></svg>
|
||||
|
After Width: | Height: | Size: 137 B |
@@ -0,0 +1 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" width="1em" height="1em" viewBox="0 0 24 24"><path fill="currentColor" d="M4 8h4V4h12v12h-4v4H4zm12 0v6h2V6h-8v2zM6 12v6h8v-6z"/></svg>
|
||||
|
After Width: | Height: | Size: 175 B |
@@ -0,0 +1 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" width="1em" height="1em" viewBox="0 0 24 24"><path fill="#60A5FA" d="M11.38 2.019a7.5 7.5 0 1 0 10.6 10.6C21.662 17.854 17.316 22 12.001 22C6.477 22 2 17.523 2 12c0-5.315 4.146-9.661 9.38-9.981"/></svg>
|
||||
|
After Width: | Height: | Size: 242 B |
@@ -1,6 +1,6 @@
|
||||
<svg width="206" height="231" viewBox="0 0 206 231" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<path d="M143.143 84C143.143 96.1503 133.293 106 121.143 106C108.992 106 99.1426 96.1503 99.1426 84C99.1426 71.8497 108.992 62 121.143 62C133.293 62 143.143 71.8497 143.143 84Z" fill="#FFC131"/>
|
||||
<ellipse cx="84.1426" cy="147" rx="22" ry="22" transform="rotate(180 84.1426 147)" fill="#24C8DB"/>
|
||||
<path fill-rule="evenodd" clip-rule="evenodd" d="M166.738 154.548C157.86 160.286 148.023 164.269 137.757 166.341C139.858 160.282 141 153.774 141 147C141 144.543 140.85 142.121 140.558 139.743C144.975 138.204 149.215 136.139 153.183 133.575C162.73 127.404 170.292 118.608 174.961 108.244C179.63 97.8797 181.207 86.3876 179.502 75.1487C177.798 63.9098 172.884 53.4021 165.352 44.8883C157.82 36.3744 147.99 30.2165 137.042 27.1546C126.095 24.0926 114.496 24.2568 103.64 27.6274C92.7839 30.998 83.1319 37.4317 75.8437 46.1553C74.9102 47.2727 74.0206 48.4216 73.176 49.5993C61.9292 50.8488 51.0363 54.0318 40.9629 58.9556C44.2417 48.4586 49.5653 38.6591 56.679 30.1442C67.0505 17.7298 80.7861 8.57426 96.2354 3.77762C111.685 -1.01901 128.19 -1.25267 143.769 3.10474C159.348 7.46215 173.337 16.2252 184.056 28.3411C194.775 40.457 201.767 55.4101 204.193 71.404C206.619 87.3978 204.374 103.752 197.73 118.501C191.086 133.25 180.324 145.767 166.738 154.548ZM41.9631 74.275L62.5557 76.8042C63.0459 72.813 63.9401 68.9018 65.2138 65.1274C57.0465 67.0016 49.2088 70.087 41.9631 74.275Z" fill="#FFC131"/>
|
||||
<path fill-rule="evenodd" clip-rule="evenodd" d="M38.4045 76.4519C47.3493 70.6709 57.2677 66.6712 67.6171 64.6132C65.2774 70.9669 64 77.8343 64 85.0001C64 87.1434 64.1143 89.26 64.3371 91.3442C60.0093 92.8732 55.8533 94.9092 51.9599 97.4256C42.4128 103.596 34.8505 112.392 30.1816 122.756C25.5126 133.12 23.9357 144.612 25.6403 155.851C27.3449 167.09 32.2584 177.598 39.7906 186.112C47.3227 194.626 57.153 200.784 68.1003 203.846C79.0476 206.907 90.6462 206.743 101.502 203.373C112.359 200.002 122.011 193.568 129.299 184.845C130.237 183.722 131.131 182.567 131.979 181.383C143.235 180.114 154.132 176.91 164.205 171.962C160.929 182.49 155.596 192.319 148.464 200.856C138.092 213.27 124.357 222.426 108.907 227.222C93.458 232.019 76.9524 232.253 61.3736 227.895C45.7948 223.538 31.8055 214.775 21.0867 202.659C10.3679 190.543 3.37557 175.59 0.949823 159.596C-1.47592 143.602 0.768139 127.248 7.41237 112.499C14.0566 97.7497 24.8183 85.2327 38.4045 76.4519ZM163.062 156.711L163.062 156.711C162.954 156.773 162.846 156.835 162.738 156.897C162.846 156.835 162.954 156.773 163.062 156.711Z" fill="#24C8DB"/>
|
||||
</svg>
|
||||
<svg width="206" height="231" viewBox="0 0 206 231" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<path d="M143.143 84C143.143 96.1503 133.293 106 121.143 106C108.992 106 99.1426 96.1503 99.1426 84C99.1426 71.8497 108.992 62 121.143 62C133.293 62 143.143 71.8497 143.143 84Z" fill="#FFC131"/>
|
||||
<ellipse cx="84.1426" cy="147" rx="22" ry="22" transform="rotate(180 84.1426 147)" fill="#24C8DB"/>
|
||||
<path fill-rule="evenodd" clip-rule="evenodd" d="M166.738 154.548C157.86 160.286 148.023 164.269 137.757 166.341C139.858 160.282 141 153.774 141 147C141 144.543 140.85 142.121 140.558 139.743C144.975 138.204 149.215 136.139 153.183 133.575C162.73 127.404 170.292 118.608 174.961 108.244C179.63 97.8797 181.207 86.3876 179.502 75.1487C177.798 63.9098 172.884 53.4021 165.352 44.8883C157.82 36.3744 147.99 30.2165 137.042 27.1546C126.095 24.0926 114.496 24.2568 103.64 27.6274C92.7839 30.998 83.1319 37.4317 75.8437 46.1553C74.9102 47.2727 74.0206 48.4216 73.176 49.5993C61.9292 50.8488 51.0363 54.0318 40.9629 58.9556C44.2417 48.4586 49.5653 38.6591 56.679 30.1442C67.0505 17.7298 80.7861 8.57426 96.2354 3.77762C111.685 -1.01901 128.19 -1.25267 143.769 3.10474C159.348 7.46215 173.337 16.2252 184.056 28.3411C194.775 40.457 201.767 55.4101 204.193 71.404C206.619 87.3978 204.374 103.752 197.73 118.501C191.086 133.25 180.324 145.767 166.738 154.548ZM41.9631 74.275L62.5557 76.8042C63.0459 72.813 63.9401 68.9018 65.2138 65.1274C57.0465 67.0016 49.2088 70.087 41.9631 74.275Z" fill="#FFC131"/>
|
||||
<path fill-rule="evenodd" clip-rule="evenodd" d="M38.4045 76.4519C47.3493 70.6709 57.2677 66.6712 67.6171 64.6132C65.2774 70.9669 64 77.8343 64 85.0001C64 87.1434 64.1143 89.26 64.3371 91.3442C60.0093 92.8732 55.8533 94.9092 51.9599 97.4256C42.4128 103.596 34.8505 112.392 30.1816 122.756C25.5126 133.12 23.9357 144.612 25.6403 155.851C27.3449 167.09 32.2584 177.598 39.7906 186.112C47.3227 194.626 57.153 200.784 68.1003 203.846C79.0476 206.907 90.6462 206.743 101.502 203.373C112.359 200.002 122.011 193.568 129.299 184.845C130.237 183.722 131.131 182.567 131.979 181.383C143.235 180.114 154.132 176.91 164.205 171.962C160.929 182.49 155.596 192.319 148.464 200.856C138.092 213.27 124.357 222.426 108.907 227.222C93.458 232.019 76.9524 232.253 61.3736 227.895C45.7948 223.538 31.8055 214.775 21.0867 202.659C10.3679 190.543 3.37557 175.59 0.949823 159.596C-1.47592 143.602 0.768139 127.248 7.41237 112.499C14.0566 97.7497 24.8183 85.2327 38.4045 76.4519ZM163.062 156.711L163.062 156.711C162.954 156.773 162.846 156.835 162.738 156.897C162.846 156.835 162.954 156.773 163.062 156.711Z" fill="#24C8DB"/>
|
||||
</svg>
|
||||
|
||||
|
Before Width: | Height: | Size: 2.5 KiB After Width: | Height: | Size: 2.5 KiB |
@@ -1,17 +1,17 @@
|
||||
[package]
|
||||
name = "to-do-app"
|
||||
version = "0.0.0"
|
||||
description = "A Tauri App"
|
||||
version = "1.8.0"
|
||||
description = "A ToDo App"
|
||||
authors = ["you"]
|
||||
edition = "2021"
|
||||
|
||||
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
|
||||
|
||||
[build-dependencies]
|
||||
tauri-build = { version = "1", features = [] }
|
||||
tauri-build = { version = "2.0.1", features = [] }
|
||||
|
||||
[dependencies]
|
||||
tauri = { version = "1", features = [ "window-show", "window-unminimize", "window-minimize", "window-maximize", "window-start-dragging", "window-hide", "window-close", "window-unmaximize", "shell-open"] }
|
||||
tauri = { version = "2.0.1", features = [] }
|
||||
serde = { version = "1", features = ["derive"] }
|
||||
serde_json = "1"
|
||||
magic-crypt = "3.1.13"
|
||||
@@ -23,13 +23,19 @@ aes = "0.7.5"
|
||||
block-modes = "0.8.1"
|
||||
block-padding = "0.3.3"
|
||||
chrono = "0.4.38"
|
||||
tauri-plugin-shell = "2.0.1"
|
||||
|
||||
[features]
|
||||
# This feature is used for production builds or when a dev server is not specified, DO NOT REMOVE!!
|
||||
custom-protocol = ["tauri/custom-protocol"]
|
||||
|
||||
[profile.release]
|
||||
lto = true # Enables link to optimizations
|
||||
opt-level = "s" # Optimize for binary size
|
||||
strip = true # Remove debug symbols
|
||||
[lib]
|
||||
name = "app_lib"
|
||||
crate-type = ["staticlib", "cdylib", "rlib"]
|
||||
|
||||
[profile.release]
|
||||
codegen-units = 1 # Allows LLVM to perform better optimization.
|
||||
lto = true # Enables link-time-optimizations.
|
||||
opt-level = "s" # Prioritizes small binary size. Use `3` if you prefer speed.
|
||||
panic = "abort" # Higher performance by disabling panic handlers.
|
||||
strip = true # Ensures debug symbols are removed.
|
||||
|
||||
@@ -1,3 +1,3 @@
|
||||
fn main() {
|
||||
tauri_build::build()
|
||||
}
|
||||
fn main() {
|
||||
tauri_build::build()
|
||||
}
|
||||
|
||||
@@ -0,0 +1,23 @@
|
||||
{
|
||||
"identifier": "migrated",
|
||||
"description": "permissions that were migrated from v1",
|
||||
"local": true,
|
||||
"windows": [
|
||||
"main"
|
||||
],
|
||||
"permissions": [
|
||||
"core:app:default",
|
||||
"core:window:allow-maximize",
|
||||
"core:window:allow-unmaximize",
|
||||
"core:window:allow-minimize",
|
||||
"core:window:allow-unminimize",
|
||||
"core:window:allow-toggle-maximize",
|
||||
"core:window:allow-show",
|
||||
"core:window:allow-hide",
|
||||
"core:window:allow-close",
|
||||
"core:window:allow-start-dragging",
|
||||
"core:event:allow-listen",
|
||||
"shell:allow-open",
|
||||
"shell:default"
|
||||
]
|
||||
}
|
||||
@@ -0,0 +1 @@
|
||||
{"migrated":{"identifier":"migrated","description":"permissions that were migrated from v1","local":true,"windows":["main"],"permissions":["core:app:default","core:window:allow-maximize","core:window:allow-unmaximize","core:window:allow-minimize","core:window:allow-unminimize","core:window:allow-toggle-maximize","core:window:allow-show","core:window:allow-hide","core:window:allow-close","core:window:allow-start-dragging","core:event:allow-listen","shell:allow-open","shell:default"]}}
|
||||
@@ -1,6 +1,6 @@
|
||||
use aes::Aes256;
|
||||
use block_modes::{BlockMode, Cbc};
|
||||
use block_modes::block_padding::Pkcs7;
|
||||
use block_modes::{BlockMode, Cbc};
|
||||
use rand::Rng;
|
||||
|
||||
type Aes256Cbc = Cbc<Aes256, Pkcs7>;
|
||||
@@ -29,4 +29,4 @@ pub fn decrypt(key: &[u8], encrypted_data: &[u8]) -> String {
|
||||
//println!("REQ HEX: {:?}", req.trim());
|
||||
//println!("REQ u8: {:?}\n", hex::decode(req.trim()).unwrap());
|
||||
|
||||
//println!("DEC AES: {:?}", crate::aes_enc_dec::decrypt(&hex::decode(crate::multi_tcp_listener::get_token()).unwrap(), &hex::decode(req.trim()).unwrap()))
|
||||
//println!("DEC AES: {:?}", crate::aes_enc_dec::decrypt(&hex::decode(crate::multi_tcp_listener::get_token()).unwrap(), &hex::decode(req.trim()).unwrap()))
|
||||
|
||||
@@ -1,97 +1,103 @@
|
||||
use std::fs::{File, read_to_string};
|
||||
use std::path::Path;
|
||||
use crate::enc_dec_file::{decrypt_file, encrypt_n_save_file};
|
||||
use serde_json::{json, Value};
|
||||
use crate::enc_dec_file::{encrypt_n_save_file, decrypt_file};
|
||||
use std::fs::{read_to_string, File};
|
||||
use std::path::Path;
|
||||
use tauri::path::BaseDirectory;
|
||||
|
||||
use rand::distributions::Uniform;
|
||||
use rand::Rng;
|
||||
use rand::distributions::Uniform; // 0.8
|
||||
use tauri::Manager;
|
||||
|
||||
#[tauri::command]
|
||||
pub fn check_or_create_config_file(app_handle: tauri::AppHandle) {
|
||||
let mut path = app_handle.path_resolver().app_local_data_dir().unwrap();
|
||||
path.push("ToDo");
|
||||
path.push("config_api");
|
||||
|
||||
path.set_extension("enc");
|
||||
let path = app_handle.path().resolve("ToDo/config_api.enc", BaseDirectory::AppLocalData).unwrap();
|
||||
|
||||
let exist: bool = Path::new(&path).exists();
|
||||
if exist{
|
||||
if exist {
|
||||
let content = read_to_string(path);
|
||||
return match content {
|
||||
Ok(_) => {},
|
||||
Ok(_) => {}
|
||||
Err(_) => {
|
||||
println!("Не удалось открыть конфигурационный файл!");
|
||||
}
|
||||
}
|
||||
} else{
|
||||
};
|
||||
} else {
|
||||
let path = Path::new(&path);
|
||||
let prefix = path.parent().unwrap();
|
||||
let file = std::fs::create_dir_all(prefix);
|
||||
match file{
|
||||
Ok(_) => {},
|
||||
Err(err) => println!("Не удалось создать директории! \n{}", err)
|
||||
match file {
|
||||
Ok(_) => {}
|
||||
Err(err) => println!("Не удалось создать директории! \n{}", err),
|
||||
}
|
||||
let file = File::create(path);
|
||||
match file{
|
||||
match file {
|
||||
Ok(_) => {
|
||||
let rand_s: String = rand::thread_rng()
|
||||
.sample_iter(Uniform::new(char::from(32), char::from(126)))
|
||||
.take(64)
|
||||
.map(char::from)
|
||||
.collect();
|
||||
let token: String = rand_s.replace("'", "").replace('"', "")
|
||||
.replace("{", "").replace("}", "").replace("[", "")
|
||||
.replace("}", "").replace("\\", "").replace("'", "");
|
||||
let token: String = rand_s
|
||||
.replace("'", "")
|
||||
.replace('"', "")
|
||||
.replace("{", "")
|
||||
.replace("}", "")
|
||||
.replace("[", "")
|
||||
.replace("}", "")
|
||||
.replace("\\", "")
|
||||
.replace("'", "");
|
||||
|
||||
|
||||
let data = String::from(format!("{}\
|
||||
let data = String::from(format!(
|
||||
"{}\
|
||||
\"port\": 49494,\
|
||||
\"autostart\": false,\
|
||||
\"token\": \"{}\"\
|
||||
{}", "{", token, "}"));
|
||||
println!("{}", data);
|
||||
{}",
|
||||
"{", token, "}"
|
||||
));
|
||||
encrypt_n_save_file(path.into(), data);
|
||||
},
|
||||
Err(err) => println!("Не удалось создать файл! \n{}", err)
|
||||
}
|
||||
Err(err) => println!("Не удалось создать файл! \n{}", err),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[tauri::command]
|
||||
pub fn get_port(app_handle: tauri::AppHandle) -> i64 {
|
||||
let mut path = app_handle.path_resolver().app_local_data_dir().unwrap();
|
||||
path.push("ToDo");
|
||||
path.push("config_api");
|
||||
let path = app_handle.path().resolve("ToDo/config_api.enc", BaseDirectory::AppLocalData).unwrap();
|
||||
|
||||
path.set_extension("enc");
|
||||
|
||||
let content = decrypt_file(path);
|
||||
let data: Value = serde_json::from_str(content.as_str()).unwrap();
|
||||
|
||||
return data["port"].as_i64().unwrap();
|
||||
let content = decrypt_file(path.clone());
|
||||
let data = serde_json::from_str::<Value>(content.as_str());
|
||||
match data {
|
||||
Ok(data) => {
|
||||
return data["port"].as_i64().unwrap();
|
||||
}
|
||||
Err(_) => {
|
||||
std::fs::remove_file(path).unwrap();
|
||||
check_or_create_config_file(app_handle.clone());
|
||||
return get_port(app_handle.clone());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[tauri::command]
|
||||
pub fn get_autostart(app_handle: tauri::AppHandle) -> bool {
|
||||
let mut path = app_handle.path_resolver().app_local_data_dir().unwrap();
|
||||
path.push("ToDo");
|
||||
path.push("config_api");
|
||||
|
||||
path.set_extension("enc");
|
||||
let path = app_handle.path().resolve("ToDo/config_api.enc", BaseDirectory::AppLocalData).unwrap();
|
||||
|
||||
let content = decrypt_file(path);
|
||||
let data: Value = serde_json::from_str(content.as_str()).unwrap();
|
||||
|
||||
return data["autostart"].as_bool().unwrap();
|
||||
let data = serde_json::from_str::<Value>(content.as_str());
|
||||
match data {
|
||||
Ok(d) => return d["autostart"].as_bool().unwrap(),
|
||||
Err(err) => {
|
||||
println!("{}", err);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[tauri::command]
|
||||
pub fn edit_settings(app_handle: tauri::AppHandle, port: i32, autostart: bool) {
|
||||
let mut path = app_handle.path_resolver().app_local_data_dir().unwrap();
|
||||
path.push("ToDo");
|
||||
path.push("config_api");
|
||||
|
||||
path.set_extension("enc");
|
||||
let path = app_handle.path().resolve("ToDo/config_api.enc", BaseDirectory::AppLocalData).unwrap();
|
||||
|
||||
let content = decrypt_file(path.clone());
|
||||
let mut data: Value = serde_json::from_str(content.as_str()).unwrap();
|
||||
@@ -103,11 +109,7 @@ pub fn edit_settings(app_handle: tauri::AppHandle, port: i32, autostart: bool) {
|
||||
|
||||
#[tauri::command]
|
||||
pub fn reset_token(app_handle: tauri::AppHandle) -> String {
|
||||
let mut path = app_handle.path_resolver().app_local_data_dir().unwrap();
|
||||
path.push("ToDo");
|
||||
path.push("config_api");
|
||||
|
||||
path.set_extension("enc");
|
||||
let path = app_handle.path().resolve("ToDo/config_api.enc", BaseDirectory::AppLocalData).unwrap();
|
||||
|
||||
let key = rand::thread_rng().gen::<[u8; 32]>(); // 256-битный ключ
|
||||
|
||||
@@ -118,4 +120,4 @@ pub fn reset_token(app_handle: tauri::AppHandle) -> String {
|
||||
encrypt_n_save_file(path.into(), data.to_string());
|
||||
|
||||
return hex::encode(&key.clone());
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,75 +1,92 @@
|
||||
use serde_json::Value;
|
||||
use chrono::{DateTime, Local};
|
||||
use serde_json::Value;
|
||||
|
||||
use crate::aes_enc_dec::encrypt;
|
||||
use crate::tasks;
|
||||
|
||||
pub fn get_tasks_tcp(token: String) -> Vec<u8>{
|
||||
encrypt(&hex::decode(token).unwrap(), tasks::get_tasks().to_string().as_str())
|
||||
pub fn get_tasks_tcp(token: String) -> Vec<u8> {
|
||||
encrypt(
|
||||
&hex::decode(token).unwrap(),
|
||||
tasks::get_tasks().to_string().as_str(),
|
||||
)
|
||||
}
|
||||
|
||||
pub fn get_task_by_id_tcp(token: String, json: Value) -> Vec<u8> {
|
||||
let tasks: Value = tasks::get_tasks();
|
||||
let res;
|
||||
if !json.get("id").is_none(){
|
||||
res = encrypt(&hex::decode(token).unwrap(), tasks[json["id"].as_str().unwrap_or("")].to_string().as_str());
|
||||
} else{
|
||||
if !json.get("id").is_none() {
|
||||
res = encrypt(
|
||||
&hex::decode(token).unwrap(),
|
||||
tasks[json["id"].as_str().unwrap_or("")]
|
||||
.to_string()
|
||||
.as_str(),
|
||||
);
|
||||
} else {
|
||||
res = encrypt(&hex::decode(token).unwrap(), "400");
|
||||
}
|
||||
res
|
||||
}
|
||||
|
||||
pub fn create_task_tcp(token: String, json: Value) -> Vec<u8> {
|
||||
if json["task"].is_object(){
|
||||
if json["task"]["name"].is_string(){
|
||||
if json["task"].is_object() {
|
||||
if json["task"]["name"].is_string() {
|
||||
let current_datetime: DateTime<Local> = Local::now();
|
||||
let date: String = current_datetime.format("%Y.%m.%d").to_string();
|
||||
let time: String = current_datetime.format("%H:%M").to_string();
|
||||
|
||||
let name = json["task"]["name"].as_str().unwrap().to_string();
|
||||
let description = json["task"]["description"].as_str().unwrap_or("").to_string();
|
||||
let description = json["task"]["description"]
|
||||
.as_str()
|
||||
.unwrap_or("")
|
||||
.to_string();
|
||||
let priority = json["task"]["priority"].as_str().unwrap_or("0").to_string();
|
||||
|
||||
encrypt(&hex::decode(token).unwrap(), tasks::add_task(date, time, name, description, priority).as_str())
|
||||
} else{
|
||||
encrypt(
|
||||
&hex::decode(token).unwrap(),
|
||||
tasks::add_task(date, time, name, description, priority).as_str(),
|
||||
)
|
||||
} else {
|
||||
encrypt(&hex::decode(token).unwrap(), "400")
|
||||
}
|
||||
} else{
|
||||
} else {
|
||||
encrypt(&hex::decode(token).unwrap(), "400")
|
||||
}
|
||||
}
|
||||
|
||||
pub fn edit_task_tcp(token: String, json: Value) -> Vec<u8> {
|
||||
if json["task"].is_object(){
|
||||
if json["task"]["id"].is_number(){
|
||||
if json["task"].is_object() {
|
||||
if json["task"]["id"].is_number() {
|
||||
let id_task = json["task"]["id"].as_number().unwrap().to_string();
|
||||
let name = json["task"]["name"].as_str().unwrap().to_string();
|
||||
let description = json["task"]["description"].as_str().unwrap_or("").to_string();
|
||||
let description = json["task"]["description"]
|
||||
.as_str()
|
||||
.unwrap_or("")
|
||||
.to_string();
|
||||
let priority = json["task"]["priority"].as_str().unwrap_or("0").to_string();
|
||||
|
||||
if tasks::edit_task(id_task, name, description, priority) {
|
||||
encrypt(&hex::decode(token).unwrap(), "200")
|
||||
} else{
|
||||
} else {
|
||||
encrypt(&hex::decode(token).unwrap(), "400")
|
||||
}
|
||||
} else{
|
||||
} else {
|
||||
encrypt(&hex::decode(token).unwrap(), "400")
|
||||
}
|
||||
} else{
|
||||
} else {
|
||||
encrypt(&hex::decode(token).unwrap(), "400")
|
||||
}
|
||||
}
|
||||
|
||||
pub fn delete_task_tcp(token: String, json: Value) -> Vec<u8> {
|
||||
let res;
|
||||
if !json.get("id").is_none(){
|
||||
if tasks::delete_task(json["id"].to_string()){
|
||||
if !json.get("id").is_none() {
|
||||
if tasks::delete_task(json["id"].to_string()) {
|
||||
res = encrypt(&hex::decode(token).unwrap(), "200");
|
||||
} else{
|
||||
} else {
|
||||
res = encrypt(&hex::decode(token).unwrap(), "400");
|
||||
}
|
||||
} else{
|
||||
} else {
|
||||
res = encrypt(&hex::decode(token).unwrap(), "400");
|
||||
}
|
||||
res
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,19 +1,20 @@
|
||||
use serde_json::Value;
|
||||
use std::io::{BufRead, BufReader, Write};
|
||||
use std::net::{TcpListener, TcpStream};
|
||||
use std::path::PathBuf;
|
||||
use std::thread;
|
||||
use std::string::String;
|
||||
use serde_json::Value;
|
||||
use tauri::{AppHandle, Manager};
|
||||
use std::thread;
|
||||
use tauri::{AppHandle, Emitter, Manager};
|
||||
use tauri::path::BaseDirectory;
|
||||
|
||||
use crate::{config_tcp, handler_commands};
|
||||
use crate::aes_enc_dec::{decrypt, encrypt};
|
||||
use crate::enc_dec_file::decrypt_file;
|
||||
use crate::aes_enc_dec::{encrypt, decrypt};
|
||||
use crate::{config_tcp, handler_commands};
|
||||
|
||||
static mut TCP_COMMAND: &str = "";
|
||||
static mut PATH_CONFIG: Option<&'static str> = None;
|
||||
|
||||
fn get_token() -> String{
|
||||
fn get_token() -> String {
|
||||
unsafe {
|
||||
let content = decrypt_file(PathBuf::from(PATH_CONFIG.unwrap()));
|
||||
let data: Value = serde_json::from_str(content.as_str()).unwrap();
|
||||
@@ -22,21 +23,25 @@ fn get_token() -> String{
|
||||
}
|
||||
} // Получение токена доступа
|
||||
|
||||
fn data_to_enc_hex(data: &str) -> String{
|
||||
fn data_to_enc_hex(data: &str) -> String {
|
||||
hex::encode(encrypt(&hex::decode(get_token()).unwrap(), data))
|
||||
} // Шифрование строки до данных обёрнутых в HEX
|
||||
|
||||
fn enc_hex_to_data(data: &str) -> String{
|
||||
decrypt(&hex::decode(get_token()).unwrap(), &hex::decode(data).unwrap())
|
||||
fn enc_hex_to_data(data: &str) -> String {
|
||||
decrypt(
|
||||
&hex::decode(get_token()).unwrap(),
|
||||
&hex::decode(data).unwrap(),
|
||||
)
|
||||
} // Расшифровка обёрнутых в HEX данных до строки
|
||||
|
||||
|
||||
fn create_tcp_listener(app_handle: AppHandle) -> TcpListener{
|
||||
fn create_tcp_listener(app_handle: AppHandle) -> TcpListener {
|
||||
let path: PathBuf = [
|
||||
app_handle.path_resolver().app_local_data_dir().unwrap(),
|
||||
app_handle.path().resolve("ToDo/config_api.enc", BaseDirectory::AppLocalData).unwrap(),
|
||||
"ToDo".into(),
|
||||
"config_api.enc".into(),
|
||||
].iter().collect(); // Путь к конфиг файлу
|
||||
]
|
||||
.iter()
|
||||
.collect(); // Путь к конфиг файлу
|
||||
|
||||
let path_str = Box::leak(Box::new(path.to_string_lossy().into_owned()));
|
||||
|
||||
@@ -44,7 +49,11 @@ fn create_tcp_listener(app_handle: AppHandle) -> TcpListener{
|
||||
PATH_CONFIG = Some(path_str);
|
||||
}
|
||||
|
||||
return match TcpListener::bind(format!("127.0.0.1:{}", config_tcp::get_port(app_handle.clone()))){ // Создание TCP слушателя на локальном порту
|
||||
return match TcpListener::bind(format!(
|
||||
"127.0.0.1:{}",
|
||||
config_tcp::get_port(app_handle.clone())
|
||||
)) {
|
||||
// Создание TCP слушателя на локальном порту
|
||||
Ok(tcp) => {
|
||||
for stream in tcp.incoming() {
|
||||
unsafe {
|
||||
@@ -53,25 +62,27 @@ fn create_tcp_listener(app_handle: AppHandle) -> TcpListener{
|
||||
|
||||
let stream = stream.unwrap(); // Получение запроса
|
||||
|
||||
let thread = thread::spawn(move || { // Создание отдельного потока
|
||||
let thread = thread::spawn(move || {
|
||||
// Создание отдельного потока
|
||||
handle_connection(stream); // Обработка запроса
|
||||
});
|
||||
|
||||
thread.join().unwrap(); // Ожидание пока завершится поток
|
||||
|
||||
unsafe {
|
||||
match TCP_COMMAND { // Сравнивание команд
|
||||
match TCP_COMMAND {
|
||||
// Сравнивание команд
|
||||
"stop" => {
|
||||
app_handle.emit_all("update_api", "").unwrap();
|
||||
app_handle.emit("update_api", "").unwrap();
|
||||
break;
|
||||
}
|
||||
_ => {}
|
||||
}
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
tcp
|
||||
},
|
||||
}
|
||||
Err(err) => {
|
||||
println!("{}", err);
|
||||
TcpListener::bind("").unwrap()
|
||||
@@ -86,49 +97,56 @@ fn handle_connection(mut stream: TcpStream) {
|
||||
Ok(_) => {
|
||||
if req.len() > 0 {
|
||||
let res = enc_hex_to_data(req.trim()); // Дешифрование
|
||||
let json: Value = serde_json::from_str(res.as_str()).unwrap_or(serde_json::json!("")); // Попытка преобразования запроса в JSON
|
||||
if json != serde_json::json!(""){
|
||||
let json: Value =
|
||||
serde_json::from_str(res.as_str()).unwrap_or(serde_json::json!("")); // Попытка преобразования запроса в JSON
|
||||
if json != serde_json::json!("") {
|
||||
println!("JSON: {:?}", json);
|
||||
println!("JSON command: {:?}", json["command"]);
|
||||
match json["command"].as_str().unwrap() { // Сравнивание команд
|
||||
match json["command"].as_str().unwrap() {
|
||||
// Сравнивание команд
|
||||
"stop" => {
|
||||
unsafe {
|
||||
TCP_COMMAND = "stop";
|
||||
}
|
||||
let res = encrypt(&hex::decode(get_token()).unwrap(), "200");
|
||||
stream.write(hex::encode(res).as_bytes()).unwrap();
|
||||
},
|
||||
}
|
||||
"status" => {
|
||||
let res = encrypt(&hex::decode(get_token()).unwrap(), "200");
|
||||
stream.write(hex::encode(res).as_bytes()).unwrap();
|
||||
},
|
||||
}
|
||||
"get_tasks" => {
|
||||
let res = handler_commands::get_tasks_tcp(get_token());
|
||||
stream.write(hex::encode(res).as_bytes()).unwrap();
|
||||
},
|
||||
}
|
||||
"get_task_by_id" => {
|
||||
let res = handler_commands::get_task_by_id_tcp(get_token(), json);
|
||||
stream.write(hex::encode(res).as_bytes()).unwrap();
|
||||
},
|
||||
}
|
||||
"create_task" => {
|
||||
let res = handler_commands::create_task_tcp(get_token(), json);
|
||||
stream.write(hex::encode(res).as_bytes()).unwrap();
|
||||
},
|
||||
}
|
||||
"edit_task" => {
|
||||
let res = handler_commands::edit_task_tcp(get_token(), json);
|
||||
stream.write(hex::encode(res).as_bytes()).unwrap();
|
||||
},
|
||||
}
|
||||
"delete_task" => {
|
||||
let res = handler_commands::delete_task_tcp(get_token(), json);
|
||||
stream.write(hex::encode(res).as_bytes()).unwrap();
|
||||
},
|
||||
}
|
||||
_ => {}
|
||||
}
|
||||
} else{
|
||||
stream.write(hex::encode(encrypt(&hex::decode(get_token()).unwrap(), "400")).as_bytes()).unwrap();
|
||||
} else {
|
||||
stream
|
||||
.write(
|
||||
hex::encode(encrypt(&hex::decode(get_token()).unwrap(), "400"))
|
||||
.as_bytes(),
|
||||
)
|
||||
.unwrap();
|
||||
}
|
||||
}
|
||||
},
|
||||
}
|
||||
what => {
|
||||
println!("{:?}", what);
|
||||
}
|
||||
@@ -136,20 +154,22 @@ fn handle_connection(mut stream: TcpStream) {
|
||||
}
|
||||
|
||||
#[tauri::command]
|
||||
pub fn start_tcp_server(app_handle: AppHandle){
|
||||
pub fn start_tcp_server(app_handle: AppHandle) {
|
||||
let handle = app_handle.clone();
|
||||
thread::spawn(move || { // Создание потока
|
||||
thread::spawn(move || {
|
||||
// Создание потока
|
||||
create_tcp_listener(handle); // Создание слушателя
|
||||
});
|
||||
app_handle.emit_all("update_api", "").unwrap(); // Глобальное сообщение для фронтенда для обновления API страницы
|
||||
app_handle.emit("update_api", "").unwrap(); // Глобальное сообщение для фронтенда для обновления API страницы
|
||||
}
|
||||
|
||||
#[tauri::command]
|
||||
pub fn get_status(app_handle: AppHandle) -> bool{
|
||||
match TcpListener::bind(format!("127.0.0.1:{}", config_tcp::get_port(app_handle))) { // Получения статуса слушателя, с помощью нового слушателя
|
||||
pub fn get_status(app_handle: AppHandle) -> bool {
|
||||
match TcpListener::bind(format!("127.0.0.1:{}", config_tcp::get_port(app_handle))) {
|
||||
// Получения статуса слушателя, с помощью нового слушателя
|
||||
Ok(_) => {
|
||||
false // Если удалось создать возвращаем false, т.к. слушателя не было
|
||||
},
|
||||
}
|
||||
Err(_) => {
|
||||
true // Иначе true
|
||||
}
|
||||
@@ -157,15 +177,20 @@ pub fn get_status(app_handle: AppHandle) -> bool{
|
||||
}
|
||||
|
||||
#[tauri::command]
|
||||
pub fn send_command_to_server(app_handle: AppHandle, command: String) -> bool{
|
||||
match TcpStream::connect(format!("127.0.0.1:{}", config_tcp::get_port(app_handle))) { // Подключение к слушателю
|
||||
pub fn send_command_to_server(app_handle: AppHandle, command: String) -> bool {
|
||||
match TcpStream::connect(format!("127.0.0.1:{}", config_tcp::get_port(app_handle))) {
|
||||
// Подключение к слушателю
|
||||
Ok(mut tcp) => {
|
||||
tcp.write_all(data_to_enc_hex(format!("{}: \"{}\" {}", "{\"command\"", command, "}\n").as_str()).as_bytes()).unwrap(); // Отправление команды
|
||||
tcp.write_all(
|
||||
data_to_enc_hex(format!("{}: \"{}\" {}", "{\"command\"", command, "}\n").as_str())
|
||||
.as_bytes(),
|
||||
)
|
||||
.unwrap(); // Отправление команды
|
||||
true
|
||||
},
|
||||
}
|
||||
Err(err) => {
|
||||
println!("{}", err);
|
||||
false
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,10 +1,10 @@
|
||||
use magic_crypt::{new_magic_crypt, MagicCryptTrait};
|
||||
use std::fs::{read_to_string, write};
|
||||
use std::path::PathBuf;
|
||||
use magic_crypt::{MagicCryptTrait, new_magic_crypt};
|
||||
|
||||
use crate::config;
|
||||
|
||||
pub fn encrypt_n_save_file(path: PathBuf, content: String){
|
||||
pub fn encrypt_n_save_file(path: PathBuf, content: String) {
|
||||
let key = config::ENC_KEY;
|
||||
let mc = new_magic_crypt!(key, 256);
|
||||
let mut encrypted: String = String::new();
|
||||
@@ -14,8 +14,8 @@ pub fn encrypt_n_save_file(path: PathBuf, content: String){
|
||||
}
|
||||
let res = write(path, encrypted);
|
||||
match res {
|
||||
Ok(_) => {},
|
||||
Err(_) => println!("File save error!")
|
||||
Ok(_) => {}
|
||||
Err(_) => println!("File save error!"),
|
||||
}
|
||||
}
|
||||
|
||||
@@ -33,17 +33,17 @@ pub fn decrypt_file(path: PathBuf) -> String {
|
||||
Ok(line) => {
|
||||
decrypted = decrypted.to_owned() + line.as_str() + "\n";
|
||||
return decrypted;
|
||||
},
|
||||
}
|
||||
Err(_) => {
|
||||
println!("Что-то пошло не так!");
|
||||
}
|
||||
}
|
||||
}
|
||||
return String::new();
|
||||
},
|
||||
}
|
||||
Err(_) => {
|
||||
println!("Что-то не так...");
|
||||
return String::new();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,54 @@
|
||||
// Learn more about Tauri commands at https://tauri.app/v1/guides/features/command
|
||||
#[path = "api/aes_enc_dec.rs"] mod aes_enc_dec;
|
||||
#[path = "api/config_tcp.rs"] mod config_tcp;
|
||||
#[path = "api/handler_commands.rs"] mod handler_commands;
|
||||
#[path = "api/multithread_tcp_listener.rs"] mod multi_tcp_listener;
|
||||
|
||||
#[path = "config.rs"] mod config;
|
||||
#[path = "enc_dec_file.rs"] mod enc_dec_file;
|
||||
#[path = "subtasks_functions.rs"] mod subtasks;
|
||||
#[path = "tasks_functions.rs"] mod tasks;
|
||||
|
||||
pub fn run() {
|
||||
unsafe {
|
||||
std::env::set_var("WEBKIT_DISABLE_DMABUF_RENDERER", "1");
|
||||
}
|
||||
tauri::Builder::default()
|
||||
.plugin(tauri_plugin_shell::init())
|
||||
.setup(|app| {
|
||||
let app_handle = app.handle();
|
||||
|
||||
config_tcp::check_or_create_config_file(app_handle.clone());
|
||||
if config_tcp::get_autostart(app_handle.clone()) {
|
||||
multi_tcp_listener::start_tcp_server(app_handle.clone());
|
||||
}
|
||||
|
||||
Ok(())
|
||||
})
|
||||
.invoke_handler(tauri::generate_handler![
|
||||
tasks::check_or_create_tasks_file,
|
||||
// Tasks
|
||||
tasks::get_tasks,
|
||||
tasks::search_tasks,
|
||||
tasks::add_task,
|
||||
tasks::edit_task,
|
||||
tasks::delete_task,
|
||||
tasks::set_task_field,
|
||||
// Subtasks
|
||||
subtasks::get_subtasks,
|
||||
subtasks::add_subtask,
|
||||
subtasks::edit_subtask,
|
||||
subtasks::delete_subtask,
|
||||
subtasks::set_subtask_field,
|
||||
multi_tcp_listener::start_tcp_server,
|
||||
multi_tcp_listener::get_status,
|
||||
multi_tcp_listener::send_command_to_server,
|
||||
config_tcp::check_or_create_config_file,
|
||||
config_tcp::get_port,
|
||||
config_tcp::get_autostart,
|
||||
config_tcp::edit_settings,
|
||||
config_tcp::reset_token
|
||||
])
|
||||
.run(tauri::generate_context!())
|
||||
.expect("error while running tauri application");
|
||||
}
|
||||
@@ -1,67 +1,5 @@
|
||||
// Prevents additional console window on Windows in release, DO NOT REMOVE!!
|
||||
#![cfg_attr(not(debug_assertions), windows_subsystem = "windows")]
|
||||
|
||||
use std::thread;
|
||||
|
||||
// Learn more about Tauri commands at https://tauri.app/v1/guides/features/command
|
||||
#[path="api/multithread_tcp_listener.rs"] mod multi_tcp_listener;
|
||||
#[path= "api/handler_commands.rs"] mod handler_commands;
|
||||
#[path= "api/config_tcp.rs"] mod config_tcp;
|
||||
#[path= "api/aes_enc_dec.rs"] mod aes_enc_dec;
|
||||
|
||||
#[path="tasks_functions.rs"] mod tasks;
|
||||
#[path="config.rs"] mod config;
|
||||
#[path="enc_dec_file.rs"] mod enc_dec_file;
|
||||
|
||||
#[tauri::command]
|
||||
fn open_api_window(app_handle: tauri::AppHandle){
|
||||
thread::spawn(move || {
|
||||
match tauri::WindowBuilder::new(
|
||||
&app_handle,
|
||||
"api",
|
||||
tauri::WindowUrl::App("api.html".into())
|
||||
).title("Api")
|
||||
//.skip_taskbar(true)
|
||||
.decorations(false)
|
||||
.build() {
|
||||
Ok(_) => {}
|
||||
Err(_) => {
|
||||
//println!("{err}");
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
fn main() {
|
||||
std::env::set_var("WEBKIT_DISABLE_DMABUF_RENDERER", "1");
|
||||
tauri::Builder::default()
|
||||
.setup(|app| {
|
||||
let app_handle = app.handle();
|
||||
|
||||
if config_tcp::get_autostart(app_handle.clone()) {
|
||||
multi_tcp_listener::start_tcp_server(app_handle);
|
||||
}
|
||||
|
||||
Ok(())
|
||||
})
|
||||
.invoke_handler(tauri::generate_handler![
|
||||
tasks::check_or_create_tasks_file,
|
||||
tasks::get_tasks,
|
||||
tasks::search_tasks,
|
||||
tasks::add_task,
|
||||
tasks::edit_task,
|
||||
tasks::delete_task,
|
||||
tasks::set_task_field,
|
||||
open_api_window,
|
||||
multi_tcp_listener::start_tcp_server,
|
||||
multi_tcp_listener::get_status,
|
||||
multi_tcp_listener::send_command_to_server,
|
||||
config_tcp::check_or_create_config_file,
|
||||
config_tcp::get_port,
|
||||
config_tcp::get_autostart,
|
||||
config_tcp::edit_settings,
|
||||
config_tcp::reset_token
|
||||
])
|
||||
.run(tauri::generate_context!())
|
||||
.expect("error while running tauri application");
|
||||
app_lib::run();
|
||||
}
|
||||
|
||||
@@ -0,0 +1,134 @@
|
||||
use crate::enc_dec_file::{decrypt_file, encrypt_n_save_file};
|
||||
use serde_json::{json, Value};
|
||||
use std::path::PathBuf;
|
||||
|
||||
#[tauri::command]
|
||||
pub fn get_subtasks(id_task: String) -> Value {
|
||||
unsafe {
|
||||
let content = decrypt_file(PathBuf::from(crate::tasks::PATH_CONFIG.unwrap()));
|
||||
let data = serde_json::from_str::<Value>(content.as_str());
|
||||
return match data {
|
||||
Ok(data) => {
|
||||
let res: &Value = &data["tasks"][id_task.clone()]["subtasks"];
|
||||
res.clone()
|
||||
}
|
||||
Err(_) => {
|
||||
std::fs::remove_file(PathBuf::from(crate::tasks::PATH_CONFIG.unwrap())).unwrap();
|
||||
let data = String::from(
|
||||
"{\
|
||||
\"id\": 0,\
|
||||
\"tasks\": {}\
|
||||
}",
|
||||
);
|
||||
encrypt_n_save_file(PathBuf::from(crate::tasks::PATH_CONFIG.unwrap()), data);
|
||||
|
||||
return get_subtasks(id_task);
|
||||
}
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
#[tauri::command]
|
||||
pub fn add_subtask(
|
||||
id_task: String,
|
||||
date: String,
|
||||
time: String,
|
||||
name: String,
|
||||
description: String,
|
||||
) -> String {
|
||||
unsafe {
|
||||
let content = decrypt_file(PathBuf::from(crate::tasks::PATH_CONFIG.unwrap()));
|
||||
let mut data: Value = serde_json::from_str(content.as_str()).unwrap();
|
||||
|
||||
let pending = data.clone();
|
||||
let id = &pending["id"];
|
||||
|
||||
let subtask = json!({
|
||||
"id": id.to_string(),
|
||||
"date": date,
|
||||
"time": time,
|
||||
"name": name,
|
||||
"description": description,
|
||||
"completed": false
|
||||
});
|
||||
|
||||
data["id"] = (id.as_i64().unwrap() + 1).into();
|
||||
|
||||
data["tasks"][id_task]["subtasks"][id.to_string()] = subtask;
|
||||
encrypt_n_save_file(
|
||||
PathBuf::from(crate::tasks::PATH_CONFIG.unwrap()),
|
||||
data.to_string(),
|
||||
);
|
||||
|
||||
return id.to_string();
|
||||
}
|
||||
}
|
||||
|
||||
#[tauri::command]
|
||||
pub fn edit_subtask(
|
||||
id_task: String,
|
||||
id_subtask: String,
|
||||
name: String,
|
||||
description: String,
|
||||
) -> bool {
|
||||
unsafe {
|
||||
let content = decrypt_file(PathBuf::from(crate::tasks::PATH_CONFIG.unwrap()));
|
||||
let mut data: Value = serde_json::from_str(content.as_str()).unwrap();
|
||||
|
||||
return if !data["tasks"][id_task.clone()]["subtasks"][id_subtask.clone()]
|
||||
.get("id")
|
||||
.is_none()
|
||||
{
|
||||
data["tasks"][id_task.clone()]["subtasks"][id_subtask.clone()]["name"] = json!(name);
|
||||
data["tasks"][id_task.clone()]["subtasks"][id_subtask.clone()]["description"] =
|
||||
json!(description);
|
||||
|
||||
encrypt_n_save_file(
|
||||
PathBuf::from(crate::tasks::PATH_CONFIG.unwrap()),
|
||||
data.to_string(),
|
||||
);
|
||||
true
|
||||
} else {
|
||||
false
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
#[tauri::command]
|
||||
pub fn delete_subtask(id_task: String, id_subtask: String) -> bool {
|
||||
unsafe {
|
||||
let content = decrypt_file(PathBuf::from(crate::tasks::PATH_CONFIG.unwrap()));
|
||||
let mut data: Value = serde_json::from_str(content.as_str()).unwrap();
|
||||
|
||||
let subtasks = data["tasks"][id_task.clone()]["subtasks"]
|
||||
.as_object_mut()
|
||||
.unwrap();
|
||||
let binding = subtasks.clone();
|
||||
for (key, value) in binding {
|
||||
if id_subtask == value["id"] {
|
||||
subtasks.remove(&key);
|
||||
data["tasks"][id_task]["subtasks"] = json!(subtasks);
|
||||
encrypt_n_save_file(
|
||||
PathBuf::from(crate::tasks::PATH_CONFIG.unwrap()),
|
||||
data.to_string(),
|
||||
);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
false
|
||||
}
|
||||
}
|
||||
|
||||
#[tauri::command]
|
||||
pub fn set_subtask_field(id_task: String, id_subtask: String, field: String, value: String) {
|
||||
unsafe {
|
||||
let content = decrypt_file(PathBuf::from(crate::tasks::PATH_CONFIG.unwrap()));
|
||||
let mut data: Value = serde_json::from_str(content.as_str()).unwrap();
|
||||
data["tasks"][id_task]["subtasks"][id_subtask][field] = json!(value);
|
||||
|
||||
encrypt_n_save_file(
|
||||
PathBuf::from(crate::tasks::PATH_CONFIG.unwrap()),
|
||||
data.to_string(),
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -1,20 +1,18 @@
|
||||
use std::fs::{read_to_string};
|
||||
use std::path::{Path, PathBuf};
|
||||
use std::fs::File;
|
||||
use serde_json::{Value, json};
|
||||
use rust_fuzzy_search::fuzzy_search_best_n;
|
||||
use serde_json::{json, Value};
|
||||
use std::fs::read_to_string;
|
||||
use std::fs::File;
|
||||
use std::path::{Path, PathBuf};
|
||||
use tauri::Manager;
|
||||
use tauri::path::BaseDirectory;
|
||||
|
||||
use crate::enc_dec_file::{encrypt_n_save_file, decrypt_file};
|
||||
use crate::enc_dec_file::{decrypt_file, encrypt_n_save_file};
|
||||
|
||||
static mut PATH_CONFIG: Option<&'static str> = None;
|
||||
pub static mut PATH_CONFIG: Option<&'static str> = None;
|
||||
|
||||
#[tauri::command]
|
||||
pub fn check_or_create_tasks_file(app_handle: tauri::AppHandle) {
|
||||
let path: PathBuf = [
|
||||
app_handle.path_resolver().app_local_data_dir().unwrap(),
|
||||
"ToDo".into(),
|
||||
"tasks.enc".into(),
|
||||
].iter().collect(); // Путь к конфиг файлу
|
||||
let path = app_handle.path().resolve("ToDo/tasks.enc", BaseDirectory::AppLocalData).unwrap();
|
||||
|
||||
let path_str = Box::leak(Box::new(path.to_string_lossy().into_owned()));
|
||||
|
||||
@@ -24,32 +22,34 @@ pub fn check_or_create_tasks_file(app_handle: tauri::AppHandle) {
|
||||
|
||||
unsafe {
|
||||
let exist: bool = Path::new(PATH_CONFIG.unwrap()).exists();
|
||||
if exist{
|
||||
if exist {
|
||||
let content = read_to_string(path);
|
||||
return match content {
|
||||
Ok(_) => {},
|
||||
Ok(_) => {}
|
||||
Err(_) => {
|
||||
println!("Не удалось открыть конфигурационный файл!");
|
||||
}
|
||||
}
|
||||
} else{
|
||||
};
|
||||
} else {
|
||||
let path = Path::new(&path);
|
||||
let prefix = path.parent().unwrap();
|
||||
let file = std::fs::create_dir_all(prefix);
|
||||
match file{
|
||||
Ok(_) => {},
|
||||
Err(err) => println!("Не удалось создать директории! \n{}", err)
|
||||
match file {
|
||||
Ok(_) => {}
|
||||
Err(err) => println!("Не удалось создать директории! \n{}", err),
|
||||
}
|
||||
let file = File::create(path);
|
||||
match file{
|
||||
match file {
|
||||
Ok(_) => {
|
||||
let data = String::from("{\
|
||||
let data = String::from(
|
||||
"{\
|
||||
\"id\": 0,\
|
||||
\"tasks\": {}\
|
||||
}");
|
||||
}",
|
||||
);
|
||||
encrypt_n_save_file(path.into(), data);
|
||||
},
|
||||
Err(err) => println!("Не удалось создать файл! \n{}", err)
|
||||
}
|
||||
Err(err) => println!("Не удалось создать файл! \n{}", err),
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -59,11 +59,26 @@ pub fn check_or_create_tasks_file(app_handle: tauri::AppHandle) {
|
||||
pub fn get_tasks() -> Value {
|
||||
unsafe {
|
||||
let content = decrypt_file(PathBuf::from(PATH_CONFIG.unwrap()));
|
||||
let data: Value = serde_json::from_str(content.as_str()).unwrap();
|
||||
let data = serde_json::from_str::<Value>(content.as_str());
|
||||
return match data {
|
||||
Ok(data) => {
|
||||
let res: &Value = &data["tasks"];
|
||||
|
||||
let res = &data["tasks"];
|
||||
res.clone()
|
||||
}
|
||||
Err(_) => {
|
||||
std::fs::remove_file(PathBuf::from(PATH_CONFIG.unwrap())).unwrap();
|
||||
let data = String::from(
|
||||
"{\
|
||||
\"id\": 0,\
|
||||
\"tasks\": {}\
|
||||
}",
|
||||
);
|
||||
encrypt_n_save_file(PathBuf::from(PATH_CONFIG.unwrap()), data);
|
||||
|
||||
return res.clone();
|
||||
return get_tasks();
|
||||
}
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
@@ -93,7 +108,15 @@ pub fn search_tasks(value: String) -> Value {
|
||||
for (name, score) in res1 {
|
||||
if score > 0.6 {
|
||||
for (key, value) in binding1["tasks"].as_object().unwrap() {
|
||||
if value.get("name").unwrap().as_str().unwrap().to_lowercase().as_str() == name {
|
||||
if value
|
||||
.get("name")
|
||||
.unwrap()
|
||||
.as_str()
|
||||
.unwrap()
|
||||
.to_lowercase()
|
||||
.as_str()
|
||||
== name
|
||||
{
|
||||
result[key] = binding1["tasks"].get(key).unwrap().clone();
|
||||
break;
|
||||
}
|
||||
@@ -106,7 +129,13 @@ pub fn search_tasks(value: String) -> Value {
|
||||
}
|
||||
|
||||
#[tauri::command]
|
||||
pub fn add_task(date: String, time: String, name: String, description: String, priority: String) -> String {
|
||||
pub fn add_task(
|
||||
date: String,
|
||||
time: String,
|
||||
name: String,
|
||||
description: String,
|
||||
priority: String,
|
||||
) -> String {
|
||||
unsafe {
|
||||
let content = decrypt_file(PathBuf::from(PATH_CONFIG.unwrap()));
|
||||
let mut data: Value = serde_json::from_str(content.as_str()).unwrap();
|
||||
@@ -121,6 +150,7 @@ pub fn add_task(date: String, time: String, name: String, description: String, p
|
||||
"name": name,
|
||||
"description": description,
|
||||
"priority": priority,
|
||||
"subtasks": {},
|
||||
"completed": false
|
||||
});
|
||||
|
||||
@@ -134,26 +164,26 @@ pub fn add_task(date: String, time: String, name: String, description: String, p
|
||||
}
|
||||
|
||||
#[tauri::command]
|
||||
pub fn edit_task(id_task: String, name: String, description: String, priority: String) -> bool{
|
||||
pub fn edit_task(id_task: String, name: String, description: String, priority: String) -> bool {
|
||||
unsafe {
|
||||
let content = decrypt_file(PathBuf::from(PATH_CONFIG.unwrap()));
|
||||
let mut data: Value = serde_json::from_str(content.as_str()).unwrap();
|
||||
|
||||
return if !data["tasks"][id_task.clone()].get("id").is_none(){
|
||||
return if !data["tasks"][id_task.clone()].get("id").is_none() {
|
||||
data["tasks"][id_task.clone()]["name"] = json!(name);
|
||||
data["tasks"][id_task.clone()]["description"] = json!(description);
|
||||
data["tasks"][id_task]["priority"] = json!(priority);
|
||||
|
||||
encrypt_n_save_file(PathBuf::from(PATH_CONFIG.unwrap()), data.to_string());
|
||||
true
|
||||
} else{
|
||||
} else {
|
||||
false
|
||||
}
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
#[tauri::command]
|
||||
pub fn delete_task(id_task: String) -> bool{
|
||||
pub fn delete_task(id_task: String) -> bool {
|
||||
unsafe {
|
||||
let content = decrypt_file(PathBuf::from(PATH_CONFIG.unwrap()));
|
||||
let mut data: Value = serde_json::from_str(content.as_str()).unwrap();
|
||||
@@ -165,7 +195,7 @@ pub fn delete_task(id_task: String) -> bool{
|
||||
tasks.remove(&key);
|
||||
data["tasks"] = json!(tasks);
|
||||
encrypt_n_save_file(PathBuf::from(PATH_CONFIG.unwrap()), data.to_string());
|
||||
return true
|
||||
return true;
|
||||
}
|
||||
}
|
||||
false
|
||||
@@ -181,4 +211,4 @@ pub fn set_task_field(id_task: String, field: String, value: String) {
|
||||
|
||||
encrypt_n_save_file(PathBuf::from(PATH_CONFIG.unwrap()), data.to_string());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -2,54 +2,36 @@
|
||||
"build": {
|
||||
"beforeDevCommand": "npm run dev",
|
||||
"beforeBuildCommand": "npm run build",
|
||||
"devPath": "http://localhost:1420",
|
||||
"distDir": "../dist"
|
||||
"frontendDist": "../dist",
|
||||
"devUrl": "http://localhost:1420"
|
||||
},
|
||||
"package": {
|
||||
"productName": "to-do-app",
|
||||
"version": "1.5.0"
|
||||
"bundle": {
|
||||
"active": true,
|
||||
"targets": "all",
|
||||
"icon": [
|
||||
"icons/32x32.png",
|
||||
"icons/128x128.png",
|
||||
"icons/128x128@2x.png",
|
||||
"icons/icon.icns",
|
||||
"icons/icon.ico"
|
||||
]
|
||||
},
|
||||
"tauri": {
|
||||
"allowlist": {
|
||||
"all": false,
|
||||
"shell": {
|
||||
"all": false,
|
||||
"open": true
|
||||
},
|
||||
"window": {
|
||||
"all": false,
|
||||
"close": true,
|
||||
"hide": true,
|
||||
"show": true,
|
||||
"maximize": true,
|
||||
"minimize": true,
|
||||
"unmaximize": true,
|
||||
"unminimize": true,
|
||||
"startDragging": true
|
||||
}
|
||||
},
|
||||
"windows": [
|
||||
{
|
||||
"title": "To Do List",
|
||||
"width": 800,
|
||||
"height": 600,
|
||||
"decorations": false
|
||||
}
|
||||
],
|
||||
"productName": "to-do-app",
|
||||
"mainBinaryName": "to-do-app",
|
||||
"version": "1.7.0",
|
||||
"identifier": "com.a-dot.dev",
|
||||
"plugins": {},
|
||||
"app": {
|
||||
"security": {
|
||||
"csp": null
|
||||
},
|
||||
"bundle": {
|
||||
"active": true,
|
||||
"targets": "all",
|
||||
"identifier": "com.a-dot.dev",
|
||||
"icon": [
|
||||
"icons/32x32.png",
|
||||
"icons/128x128.png",
|
||||
"icons/128x128@2x.png",
|
||||
"icons/icon.icns",
|
||||
"icons/icon.ico"
|
||||
]
|
||||
}
|
||||
"windows": [
|
||||
{
|
||||
"width": 800,
|
||||
"height": 600,
|
||||
"decorations": false,
|
||||
"title": "To Do List"
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,211 +1,194 @@
|
||||
<script setup>
|
||||
import {Icon} from "@iconify/vue";
|
||||
import {onBeforeMount, ref} from "vue";
|
||||
import {appWindow} from "@tauri-apps/api/window";
|
||||
|
||||
import {useDark, useToggle} from "@vueuse/core";
|
||||
import {invoke} from "@tauri-apps/api";
|
||||
import ConfirmModal from "./api_components/ConfirmModal.vue";
|
||||
import {listen} from "@tauri-apps/api/event";
|
||||
import SettingsModal from "./api_components/SettingsModal.vue";
|
||||
import PageHandler from "./api_wiki/PageHandler.vue";
|
||||
|
||||
const isDark = useDark();
|
||||
const toggleDark = useToggle(isDark);
|
||||
|
||||
let wiki_open = ref(false);
|
||||
|
||||
let pending = ref(true);
|
||||
let api_status = ref(false);
|
||||
|
||||
let confirm_modal = ref(false);
|
||||
let settings_modal = ref(false);
|
||||
|
||||
let settings_port = ref(0);
|
||||
let settings_autostart = ref(true);
|
||||
|
||||
let input_val = ref((() => {
|
||||
let string = "";
|
||||
for(let i = 0; i < 5; i++) string += (Math.random() + 1).toString(36).substring(2);
|
||||
return string;
|
||||
})())
|
||||
|
||||
async function get_status() {
|
||||
pending.value = true;
|
||||
await invoke('get_status')
|
||||
.then((res) => {
|
||||
api_status.value = res;
|
||||
});
|
||||
}
|
||||
|
||||
onBeforeMount(async () => {
|
||||
await invoke('check_or_create_config_file')
|
||||
.then(async () => {
|
||||
await get_status();
|
||||
pending.value = false;
|
||||
});
|
||||
|
||||
await listen('update_api', async () => {
|
||||
await get_status();
|
||||
pending.value = false;
|
||||
})
|
||||
});
|
||||
|
||||
async function run_api() {
|
||||
await invoke('start_tcp_server');
|
||||
}
|
||||
|
||||
async function stop_api() {
|
||||
await invoke('send_command_to_server', {
|
||||
command: "stop"
|
||||
});
|
||||
}
|
||||
|
||||
async function open_settings() {
|
||||
await invoke('get_port')
|
||||
.then(async (res) => {
|
||||
settings_port.value = res;
|
||||
await invoke('get_autostart')
|
||||
.then((res) => {
|
||||
settings_autostart.value = res;
|
||||
settings_modal.value = true;
|
||||
})
|
||||
})
|
||||
}
|
||||
|
||||
async function regen_token(){
|
||||
confirm_modal.value = false;
|
||||
await invoke('reset_token')
|
||||
.then((res) => {
|
||||
let input = document.getElementById('token_con');
|
||||
let timer = document.getElementById('timer');
|
||||
input.type = "text";
|
||||
input_val.value = res;
|
||||
|
||||
let i = 15;
|
||||
timer.textContent = i.toString() + "s";
|
||||
|
||||
let timerI = setInterval(() => {
|
||||
i--;
|
||||
if(i===0){
|
||||
input_val.value = (() => {
|
||||
let string = "";
|
||||
for(let i = 0; i < 5; i++) string += (Math.random() + 1).toString(36).substring(2);
|
||||
return string;
|
||||
})();
|
||||
input.type = "password";
|
||||
timer.textContent = "";
|
||||
clearInterval(timerI);
|
||||
} else{
|
||||
timer.textContent = i.toString() + "s";
|
||||
}
|
||||
}, 1000);
|
||||
});
|
||||
}
|
||||
|
||||
function select_input(event){
|
||||
event.target.select();
|
||||
}
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<div data-tauri-drag-region class="z-50 titlebar h-[30px] select-none fixed flex justify-end top-0 right-0 left-0 bg-gray-100 dark:bg-zinc-800">
|
||||
<p class="fixed left-2 top-0.5 text-lg dark:text-white">
|
||||
<span v-if="!wiki_open">API</span>
|
||||
<span v-else>API Wiki</span>
|
||||
</p>
|
||||
<div @click="appWindow.minimize()" class="titlebar-button hover:bg-gray-200 dark:hover:bg-gray-700 justify-center inline-flex w-[30px] h-[30px] items-center dark:text-white" id="titlebar-minimize">
|
||||
<Icon class="" icon="mdi:window-minimize" width="20" height="20"/>
|
||||
</div>
|
||||
<div @click="appWindow.toggleMaximize()" class="titlebar-button hover:bg-gray-200 dark:hover:bg-gray-700 justify-center inline-flex w-[30px] h-[30px] items-center dark:text-white" id="titlebar-maximize">
|
||||
<Icon class="" icon="mdi:window-maximize" width="20" height="20"/>
|
||||
</div>
|
||||
<div @click="appWindow.close()" class="titlebar-button hover:bg-gray-200 dark:hover:bg-gray-700 justify-center inline-flex w-[30px] h-[30px] items-center dark:text-white" id="titlebar-close">
|
||||
<Icon class="" icon="mdi:close" width="26" height="26"/>
|
||||
</div>
|
||||
</div>
|
||||
<div v-if="pending">
|
||||
<Icon icon="line-md:loading-twotone-loop" width="96" height="96" class="text-green-500 absolute top-[50%] left-[50%] translate-x-[-50%] translate-y-[-50%]"/>
|
||||
</div>
|
||||
<div v-else>
|
||||
<button class="absolute top-10 right-2" @click="toggleDark()">
|
||||
<Icon v-if="!isDark" class="group-hover:invert text-yellow-400" icon="flowbite:sun-solid" width="38" height="38"/>
|
||||
<Icon v-else class="group-hover:invert text-blue-400" icon="ri:moon-fill" width="38" height="38"/>
|
||||
</button>
|
||||
<div v-if="!wiki_open">
|
||||
<button class="absolute top-10 left-2" @click="open_settings">
|
||||
<Icon icon="material-symbols:settings-outline" width="38" height="38" class="text-zinc-600 dark:text-zinc-200"/>
|
||||
</button>
|
||||
<div class="mt-8">
|
||||
<div class="mt-[28vh] grid grid-cols-1 grid-rows-3 sm:grid-cols-2 sm:grid-rows-2 gap-2.5 dark:text-white md:mx-16 lg:mx-24">
|
||||
<div class="text-center shadow dark:shadow-white mx-4 sm:mx-0 sm:ml-4 rounded-lg p-1 row-span-1">
|
||||
<p class="mb-4">Статус</p>
|
||||
<div v-if="!api_status" class="flex text-center align-middle items-center">
|
||||
<Icon class="text-red-500" icon="oui:dot" width="36" height="36"/>
|
||||
<span>Не подключено</span>
|
||||
<button @click="run_api" class="bg-green-500 text-white rounded-lg py-1 px-2 ml-2 hover:bg-green-600 transition-colors">Запустить</button>
|
||||
</div>
|
||||
<div v-else class="flex text-center align-middle items-center">
|
||||
<Icon class="text-green-500" icon="oui:dot" width="36" height="36"/>
|
||||
<span>Работает</span>
|
||||
<button @click="stop_api" class="bg-green-500 text-white rounded-lg py-1 px-2 ml-2 hover:bg-green-600 transition-colors">Остановить</button>
|
||||
</div>
|
||||
</div>
|
||||
<div class="grid text-center shadow dark:shadow-white mx-4 sm:mx-0 sm:mr-4 rounded-lg p-1 justify-center items-center row-start-2 sm:row-span-1">
|
||||
<div v-if="api_status">
|
||||
<p class="mb-2">Токен подключения</p>
|
||||
<div>
|
||||
<input @click="select_input" size="30" id="token_con" readonly type="password" class="rounded-lg my-0.5 px-2 dark:bg-zinc-700"
|
||||
:value="input_val">
|
||||
</div>
|
||||
<div class="flex justify-center items-center">
|
||||
<button class="text-[15px] m-1 rounded-lg bg-red-500 text-white py-1 px-2" @click="confirm_modal = true">Сгенерировать новый</button>
|
||||
<p id="timer" class="text-xl ml-2"></p>
|
||||
</div>
|
||||
</div>
|
||||
<p v-else class="underline decoration-red-500 decoration-2 underline-offset-2">Нет подключения</p>
|
||||
</div>
|
||||
<div @click="wiki_open = true" class="cursor-pointer group mt-2 text-center shadow dark:shadow-white hover:shadow-md dark:hover:shadow-white ml-4 mr-6 sm:mr-4 rounded-lg p-1 col-start-1 col-end-3 row-start-3 sm:row-span-2">
|
||||
<Icon class="mt-1 text-zinc-800 dark:text-zinc-100 mx-auto" icon="mingcute:question-line" width="48" height="48"/>
|
||||
<p class="text-xl mt-1 mb-2.5 group-hover:underline">Как этим пользоваться?</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div v-if="confirm_modal || settings_modal" class="bg-black w-[100vw] h-full fixed top-0 opacity-70 z-40"/>
|
||||
<ConfirmModal v-if="confirm_modal" @close="confirm_modal = false" @yes="regen_token"/>
|
||||
<SettingsModal v-if="settings_modal" @close="settings_modal = false" :autostart="settings_autostart" :port="settings_port"/>
|
||||
</div>
|
||||
<div v-else>
|
||||
<button class="absolute top-10 left-2" @click="wiki_open = false">
|
||||
<Icon icon="material-symbols:arrow-back" width="38" height="38" class="text-zinc-600 dark:text-zinc-200"/>
|
||||
</button>
|
||||
<PageHandler/>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<style>
|
||||
@tailwind base;
|
||||
@tailwind components;
|
||||
@tailwind utilities;
|
||||
|
||||
:root {
|
||||
overflow-x: hidden;
|
||||
font-family: Inter, Avenir, Helvetica, Arial, sans-serif;
|
||||
font-size: 16px;
|
||||
line-height: 24px;
|
||||
font-weight: 400;
|
||||
|
||||
background-color: rgb(244 245 246);
|
||||
|
||||
font-synthesis: none;
|
||||
text-rendering: optimizeLegibility;
|
||||
-webkit-font-smoothing: antialiased;
|
||||
-moz-osx-font-smoothing: grayscale;
|
||||
-webkit-text-size-adjust: 100%;
|
||||
}
|
||||
|
||||
:root.dark {
|
||||
background-color: rgb(35, 35, 35);
|
||||
}
|
||||
<script setup>
|
||||
import {onBeforeMount, ref} from "vue";
|
||||
import {getCurrentWebviewWindow} from "@tauri-apps/api/webviewWindow";
|
||||
|
||||
import {useDark} from "@vueuse/core";
|
||||
useDark();
|
||||
import {invoke} from "@tauri-apps/api/core";
|
||||
import ConfirmModal from "./api_components/ConfirmModal.vue";
|
||||
import {listen} from "@tauri-apps/api/event";
|
||||
import SettingsModal from "./api_components/SettingsModal.vue";
|
||||
import PageHandler from "./api_wiki/PageHandler.vue";
|
||||
const appWindow = getCurrentWebviewWindow()
|
||||
|
||||
|
||||
let wiki_open = ref(false);
|
||||
|
||||
let pending = ref(true);
|
||||
let api_status = ref(false);
|
||||
|
||||
let confirm_modal = ref(false);
|
||||
let settings_modal = ref(false);
|
||||
|
||||
let settings_port = ref(0);
|
||||
let settings_autostart = ref(true);
|
||||
|
||||
let input_val = ref((() => {
|
||||
let string = "";
|
||||
for(let i = 0; i < 5; i++) string += (Math.random() + 1).toString(36).substring(2);
|
||||
return string;
|
||||
})())
|
||||
|
||||
async function get_status() {
|
||||
pending.value = true;
|
||||
await invoke('get_status')
|
||||
.then((res) => {
|
||||
api_status.value = res;
|
||||
});
|
||||
}
|
||||
|
||||
onBeforeMount(async () => {
|
||||
await invoke('check_or_create_config_file')
|
||||
.then(async () => {
|
||||
await get_status();
|
||||
pending.value = false;
|
||||
});
|
||||
|
||||
await listen('update_api', async () => {
|
||||
await get_status();
|
||||
pending.value = false;
|
||||
})
|
||||
});
|
||||
|
||||
async function run_api() {
|
||||
await invoke('start_tcp_server');
|
||||
}
|
||||
|
||||
async function stop_api() {
|
||||
await invoke('send_command_to_server', {
|
||||
command: "stop"
|
||||
});
|
||||
}
|
||||
|
||||
async function open_settings() {
|
||||
await invoke('get_port')
|
||||
.then(async (res) => {
|
||||
settings_port.value = res;
|
||||
await invoke('get_autostart')
|
||||
.then((res) => {
|
||||
settings_autostart.value = res;
|
||||
settings_modal.value = true;
|
||||
})
|
||||
})
|
||||
}
|
||||
|
||||
async function regen_token(){
|
||||
confirm_modal.value = false;
|
||||
await invoke('reset_token')
|
||||
.then((res) => {
|
||||
let input = document.getElementById('token_con');
|
||||
let timer = document.getElementById('timer');
|
||||
input.type = "text";
|
||||
input_val.value = res;
|
||||
|
||||
let i = 15;
|
||||
timer.textContent = i.toString() + "s";
|
||||
|
||||
let timerI = setInterval(() => {
|
||||
i--;
|
||||
if(i===0){
|
||||
input_val.value = (() => {
|
||||
let string = "";
|
||||
for(let i = 0; i < 5; i++) string += (Math.random() + 1).toString(36).substring(2);
|
||||
return string;
|
||||
})();
|
||||
input.type = "password";
|
||||
timer.textContent = "";
|
||||
clearInterval(timerI);
|
||||
} else{
|
||||
timer.textContent = i.toString() + "s";
|
||||
}
|
||||
}, 1000);
|
||||
});
|
||||
}
|
||||
|
||||
function select_input(event){
|
||||
event.target.select();
|
||||
}
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<div v-if="pending">
|
||||
<img src="/icon/eos-icons--three-dots-loading.svg" alt="Loading" style="width: 96px; height: 96px" class="dark:invert absolute top-[50%] left-[50%] translate-x-[-50%] translate-y-[-50%]">
|
||||
</div>
|
||||
<div v-else>
|
||||
<slot/>
|
||||
<div v-if="!wiki_open">
|
||||
<button class="absolute top-10 right-2" @click="open_settings">
|
||||
<img src="/icon/material-symbols--settings-outline.svg" alt="API settings" style="width: 38px; height: 38px" class="dark:invert">
|
||||
</button>
|
||||
<div class="mt-8">
|
||||
<div class="mt-[28vh] grid grid-cols-1 grid-rows-3 sm:grid-cols-2 sm:grid-rows-2 gap-2.5 dark:text-white md:mx-16 lg:mx-24">
|
||||
<div class="text-center shadow dark:shadow-white mx-4 sm:mx-0 sm:ml-4 rounded-lg p-1 row-span-1">
|
||||
<p class="mb-4">Статус</p>
|
||||
<div v-if="!api_status" class="flex text-center align-middle items-center">
|
||||
<img src="/icon/api/oui--dot.svg" alt="Api status"
|
||||
style="width: 36px; height: 36px; filter: invert(58%) sepia(88%) saturate(5276%) hue-rotate(336deg) brightness(95%) contrast(98%);">
|
||||
<span>Не подключено</span>
|
||||
<button @click="run_api" class="bg-green-500 text-white rounded-lg py-1 px-2 ml-2 hover:bg-green-600 transition-colors">Запустить</button>
|
||||
</div>
|
||||
<div v-else class="flex text-center align-middle items-center">
|
||||
<img src="/icon/api/oui--dot.svg" alt="Api status"
|
||||
style="width: 36px; height: 36px; filter: invert(69%) sepia(18%) saturate(2201%) hue-rotate(86deg) brightness(91%) contrast(77%);">
|
||||
<span>Работает</span>
|
||||
<button @click="stop_api" class="bg-green-500 text-white rounded-lg py-1 px-2 ml-2 hover:bg-green-600 transition-colors">Остановить</button>
|
||||
</div>
|
||||
</div>
|
||||
<div class="grid text-center shadow dark:shadow-white mx-4 sm:mx-0 sm:mr-4 rounded-lg p-1 justify-center items-center row-start-2 sm:row-span-1">
|
||||
<div v-if="api_status">
|
||||
<p class="mb-2">Токен подключения</p>
|
||||
<div>
|
||||
<input @click="select_input" size="30" id="token_con" readonly type="password" class="rounded-lg my-0.5 px-2 dark:bg-zinc-700"
|
||||
:value="input_val">
|
||||
</div>
|
||||
<div class="flex justify-center items-center">
|
||||
<button class="text-[15px] m-1 rounded-lg bg-red-500 text-white py-1 px-2" @click="confirm_modal = true">Сгенерировать новый</button>
|
||||
<p id="timer" class="text-xl ml-2"></p>
|
||||
</div>
|
||||
</div>
|
||||
<p v-else class="underline decoration-red-500 decoration-2 underline-offset-2">Нет подключения</p>
|
||||
</div>
|
||||
<div @click="wiki_open = true" class="cursor-pointer group mt-2 text-center shadow dark:shadow-white hover:shadow-md dark:hover:shadow-white ml-4 mr-6 sm:mr-4 rounded-lg p-1 col-start-1 col-end-3 row-start-3 sm:row-span-2">
|
||||
<img src="/icon/mingcute--question-line.svg" alt="How it works" style="width: 48px; height: 48px" class="mt-1 mx-auto dark:invert">
|
||||
<p class="text-xl mt-1 mb-2.5 group-hover:underline">Как этим пользоваться?</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div v-if="confirm_modal || settings_modal" class="bg-black w-[100vw] h-full fixed top-0 opacity-70 z-40"/>
|
||||
<ConfirmModal v-if="confirm_modal" @close="confirm_modal = false" @yes="regen_token"/>
|
||||
<SettingsModal v-if="settings_modal" @close="settings_modal = false" :autostart="settings_autostart" :port="settings_port"/>
|
||||
</div>
|
||||
<div v-else>
|
||||
<button class="absolute top-10 left-12" @click="wiki_open = false">
|
||||
<img src="/icon/material-symbols--arrow-back.svg" alt="Back" style="width: 34px; height: 34px" class="dark:invert">
|
||||
</button>
|
||||
<PageHandler/>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<style>
|
||||
@tailwind base;
|
||||
@tailwind components;
|
||||
@tailwind utilities;
|
||||
|
||||
:root {
|
||||
overflow-x: hidden;
|
||||
font-family: Inter, Avenir, Helvetica, Arial, sans-serif;
|
||||
font-size: 16px;
|
||||
line-height: 24px;
|
||||
font-weight: 400;
|
||||
|
||||
background-color: rgb(244 245 246);
|
||||
|
||||
font-synthesis: none;
|
||||
text-rendering: optimizeLegibility;
|
||||
-webkit-font-smoothing: antialiased;
|
||||
-moz-osx-font-smoothing: grayscale;
|
||||
-webkit-text-size-adjust: 100%;
|
||||
}
|
||||
|
||||
:root.dark {
|
||||
background-color: rgb(35, 35, 35);
|
||||
}
|
||||
</style>
|
||||
@@ -1,265 +1,333 @@
|
||||
<script setup>
|
||||
import {onBeforeMount, ref} from "vue";
|
||||
import {invoke} from "@tauri-apps/api";
|
||||
import {Icon} from "@iconify/vue";
|
||||
import EditModal from "./components/modals/EditModal.vue";
|
||||
import CreateModal from "./components/modals/CreateModal.vue";
|
||||
|
||||
import { appWindow } from '@tauri-apps/api/window';
|
||||
|
||||
import {useDark, useToggle} from "@vueuse/core";
|
||||
import PriorityModal from "./components/modals/PriorityModal.vue";
|
||||
import {listen} from "@tauri-apps/api/event";
|
||||
|
||||
const isDark = useDark();
|
||||
const toggleDark = useToggle(isDark);
|
||||
|
||||
let tasks = ref({});
|
||||
let pending = ref(true);
|
||||
let api_status = ref(false);
|
||||
|
||||
let create_modal = ref(false);
|
||||
let edit_modal = ref(false);
|
||||
let priority_modal = ref(false);
|
||||
|
||||
let search_text = ref("");
|
||||
|
||||
let to_edit_id = ref("");
|
||||
let to_edit_name = ref("");
|
||||
let to_edit_description = ref("");
|
||||
let to_edit_priority = ref(0);
|
||||
|
||||
let to_change_id = ref("");
|
||||
let to_change_name = ref("");
|
||||
let to_change_priority = ref("");
|
||||
|
||||
async function get_status() {
|
||||
await invoke('get_status')
|
||||
.then((res) => {
|
||||
api_status.value = res;
|
||||
});
|
||||
}
|
||||
|
||||
onBeforeMount(async () => {
|
||||
await invoke('check_or_create_tasks_file')
|
||||
.then(async () => {
|
||||
await get_tasks();
|
||||
await get_status();
|
||||
pending.value = false;
|
||||
});
|
||||
|
||||
await listen('update_api', async () => {
|
||||
await get_status();
|
||||
})
|
||||
});
|
||||
|
||||
|
||||
function compare_priority(a, b) {
|
||||
if ( parseInt(a.priority) < parseInt(b.priority) ){
|
||||
return -1;
|
||||
}
|
||||
if ( parseInt(a.priority) > parseInt(b.priority) ){
|
||||
return 1;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
async function get_tasks(){
|
||||
let tmp_tasks = [];
|
||||
await invoke('get_tasks').then((res) => {
|
||||
for (let value in res){
|
||||
tmp_tasks.push(res[value]);
|
||||
}
|
||||
tasks.value = tmp_tasks.sort(compare_priority).reverse();
|
||||
});
|
||||
}
|
||||
|
||||
async function search_tasks(event){
|
||||
let tmp_tasks = [];
|
||||
if (event.target.value) {
|
||||
await invoke('search_tasks', {
|
||||
value: event.target.value
|
||||
}).then((res) => {
|
||||
for (let value in res){
|
||||
tmp_tasks.push(res[value]);
|
||||
}
|
||||
tasks.value = tmp_tasks.sort(compare_priority).reverse();
|
||||
});
|
||||
} else{
|
||||
await get_tasks();
|
||||
}
|
||||
}
|
||||
|
||||
async function set_task_field(id_task, completed){
|
||||
let field = "completed"
|
||||
id_task = id_task.toString();
|
||||
completed = completed.toString();
|
||||
if (completed === "false"){
|
||||
completed = "true"
|
||||
} else{
|
||||
completed = "false"
|
||||
}
|
||||
await invoke('set_task_field', {
|
||||
idTask: id_task,
|
||||
field: field,
|
||||
value: completed
|
||||
}).then(async () => {
|
||||
for (let index in tasks.value){
|
||||
if (tasks.value[index].id === id_task){
|
||||
tasks.value[index][field] = completed;
|
||||
break;
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
async function delete_task(id_task){
|
||||
id_task = id_task.toString();
|
||||
await invoke('delete_task', {
|
||||
idTask: id_task
|
||||
}).then(async () => {
|
||||
await get_tasks();
|
||||
});
|
||||
}
|
||||
|
||||
function edit_task(id_task, name, description, priority){
|
||||
window.scrollTo({top:0});
|
||||
to_edit_id.value = id_task;
|
||||
to_edit_name.value = name;
|
||||
to_edit_description.value = description;
|
||||
to_edit_priority.value = priority;
|
||||
edit_modal.value = true;
|
||||
}
|
||||
|
||||
function change_priority(id_task, name, priority){
|
||||
window.scrollTo({top:0});
|
||||
to_change_id.value = id_task;
|
||||
to_change_name.value = name;
|
||||
to_change_priority.value = priority;
|
||||
priority_modal.value = true;
|
||||
}
|
||||
|
||||
|
||||
async function open_api(){
|
||||
await invoke('open_api_window');
|
||||
}
|
||||
</script>
|
||||
|
||||
|
||||
<template>
|
||||
<div data-tauri-drag-region class="z-50 titlebar h-[30px] select-none fixed flex justify-end top-0 right-0 left-0 bg-gray-100 dark:bg-zinc-800">
|
||||
<p class="fixed left-2 top-0.5 text-lg dark:text-white">To Do</p>
|
||||
<div @click="appWindow.minimize()" class="titlebar-button hover:bg-gray-200 dark:hover:bg-gray-700 justify-center inline-flex w-[30px] h-[30px] items-center dark:text-white" id="titlebar-minimize">
|
||||
<Icon class="" icon="mdi:window-minimize" width="20" height="20"/>
|
||||
</div>
|
||||
<div @click="appWindow.toggleMaximize()" class="titlebar-button hover:bg-gray-200 dark:hover:bg-gray-700 justify-center inline-flex w-[30px] h-[30px] items-center dark:text-white" id="titlebar-maximize">
|
||||
<Icon class="" icon="mdi:window-maximize" width="20" height="20"/>
|
||||
</div>
|
||||
<div @click="appWindow.close()" class="titlebar-button hover:bg-gray-200 dark:hover:bg-gray-700 justify-center inline-flex w-[30px] h-[30px] items-center dark:text-white" id="titlebar-close">
|
||||
<Icon class="" icon="mdi:close" width="26" height="26"/>
|
||||
</div>
|
||||
</div>
|
||||
<div v-if="pending">
|
||||
<Icon icon="line-md:loading-twotone-loop" width="96" height="96" class="text-green-500 absolute top-[50%] left-[50%] translate-x-[-50%] translate-y-[-50%]"/>
|
||||
</div>
|
||||
<div v-else>
|
||||
<button class="absolute bottom-2 left-2 opacity-50 hover:opacity-100" @click="open_api()">
|
||||
<Icon icon="icon-park-outline:earth" width="40" height="40" class="relative text-zinc-400 dark:text-zinc-300"/>
|
||||
<Icon v-if="api_status" class="absolute bottom-4 left-4 text-green-500" icon="oui:dot" width="36" height="36"/>
|
||||
<Icon v-else class="absolute bottom-4 left-4 text-red-500" icon="oui:dot" width="36" height="36"/>
|
||||
</button>
|
||||
<button class="absolute top-10 right-2" @click="toggleDark()">
|
||||
<Icon v-if="!isDark" class="group-hover:invert text-yellow-400" icon="flowbite:sun-solid" width="38" height="38"/>
|
||||
<Icon v-else class="group-hover:invert text-blue-400" icon="ri:moon-fill" width="38" height="38"/>
|
||||
</button>
|
||||
<div id="search" class="relative flex h-10 mx-[15vw] lg:mx-[25vw] mt-12">
|
||||
<div class="!absolute right-1 top-1 z-10">
|
||||
<button
|
||||
v-if="search_text !== ''"
|
||||
@click="search_text = ''; get_tasks()"
|
||||
class="p-[1px] pt-[1px] pb-[2px] mr-0.5 rounded-lg group text-gray-400 hover:text-gray-500 transition-colors"
|
||||
type="button"
|
||||
data-ripple-light="true"
|
||||
>
|
||||
<Icon icon="ic:baseline-close" width="26" height="26"/>
|
||||
</button>
|
||||
<button
|
||||
@click="create_modal = !create_modal"
|
||||
class="p-[1px] pt-[1px] pb-[2px] border-green-400 border-2 rounded-lg group hover:bg-green-400 transition-colors"
|
||||
type="button"
|
||||
data-ripple-light="true"
|
||||
>
|
||||
<Icon class="group-hover:invert dark:invert" icon="ic:baseline-plus" width="26" height="26"/>
|
||||
</button>
|
||||
</div>
|
||||
<input
|
||||
v-model="search_text"
|
||||
type="text"
|
||||
class="peer h-full w-full rounded-lg border border-blue-gray-200 bg-transparent px-3 py-2.5 pr-20 font-sans text-sm font-normal text-blue-gray-700 dark:text-white dark:border-white outline outline-0 transition-all placeholder-shown:border placeholder-shown:border-blue-gray-200 placeholder-shown:border-t-blue-gray-200 focus:border-2 focus:border-green-400 focus:border-t-transparent focus:outline-0 disabled:border-0 disabled:bg-blue-gray-50"
|
||||
:class="{'dark:border-t-[rgb(30,30,30)]': search_text.length}"
|
||||
placeholder=" "
|
||||
@input="search_tasks"
|
||||
/>
|
||||
<label class="text-gray-400 dark:text-gray-300 focus:text-black before:content[' '] after:content[' '] pointer-events-none absolute left-0 -top-1.5 flex h-full w-full select-none text-[11px] font-normal leading-tight text-blue-gray-400 transition-all before:pointer-events-none before:mt-[6.5px] before:mr-1 before:box-border before:block before:h-1.5 before:w-2.5 before:rounded-tl-md before:border-t before:border-l before:border-blue-gray-200 before:transition-all after:pointer-events-none after:mt-[6.5px] after:ml-1 after:box-border after:block after:h-1.5 after:w-2.5 after:flex-grow after:rounded-tr-lg after:border-t after:border-r after:border-blue-gray-200 after:transition-all peer-placeholder-shown:text-sm peer-placeholder-shown:leading-[3.75] peer-placeholder-shown:text-blue-gray-500 peer-placeholder-shown:before:border-transparent peer-placeholder-shown:after:border-transparent peer-focus:text-[11px] peer-focus:leading-tight peer-focus:text-green-400 peer-focus:before:border-t-2 peer-focus:before:border-l-2 peer-focus:before:!border-green-400 peer-focus:after:border-t-2 peer-focus:after:border-r-2 peer-focus:after:!border-green-400 peer-disabled:text-transparent peer-disabled:before:border-transparent peer-disabled:after:border-transparent peer-disabled:peer-placeholder-shown:text-blue-gray-500">
|
||||
Поиск...
|
||||
</label>
|
||||
</div>
|
||||
<div id="list" class="relative mx-[14vw] lg:mx-[22vw] mt-5">
|
||||
<ul role="list" class="divide-y divide-gray-200 dark:divide-gray-500">
|
||||
<li v-for="task in tasks" :id="task.id" class="flex justify-between gap-x-6 py-5 group/task">
|
||||
<div @click="set_task_field(task.id, task.completed)" class="inline-flex items-center space-x-2 min-w-0 gap-x-2 group-hover/task:cursor-pointer">
|
||||
<p class="text-sm flex-none border rounded-full" :class="[ task.completed === 'true' ? 'border-green-500 group-hover:border-green-400' : 'border-gray-300 group-hover:border-gray-500' ]"><Icon class="m-0.5" :class="[ task.completed === 'true' ? 'text-green-500 group-hover:text-green-400' : 'text-gray-50 group-hover:text-gray-500 dark:text-[rgb(30,30,30)]' ]" icon="material-symbols:check" width="20" height="20"/></p>
|
||||
<div class="min-w-0 flex-auto">
|
||||
<p class="truncate sm:text-[2vw] lg:text-[1.5vw] xl:text-[1vw] font-semibold leading-6 decoration-2" :class="[ task.completed === 'true' ? 'line-through text-gray-400' : 'text-gray-900 dark:text-gray-300' ]" >{{task.name}}</p>
|
||||
<p class="truncate text-xs leading-5 text-gray-500 dark:text-gray-400">{{task.description}}</p>
|
||||
</div>
|
||||
</div>
|
||||
<div class="shrink-0 group-hover/task:opacity-100 transition-all">
|
||||
<div class="inline-flex items-center gap-x-1">
|
||||
<div title="Приоритет" class="mr-4 cursor-pointer drop-shadow-sm dark:drop-shadow-[0_1px_1px_rgba(255,255,255,0.35)]" @click="change_priority(task.id, task.name, task.priority)">
|
||||
<Icon v-if="parseInt(task.priority) === 0" class="opacity-60 dark:text-white text-gray-500" icon="streamline:signal-none-solid" width="26" height="26"/>
|
||||
<Icon v-if="parseInt(task.priority) > 0 && parseInt(task.priority) < 4" class="opacity-80 text-orange-300" icon="streamline:signal-low-solid" width="26" height="26"/>
|
||||
<Icon v-if="parseInt(task.priority) >= 4 && parseInt(task.priority) < 7" class="opacity-85 text-amber-500" icon="streamline:signal-medium-solid" width="26" height="26"/>
|
||||
<Icon v-if="parseInt(task.priority) >= 7 && parseInt(task.priority) < 9" class="opacity-90 text-orange-600" icon="streamline:signal-full-solid" width="26" height="26"/>
|
||||
<Icon v-if="parseInt(task.priority) >= 9" class="opacity-100 text-red-500" icon="heroicons-solid:exclamation" width="32" height="32"/>
|
||||
</div>
|
||||
<p class="mt-0.5 text-xs text-gray-400 mr-2 text-center">{{task.date}}<br>{{task.time}}</p>
|
||||
<button @click="edit_task(task.id, task.name, task.description, task.priority)" class="group/button border-2 rounded-lg p-1.5 border-green-400/70 hover:bg-green-400 hover:border-green-400 transition-colors"><Icon class="group-hover/button:invert dark:invert" icon="mdi:pencil" width="24" height="24" style="color: black"/></button>
|
||||
<button @click="delete_task(task.id)" class="group/button border-2 rounded-lg p-1.5 border-red-400/70 hover:bg-red-500 hover:border-red-500 transition-colors"><Icon class="group-hover/button:invert dark:invert" icon="ic:baseline-delete" width="24" height="24" style="color: black"/></button>
|
||||
</div>
|
||||
</div>
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
<div v-if="create_modal || edit_modal || priority_modal" class="bg-black w-[100vw] h-full fixed top-0 opacity-70 z-40"/>
|
||||
<CreateModal v-if="create_modal" @close="create_modal = !create_modal"/>
|
||||
<EditModal v-if="edit_modal" :task_id="to_edit_id" :task_name="to_edit_name" :task_description="to_edit_description" :task_priority="to_edit_priority" @close="edit_modal = !edit_modal"/>
|
||||
<PriorityModal v-if="priority_modal" :task_id="to_change_id" :task_name="to_change_name" :task_priority="to_change_priority" @close="priority_modal = !priority_modal"/>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<style>
|
||||
@tailwind base;
|
||||
@tailwind components;
|
||||
@tailwind utilities;
|
||||
|
||||
:root {
|
||||
overflow-x: hidden;
|
||||
font-family: Inter, Avenir, Helvetica, Arial, sans-serif;
|
||||
font-size: 16px;
|
||||
line-height: 24px;
|
||||
font-weight: 400;
|
||||
|
||||
background-color: rgb(249 250 251);
|
||||
|
||||
font-synthesis: none;
|
||||
text-rendering: optimizeLegibility;
|
||||
-webkit-font-smoothing: antialiased;
|
||||
-moz-osx-font-smoothing: grayscale;
|
||||
-webkit-text-size-adjust: 100%;
|
||||
}
|
||||
|
||||
:root.dark {
|
||||
background-color: rgb(30, 30, 30);
|
||||
}
|
||||
</style>
|
||||
<script setup>
|
||||
import {onBeforeMount, ref} from "vue";
|
||||
import {useDark} from "@vueuse/core";
|
||||
useDark();
|
||||
import {invoke} from "@tauri-apps/api/core";
|
||||
import EditModal from "./components/modals/EditModal.vue";
|
||||
import CreateModal from "./components/modals/CreateModal.vue";
|
||||
|
||||
import { getCurrentWebviewWindow } from '@tauri-apps/api/webviewWindow';
|
||||
|
||||
import PriorityModal from "./components/modals/PriorityModal.vue";
|
||||
import ApiApp from "./ApiApp.vue";
|
||||
import SubtaskViewModal from "./components/modals/subtasks/ViewModal.vue";
|
||||
import Menu from "./components/Menu.vue";
|
||||
const appWindow = getCurrentWebviewWindow()
|
||||
|
||||
|
||||
let page = ref('home');
|
||||
|
||||
let tasks = ref({});
|
||||
let pending = ref(true);
|
||||
let menu_opened = ref(false);
|
||||
|
||||
let create_modal = ref(false);
|
||||
let edit_modal = ref(false);
|
||||
let priority_modal = ref(false);
|
||||
let subtask_modal = ref(false);
|
||||
|
||||
let search_text = ref("");
|
||||
|
||||
let to_edit_id = ref("");
|
||||
let to_edit_name = ref("");
|
||||
let to_edit_description = ref("");
|
||||
let to_edit_priority = ref(0);
|
||||
|
||||
let to_change_id = ref("");
|
||||
let to_change_name = ref("");
|
||||
let to_change_priority = ref("");
|
||||
|
||||
let id_task_for_subtasks = ref("");
|
||||
|
||||
|
||||
onBeforeMount(async () => {
|
||||
await invoke('check_or_create_tasks_file')
|
||||
.then(async () => {
|
||||
await get_tasks();
|
||||
pending.value = false;
|
||||
});
|
||||
});
|
||||
|
||||
// Tasks
|
||||
function reorder_tasks(){
|
||||
tasks.value = tasks.value.sort(function (a, b) {
|
||||
if ( parseInt(a.priority) < parseInt(b.priority) ){
|
||||
return 1;
|
||||
}
|
||||
if ( parseInt(a.priority) > parseInt(b.priority) ){
|
||||
return -1;
|
||||
}
|
||||
return 0;
|
||||
});
|
||||
|
||||
tasks.value = tasks.value.sort(function (a, b) {
|
||||
if ( a.completed === 'true' && b.completed === 'false' ){
|
||||
return 1;
|
||||
}
|
||||
if ( a.completed === 'false' && b.completed === 'true' ){
|
||||
return -1;
|
||||
}
|
||||
return 0;
|
||||
});
|
||||
}
|
||||
async function get_tasks(){
|
||||
let tmp_tasks = [];
|
||||
await invoke('get_tasks').then((res) => {
|
||||
for (let value in res){
|
||||
tmp_tasks.push(res[value]);
|
||||
}
|
||||
tasks.value = tmp_tasks;
|
||||
reorder_tasks();
|
||||
});
|
||||
}
|
||||
async function search_tasks(event){
|
||||
let tmp_tasks = [];
|
||||
if (event.target.value.length > 1) {
|
||||
await invoke('search_tasks', {
|
||||
value: event.target.value
|
||||
}).then((res) => {
|
||||
for (let value in res){
|
||||
tmp_tasks.push(res[value]);
|
||||
}
|
||||
tasks.value = tmp_tasks;
|
||||
reorder_tasks();
|
||||
});
|
||||
} else{
|
||||
await get_tasks();
|
||||
}
|
||||
}
|
||||
async function set_task_field(id_task, completed){
|
||||
let field = "completed"
|
||||
id_task = id_task.toString();
|
||||
completed = completed.toString();
|
||||
if (completed === "false"){
|
||||
completed = "true"
|
||||
} else{
|
||||
completed = "false"
|
||||
}
|
||||
await invoke('set_task_field', {
|
||||
idTask: id_task,
|
||||
field: field,
|
||||
value: completed
|
||||
}).then(async () => {
|
||||
for (let index in tasks.value){
|
||||
if (tasks.value[index].id === id_task){
|
||||
tasks.value[index][field] = completed;
|
||||
break;
|
||||
}
|
||||
}
|
||||
reorder_tasks();
|
||||
});
|
||||
}
|
||||
|
||||
async function delete_task(id_task){
|
||||
id_task = id_task.toString();
|
||||
await invoke('delete_task', {
|
||||
idTask: id_task
|
||||
}).then(async () => {
|
||||
await get_tasks();
|
||||
});
|
||||
}
|
||||
function edit_task(id_task, name, description, priority){
|
||||
window.scrollTo({top:0});
|
||||
to_edit_id.value = id_task;
|
||||
to_edit_name.value = name;
|
||||
to_edit_description.value = description;
|
||||
to_edit_priority.value = priority;
|
||||
edit_modal.value = true;
|
||||
}
|
||||
|
||||
function change_priority(id_task, name, priority){
|
||||
window.scrollTo({top:0});
|
||||
to_change_id.value = id_task;
|
||||
to_change_name.value = name;
|
||||
to_change_priority.value = priority;
|
||||
priority_modal.value = true;
|
||||
}
|
||||
|
||||
// Subtasks
|
||||
function count_subtasks(subtasks){
|
||||
let completed = 0;
|
||||
Object.entries(subtasks).forEach((key) => {
|
||||
if(subtasks[key[0]].completed === "true"){
|
||||
completed++;
|
||||
}
|
||||
});
|
||||
return completed;
|
||||
}
|
||||
|
||||
function change_api(){
|
||||
if (page.value !== 'api')
|
||||
page.value = 'api';
|
||||
else
|
||||
page.value = 'home';
|
||||
}
|
||||
function change_sync(){
|
||||
if (page.value !== 'sync')
|
||||
page.value = 'sync';
|
||||
else
|
||||
page.value = 'home';
|
||||
}
|
||||
function reload_page(){
|
||||
location.reload();
|
||||
}
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<!--Title bar-->
|
||||
<div data-tauri-drag-region class="z-50 titlebar h-[30px] select-none fixed flex justify-end top-0 right-0 left-0 bg-gray-100 dark:bg-zinc-800">
|
||||
<p class="fixed left-2 top-1 dark:text-white">To Do</p>
|
||||
<div @click="appWindow.minimize()" class="titlebar-button hover:bg-gray-200 dark:hover:bg-gray-700 justify-center inline-flex w-[30px] h-[30px] items-center dark:text-white" id="titlebar-minimize">
|
||||
<img src="/icon/window/mdi--window-minimize.svg" alt="Window minimize" style="width: 20px; height: 20px" class="dark:invert">
|
||||
</div>
|
||||
<div @click="appWindow.toggleMaximize()" class="titlebar-button hover:bg-gray-200 dark:hover:bg-gray-700 justify-center inline-flex w-[30px] h-[30px] items-center dark:text-white" id="titlebar-maximize">
|
||||
<img src="/icon/window/mdi--window-maximize.svg" alt="Window maximize" style="width: 20px; height: 20px" class="dark:invert">
|
||||
</div>
|
||||
<div @click="appWindow.close()" class="titlebar-button hover:bg-gray-200 dark:hover:bg-gray-700 justify-center inline-flex w-[30px] h-[30px] items-center dark:text-white" id="titlebar-close">
|
||||
<img src="/icon/window/mdi--window-close.svg" alt="Window close" style="width: 24px; height: 24px" class="dark:invert">
|
||||
</div>
|
||||
</div>
|
||||
<!--Home-->
|
||||
<div v-if="page==='home'">
|
||||
<!--Pending logo-->
|
||||
<div v-if="pending">
|
||||
<img src="/icon/eos-icons--three-dots-loading.svg" alt="Loading" style="width: 96px; height: 96px" class="dark:invert absolute top-[50%] left-[50%] translate-x-[-50%] translate-y-[-50%]">
|
||||
</div>
|
||||
<div v-else>
|
||||
<!--Menu button-->
|
||||
<button class="absolute top-9 left-2" @click="menu_opened = true">
|
||||
<img src="/icon/mingcute--menu-fill.svg" alt="Menu" style="width: 38px; height: 38px" class="dark:invert">
|
||||
</button>
|
||||
<!--Menu-->
|
||||
<Menu v-if="menu_opened" @close="menu_opened = !menu_opened" @home="reload_page"
|
||||
@api="change_api(); menu_opened = !menu_opened" @sync="change_sync(); menu_opened = !menu_opened"/>
|
||||
<!--Search-->
|
||||
<div id="search" class="relative flex h-10 mx-[15vw] lg:mx-[25vw] mt-12">
|
||||
<div class="!absolute right-1 top-1 z-10">
|
||||
<button
|
||||
v-if="search_text !== ''"
|
||||
@click="search_text = ''; get_tasks()"
|
||||
class="p-[1px] pt-[1px] pb-[2px] mr-0.5 rounded-lg group text-gray-400 hover:text-gray-500 transition-colors"
|
||||
type="button"
|
||||
data-ripple-light="true"
|
||||
>
|
||||
<img src="/icon/window/mdi--window-close.svg" alt="Clear search" style="width: 26px; height: 26px" class="dark:invert mt-0.5">
|
||||
</button>
|
||||
</div>
|
||||
<input
|
||||
v-model="search_text"
|
||||
type="text"
|
||||
class="peer h-full w-full rounded-lg border border-blue-gray-200 bg-transparent px-3 py-2.5 pr-20 font-sans text-sm font-normal text-blue-gray-700 dark:text-white dark:border-white outline outline-0 transition-all placeholder-shown:border placeholder-shown:border-blue-gray-200 placeholder-shown:border-t-blue-gray-200 focus:border-2 focus:border-green-400 focus:border-t-transparent focus:outline-0 disabled:border-0 disabled:bg-blue-gray-50"
|
||||
:class="{'dark:border-t-[rgb(30,30,30)]': search_text.length}"
|
||||
placeholder=" "
|
||||
@input="search_tasks"
|
||||
/>
|
||||
<label class="text-gray-400 dark:text-gray-300 focus:text-black before:content[' '] after:content[' '] pointer-events-none absolute left-0 -top-1.5 flex h-full w-full select-none text-[11px] font-normal leading-tight text-blue-gray-400 transition-all before:pointer-events-none before:mt-[6.5px] before:mr-1 before:box-border before:block before:h-1.5 before:w-2.5 before:rounded-tl-md before:border-t before:border-l before:border-blue-gray-200 before:transition-all after:pointer-events-none after:mt-[6.5px] after:ml-1 after:box-border after:block after:h-1.5 after:w-2.5 after:flex-grow after:rounded-tr-lg after:border-t after:border-r after:border-blue-gray-200 after:transition-all peer-placeholder-shown:text-sm peer-placeholder-shown:leading-[3.75] peer-placeholder-shown:text-blue-gray-500 peer-placeholder-shown:before:border-transparent peer-placeholder-shown:after:border-transparent peer-focus:text-[11px] peer-focus:leading-tight peer-focus:text-green-400 peer-focus:before:border-t-2 peer-focus:before:border-l-2 peer-focus:before:!border-green-400 peer-focus:after:border-t-2 peer-focus:after:border-r-2 peer-focus:after:!border-green-400 peer-disabled:text-transparent peer-disabled:before:border-transparent peer-disabled:after:border-transparent peer-disabled:peer-placeholder-shown:text-blue-gray-500">
|
||||
Поиск...
|
||||
</label>
|
||||
</div>
|
||||
<!--Create task-->
|
||||
<button @click="create_modal = !create_modal" type="button" data-ripple-light="true"
|
||||
class="p-[1px] pt-[1px] pb-[2px] fixed top-12 right-6 border-green-500 border-2 rounded-lg group bg-green-500 hover:bg-transparent transition-colors"
|
||||
><img src="/icon/tasks/ic--baseline-plus.svg" alt="Create task" style="width: 32px; height: 32px" class="invert group-hover:invert-0 dark:group-hover:invert">
|
||||
</button>
|
||||
<!--Tasks list-->
|
||||
<div id="list" class="relative mx-[14vw] lg:mx-[22vw] mt-5">
|
||||
<ul role="list" class="divide-y divide-gray-200 dark:divide-gray-500">
|
||||
<li v-for="task in tasks" :id="task.id" class="flex justify-between gap-x-6 py-5 group/task" :class="[id_task_for_subtasks === task.id ? 'border-2 border-white' : '']">
|
||||
<div @click="set_task_field(task.id, task.completed)" class="inline-flex items-center space-x-2 min-w-0 gap-x-2 group-hover/task:cursor-pointer py-2">
|
||||
<p class="text-sm flex-none border rounded-full" :class="[ task.completed === 'true' ? 'border-green-500 group-hover:border-green-400' : 'border-gray-300 group-hover:border-gray-500' ]">
|
||||
<img src="/icon/tasks/material-symbols--check.svg" alt="Check task" style="width: 20px; height: 20px" class="m-0.5"
|
||||
:class="[ task.completed === 'true' ? 'opacity-100' : 'opacity-0' ]">
|
||||
</p>
|
||||
<div class="min-w-0 flex-auto">
|
||||
<p class="truncate sm:text-[2vw] lg:text-[1.5vw] xl:text-[1vw] font-semibold leading-6 decoration-2" :class="[ task.completed === 'true' ? 'line-through text-gray-400' : 'text-gray-900 dark:text-gray-300' ]" >{{task.name}}</p>
|
||||
<p class="truncate text-xs leading-5 text-gray-500 dark:text-gray-400">{{task.description}}</p>
|
||||
</div>
|
||||
</div>
|
||||
<div class="shrink-0 group-hover/task:opacity-100 transition-all">
|
||||
<div class="inline-flex items-center gap-x-1">
|
||||
<div class="mr-4 text-zinc-400 cursor-pointer" @click="subtask_modal = !subtask_modal; id_task_for_subtasks=task.id">
|
||||
<p v-if="Object.keys(task.subtasks).length === 0"
|
||||
class="hidden group-hover/task:flex font-bold text-xl line-through">0/0</p>
|
||||
<p v-else>
|
||||
<span class="hidden group-hover/task:flex font-bold text-xl"
|
||||
:class="[Object.keys(task.subtasks).length === count_subtasks(task.subtasks) ? 'line-through' : '']">
|
||||
{{Object.keys(task.subtasks).length}}/{{count_subtasks(task.subtasks)}}
|
||||
</span>
|
||||
</p>
|
||||
</div>
|
||||
<div class="hidden group-hover/task:flex gap-1">
|
||||
<button @click="edit_task(task.id, task.name, task.description, task.priority)" class="group/button border-2 rounded-lg p-1.5 border-green-400/70 hover:bg-green-400 hover:border-green-400 transition-colors">
|
||||
<img src="/icon/tasks/mdi--pencil.svg" alt="Edit task" style="width: 24px; height: 24px" class="dark:invert">
|
||||
</button>
|
||||
<button @click="delete_task(task.id)" class="group/button border-2 rounded-lg p-1.5 border-red-400/70 hover:bg-red-500 hover:border-red-500 transition-colors">
|
||||
<img src="/icon/tasks/ic--baseline-delete.svg" alt="Delete task" style="width: 24px; height: 24px" class="dark:invert">
|
||||
</button>
|
||||
<p class="mt-0.5 text-xs text-gray-400 mx-2 text-center">{{task.date}}<br>{{task.time}}</p>
|
||||
</div>
|
||||
<div title="Приоритет" class="mr-3 cursor-pointer drop-shadow-sm dark:drop-shadow-[0_1px_1px_rgba(255,255,255,0.35)]" @click="change_priority(task.id, task.name, task.priority)">
|
||||
<img v-if="parseInt(task.priority) === 0" src="/icon/tasks/streamline--signal-none-solid.svg" alt="Task priority" style="width: 26px; height: 26px" class="opacity-60 mr-1">
|
||||
<img v-if="parseInt(task.priority) > 0 && parseInt(task.priority) < 4" src="/icon/tasks/streamline--signal-low-solid.svg" alt="Task priority" style="width: 26px; height: 26px" class="opacity-80 mr-1">
|
||||
<img v-if="parseInt(task.priority) >= 4 && parseInt(task.priority) < 7" src="/icon/tasks/streamline--signal-medium-solid.svg" alt="Task priority" style="width: 26px; height: 26px" class="opacity-85 mr-1">
|
||||
<img v-if="parseInt(task.priority) >= 7 && parseInt(task.priority) < 9" src="/icon/tasks/streamline--signal-full-solid.svg" alt="Task priority" style="width: 26px; height: 26px" class="opacity-900 mr-1">
|
||||
<img v-if="parseInt(task.priority) >= 9" src="/icon/tasks/heroicons--exclamation-triangle-16-solid.svg" alt="Task priority" style="width: 32px; height: 32px" class="opacity-100">
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
<!--Modals-->
|
||||
<div v-if="create_modal || edit_modal || priority_modal || subtask_modal" class="bg-black w-[100vw] h-full fixed top-0 opacity-70 z-40"/>
|
||||
<CreateModal v-if="create_modal" @close="create_modal = !create_modal; get_tasks()"/>
|
||||
<EditModal v-if="edit_modal" :task_id="to_edit_id" :task_name="to_edit_name" :task_description="to_edit_description" :task_priority="to_edit_priority" @close="edit_modal = !edit_modal"/>
|
||||
<PriorityModal v-if="priority_modal" :task_id="to_change_id" :task_name="to_change_name" :task_priority="to_change_priority" @close="priority_modal = !priority_modal"/>
|
||||
<SubtaskViewModal v-if="subtask_modal" :task_id="id_task_for_subtasks" @close="subtask_modal = !subtask_modal; id_task_for_subtasks = ''; get_tasks()"/>
|
||||
</div>
|
||||
</div>
|
||||
<!--Api-->
|
||||
<div v-else-if="page==='api'">
|
||||
<ApiApp @close="change_api()">
|
||||
<!--Menu button-->
|
||||
<button class="absolute top-9 left-2" @click="menu_opened = true">
|
||||
<img src="/icon/mingcute--menu-fill.svg" alt="Menu" style="width: 38px; height: 38px" class="dark:invert">
|
||||
</button>
|
||||
<!--Menu-->
|
||||
<Menu v-if="menu_opened" @close="menu_opened = !menu_opened"
|
||||
@home="reload_page" @api="change_api" @sync="change_sync"/>
|
||||
</ApiApp>
|
||||
</div>
|
||||
<div v-else-if="page==='sync'">
|
||||
In progress...
|
||||
</div>
|
||||
<div v-else>
|
||||
What?
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<style>
|
||||
@tailwind base;
|
||||
@tailwind components;
|
||||
@tailwind utilities;
|
||||
|
||||
:root {
|
||||
overflow-x: hidden;
|
||||
font-family: Inter, Avenir, Helvetica, Arial, sans-serif;
|
||||
font-size: 16px;
|
||||
line-height: 24px;
|
||||
font-weight: 400;
|
||||
|
||||
background-color: rgb(249 250 251);
|
||||
|
||||
font-synthesis: none;
|
||||
text-rendering: optimizeLegibility;
|
||||
-webkit-font-smoothing: antialiased;
|
||||
-moz-osx-font-smoothing: grayscale;
|
||||
-webkit-text-size-adjust: 100%;
|
||||
}
|
||||
|
||||
:root.dark {
|
||||
background-color: rgb(30, 30, 30);
|
||||
}
|
||||
</style>
|
||||
|
||||
@@ -1,12 +1,12 @@
|
||||
<template>
|
||||
<details class="group pt-1 text-md border-gray-400 open:border open:px-1 open:py-1">
|
||||
<summary class="flex cursor-pointer flex-row items-center justify-between py-1 font-semibold text-gray-800 marker:[font-size:0px]">
|
||||
<p class="mx-1 dark:text-gray-100 inline-flex items-center"><slot name="title"/></p>
|
||||
<svg class="h-6 w-6 rotate-0 transform text-gray-400 group-open:rotate-180" xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" stroke-width="2" stroke="currentColor" aria-hidden="true">
|
||||
<path stroke-linecap="round" stroke-linejoin="round" d="M19 9l-7 7-7-7"></path>
|
||||
</svg>
|
||||
</summary>
|
||||
<p class="mx-2 text-gray-700 dark:text-gray-200"><slot name="body"/></p>
|
||||
</details>
|
||||
<hr class="border-gray-400">
|
||||
<template>
|
||||
<details class="group pt-1 text-md border-gray-400 open:border open:px-1 open:py-1">
|
||||
<summary class="flex cursor-pointer flex-row items-center justify-between py-1 font-semibold text-gray-800 marker:[font-size:0px]">
|
||||
<p class="mx-1 dark:text-gray-100 inline-flex items-center"><slot name="title"/></p>
|
||||
<svg class="h-6 w-6 rotate-0 transform text-gray-400 group-open:rotate-180" xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" stroke-width="2" stroke="currentColor" aria-hidden="true">
|
||||
<path stroke-linecap="round" stroke-linejoin="round" d="M19 9l-7 7-7-7"></path>
|
||||
</svg>
|
||||
</summary>
|
||||
<p class="mx-2 text-gray-700 dark:text-gray-200"><slot name="body"/></p>
|
||||
</details>
|
||||
<hr class="border-gray-400">
|
||||
</template>
|
||||
@@ -1,35 +1,35 @@
|
||||
<script setup>
|
||||
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<div id="create-task-modal" tabindex="-1" aria-hidden="true" class="absolute left-[50%] translate-x-[-50%] overflow-y-auto overflow-x-hidden w-50 top-20 lg:top-32 z-50">
|
||||
<div class="relative p-4 max-h-full">
|
||||
<!-- Modal content -->
|
||||
<div class="relative bg-white rounded-lg shadow dark:bg-[rgb(50,50,50)]">
|
||||
<!-- Modal header -->
|
||||
<div class="flex items-center justify-between px-5 py-2 md:py-3 border-b rounded-t">
|
||||
<h3 class="text-xl font-semibold text-gray-700 dark:text-gray-100 mr-2">
|
||||
Вы уверены?
|
||||
</h3>
|
||||
<button type="button" @click="$emit('close')" class="transition-colors text-gray-400 dark:text-gray-300 bg-transparent hover:bg-gray-200 hover:text-gray-900 rounded-lg text-sm w-8 h-8 ms-auto inline-flex justify-center items-center" data-modal-hide="default-modal">
|
||||
<svg class="w-3 h-3" aria-hidden="true" xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 14 14">
|
||||
<path stroke="currentColor" stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="m1 1 6 6m0 0 6 6M7 7l6-6M7 7l-6 6"/>
|
||||
</svg>
|
||||
</button>
|
||||
</div>
|
||||
<!-- Modal body
|
||||
<div class="p-4 md:p-5 space-y-4"></div>
|
||||
Modal footer -->
|
||||
<div class="flex items-center py-3 px-5 md:p-3 border-t border-gray-200 rounded-b">
|
||||
<button @click="$emit('yes')" class="text-white bg-red-500 hover:bg-red-600 transition-colors font-medium rounded-lg text-sm px-5 py-2.5 text-center">Да</button>
|
||||
<button @click="$emit('close')" class="ml-2 bg-zinc-200 hover:bg-zinc-300 text-black transition-colors font-medium rounded-lg text-sm px-5 py-2.5 text-center">Нет</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<style scoped>
|
||||
|
||||
<script setup>
|
||||
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<div id="create-task-modal" tabindex="-1" aria-hidden="true" class="absolute left-[50%] translate-x-[-50%] overflow-y-auto overflow-x-hidden w-50 top-20 lg:top-32 z-50">
|
||||
<div class="relative p-4 max-h-full">
|
||||
<!-- Modal content -->
|
||||
<div class="relative bg-white rounded-lg shadow dark:bg-[rgb(50,50,50)]">
|
||||
<!-- Modal header -->
|
||||
<div class="flex items-center justify-between px-5 py-2 md:py-3 border-b rounded-t">
|
||||
<h3 class="text-xl font-semibold text-gray-700 dark:text-gray-100 mr-2">
|
||||
Вы уверены?
|
||||
</h3>
|
||||
<button type="button" @click="$emit('close')" class="transition-colors text-gray-400 dark:text-gray-300 bg-transparent hover:bg-gray-200 hover:text-gray-900 rounded-lg text-sm w-8 h-8 ms-auto inline-flex justify-center items-center" data-modal-hide="default-modal">
|
||||
<svg class="w-3 h-3" aria-hidden="true" xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 14 14">
|
||||
<path stroke="currentColor" stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="m1 1 6 6m0 0 6 6M7 7l6-6M7 7l-6 6"/>
|
||||
</svg>
|
||||
</button>
|
||||
</div>
|
||||
<!-- Modal body
|
||||
<div class="p-4 md:p-5 space-y-4"></div>
|
||||
Modal footer -->
|
||||
<div class="flex items-center py-3 px-5 md:p-3 border-t border-gray-200 rounded-b">
|
||||
<button @click="$emit('yes')" class="text-white bg-red-500 hover:bg-red-600 transition-colors font-medium rounded-lg text-sm px-5 py-2.5 text-center">Да</button>
|
||||
<button @click="$emit('close')" class="ml-2 bg-zinc-200 hover:bg-zinc-300 text-black transition-colors font-medium rounded-lg text-sm px-5 py-2.5 text-center">Нет</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<style scoped>
|
||||
|
||||
</style>
|
||||
@@ -1,81 +1,81 @@
|
||||
<script setup>
|
||||
import {ref} from "vue";
|
||||
import {invoke} from "@tauri-apps/api";
|
||||
|
||||
const props = defineProps({
|
||||
port: Number,
|
||||
autostart: Boolean
|
||||
})
|
||||
|
||||
const emit = defineEmits(['close'])
|
||||
|
||||
let port = ref(props.port);
|
||||
let autostart = ref(props.autostart);
|
||||
|
||||
let error = ref(" ");
|
||||
|
||||
async function save_settings(){
|
||||
if (port.value < 1 || port.value > 65536){
|
||||
error.value = "Номер порта не может быть меньше 1 или больше 65536"
|
||||
} else {
|
||||
await invoke('edit_settings', {
|
||||
port: port.value,
|
||||
autostart: autostart.value
|
||||
}).then(() => {
|
||||
emit('close');
|
||||
})
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<div id="create-task-modal" tabindex="-1" aria-hidden="true" class="px-[5vw] md:px-[10vw] lg:px-[15vw] absolute overflow-y-auto overflow-x-hidden w-full top-20 lg:top-24 z-50">
|
||||
<div class="relative p-4 max-h-full">
|
||||
<!-- Modal content -->
|
||||
<form class="relative bg-white rounded-lg shadow dark:bg-[rgb(50,50,50)]" @submit.prevent="save_settings">
|
||||
<!-- Modal header -->
|
||||
<div class="flex items-center justify-between px-5 py-2 md:py-3 border-b rounded-t">
|
||||
<h3 class="text-xl font-semibold text-gray-700 dark:text-gray-100">
|
||||
Настройки
|
||||
</h3>
|
||||
<button type="button" @click="$emit('close')" class="transition-colors text-gray-400 dark:text-gray-300 bg-transparent hover:bg-gray-200 hover:text-gray-900 rounded-lg text-sm w-8 h-8 ms-auto inline-flex justify-center items-center" data-modal-hide="default-modal">
|
||||
<svg class="w-3 h-3" aria-hidden="true" xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 14 14">
|
||||
<path stroke="currentColor" stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="m1 1 6 6m0 0 6 6M7 7l6-6M7 7l-6 6"/>
|
||||
</svg>
|
||||
</button>
|
||||
</div>
|
||||
<!-- Modal body -->
|
||||
<div class="px-4 md:px-5 pt-4">
|
||||
<div>
|
||||
<div>
|
||||
<label id="autostart" class="items-center me-5 cursor-pointer">
|
||||
<p class="text-sm font-medium text-gray-900 dark:text-gray-200 mb-0.5">Запуск при старте</p>
|
||||
<input v-model="autostart" type="checkbox" value="" class="sr-only peer">
|
||||
<div class="relative w-11 h-6 bg-gray-200 rounded-full peer dark:bg-gray-700
|
||||
peer-checked:after:translate-x-full rtl:peer-checked:after:-translate-x-full
|
||||
peer-checked:after:border-white after:content-[''] after:absolute after:top-0.5 after:start-[2px]
|
||||
after:bg-white after:border-gray-300 after:border after:rounded-full after:h-5 after:w-5
|
||||
after:transition-all dark:border-gray-600 peer-checked:bg-green-600"/>
|
||||
</label>
|
||||
<div class="flex items-center justify-between">
|
||||
<label for="port" class="block text-sm font-medium leading-6 text-gray-900 dark:text-gray-200">Порт</label>
|
||||
</div>
|
||||
<div class="mb-3">
|
||||
<input @input="error = ' '" v-model="port" type="number" id="port" name="port" placeholder="1-65536" class="px-2 py-1.5 block w-48 rounded-md border-0 text-gray-900 shadow-sm ring-1 ring-inset ring-gray-300 placeholder:text-gray-400 focus:ring-inset sm:text-sm sm:leading-6" />
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<!-- Modal footer -->
|
||||
<div class="flex items-center py-1 px-5 md:p-3 border-t border-gray-200 rounded-b">
|
||||
<input type="submit" class="text-white bg-green-500 hover:bg-green-600 transition-colors font-medium rounded-lg text-sm px-5 py-2.5 text-center" value="Сохранить">
|
||||
<p class="ml-3 text-red-500">{{error}}</p>
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<style scoped>
|
||||
|
||||
<script setup>
|
||||
import {ref} from "vue";
|
||||
import {invoke} from "@tauri-apps/api/core";
|
||||
|
||||
const props = defineProps({
|
||||
port: Number,
|
||||
autostart: Boolean
|
||||
})
|
||||
|
||||
const emit = defineEmits(['close'])
|
||||
|
||||
let port = ref(props.port);
|
||||
let autostart = ref(props.autostart);
|
||||
|
||||
let error = ref(" ");
|
||||
|
||||
async function save_settings(){
|
||||
if (port.value < 1 || port.value > 65536){
|
||||
error.value = "Номер порта не может быть меньше 1 или больше 65536"
|
||||
} else {
|
||||
await invoke('edit_settings', {
|
||||
port: port.value,
|
||||
autostart: autostart.value
|
||||
}).then(() => {
|
||||
emit('close');
|
||||
})
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<div id="create-task-modal" tabindex="-1" aria-hidden="true" class="px-[5vw] md:px-[10vw] lg:px-[15vw] absolute overflow-y-auto overflow-x-hidden w-full top-20 lg:top-24 z-50">
|
||||
<div class="relative p-4 max-h-full">
|
||||
<!-- Modal content -->
|
||||
<form class="relative bg-white rounded-lg shadow dark:bg-[rgb(50,50,50)]" @submit.prevent="save_settings">
|
||||
<!-- Modal header -->
|
||||
<div class="flex items-center justify-between px-5 py-2 md:py-3 border-b rounded-t">
|
||||
<h3 class="text-xl font-semibold text-gray-700 dark:text-gray-100">
|
||||
Настройки
|
||||
</h3>
|
||||
<button type="button" @click="$emit('close')" class="transition-colors text-gray-400 dark:text-gray-300 bg-transparent hover:bg-gray-200 hover:text-gray-900 rounded-lg text-sm w-8 h-8 ms-auto inline-flex justify-center items-center" data-modal-hide="default-modal">
|
||||
<svg class="w-3 h-3" aria-hidden="true" xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 14 14">
|
||||
<path stroke="currentColor" stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="m1 1 6 6m0 0 6 6M7 7l6-6M7 7l-6 6"/>
|
||||
</svg>
|
||||
</button>
|
||||
</div>
|
||||
<!-- Modal body -->
|
||||
<div class="px-4 md:px-5 pt-4">
|
||||
<div>
|
||||
<div>
|
||||
<label id="autostart" class="items-center me-5 cursor-pointer">
|
||||
<p class="text-sm font-medium text-gray-900 dark:text-gray-200 mb-0.5">Запуск при старте</p>
|
||||
<input v-model="autostart" type="checkbox" value="" class="sr-only peer">
|
||||
<div class="relative w-11 h-6 bg-gray-200 rounded-full peer dark:bg-gray-700
|
||||
peer-checked:after:translate-x-full rtl:peer-checked:after:-translate-x-full
|
||||
peer-checked:after:border-white after:content-[''] after:absolute after:top-0.5 after:start-[2px]
|
||||
after:bg-white after:border-gray-300 after:border after:rounded-full after:h-5 after:w-5
|
||||
after:transition-all dark:border-gray-600 peer-checked:bg-green-600"/>
|
||||
</label>
|
||||
<div class="flex items-center justify-between">
|
||||
<label for="port" class="block text-sm font-medium leading-6 text-gray-900 dark:text-gray-200">Порт</label>
|
||||
</div>
|
||||
<div class="mb-3">
|
||||
<input @input="error = ' '" v-model="port" type="number" id="port" name="port" placeholder="1-65536" class="px-2 py-1.5 block w-48 rounded-md border-0 text-gray-900 shadow-sm ring-1 ring-inset ring-gray-300 placeholder:text-gray-400 focus:ring-inset sm:text-sm sm:leading-6" />
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<!-- Modal footer -->
|
||||
<div class="flex items-center py-1 px-5 md:p-3 border-t border-gray-200 rounded-b">
|
||||
<input type="submit" class="text-white bg-green-500 hover:bg-green-600 transition-colors font-medium rounded-lg text-sm px-5 py-2.5 text-center" value="Сохранить">
|
||||
<p class="ml-3 text-red-500">{{error}}</p>
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<style scoped>
|
||||
|
||||
</style>
|
||||
@@ -1,13 +1,9 @@
|
||||
<script setup>
|
||||
import {Icon} from "@iconify/vue";
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<div class="border-zinc-400 dark:border-zinc-200 border bg-zinc-300 dark:bg-zinc-700 rounded-2xl p-2 mx-1 my-2 dark:text-gray-100">
|
||||
<div class="flex">
|
||||
<Icon icon="material-symbols:code" width="26" height="26"/>
|
||||
<p class="text-md ml-1 mt-0.5"><slot name="title"/></p>
|
||||
</div>
|
||||
<p class="text-lg m-1 dark:text-gray-100 whitespace-pre-wrap"><slot name="body"/></p>
|
||||
</div>
|
||||
<template>
|
||||
<div class="border-zinc-400 dark:border-zinc-200 border bg-zinc-300 dark:bg-zinc-700 rounded-2xl p-2 mx-1 my-2 dark:text-gray-100">
|
||||
<div class="flex">
|
||||
<img src="/icon/api/wiki/material-symbols--code.svg" alt="Code" style="width: 26px; height: 26px" class="dark:invert">
|
||||
<p class="text-md ml-1 mt-0.5"><slot name="title"/></p>
|
||||
</div>
|
||||
<p class="text-lg m-1 dark:text-gray-100 whitespace-pre-wrap"><slot name="body"/></p>
|
||||
</div>
|
||||
</template>
|
||||
@@ -1,13 +1,9 @@
|
||||
<script setup>
|
||||
import {Icon} from "@iconify/vue";
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<div class="border-yellow-500 dark:border-yellow-600 border bg-yellow-50 dark:bg-gray-700 rounded-2xl p-2 m-2 text-yellow-600 dark:text-yellow-300">
|
||||
<div class="flex">
|
||||
<Icon icon="ic:round-warning" width="32" height="32"/>
|
||||
<p class="text-xl ml-1 mt-0.5"><slot name="title"/></p>
|
||||
</div>
|
||||
<p class="text-lg m-1"><slot name="body"/></p>
|
||||
</div>
|
||||
<template>
|
||||
<div class="border-yellow-500 dark:border-yellow-600 border bg-yellow-50 dark:bg-gray-700 rounded-2xl p-2 m-2 text-yellow-600 dark:text-yellow-300">
|
||||
<div class="flex">
|
||||
<img src="/icon/api/wiki/ic--round-warning.svg" alt="Warning" style="width: 32px; height: 32px">
|
||||
<p class="text-xl ml-1 mt-0.5"><slot name="title"/></p>
|
||||
</div>
|
||||
<p class="text-lg m-1"><slot name="body"/></p>
|
||||
</div>
|
||||
</template>
|
||||
@@ -1,376 +1,377 @@
|
||||
<script setup>
|
||||
import {ref} from "vue";
|
||||
import {Icon} from "@iconify/vue";
|
||||
import Warning from "../api_components/notes/Warning.vue";
|
||||
import AccordionElement from "../api_components/AccordionElement.vue";
|
||||
import Code from "../api_components/notes/Code.vue";
|
||||
|
||||
let page = ref('main');
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<div class="absolute top-11 left-[50%] translate-x-[-50%] flex text-lg dark:text-white gap-6">
|
||||
<div @click="page = 'main'" title="Основы" class="cursor-pointer border-b-2" :class="[page === 'main' ? 'border-b-black dark:border-b-white': 'border-b-transparent']">
|
||||
<Icon icon="material-symbols:home" width="40" height="40"/>
|
||||
</div>
|
||||
<div @click="page = 'commands'" title="Команды" class="cursor-pointer border-b-2" :class="[page === 'commands' ? 'border-b-black dark:border-b-white': 'border-b-transparent']">
|
||||
<Icon icon="ri:slash-commands-2" width="38" height="38"/>
|
||||
</div>
|
||||
<div @click="page = 'examples'" title="Примеры" class="cursor-pointer border-b-2" :class="[page === 'examples' ? 'border-b-black dark:border-b-white': 'border-b-transparent']">
|
||||
<Icon icon="majesticons:list-box" width="40" height="40"/>
|
||||
</div>
|
||||
</div>
|
||||
<div class="mt-24 mx-8 dark:text-white">
|
||||
<div v-if="page === 'main'" class="lg:mx-12 lg:mt-32 xl:mx-24 xl:mt-48">
|
||||
<p class="text-2xl ml-4 leading-9">Что это такое?</p>
|
||||
<hr>
|
||||
<p class="text-lg ml-2 mt-2 leading-8">
|
||||
<span class="font-bold">API</span>(application programming interface) - описание способов, взаимодействия компьютерной программы с другими.
|
||||
API (интерфейс программирования приложения) упрощает процесс программирования при создании приложений, предоставляя только объекты или действия, необходимые разработчику.
|
||||
</p>
|
||||
<Warning>
|
||||
<template v-slot:title>Внимание!</template>
|
||||
<template v-slot:body>
|
||||
Эта программа предоставляет API с защищённым соединением с помощью AES256.
|
||||
Подключение без защиты не удастся. Возможно будет добавлена поддержка незащищённого соединения.
|
||||
</template>
|
||||
</Warning>
|
||||
<hr>
|
||||
<p class="text-lg ml-2 mt-2 leading-10">
|
||||
<span class="flex">Доступные команды описаны на вкладке "Команды" - <Icon class="ml-2" icon="ri:slash-commands-2" width="38" height="38"/></span>
|
||||
<span class="flex">Примеры описаны на вкладке "Примеры" - <Icon class="ml-2" icon="majesticons:list-box" width="40" height="40"/></span>
|
||||
</p>
|
||||
</div>
|
||||
<div v-if="page === 'commands'" class="lg:mx-12 lg:mt-32 xl:mx-24 xl:mt-48">
|
||||
<p class="text-2xl ml-4 leading-9">Доступные команды</p>
|
||||
|
||||
<div class="px-3 mx-6 mt-0 grid grid-cols-1 gap-y-1">
|
||||
<AccordionElement>
|
||||
<template v-slot:title>
|
||||
<div class="text-white bg-blue-400 rounded-lg py-1 px-2 mr-2">GET</div>
|
||||
status
|
||||
</template>
|
||||
<template v-slot:body>
|
||||
<span class="ml-6 text-lg">Функция для проверки статуса сервера</span>
|
||||
<Code>
|
||||
<template v-slot:title>Структура запроса</template>
|
||||
<template v-slot:body>
|
||||
{ <br>
|
||||
 "command": "status" <br>
|
||||
} <br>
|
||||
</template>
|
||||
</Code>
|
||||
<div class="border border-zinc-400 rounded-xl p-2 mx-1 my-2">
|
||||
<p class="text-lg">Параметры</p>
|
||||
<hr>
|
||||
<div class="mx-3">
|
||||
<div class="my-3">
|
||||
<p>
|
||||
command <span class="text-red-500 text-sm">*обязательно</span><br>
|
||||
название команды <span class="opacity-80">[String]</span>
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="border border-zinc-400 rounded-xl p-2 mx-1 my-2">
|
||||
<p class="text-lg">Результат</p>
|
||||
<hr>
|
||||
<div class="mx-3">
|
||||
<div class="my-2">
|
||||
<p>
|
||||
200 - сервер работает <br>
|
||||
*нет доступа* - сервер не работает <br>
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
</AccordionElement>
|
||||
<AccordionElement>
|
||||
<template v-slot:title>
|
||||
<div class="text-white bg-blue-400 rounded-lg py-1 px-2 mr-2">GET</div>
|
||||
get_tasks
|
||||
</template>
|
||||
<template v-slot:body>
|
||||
<span class="ml-6 text-lg">Функция для получения всех задач</span>
|
||||
<Code>
|
||||
<template v-slot:title>Структура запроса</template>
|
||||
<template v-slot:body>
|
||||
{ <br>
|
||||
 "command": "get_tasks" <br>
|
||||
} <br>
|
||||
</template>
|
||||
</Code>
|
||||
<div class="border border-zinc-400 rounded-xl p-2 mx-1 my-2">
|
||||
<p class="text-lg">Параметры</p>
|
||||
<hr>
|
||||
<div class="mx-3">
|
||||
<div class="my-3">
|
||||
<p>
|
||||
command <span class="text-red-500 text-sm">*обязательно</span><br>
|
||||
название команды <span class="opacity-80">[String]</span>
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="border border-zinc-400 rounded-xl p-2 mx-1 my-2">
|
||||
<p class="text-lg">Результат</p>
|
||||
<hr>
|
||||
<div class="mx-3">
|
||||
<div class="my-2">
|
||||
<p>
|
||||
JSON - список задач <br>
|
||||
400 - неверные параметры запроса <br>
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
</AccordionElement>
|
||||
<AccordionElement>
|
||||
<template v-slot:title>
|
||||
<div class="text-white bg-blue-400 rounded-lg py-1 px-2 mr-2">GET</div>
|
||||
get_task_by_id
|
||||
</template>
|
||||
<template v-slot:body>
|
||||
<span class="ml-6 text-lg">Функция для получения задачи по id</span>
|
||||
<Code>
|
||||
<template v-slot:title>Структура запроса</template>
|
||||
<template v-slot:body>
|
||||
{ <br>
|
||||
 "command": "get_task_by_id" <br>
|
||||
 "id": "id задачи" <br>
|
||||
} <br>
|
||||
</template>
|
||||
</Code>
|
||||
<div class="border border-zinc-400 rounded-xl p-2 mx-1 my-2">
|
||||
<p class="text-lg">Параметры</p>
|
||||
<hr>
|
||||
<div class="mx-3">
|
||||
<div class="my-3">
|
||||
<p>
|
||||
command <span class="text-red-500 text-sm">*обязательно</span><br>
|
||||
название команды <span class="opacity-80">[String]</span><br>
|
||||
id <span class="text-red-500 text-sm">*обязательно</span><br>
|
||||
id задачи <span class="opacity-80">[String]</span><br>
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="border border-zinc-400 rounded-xl p-2 mx-1 my-2">
|
||||
<p class="text-lg">Результат</p>
|
||||
<hr>
|
||||
<div class="mx-3">
|
||||
<div class="my-2">
|
||||
<p>
|
||||
JSON - задача <br>
|
||||
null - задача не найдена <br>
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
</AccordionElement>
|
||||
<AccordionElement>
|
||||
<template v-slot:title>
|
||||
<div class="text-white bg-green-500 rounded-lg py-1 px-2 mr-2">POST</div>
|
||||
create_task
|
||||
</template>
|
||||
<template v-slot:body>
|
||||
<span class="ml-6 text-lg">Функция для создания задачи</span>
|
||||
<Code>
|
||||
<template v-slot:title>Структура запроса</template>
|
||||
<template v-slot:body>
|
||||
{ <br>
|
||||
 "command": "get_task_by_id" <br>
|
||||
 "task": { <br>
|
||||
  "name": "Новая задача" <br>
|
||||
  "description": "Описание" <br>
|
||||
  "priority": "4" <br>
|
||||
 } <br>
|
||||
} <br>
|
||||
</template>
|
||||
</Code>
|
||||
<div class="border border-zinc-400 rounded-xl p-2 mx-1 my-2">
|
||||
<p class="text-lg">Параметры</p>
|
||||
<hr>
|
||||
<div class="mx-3">
|
||||
<div class="my-3">
|
||||
<p>
|
||||
command <span class="text-red-500 text-sm">*обязательно</span><br>
|
||||
название команды <span class="opacity-80">[String]</span><br>
|
||||
task <span class="text-red-500 text-sm">*обязательно</span><br>
|
||||
задача <span class="opacity-80">[JSON Object]</span><br>
|
||||
 name <span class="text-red-500 text-sm">*обязательно</span><br>
|
||||
  название задачи <span class="opacity-80">[String]</span><br>
|
||||
 description<br>
|
||||
  описание задачи <span class="opacity-80">[String]</span><br>
|
||||
 priority<br>
|
||||
  проритет задачи (0-10) <span class="opacity-80">[String]</span><br>
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="border border-zinc-400 rounded-xl p-2 mx-1 my-2">
|
||||
<p class="text-lg">Результат</p>
|
||||
<hr>
|
||||
<div class="mx-3">
|
||||
<div class="my-2">
|
||||
<p>
|
||||
String - id, созданной задачи <br>
|
||||
400 - неверный параметр запроса <br>
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
</AccordionElement>
|
||||
<AccordionElement>
|
||||
<template v-slot:title>
|
||||
<div class="text-white bg-yellow-500 rounded-lg py-1 px-2 mr-2">PUT</div>
|
||||
edit_task
|
||||
</template>
|
||||
<template v-slot:body>
|
||||
<span class="ml-6 text-lg">Функция для изменения задачи по id</span>
|
||||
<Code>
|
||||
<template v-slot:title>Структура запроса</template>
|
||||
<template v-slot:body>
|
||||
{ <br>
|
||||
 "command": "edit_task" <br>
|
||||
 "task": { <br>
|
||||
  "id": "2" <br>
|
||||
  "name": "Новая задача" <br>
|
||||
  "description": "Описание" <br>
|
||||
  "priority": "4" <br>
|
||||
 } <br>
|
||||
} <br>
|
||||
</template>
|
||||
</Code>
|
||||
<div class="border border-zinc-400 rounded-xl p-2 mx-1 my-2">
|
||||
<p class="text-lg">Параметры</p>
|
||||
<hr>
|
||||
<div class="mx-3">
|
||||
<div class="my-3">
|
||||
<p>
|
||||
command <span class="text-red-500 text-sm">*обязательно</span><br>
|
||||
название команды <span class="opacity-80">[String]</span><br>
|
||||
task <span class="text-red-500 text-sm">*обязательно</span><br>
|
||||
задача <span class="opacity-80">[JSON Object]</span><br>
|
||||
 id <span class="text-red-500 text-sm">*обязательно</span><br>
|
||||
  id задачи <span class="opacity-80">[String]</span><br>
|
||||
 name <span class="text-red-500 text-sm">*обязательно</span><br>
|
||||
  название задачи <span class="opacity-80">[String]</span><br>
|
||||
 description<br>
|
||||
  описание задачи <span class="opacity-80">[String]</span><br>
|
||||
 priority<br>
|
||||
  проритет задачи (0-10) <span class="opacity-80">[String]</span><br>
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="border border-zinc-400 rounded-xl p-2 mx-1 my-2">
|
||||
<p class="text-lg">Результат</p>
|
||||
<hr>
|
||||
<div class="mx-3">
|
||||
<div class="my-2">
|
||||
<p>
|
||||
200 - задача изменена <br>
|
||||
400 - неверный параметр запроса <br>
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
</AccordionElement>
|
||||
<AccordionElement>
|
||||
<template v-slot:title>
|
||||
<div class="text-white bg-red-400 rounded-lg py-1 px-2 mr-2">DELETE</div>
|
||||
delete_task
|
||||
</template>
|
||||
<template v-slot:body>
|
||||
<span class="ml-6 text-lg">Функция для удаления задачи по id</span>
|
||||
<Code>
|
||||
<template v-slot:title>Структура запроса</template>
|
||||
<template v-slot:body>
|
||||
{ <br>
|
||||
 "command": "delete_task" <br>
|
||||
 "id": "id задачи" <br>
|
||||
} <br>
|
||||
</template>
|
||||
</Code>
|
||||
<div class="border border-zinc-400 rounded-xl p-2 mx-1 my-2">
|
||||
<p class="text-lg">Параметры</p>
|
||||
<hr>
|
||||
<div class="mx-3">
|
||||
<div class="my-3">
|
||||
<p>
|
||||
command <span class="text-red-500 text-sm">*обязательно</span><br>
|
||||
название команды <span class="opacity-80">[String]</span><br>
|
||||
id <span class="text-red-500 text-sm">*обязательно</span><br>
|
||||
id задачи <span class="opacity-80">[String]</span><br>
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="border border-zinc-400 rounded-xl p-2 mx-1 my-2">
|
||||
<p class="text-lg">Результат</p>
|
||||
<hr>
|
||||
<div class="mx-3">
|
||||
<div class="my-2">
|
||||
<p>
|
||||
200 - задача удалена <br>
|
||||
400 - неверный параметр запроса <br>
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
</AccordionElement>
|
||||
<AccordionElement>
|
||||
<template v-slot:title>
|
||||
<div class="text-white bg-green-500 rounded-lg py-1 px-2 mr-2">POST</div>
|
||||
stop
|
||||
</template>
|
||||
<template v-slot:body>
|
||||
<span class="ml-6 text-lg">Функция для выключения API</span>
|
||||
<Code>
|
||||
<template v-slot:title>Структура запроса</template>
|
||||
<template v-slot:body>
|
||||
{ <br>
|
||||
 "command": "stop" <br>
|
||||
} <br>
|
||||
</template>
|
||||
</Code>
|
||||
<div class="border border-zinc-400 rounded-xl p-2 mx-1 my-2">
|
||||
<p class="text-lg">Параметры</p>
|
||||
<hr>
|
||||
<div class="mx-3">
|
||||
<div class="my-3">
|
||||
<p>
|
||||
command <span class="text-red-500 text-sm">*обязательно</span><br>
|
||||
название команды <span class="opacity-80">[String]</span><br>
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="border border-zinc-400 rounded-xl p-2 mx-1 my-2">
|
||||
<p class="text-lg">Результат</p>
|
||||
<hr>
|
||||
<div class="mx-3">
|
||||
<div class="my-2">
|
||||
<p>
|
||||
200 - API выключается <br>
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
</AccordionElement>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<style scoped>
|
||||
|
||||
<script setup>
|
||||
import {ref} from "vue";
|
||||
import Warning from "../api_components/notes/Warning.vue";
|
||||
import AccordionElement from "../api_components/AccordionElement.vue";
|
||||
import Code from "../api_components/notes/Code.vue";
|
||||
|
||||
let page = ref('main');
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<div class="absolute top-11 left-[50%] translate-x-[-50%] flex text-lg dark:text-white gap-6">
|
||||
<div @click="page = 'main'" title="Основы" class="cursor-pointer border-b-2" :class="[page === 'main' ? 'border-b-black dark:border-b-white': 'border-b-transparent']">
|
||||
<img src="/icon/api/wiki/material-symbols--home.svg" alt="Home page" style="width: 40px; height: 40px" class="dark:invert">
|
||||
</div>
|
||||
<div @click="page = 'commands'" title="Команды" class="cursor-pointer border-b-2" :class="[page === 'commands' ? 'border-b-black dark:border-b-white': 'border-b-transparent']">
|
||||
<img src="/icon/api/wiki/ri--slash-commands-2.svg" alt="Commands page" style="width: 38px; height: 38px" class="dark:invert">
|
||||
</div>
|
||||
<div @click="page = 'examples'" title="Примеры" class="cursor-pointer border-b-2" :class="[page === 'examples' ? 'border-b-black dark:border-b-white': 'border-b-transparent']">
|
||||
<img src="/icon/api/wiki/majesticons--list-box.svg" alt="Code page" style="width: 40px; height: 40px" class="dark:invert">
|
||||
</div>
|
||||
</div>
|
||||
<div class="mt-24 mx-8 dark:text-white">
|
||||
<div v-if="page === 'main'" class="lg:mx-12 lg:mt-32 xl:mx-24 xl:mt-48">
|
||||
<p class="text-2xl ml-4 leading-9">Что это такое?</p>
|
||||
<hr>
|
||||
<p class="text-lg ml-2 mt-2 leading-8">
|
||||
<span class="font-bold">API</span>(application programming interface) - описание способов, взаимодействия компьютерной программы с другими.
|
||||
API (интерфейс программирования приложения) упрощает процесс программирования при создании приложений, предоставляя только объекты или действия, необходимые разработчику.
|
||||
</p>
|
||||
<Warning>
|
||||
<template v-slot:title>Внимание!</template>
|
||||
<template v-slot:body>
|
||||
Эта программа предоставляет API с защищённым соединением с помощью AES256.
|
||||
Подключение без защиты не удастся. Возможно будет добавлена поддержка незащищённого соединения.
|
||||
</template>
|
||||
</Warning>
|
||||
<hr>
|
||||
<p class="text-lg ml-2 mt-2 leading-10">
|
||||
<span class="flex">Доступные команды описаны на вкладке "Команды" -
|
||||
<img src="/icon/api/wiki/ri--slash-commands-2.svg" alt="Commands page" style="width: 38px; height: 38px" class="dark:invert"></span>
|
||||
<span class="flex">Примеры описаны на вкладке "Примеры" -
|
||||
<img src="/icon/api/wiki/majesticons--list-box.svg" alt="Code page" style="width: 40px; height: 40px" class="dark:invert"></span>
|
||||
</p>
|
||||
</div>
|
||||
<div v-if="page === 'commands'" class="lg:mx-12 lg:mt-32 xl:mx-24 xl:mt-48">
|
||||
<p class="text-2xl ml-4 leading-9">Доступные команды</p>
|
||||
|
||||
<div class="px-3 mx-6 mt-0 grid grid-cols-1 gap-y-1">
|
||||
<AccordionElement>
|
||||
<template v-slot:title>
|
||||
<div class="text-white bg-blue-400 rounded-lg py-1 px-2 mr-2">GET</div>
|
||||
status
|
||||
</template>
|
||||
<template v-slot:body>
|
||||
<span class="ml-6 text-lg">Функция для проверки статуса сервера</span>
|
||||
<Code>
|
||||
<template v-slot:title>Структура запроса</template>
|
||||
<template v-slot:body>
|
||||
{ <br>
|
||||
 "command": "status" <br>
|
||||
} <br>
|
||||
</template>
|
||||
</Code>
|
||||
<div class="border border-zinc-400 rounded-xl p-2 mx-1 my-2">
|
||||
<p class="text-lg">Параметры</p>
|
||||
<hr>
|
||||
<div class="mx-3">
|
||||
<div class="my-3">
|
||||
<p>
|
||||
command <span class="text-red-500 text-sm">*обязательно</span><br>
|
||||
название команды <span class="opacity-80">[String]</span>
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="border border-zinc-400 rounded-xl p-2 mx-1 my-2">
|
||||
<p class="text-lg">Результат</p>
|
||||
<hr>
|
||||
<div class="mx-3">
|
||||
<div class="my-2">
|
||||
<p>
|
||||
200 - сервер работает <br>
|
||||
*нет доступа* - сервер не работает <br>
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
</AccordionElement>
|
||||
<AccordionElement>
|
||||
<template v-slot:title>
|
||||
<div class="text-white bg-blue-400 rounded-lg py-1 px-2 mr-2">GET</div>
|
||||
get_tasks
|
||||
</template>
|
||||
<template v-slot:body>
|
||||
<span class="ml-6 text-lg">Функция для получения всех задач</span>
|
||||
<Code>
|
||||
<template v-slot:title>Структура запроса</template>
|
||||
<template v-slot:body>
|
||||
{ <br>
|
||||
 "command": "get_tasks" <br>
|
||||
} <br>
|
||||
</template>
|
||||
</Code>
|
||||
<div class="border border-zinc-400 rounded-xl p-2 mx-1 my-2">
|
||||
<p class="text-lg">Параметры</p>
|
||||
<hr>
|
||||
<div class="mx-3">
|
||||
<div class="my-3">
|
||||
<p>
|
||||
command <span class="text-red-500 text-sm">*обязательно</span><br>
|
||||
название команды <span class="opacity-80">[String]</span>
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="border border-zinc-400 rounded-xl p-2 mx-1 my-2">
|
||||
<p class="text-lg">Результат</p>
|
||||
<hr>
|
||||
<div class="mx-3">
|
||||
<div class="my-2">
|
||||
<p>
|
||||
JSON - список задач <br>
|
||||
400 - неверные параметры запроса <br>
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
</AccordionElement>
|
||||
<AccordionElement>
|
||||
<template v-slot:title>
|
||||
<div class="text-white bg-blue-400 rounded-lg py-1 px-2 mr-2">GET</div>
|
||||
get_task_by_id
|
||||
</template>
|
||||
<template v-slot:body>
|
||||
<span class="ml-6 text-lg">Функция для получения задачи по id</span>
|
||||
<Code>
|
||||
<template v-slot:title>Структура запроса</template>
|
||||
<template v-slot:body>
|
||||
{ <br>
|
||||
 "command": "get_task_by_id" <br>
|
||||
 "id": "id задачи" <br>
|
||||
} <br>
|
||||
</template>
|
||||
</Code>
|
||||
<div class="border border-zinc-400 rounded-xl p-2 mx-1 my-2">
|
||||
<p class="text-lg">Параметры</p>
|
||||
<hr>
|
||||
<div class="mx-3">
|
||||
<div class="my-3">
|
||||
<p>
|
||||
command <span class="text-red-500 text-sm">*обязательно</span><br>
|
||||
название команды <span class="opacity-80">[String]</span><br>
|
||||
id <span class="text-red-500 text-sm">*обязательно</span><br>
|
||||
id задачи <span class="opacity-80">[String]</span><br>
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="border border-zinc-400 rounded-xl p-2 mx-1 my-2">
|
||||
<p class="text-lg">Результат</p>
|
||||
<hr>
|
||||
<div class="mx-3">
|
||||
<div class="my-2">
|
||||
<p>
|
||||
JSON - задача <br>
|
||||
null - задача не найдена <br>
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
</AccordionElement>
|
||||
<AccordionElement>
|
||||
<template v-slot:title>
|
||||
<div class="text-white bg-green-500 rounded-lg py-1 px-2 mr-2">POST</div>
|
||||
create_task
|
||||
</template>
|
||||
<template v-slot:body>
|
||||
<span class="ml-6 text-lg">Функция для создания задачи</span>
|
||||
<Code>
|
||||
<template v-slot:title>Структура запроса</template>
|
||||
<template v-slot:body>
|
||||
{ <br>
|
||||
 "command": "get_task_by_id" <br>
|
||||
 "task": { <br>
|
||||
  "name": "Новая задача" <br>
|
||||
  "description": "Описание" <br>
|
||||
  "priority": "4" <br>
|
||||
 } <br>
|
||||
} <br>
|
||||
</template>
|
||||
</Code>
|
||||
<div class="border border-zinc-400 rounded-xl p-2 mx-1 my-2">
|
||||
<p class="text-lg">Параметры</p>
|
||||
<hr>
|
||||
<div class="mx-3">
|
||||
<div class="my-3">
|
||||
<p>
|
||||
command <span class="text-red-500 text-sm">*обязательно</span><br>
|
||||
название команды <span class="opacity-80">[String]</span><br>
|
||||
task <span class="text-red-500 text-sm">*обязательно</span><br>
|
||||
задача <span class="opacity-80">[JSON Object]</span><br>
|
||||
 name <span class="text-red-500 text-sm">*обязательно</span><br>
|
||||
  название задачи <span class="opacity-80">[String]</span><br>
|
||||
 description<br>
|
||||
  описание задачи <span class="opacity-80">[String]</span><br>
|
||||
 priority<br>
|
||||
  проритет задачи (0-10) <span class="opacity-80">[String]</span><br>
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="border border-zinc-400 rounded-xl p-2 mx-1 my-2">
|
||||
<p class="text-lg">Результат</p>
|
||||
<hr>
|
||||
<div class="mx-3">
|
||||
<div class="my-2">
|
||||
<p>
|
||||
String - id, созданной задачи <br>
|
||||
400 - неверный параметр запроса <br>
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
</AccordionElement>
|
||||
<AccordionElement>
|
||||
<template v-slot:title>
|
||||
<div class="text-white bg-yellow-500 rounded-lg py-1 px-2 mr-2">PUT</div>
|
||||
edit_task
|
||||
</template>
|
||||
<template v-slot:body>
|
||||
<span class="ml-6 text-lg">Функция для изменения задачи по id</span>
|
||||
<Code>
|
||||
<template v-slot:title>Структура запроса</template>
|
||||
<template v-slot:body>
|
||||
{ <br>
|
||||
 "command": "edit_task" <br>
|
||||
 "task": { <br>
|
||||
  "id": "2" <br>
|
||||
  "name": "Новая задача" <br>
|
||||
  "description": "Описание" <br>
|
||||
  "priority": "4" <br>
|
||||
 } <br>
|
||||
} <br>
|
||||
</template>
|
||||
</Code>
|
||||
<div class="border border-zinc-400 rounded-xl p-2 mx-1 my-2">
|
||||
<p class="text-lg">Параметры</p>
|
||||
<hr>
|
||||
<div class="mx-3">
|
||||
<div class="my-3">
|
||||
<p>
|
||||
command <span class="text-red-500 text-sm">*обязательно</span><br>
|
||||
название команды <span class="opacity-80">[String]</span><br>
|
||||
task <span class="text-red-500 text-sm">*обязательно</span><br>
|
||||
задача <span class="opacity-80">[JSON Object]</span><br>
|
||||
 id <span class="text-red-500 text-sm">*обязательно</span><br>
|
||||
  id задачи <span class="opacity-80">[String]</span><br>
|
||||
 name <span class="text-red-500 text-sm">*обязательно</span><br>
|
||||
  название задачи <span class="opacity-80">[String]</span><br>
|
||||
 description<br>
|
||||
  описание задачи <span class="opacity-80">[String]</span><br>
|
||||
 priority<br>
|
||||
  проритет задачи (0-10) <span class="opacity-80">[String]</span><br>
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="border border-zinc-400 rounded-xl p-2 mx-1 my-2">
|
||||
<p class="text-lg">Результат</p>
|
||||
<hr>
|
||||
<div class="mx-3">
|
||||
<div class="my-2">
|
||||
<p>
|
||||
200 - задача изменена <br>
|
||||
400 - неверный параметр запроса <br>
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
</AccordionElement>
|
||||
<AccordionElement>
|
||||
<template v-slot:title>
|
||||
<div class="text-white bg-red-400 rounded-lg py-1 px-2 mr-2">DELETE</div>
|
||||
delete_task
|
||||
</template>
|
||||
<template v-slot:body>
|
||||
<span class="ml-6 text-lg">Функция для удаления задачи по id</span>
|
||||
<Code>
|
||||
<template v-slot:title>Структура запроса</template>
|
||||
<template v-slot:body>
|
||||
{ <br>
|
||||
 "command": "delete_task" <br>
|
||||
 "id": "id задачи" <br>
|
||||
} <br>
|
||||
</template>
|
||||
</Code>
|
||||
<div class="border border-zinc-400 rounded-xl p-2 mx-1 my-2">
|
||||
<p class="text-lg">Параметры</p>
|
||||
<hr>
|
||||
<div class="mx-3">
|
||||
<div class="my-3">
|
||||
<p>
|
||||
command <span class="text-red-500 text-sm">*обязательно</span><br>
|
||||
название команды <span class="opacity-80">[String]</span><br>
|
||||
id <span class="text-red-500 text-sm">*обязательно</span><br>
|
||||
id задачи <span class="opacity-80">[String]</span><br>
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="border border-zinc-400 rounded-xl p-2 mx-1 my-2">
|
||||
<p class="text-lg">Результат</p>
|
||||
<hr>
|
||||
<div class="mx-3">
|
||||
<div class="my-2">
|
||||
<p>
|
||||
200 - задача удалена <br>
|
||||
400 - неверный параметр запроса <br>
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
</AccordionElement>
|
||||
<AccordionElement>
|
||||
<template v-slot:title>
|
||||
<div class="text-white bg-green-500 rounded-lg py-1 px-2 mr-2">POST</div>
|
||||
stop
|
||||
</template>
|
||||
<template v-slot:body>
|
||||
<span class="ml-6 text-lg">Функция для выключения API</span>
|
||||
<Code>
|
||||
<template v-slot:title>Структура запроса</template>
|
||||
<template v-slot:body>
|
||||
{ <br>
|
||||
 "command": "stop" <br>
|
||||
} <br>
|
||||
</template>
|
||||
</Code>
|
||||
<div class="border border-zinc-400 rounded-xl p-2 mx-1 my-2">
|
||||
<p class="text-lg">Параметры</p>
|
||||
<hr>
|
||||
<div class="mx-3">
|
||||
<div class="my-3">
|
||||
<p>
|
||||
command <span class="text-red-500 text-sm">*обязательно</span><br>
|
||||
название команды <span class="opacity-80">[String]</span><br>
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="border border-zinc-400 rounded-xl p-2 mx-1 my-2">
|
||||
<p class="text-lg">Результат</p>
|
||||
<hr>
|
||||
<div class="mx-3">
|
||||
<div class="my-2">
|
||||
<p>
|
||||
200 - API выключается <br>
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
</AccordionElement>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<style scoped>
|
||||
|
||||
</style>
|
||||
@@ -1,4 +0,0 @@
|
||||
import { createApp } from "vue";
|
||||
import ApiApp from "./ApiApp.vue";
|
||||
|
||||
createApp(ApiApp).mount("#app_api");
|
||||
@@ -0,0 +1,40 @@
|
||||
<script setup>
|
||||
import {useDark, useToggle} from "@vueuse/core";
|
||||
|
||||
let emit = defineEmits(['close', 'home', 'api', 'sync']);
|
||||
|
||||
const isDark = useDark();
|
||||
const toggleDark = useToggle(isDark);
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<div class="fixed h-full w-full top-0 bottom-0 left-0 right-0 bg-zinc-900/60 z-10"/>
|
||||
<div class="fixed top-0 bottom-0 w-56 pt-9 h-full bg-zinc-200 dark:bg-zinc-700 z-20 dark:text-white">
|
||||
<button class="ml-2" @click="emit('close')">
|
||||
<img src="/icon/mingcute--menu-fill.svg" alt="Menu" style="width: 38px; height: 38px" class="dark:invert">
|
||||
</button>
|
||||
<ul class="divide-y divide-gray-300 dark:divide-gray-500">
|
||||
<li class="py-2 px-4"><button class="flex" @click="emit('home')">
|
||||
<img src="/icon/tasks/mdi--pencil.svg" alt="tasks" style="width: 32px; height: 32px" class="dark:invert">
|
||||
<p class="pl-2 pt-0.5 text-lg">Задачи</p>
|
||||
</button></li>
|
||||
<li class="py-2 px-4"><button class="flex" @click="emit('api')">
|
||||
<img src="/icon/ant-design--api-outlined.svg" alt="tasks" style="width: 32px; height: 32px" class="dark:invert">
|
||||
<p class="pl-2 pt-0.5 text-lg">API</p>
|
||||
</button></li>
|
||||
<li class="py-2 px-4"><button class="flex" @click="emit('sync')">
|
||||
<img src="/icon/material-symbols--sync-alt.svg" alt="tasks" style="width: 32px; height: 32px" class="dark:invert">
|
||||
<p class="pl-2 pt-0.5 text-lg">Синхронизация</p>
|
||||
</button></li>
|
||||
</ul>
|
||||
<!--Theme button-->
|
||||
<button class="fixed bottom-3 left-3" @click="toggleDark()">
|
||||
<img v-if="!isDark" src="/icon/window/flowbite--sun-solid.svg" alt="Light theme" style="width: 38px; height: 38px">
|
||||
<img v-else src="/icon/window/ri--moon-fill.svg" alt="Dark theme" style="width: 38px; height: 38px">
|
||||
</button>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<style scoped>
|
||||
|
||||
</style>
|
||||
@@ -1,94 +1,95 @@
|
||||
<script setup>
|
||||
import {invoke} from "@tauri-apps/api";
|
||||
import {ref} from "vue";
|
||||
import {Icon} from "@iconify/vue";
|
||||
|
||||
let name = "";
|
||||
let description = "";
|
||||
let priority = ref(0);
|
||||
|
||||
let error = ref(" ");
|
||||
|
||||
async function create_task(){
|
||||
if (name.replace(/ /g,'') === ""){
|
||||
error.value = "Название задачи не может быть пустым!";
|
||||
} else{
|
||||
let nowDateTime = new Date();
|
||||
let date = nowDateTime.getFullYear() + "." + ('0' + (nowDateTime.getMonth()+1)).slice(-2) + '.'
|
||||
+ ('0' + nowDateTime.getDate()).slice(-2);
|
||||
let time = ('0' + nowDateTime.getHours()).slice(-2) + ":" + ('0' + nowDateTime.getMinutes()).slice(-2);
|
||||
|
||||
await invoke('add_task', {
|
||||
date: date,
|
||||
time: time,
|
||||
name: name,
|
||||
description: description,
|
||||
priority: priority.value.toString()
|
||||
});
|
||||
|
||||
location.reload();
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<div id="create-task-modal" tabindex="-1" aria-hidden="true" class="px-[5vw] md:px-[10vw] lg:px-[15vw] absolute overflow-y-auto overflow-x-hidden w-full top-20 lg:top-24 z-50">
|
||||
<div class="relative p-4 max-h-full">
|
||||
<!-- Modal content -->
|
||||
<form class="relative bg-white rounded-lg shadow dark:bg-[rgb(50,50,50)]" @submit.prevent="create_task">
|
||||
<!-- Modal header -->
|
||||
<div class="flex items-center justify-between px-5 py-2 md:py-3 border-b rounded-t">
|
||||
<h3 class="text-xl font-semibold text-gray-700 dark:text-gray-100">
|
||||
Новая задача
|
||||
</h3>
|
||||
<button type="button" @click="$emit('close')" class="transition-colors text-gray-400 dark:text-gray-300 bg-transparent hover:bg-gray-200 hover:text-gray-900 rounded-lg text-sm w-8 h-8 ms-auto inline-flex justify-center items-center" data-modal-hide="default-modal">
|
||||
<svg class="w-3 h-3" aria-hidden="true" xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 14 14">
|
||||
<path stroke="currentColor" stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="m1 1 6 6m0 0 6 6M7 7l6-6M7 7l-6 6"/>
|
||||
</svg>
|
||||
</button>
|
||||
</div>
|
||||
<!-- Modal body -->
|
||||
<div class="p-4 md:p-5 space-y-4">
|
||||
<div class="space-y-6">
|
||||
<div>
|
||||
<label for="name" class="block text-sm font-medium leading-6 text-gray-900 dark:text-gray-200">Задача</label>
|
||||
<div class="mt-2">
|
||||
<input @input="error=' '" maxlength="80" v-model="name" id="name" name="name" type="text" class="px-2 block w-full rounded-md border-0 py-1.5 text-gray-900 shadow-sm ring-1 ring-inset ring-gray-300 placeholder:text-gray-400 focus:ring-inset sm:text-sm sm:leading-6" />
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div>
|
||||
<div class="flex items-center justify-between">
|
||||
<label for="description" class="block text-sm font-medium leading-6 text-gray-900 dark:text-gray-200">Описание</label>
|
||||
</div>
|
||||
<div class="mt-2">
|
||||
<textarea v-model="description" id="description" name="description" class="px-2 py-1.5 block w-full rounded-md border-0 text-gray-900 shadow-sm ring-1 ring-inset ring-gray-300 placeholder:text-gray-400 focus:ring-inset sm:text-sm sm:leading-6" />
|
||||
</div>
|
||||
</div>
|
||||
<div>
|
||||
<div class="flex items-center justify-between">
|
||||
<label for="priority" class="block text-sm font-medium leading-6 text-gray-900 dark:text-gray-200">Приоритет</label>
|
||||
</div>
|
||||
<div class="mt-2 flex gap-3 ml-2">
|
||||
<Icon @click="priority = 0" :class="[priority === 0 ? 'opacity-100' : 'opacity-50']" class="dark:text-white text-gray-500 cursor-pointer" icon="streamline:signal-none-solid" width="26" height="26"/>
|
||||
<Icon @click="priority = 1" :class="[priority > 0 && priority < 4 ? 'opacity-100' : 'opacity-50']" class="text-orange-300 cursor-pointer" icon="streamline:signal-low-solid" width="26" height="26"/>
|
||||
<Icon @click="priority = 5" :class="[priority >= 4 && priority < 7 ? 'opacity-100' : 'opacity-50']" class="text-amber-500 cursor-pointer" icon="streamline:signal-medium-solid" width="26" height="26"/>
|
||||
<Icon @click="priority = 8" :class="[priority >= 7 && priority < 9 ? 'opacity-100' : 'opacity-50']" class="text-orange-600 cursor-pointer" icon="streamline:signal-full-solid" width="26" height="26"/>
|
||||
<Icon @click="priority = 10" :class="[priority >= 9 ? 'opacity-100' : 'opacity-50']" class="text-red-500 cursor-pointer" icon="heroicons-solid:exclamation" width="32" height="32"/>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<!-- Modal footer -->
|
||||
<div class="flex items-center py-3 px-5 md:p-3 border-t border-gray-200 rounded-b">
|
||||
<input type="submit" class="text-white bg-green-500 hover:bg-green-600 transition-colors font-medium rounded-lg text-sm px-5 py-2.5 text-center" value="Создать">
|
||||
<p class="ml-3 text-red-500">{{error}}</p>
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<style scoped>
|
||||
|
||||
<script setup>
|
||||
import {invoke} from "@tauri-apps/api/core";
|
||||
import {ref} from "vue";
|
||||
|
||||
let emit = defineEmits(['close']);
|
||||
|
||||
let name = "";
|
||||
let description = "";
|
||||
let priority = ref(0);
|
||||
|
||||
let error = ref(" ");
|
||||
|
||||
async function create_task(){
|
||||
if (name.replace(/ /g,'') === ""){
|
||||
error.value = "Название задачи не может быть пустым!";
|
||||
} else{
|
||||
let nowDateTime = new Date();
|
||||
let date = nowDateTime.getFullYear() + "." + ('0' + (nowDateTime.getMonth()+1)).slice(-2) + '.'
|
||||
+ ('0' + nowDateTime.getDate()).slice(-2);
|
||||
let time = ('0' + nowDateTime.getHours()).slice(-2) + ":" + ('0' + nowDateTime.getMinutes()).slice(-2);
|
||||
|
||||
await invoke('add_task', {
|
||||
date: date,
|
||||
time: time,
|
||||
name: name,
|
||||
description: description,
|
||||
priority: priority.value.toString()
|
||||
});
|
||||
|
||||
emit('close');
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<div id="create-task-modal" tabindex="-1" aria-hidden="true" class="px-[5vw] md:px-[10vw] lg:px-[15vw] absolute overflow-y-auto overflow-x-hidden w-full top-20 lg:top-24 z-50">
|
||||
<div class="relative p-4 max-h-full">
|
||||
<!-- Modal content -->
|
||||
<form class="relative bg-white rounded-lg shadow dark:bg-[rgb(50,50,50)]" @submit.prevent="create_task">
|
||||
<!-- Modal header -->
|
||||
<div class="flex items-center justify-between px-5 py-2 md:py-3 border-b rounded-t">
|
||||
<h3 class="text-xl font-semibold text-gray-700 dark:text-gray-100">
|
||||
Новая задача
|
||||
</h3>
|
||||
<button type="button" @click="$emit('close')" class="transition-colors text-gray-400 dark:text-gray-300 bg-transparent hover:bg-gray-200 hover:text-gray-900 rounded-lg text-sm w-8 h-8 ms-auto inline-flex justify-center items-center" data-modal-hide="default-modal">
|
||||
<svg class="w-3 h-3" aria-hidden="true" xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 14 14">
|
||||
<path stroke="currentColor" stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="m1 1 6 6m0 0 6 6M7 7l6-6M7 7l-6 6"/>
|
||||
</svg>
|
||||
</button>
|
||||
</div>
|
||||
<!-- Modal body -->
|
||||
<div class="p-4 md:p-5 space-y-4">
|
||||
<div class="space-y-6">
|
||||
<div>
|
||||
<label for="name" class="block text-sm font-medium leading-6 text-gray-900 dark:text-gray-200">Задача</label>
|
||||
<div class="mt-2">
|
||||
<input @input="error=' '" maxlength="80" v-model="name" id="name" name="name" type="text" class="px-2 block w-full rounded-md border-0 py-1.5 text-gray-900 shadow-sm ring-1 ring-inset ring-gray-300 placeholder:text-gray-400 focus:ring-inset sm:text-sm sm:leading-6" />
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div>
|
||||
<div class="flex items-center justify-between">
|
||||
<label for="description" class="block text-sm font-medium leading-6 text-gray-900 dark:text-gray-200">Описание</label>
|
||||
</div>
|
||||
<div class="mt-2">
|
||||
<textarea v-model="description" id="description" name="description" class="px-2 py-1.5 block w-full rounded-md border-0 text-gray-900 shadow-sm ring-1 ring-inset ring-gray-300 placeholder:text-gray-400 focus:ring-inset sm:text-sm sm:leading-6" />
|
||||
</div>
|
||||
</div>
|
||||
<div>
|
||||
<div class="flex items-center justify-between">
|
||||
<label for="priority" class="block text-sm font-medium leading-6 text-gray-900 dark:text-gray-200">Приоритет</label>
|
||||
</div>
|
||||
<div class="mt-2 flex gap-3 ml-2">
|
||||
<img @click="priority = 0" :class="[priority === 0 ? 'opacity-100' : 'opacity-50']" class="cursor-pointer" src="/icon/tasks/streamline--signal-none-solid.svg" alt="Task priority" style="width: 26px; height: 26px">
|
||||
<img @click="priority = 1" :class="[priority > 0 && priority < 4 ? 'opacity-100' : 'opacity-50']" class="cursor-pointer" src="/icon/tasks/streamline--signal-low-solid.svg" alt="Task priority" style="width: 26px; height: 26px">
|
||||
<img @click="priority = 5" :class="[priority >= 4 && priority < 7 ? 'opacity-100' : 'opacity-50']" class="cursor-pointer" src="/icon/tasks/streamline--signal-medium-solid.svg" alt="Task priority" style="width: 26px; height: 26px">
|
||||
<img @click="priority = 8" :class="[priority >= 7 && priority < 9 ? 'opacity-100' : 'opacity-50']" class="cursor-pointer" src="/icon/tasks/streamline--signal-full-solid.svg" alt="Task priority" style="width: 26px; height: 26px">
|
||||
<img @click="priority = 10" :class="[priority >= 9 ? 'opacity-100' : 'opacity-50']" class="cursor-pointer" src="/icon/tasks/heroicons--exclamation-triangle-16-solid.svg" alt="Task priority" style="width: 32px; height: 32px">
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<!-- Modal footer -->
|
||||
<div class="flex items-center py-3 px-5 md:p-3 border-t border-gray-200 rounded-b">
|
||||
<input type="submit" class="text-white bg-green-500 hover:bg-green-600 transition-colors font-medium rounded-lg text-sm px-5 py-2.5 text-center" value="Создать">
|
||||
<p class="ml-3 text-red-500">{{error}}</p>
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<style scoped>
|
||||
|
||||
</style>
|
||||
@@ -1,86 +1,85 @@
|
||||
<script setup>
|
||||
import {invoke} from "@tauri-apps/api";
|
||||
import {Icon} from "@iconify/vue";
|
||||
import {ref} from "vue";
|
||||
|
||||
const props = defineProps({
|
||||
task_id: String,
|
||||
task_name: String,
|
||||
task_description: String,
|
||||
task_priority: String
|
||||
})
|
||||
|
||||
let name = props.task_name;
|
||||
let description = props.task_description;
|
||||
let priority = ref(parseInt(props.task_priority));
|
||||
|
||||
async function send_edited_task(){
|
||||
await invoke('edit_task', {
|
||||
idTask: props.task_id,
|
||||
name: name,
|
||||
description: description,
|
||||
priority: priority.value.toString()
|
||||
}).then(() => location.reload());
|
||||
}
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<div id="create-task-modal" tabindex="-1" aria-hidden="true" class="px-[5vw] md:px-[10vw] lg:px-[15vw] absolute overflow-y-auto overflow-x-hidden w-full top-20 lg:top-24 z-50">
|
||||
<div class="relative p-4 max-h-full">
|
||||
<!-- Modal content -->
|
||||
<form class="relative bg-white rounded-lg shadow dark:bg-[rgb(50,50,50)]" @submit.prevent="send_edited_task">
|
||||
<!-- Modal header -->
|
||||
<div class="flex items-center justify-between px-5 py-2 md:py-3 border-b rounded-t">
|
||||
<h3 class="text-xl font-semibold text-gray-700 dark:text-gray-100">
|
||||
Изменить задачу
|
||||
</h3>
|
||||
<button type="button" @click="$emit('close')" class="transition-colors text-gray-400 dark:text-gray-300 bg-transparent hover:bg-gray-200 hover:text-gray-900 rounded-lg text-sm w-8 h-8 ms-auto inline-flex justify-center items-center" data-modal-hide="default-modal">
|
||||
<svg class="w-3 h-3" aria-hidden="true" xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 14 14">
|
||||
<path stroke="currentColor" stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="m1 1 6 6m0 0 6 6M7 7l6-6M7 7l-6 6"/>
|
||||
</svg>
|
||||
</button>
|
||||
</div>
|
||||
<!-- Modal body -->
|
||||
<div class="p-4 md:p-5 space-y-4">
|
||||
<div class="space-y-6">
|
||||
<div>
|
||||
<label for="name" class="block text-sm font-medium leading-6 text-gray-900 dark:text-gray-200">Задача</label>
|
||||
<div class="mt-2">
|
||||
<input v-model="name" id="name" name="name" type="text" required class="px-2 block w-full rounded-md border-0 py-1.5 text-gray-900 shadow-sm ring-1 ring-inset ring-gray-300 placeholder:text-gray-400 focus:ring-inset sm:text-sm sm:leading-6" />
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div>
|
||||
<div class="flex items-center justify-between">
|
||||
<label for="description" class="block text-sm font-medium leading-6 text-gray-900 dark:text-gray-200">Описание</label>
|
||||
</div>
|
||||
<div class="mt-2">
|
||||
<textarea v-model="description" id="description" name="description" class="px-2 py-1.5 block w-full rounded-md border-0 text-gray-900 shadow-sm ring-1 ring-inset ring-gray-300 placeholder:text-gray-400 focus:ring-inset sm:text-sm sm:leading-6" />
|
||||
</div>
|
||||
</div>
|
||||
<div>
|
||||
<div class="flex items-center justify-between">
|
||||
<label for="priority" class="block text-sm font-medium leading-6 text-gray-900 dark:text-gray-200">Приоритет</label>
|
||||
</div>
|
||||
<div class="mt-2 flex gap-3 ml-2">
|
||||
<Icon @click="priority = 0" :class="[priority === 0 ? 'opacity-100' : 'opacity-50']" class="dark:text-white text-gray-500 cursor-pointer" icon="streamline:signal-none-solid" width="26" height="26"/>
|
||||
<Icon @click="priority = 1" :class="[priority > 0 && priority < 4 ? 'opacity-100' : 'opacity-50']" class="text-orange-300 cursor-pointer" icon="streamline:signal-low-solid" width="26" height="26"/>
|
||||
<Icon @click="priority = 5" :class="[priority >= 4 && priority < 7 ? 'opacity-100' : 'opacity-50']" class="text-amber-500 cursor-pointer" icon="streamline:signal-medium-solid" width="26" height="26"/>
|
||||
<Icon @click="priority = 8" :class="[priority >= 7 && priority < 9 ? 'opacity-100' : 'opacity-50']" class="text-orange-600 cursor-pointer" icon="streamline:signal-full-solid" width="26" height="26"/>
|
||||
<Icon @click="priority = 10" :class="[priority >= 9 ? 'opacity-100' : 'opacity-50']" class="text-red-500 cursor-pointer" icon="heroicons-solid:exclamation" width="32" height="32"/>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<!-- Modal footer -->
|
||||
<div class="flex items-center py-3 px-5 md:p-3 border-t border-gray-200 rounded-b">
|
||||
<input type="submit" class="text-white bg-green-500 hover:bg-green-600 transition-colors font-medium rounded-lg text-sm px-5 py-2.5 text-center" value="Изменить">
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<style scoped>
|
||||
|
||||
<script setup>
|
||||
import {invoke} from "@tauri-apps/api/core";
|
||||
import {ref} from "vue";
|
||||
|
||||
const props = defineProps({
|
||||
task_id: String,
|
||||
task_name: String,
|
||||
task_description: String,
|
||||
task_priority: String
|
||||
})
|
||||
|
||||
let name = props.task_name;
|
||||
let description = props.task_description;
|
||||
let priority = ref(parseInt(props.task_priority));
|
||||
|
||||
async function send_edited_task(){
|
||||
await invoke('edit_task', {
|
||||
idTask: props.task_id,
|
||||
name: name,
|
||||
description: description,
|
||||
priority: priority.value.toString()
|
||||
}).then(() => location.reload());
|
||||
}
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<div id="edit-task-modal" tabindex="-1" aria-hidden="true" class="px-[5vw] md:px-[10vw] lg:px-[15vw] absolute overflow-y-auto overflow-x-hidden w-full top-20 lg:top-24 z-50">
|
||||
<div class="relative p-4 max-h-full">
|
||||
<!-- Modal content -->
|
||||
<form class="relative bg-white rounded-lg shadow dark:bg-[rgb(50,50,50)]" @submit.prevent="send_edited_task">
|
||||
<!-- Modal header -->
|
||||
<div class="flex items-center justify-between px-5 py-2 md:py-3 border-b rounded-t">
|
||||
<h3 class="text-xl font-semibold text-gray-700 dark:text-gray-100">
|
||||
Изменить задачу
|
||||
</h3>
|
||||
<button type="button" @click="$emit('close')" class="transition-colors text-gray-400 dark:text-gray-300 bg-transparent hover:bg-gray-200 hover:text-gray-900 rounded-lg text-sm w-8 h-8 ms-auto inline-flex justify-center items-center" data-modal-hide="default-modal">
|
||||
<svg class="w-3 h-3" aria-hidden="true" xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 14 14">
|
||||
<path stroke="currentColor" stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="m1 1 6 6m0 0 6 6M7 7l6-6M7 7l-6 6"/>
|
||||
</svg>
|
||||
</button>
|
||||
</div>
|
||||
<!-- Modal body -->
|
||||
<div class="p-4 md:p-5 space-y-4">
|
||||
<div class="space-y-6">
|
||||
<div>
|
||||
<label for="name" class="block text-sm font-medium leading-6 text-gray-900 dark:text-gray-200">Задача</label>
|
||||
<div class="mt-2">
|
||||
<input v-model="name" id="name" name="name" type="text" required class="px-2 block w-full rounded-md border-0 py-1.5 text-gray-900 shadow-sm ring-1 ring-inset ring-gray-300 placeholder:text-gray-400 focus:ring-inset sm:text-sm sm:leading-6" />
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div>
|
||||
<div class="flex items-center justify-between">
|
||||
<label for="description" class="block text-sm font-medium leading-6 text-gray-900 dark:text-gray-200">Описание</label>
|
||||
</div>
|
||||
<div class="mt-2">
|
||||
<textarea v-model="description" id="description" name="description" class="px-2 py-1.5 block w-full rounded-md border-0 text-gray-900 shadow-sm ring-1 ring-inset ring-gray-300 placeholder:text-gray-400 focus:ring-inset sm:text-sm sm:leading-6" />
|
||||
</div>
|
||||
</div>
|
||||
<div>
|
||||
<div class="flex items-center justify-between">
|
||||
<label for="priority" class="block text-sm font-medium leading-6 text-gray-900 dark:text-gray-200">Приоритет</label>
|
||||
</div>
|
||||
<div class="mt-2 flex gap-3 ml-2">
|
||||
<img @click="priority = 0" :class="[priority === 0 ? 'opacity-100' : 'opacity-50']" class="cursor-pointer" src="/icon/tasks/streamline--signal-none-solid.svg" alt="Task priority" style="width: 26px; height: 26px">
|
||||
<img @click="priority = 1" :class="[priority > 0 && priority < 4 ? 'opacity-100' : 'opacity-50']" class="cursor-pointer" src="/icon/tasks/streamline--signal-low-solid.svg" alt="Task priority" style="width: 26px; height: 26px">
|
||||
<img @click="priority = 5" :class="[priority >= 4 && priority < 7 ? 'opacity-100' : 'opacity-50']" class="cursor-pointer" src="/icon/tasks/streamline--signal-medium-solid.svg" alt="Task priority" style="width: 26px; height: 26px">
|
||||
<img @click="priority = 8" :class="[priority >= 7 && priority < 9 ? 'opacity-100' : 'opacity-50']" class="cursor-pointer" src="/icon/tasks/streamline--signal-full-solid.svg" alt="Task priority" style="width: 26px; height: 26px">
|
||||
<img @click="priority = 10" :class="[priority >= 9 ? 'opacity-100' : 'opacity-50']" class="cursor-pointer" src="/icon/tasks/heroicons--exclamation-triangle-16-solid.svg" alt="Task priority" style="width: 32px; height: 32px">
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<!-- Modal footer -->
|
||||
<div class="flex items-center py-3 px-5 md:p-3 border-t border-gray-200 rounded-b">
|
||||
<input type="submit" class="text-white bg-green-500 hover:bg-green-600 transition-colors font-medium rounded-lg text-sm px-5 py-2.5 text-center" value="Изменить">
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<style scoped>
|
||||
|
||||
</style>
|
||||
@@ -1,73 +1,72 @@
|
||||
<script setup>
|
||||
import {invoke} from "@tauri-apps/api";
|
||||
import {Icon} from "@iconify/vue";
|
||||
import {ref} from "vue";
|
||||
|
||||
const props = defineProps({
|
||||
task_id: String,
|
||||
task_name: String,
|
||||
task_priority: String
|
||||
})
|
||||
|
||||
let priority = ref(parseInt(props.task_priority));
|
||||
|
||||
async function send_edited_task(){
|
||||
await invoke('set_task_field', {
|
||||
idTask: props.task_id,
|
||||
field: 'priority',
|
||||
value: priority.value.toString()
|
||||
}).then(() => location.reload());
|
||||
}
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<div id="create-task-modal" tabindex="-1" aria-hidden="true" class="absolute left-[50%] translate-x-[-50%] overflow-y-auto overflow-x-hidden w-50 top-20 lg:top-32 z-50">
|
||||
<div class="relative p-4 max-h-full">
|
||||
<!-- Modal content -->
|
||||
<form class="relative bg-white rounded-lg shadow dark:bg-[rgb(50,50,50)]" @submit.prevent="send_edited_task">
|
||||
<!-- Modal header -->
|
||||
<div class="flex items-center justify-between px-5 py-2 md:py-3 border-b rounded-t">
|
||||
<h3 class="text-xl font-semibold text-gray-700 dark:text-gray-100 mr-2">
|
||||
Изменение приоритета
|
||||
</h3>
|
||||
<button type="button" @click="$emit('close')" class="transition-colors text-gray-400 dark:text-gray-300 bg-transparent hover:bg-gray-200 hover:text-gray-900 rounded-lg text-sm w-8 h-8 ms-auto inline-flex justify-center items-center" data-modal-hide="default-modal">
|
||||
<svg class="w-3 h-3" aria-hidden="true" xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 14 14">
|
||||
<path stroke="currentColor" stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="m1 1 6 6m0 0 6 6M7 7l6-6M7 7l-6 6"/>
|
||||
</svg>
|
||||
</button>
|
||||
</div>
|
||||
<!-- Modal body -->
|
||||
<div class="p-4 md:p-5 space-y-4">
|
||||
<div class="space-y-6">
|
||||
<div>
|
||||
<div class="flex items-center justify-between">
|
||||
<label for="name" class="block text-sm font-medium leading-6 text-gray-900 dark:text-gray-200">Название задачи</label>
|
||||
</div>
|
||||
<div class="m-1 text-ellipsis" id="name">
|
||||
<p class="dark:text-white">{{props.task_name}}</p>
|
||||
</div>
|
||||
<div class="flex items-center justify-between">
|
||||
<label for="priority" class="block text-sm font-medium leading-6 text-gray-900 dark:text-gray-200">Приоритет</label>
|
||||
</div>
|
||||
<div class="mt-2 flex gap-3 ml-2" id="priority">
|
||||
<Icon @click="priority = 0" :class="[priority === 0 ? 'opacity-100' : 'opacity-50']" class="dark:text-white text-gray-500 cursor-pointer" icon="streamline:signal-none-solid" width="26" height="26"/>
|
||||
<Icon @click="priority = 1" :class="[priority > 0 && priority < 4 ? 'opacity-100' : 'opacity-50']" class="text-orange-300 cursor-pointer" icon="streamline:signal-low-solid" width="26" height="26"/>
|
||||
<Icon @click="priority = 5" :class="[priority >= 4 && priority < 7 ? 'opacity-100' : 'opacity-50']" class="text-amber-500 cursor-pointer" icon="streamline:signal-medium-solid" width="26" height="26"/>
|
||||
<Icon @click="priority = 8" :class="[priority >= 7 && priority < 9 ? 'opacity-100' : 'opacity-50']" class="text-orange-600 cursor-pointer" icon="streamline:signal-full-solid" width="26" height="26"/>
|
||||
<Icon @click="priority = 10" :class="[priority >= 9 ? 'opacity-100' : 'opacity-50']" class="text-red-500 cursor-pointer" icon="heroicons-solid:exclamation" width="32" height="32"/>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<!-- Modal footer -->
|
||||
<div class="flex items-center py-3 px-5 md:p-3 border-t border-gray-200 rounded-b">
|
||||
<input type="submit" class="text-white bg-green-500 hover:bg-green-600 transition-colors font-medium rounded-lg text-sm px-5 py-2.5 text-center" value="Изменить">
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<style scoped>
|
||||
|
||||
<script setup>
|
||||
import {invoke} from "@tauri-apps/api/core";
|
||||
import {ref} from "vue";
|
||||
|
||||
const props = defineProps({
|
||||
task_id: String,
|
||||
task_name: String,
|
||||
task_priority: String
|
||||
})
|
||||
|
||||
let priority = ref(parseInt(props.task_priority));
|
||||
|
||||
async function send_edited_task(){
|
||||
await invoke('set_task_field', {
|
||||
idTask: props.task_id,
|
||||
field: 'priority',
|
||||
value: priority.value.toString()
|
||||
}).then(() => location.reload());
|
||||
}
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<div id="priority-modal" tabindex="-1" aria-hidden="true" class="absolute left-[50%] translate-x-[-50%] overflow-y-auto overflow-x-hidden w-50 top-20 lg:top-32 z-50">
|
||||
<div class="relative p-4 max-h-full">
|
||||
<!-- Modal content -->
|
||||
<form class="relative bg-white rounded-lg shadow dark:bg-[rgb(50,50,50)]" @submit.prevent="send_edited_task">
|
||||
<!-- Modal header -->
|
||||
<div class="flex items-center justify-between px-5 py-2 md:py-3 border-b rounded-t">
|
||||
<h3 class="text-xl font-semibold text-gray-700 dark:text-gray-100 mr-2">
|
||||
Изменение приоритета
|
||||
</h3>
|
||||
<button type="button" @click="$emit('close')" class="transition-colors text-gray-400 dark:text-gray-300 bg-transparent hover:bg-gray-200 hover:text-gray-900 rounded-lg text-sm w-8 h-8 ms-auto inline-flex justify-center items-center" data-modal-hide="default-modal">
|
||||
<svg class="w-3 h-3" aria-hidden="true" xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 14 14">
|
||||
<path stroke="currentColor" stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="m1 1 6 6m0 0 6 6M7 7l6-6M7 7l-6 6"/>
|
||||
</svg>
|
||||
</button>
|
||||
</div>
|
||||
<!-- Modal body -->
|
||||
<div class="p-4 md:p-5 space-y-4">
|
||||
<div class="space-y-6">
|
||||
<div>
|
||||
<div class="flex items-center justify-between">
|
||||
<label for="name" class="block text-sm font-medium leading-6 text-gray-900 dark:text-gray-200">Название задачи</label>
|
||||
</div>
|
||||
<div class="m-1 text-ellipsis" id="name">
|
||||
<p class="dark:text-white">{{props.task_name}}</p>
|
||||
</div>
|
||||
<div class="flex items-center justify-between">
|
||||
<label for="priority" class="block text-sm font-medium leading-6 text-gray-900 dark:text-gray-200">Приоритет</label>
|
||||
</div>
|
||||
<div class="mt-2 flex gap-3 ml-2" id="priority">
|
||||
<img @click="priority = 0" :class="[priority === 0 ? 'opacity-100' : 'opacity-50']" class="cursor-pointer" src="/icon/tasks/streamline--signal-none-solid.svg" alt="Task priority" style="width: 26px; height: 26px">
|
||||
<img @click="priority = 1" :class="[priority > 0 && priority < 4 ? 'opacity-100' : 'opacity-50']" class="cursor-pointer" src="/icon/tasks/streamline--signal-low-solid.svg" alt="Task priority" style="width: 26px; height: 26px">
|
||||
<img @click="priority = 5" :class="[priority >= 4 && priority < 7 ? 'opacity-100' : 'opacity-50']" class="cursor-pointer" src="/icon/tasks/streamline--signal-medium-solid.svg" alt="Task priority" style="width: 26px; height: 26px">
|
||||
<img @click="priority = 8" :class="[priority >= 7 && priority < 9 ? 'opacity-100' : 'opacity-50']" class="cursor-pointer" src="/icon/tasks/streamline--signal-full-solid.svg" alt="Task priority" style="width: 26px; height: 26px">
|
||||
<img @click="priority = 10" :class="[priority >= 9 ? 'opacity-100' : 'opacity-50']" class="cursor-pointer" src="/icon/tasks/heroicons--exclamation-triangle-16-solid.svg" alt="Task priority" style="width: 32px; height: 32px">
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<!-- Modal footer -->
|
||||
<div class="flex items-center py-3 px-5 md:p-3 border-t border-gray-200 rounded-b">
|
||||
<input type="submit" class="text-white bg-green-500 hover:bg-green-600 transition-colors font-medium rounded-lg text-sm px-5 py-2.5 text-center" value="Изменить">
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<style scoped>
|
||||
|
||||
</style>
|
||||
@@ -0,0 +1,85 @@
|
||||
<script setup>
|
||||
import {ref} from "vue";
|
||||
import {invoke} from "@tauri-apps/api/core";
|
||||
|
||||
const props = defineProps({task_id: String})
|
||||
const emit = defineEmits(['close'])
|
||||
|
||||
let task_id = ref(props.task_id);
|
||||
|
||||
let name = "";
|
||||
let description = "";
|
||||
let error = ref(" ");
|
||||
|
||||
async function create_subtask(){
|
||||
if (name.replace(/ /g,'') === ""){
|
||||
error.value = "Название подзадачи не может быть пустым!";
|
||||
} else{
|
||||
let nowDateTime = new Date();
|
||||
let date = nowDateTime.getFullYear() + "." + ('0' + (nowDateTime.getMonth()+1)).slice(-2) + '.'
|
||||
+ ('0' + nowDateTime.getDate()).slice(-2);
|
||||
let time = ('0' + nowDateTime.getHours()).slice(-2) + ":" + ('0' + nowDateTime.getMinutes()).slice(-2);
|
||||
|
||||
console.log(props.task_id)
|
||||
await invoke('add_subtask', {
|
||||
idTask: props.task_id,
|
||||
date: date,
|
||||
time: time,
|
||||
name: name,
|
||||
description: description
|
||||
});
|
||||
|
||||
emit('close');
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<div id="create-task-modal" tabindex="-1" aria-hidden="true" class="px-[5vw] md:px-[10vw] lg:px-[15vw] absolute overflow-y-auto overflow-x-hidden w-full top-20 lg:top-24 z-50">
|
||||
<div class="relative p-4 max-h-full">
|
||||
<!-- Modal content -->
|
||||
<form class="relative bg-white rounded-lg shadow dark:bg-[rgb(50,50,50)]" @submit.prevent="create_subtask">
|
||||
<!-- Modal header -->
|
||||
<div class="flex items-center justify-between px-5 py-2 md:py-3 border-b rounded-t">
|
||||
<h3 class="text-xl font-semibold text-gray-700 dark:text-gray-100">
|
||||
Новая подзадача
|
||||
</h3>
|
||||
<button type="button" @click="$emit('close')" class="transition-colors text-gray-400 dark:text-gray-300 bg-transparent hover:bg-gray-200 hover:text-gray-900 rounded-lg text-sm w-8 h-8 ms-auto inline-flex justify-center items-center" data-modal-hide="default-modal">
|
||||
<svg class="w-3 h-3" aria-hidden="true" xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 14 14">
|
||||
<path stroke="currentColor" stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="m1 1 6 6m0 0 6 6M7 7l6-6M7 7l-6 6"/>
|
||||
</svg>
|
||||
</button>
|
||||
</div>
|
||||
<!-- Modal body -->
|
||||
<div class="p-4 md:p-5 space-y-4">
|
||||
<div class="space-y-6">
|
||||
<div>
|
||||
<label for="name" class="block text-sm font-medium leading-6 text-gray-900 dark:text-gray-200">Задача</label>
|
||||
<div class="mt-2">
|
||||
<input @input="error=' '" maxlength="80" v-model="name" id="name" name="name" type="text" class="px-2 block w-full rounded-md border-0 py-1.5 text-gray-900 shadow-sm ring-1 ring-inset ring-gray-300 placeholder:text-gray-400 focus:ring-inset sm:text-sm sm:leading-6" />
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div>
|
||||
<div class="flex items-center justify-between">
|
||||
<label for="description" class="block text-sm font-medium leading-6 text-gray-900 dark:text-gray-200">Описание</label>
|
||||
</div>
|
||||
<div class="mt-2">
|
||||
<textarea v-model="description" id="description" name="description" class="px-2 py-1.5 block w-full rounded-md border-0 text-gray-900 shadow-sm ring-1 ring-inset ring-gray-300 placeholder:text-gray-400 focus:ring-inset sm:text-sm sm:leading-6" />
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<!-- Modal footer -->
|
||||
<div class="flex items-center py-3 px-5 md:p-3 border-t border-gray-200 rounded-b">
|
||||
<input type="submit" class="text-white bg-green-500 hover:bg-green-600 transition-colors font-medium rounded-lg text-sm px-5 py-2.5 text-center" value="Создать">
|
||||
<p class="ml-3 text-red-500">{{error}}</p>
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<style scoped>
|
||||
|
||||
</style>
|
||||
@@ -0,0 +1,71 @@
|
||||
<script setup>
|
||||
import {invoke} from "@tauri-apps/api/core";
|
||||
|
||||
const props = defineProps({
|
||||
task_id: String,
|
||||
subtask_id: String,
|
||||
subtask_name: String,
|
||||
subtask_description: String,
|
||||
})
|
||||
|
||||
let name = props.subtask_name;
|
||||
let description = props.subtask_description;
|
||||
|
||||
async function send_edited_subtask(){
|
||||
await invoke('edit_subtask', {
|
||||
idTask: props.task_id,
|
||||
idSubtask: props.subtask_id,
|
||||
name: name,
|
||||
description: description
|
||||
}).then(() => location.reload());
|
||||
}
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<div id="edit-task-modal" tabindex="-1" aria-hidden="true" class="px-[5vw] md:px-[10vw] lg:px-[15vw] absolute overflow-y-auto overflow-x-hidden w-full top-20 lg:top-24 z-50">
|
||||
<div class="relative p-4 max-h-full">
|
||||
<!-- Modal content -->
|
||||
<form class="relative bg-white rounded-lg shadow dark:bg-[rgb(50,50,50)]" @submit.prevent="send_edited_subtask">
|
||||
<!-- Modal header -->
|
||||
<div class="flex items-center justify-between px-5 py-2 md:py-3 border-b rounded-t">
|
||||
<h3 class="text-xl font-semibold text-gray-700 dark:text-gray-100">
|
||||
Изменить подзадачу
|
||||
</h3>
|
||||
<button type="button" @click="$emit('close')" class="transition-colors text-gray-400 dark:text-gray-300 bg-transparent hover:bg-gray-200 hover:text-gray-900 rounded-lg text-sm w-8 h-8 ms-auto inline-flex justify-center items-center" data-modal-hide="default-modal">
|
||||
<svg class="w-3 h-3" aria-hidden="true" xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 14 14">
|
||||
<path stroke="currentColor" stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="m1 1 6 6m0 0 6 6M7 7l6-6M7 7l-6 6"/>
|
||||
</svg>
|
||||
</button>
|
||||
</div>
|
||||
<!-- Modal body -->
|
||||
<div class="p-4 md:p-5 space-y-4">
|
||||
<div class="space-y-6">
|
||||
<div>
|
||||
<label for="name" class="block text-sm font-medium leading-6 text-gray-900 dark:text-gray-200">Задача</label>
|
||||
<div class="mt-2">
|
||||
<input v-model="name" id="name" name="name" type="text" required class="px-2 block w-full rounded-md border-0 py-1.5 text-gray-900 shadow-sm ring-1 ring-inset ring-gray-300 placeholder:text-gray-400 focus:ring-inset sm:text-sm sm:leading-6" />
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div>
|
||||
<div class="flex items-center justify-between">
|
||||
<label for="description" class="block text-sm font-medium leading-6 text-gray-900 dark:text-gray-200">Описание</label>
|
||||
</div>
|
||||
<div class="mt-2">
|
||||
<textarea v-model="description" id="description" name="description" class="px-2 py-1.5 block w-full rounded-md border-0 text-gray-900 shadow-sm ring-1 ring-inset ring-gray-300 placeholder:text-gray-400 focus:ring-inset sm:text-sm sm:leading-6" />
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<!-- Modal footer -->
|
||||
<div class="flex items-center py-3 px-5 md:p-3 border-t border-gray-200 rounded-b">
|
||||
<input type="submit" class="text-white bg-green-500 hover:bg-green-600 transition-colors font-medium rounded-lg text-sm px-5 py-2.5 text-center" value="Изменить">
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<style scoped>
|
||||
|
||||
</style>
|
||||
@@ -0,0 +1,73 @@
|
||||
<script setup>
|
||||
import {onBeforeMount, ref} from "vue";
|
||||
import {invoke} from "@tauri-apps/api/core";
|
||||
import CreateSubModal from "./CreateSubModal.vue";
|
||||
|
||||
const props = defineProps({
|
||||
task_id: String,
|
||||
})
|
||||
|
||||
let task_id = ref(props.task_id);
|
||||
|
||||
let subtasks = ref({});
|
||||
|
||||
let create_modal = ref(false);
|
||||
|
||||
onBeforeMount(async () => {
|
||||
await init();
|
||||
});
|
||||
|
||||
async function init(){
|
||||
create_modal.value = false;
|
||||
await invoke('get_subtasks', {
|
||||
idTask: task_id.value
|
||||
}).then((res) => {
|
||||
subtasks.value = res;
|
||||
})
|
||||
}
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<div id="subtasks-modal" tabindex="-1" aria-hidden="true" class="absolute left-[50%] translate-x-[-50%] overflow-y-auto overflow-x-hidden w-50 top-20 lg:top-32 z-50">
|
||||
<div class="relative p-4 max-h-full">
|
||||
<!-- Modal content -->
|
||||
<div class="relative bg-white rounded-lg shadow dark:bg-[rgb(50,50,50)]">
|
||||
<!-- Modal header -->
|
||||
<div class="flex items-center justify-between px-5 py-2 md:py-3 border-b rounded-t">
|
||||
<h3 class="text-xl font-semibold text-gray-700 dark:text-gray-100 mr-2">
|
||||
Подзадачи
|
||||
</h3>
|
||||
<button type="button" @click="$emit('close')" class="transition-colors text-gray-400 dark:text-gray-300 bg-transparent hover:bg-gray-200 hover:text-gray-900 rounded-lg text-sm w-8 h-8 ms-auto inline-flex justify-center items-center" data-modal-hide="default-modal">
|
||||
<svg class="w-3 h-3" aria-hidden="true" xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 14 14">
|
||||
<path stroke="currentColor" stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="m1 1 6 6m0 0 6 6M7 7l6-6M7 7l-6 6"/>
|
||||
</svg>
|
||||
</button>
|
||||
</div>
|
||||
<!-- Modal body -->
|
||||
<div class="p-4 md:p-5 space-y-4 text-white">
|
||||
<div class="space-y-6">
|
||||
<div v-if="Object.keys(subtasks).length === 0" class="py-10 px-24">
|
||||
<p>Пусто</p>
|
||||
</div>
|
||||
<div v-else>
|
||||
<div v-for="subtask in subtasks">
|
||||
<p>{{subtask.id}}</p>
|
||||
<p>{{subtask.name}}</p>
|
||||
<p>{{subtask.description}}</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<!-- Modal footer -->
|
||||
<div class="flex items-center py-3 px-5 md:p-3 border-t border-gray-200 rounded-b">
|
||||
<button @click="create_modal = !create_modal" class="text-white bg-green-500 hover:bg-green-600 transition-colors font-medium rounded-lg text-sm px-5 py-2.5 text-center">Создать</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<CreateSubModal v-if="create_modal" :task_id="task_id" @close="init"/>
|
||||
</template>
|
||||
|
||||
<style scoped>
|
||||
|
||||
</style>
|
||||
@@ -1,4 +1,4 @@
|
||||
import { createApp } from "vue";
|
||||
import App from "./App.vue";
|
||||
|
||||
createApp(App).mount("#app");
|
||||
import { createApp } from "vue";
|
||||
import App from "./App.vue";
|
||||
|
||||
createApp(App).mount("#app");
|
||||
|
||||
@@ -1,13 +1,13 @@
|
||||
/** @type {import('tailwindcss').Config} */
|
||||
export default {
|
||||
darkMode: 'selector',
|
||||
content: [
|
||||
"./index.html",
|
||||
"./src/**/*.{vue,js,ts,jsx,tsx}",
|
||||
],
|
||||
theme: {
|
||||
extend: {},
|
||||
},
|
||||
plugins: [],
|
||||
}
|
||||
|
||||
/** @type {import('tailwindcss').Config} */
|
||||
export default {
|
||||
darkMode: 'selector',
|
||||
content: [
|
||||
"./index.html",
|
||||
"./src/**/*.{vue,js,ts,jsx,tsx}",
|
||||
],
|
||||
theme: {
|
||||
extend: {},
|
||||
},
|
||||
plugins: [],
|
||||
}
|
||||
|
||||
|
||||
@@ -1,21 +1,21 @@
|
||||
import { defineConfig } from "vite";
|
||||
import vue from "@vitejs/plugin-vue";
|
||||
|
||||
// https://vitejs.dev/config/
|
||||
export default defineConfig(async () => ({
|
||||
plugins: [vue()],
|
||||
|
||||
// Vite options tailored for Tauri development and only applied in `tauri dev` or `tauri build`
|
||||
//
|
||||
// 1. prevent vite from obscuring rust errors
|
||||
clearScreen: false,
|
||||
// 2. tauri expects a fixed port, fail if that port is not available
|
||||
server: {
|
||||
port: 1420,
|
||||
strictPort: true,
|
||||
watch: {
|
||||
// 3. tell vite to ignore watching `src-tauri`
|
||||
ignored: ["**/src-tauri/**"],
|
||||
},
|
||||
},
|
||||
}));
|
||||
import { defineConfig } from "vite";
|
||||
import vue from "@vitejs/plugin-vue";
|
||||
|
||||
// https://vitejs.dev/config/
|
||||
export default defineConfig(() => ({
|
||||
plugins: [vue()],
|
||||
|
||||
// Vite options tailored for Tauri development and only applied in `tauri dev` or `tauri build`
|
||||
//
|
||||
// 1. prevent vite from obscuring rust errors
|
||||
clearScreen: false,
|
||||
// 2. tauri expects a fixed port, fail if that port is not available
|
||||
server: {
|
||||
port: 1420,
|
||||
strictPort: true,
|
||||
watch: {
|
||||
// 3. tell vite to ignore watching `src-tauri`
|
||||
ignored: ["**/src-tauri/**"],
|
||||
},
|
||||
}
|
||||
}));
|
||||
|
||||