v1.7.0 - Подзадачи
This commit is contained in:
Generated
+2
-2
@@ -1,12 +1,12 @@
|
|||||||
{
|
{
|
||||||
"name": "to-do-app",
|
"name": "to-do-app",
|
||||||
"version": "0.0.0",
|
"version": "1.7.0",
|
||||||
"lockfileVersion": 3,
|
"lockfileVersion": 3,
|
||||||
"requires": true,
|
"requires": true,
|
||||||
"packages": {
|
"packages": {
|
||||||
"": {
|
"": {
|
||||||
"name": "to-do-app",
|
"name": "to-do-app",
|
||||||
"version": "0.0.0",
|
"version": "1.4.0",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@tauri-apps/api": "^1",
|
"@tauri-apps/api": "^1",
|
||||||
"@vueuse/core": "^10.10.0",
|
"@vueuse/core": "^10.10.0",
|
||||||
|
|||||||
+1
-1
@@ -1,7 +1,7 @@
|
|||||||
{
|
{
|
||||||
"name": "to-do-app",
|
"name": "to-do-app",
|
||||||
"private": true,
|
"private": true,
|
||||||
"version": "1.4.0",
|
"version": "1.7.0",
|
||||||
"type": "module",
|
"type": "module",
|
||||||
"scripts": {
|
"scripts": {
|
||||||
"dev": "vite",
|
"dev": "vite",
|
||||||
|
|||||||
@@ -4,7 +4,7 @@ use serde_json::{json, Value};
|
|||||||
use crate::enc_dec_file::{encrypt_n_save_file, decrypt_file};
|
use crate::enc_dec_file::{encrypt_n_save_file, decrypt_file};
|
||||||
|
|
||||||
use rand::Rng;
|
use rand::Rng;
|
||||||
use rand::distributions::Uniform; // 0.8
|
use rand::distributions::Uniform;
|
||||||
|
|
||||||
#[tauri::command]
|
#[tauri::command]
|
||||||
pub fn check_or_create_config_file(app_handle: tauri::AppHandle) {
|
pub fn check_or_create_config_file(app_handle: tauri::AppHandle) {
|
||||||
@@ -49,7 +49,6 @@ pub fn check_or_create_config_file(app_handle: tauri::AppHandle) {
|
|||||||
\"autostart\": false,\
|
\"autostart\": false,\
|
||||||
\"token\": \"{}\"\
|
\"token\": \"{}\"\
|
||||||
{}", "{", token, "}"));
|
{}", "{", token, "}"));
|
||||||
println!("{}", data);
|
|
||||||
encrypt_n_save_file(path.into(), data);
|
encrypt_n_save_file(path.into(), data);
|
||||||
},
|
},
|
||||||
Err(err) => println!("Не удалось создать файл! \n{}", err)
|
Err(err) => println!("Не удалось создать файл! \n{}", err)
|
||||||
@@ -65,10 +64,18 @@ pub fn get_port(app_handle: tauri::AppHandle) -> i64 {
|
|||||||
|
|
||||||
path.set_extension("enc");
|
path.set_extension("enc");
|
||||||
|
|
||||||
let content = decrypt_file(path);
|
let content = decrypt_file(path.clone());
|
||||||
let data: Value = serde_json::from_str(content.as_str()).unwrap();
|
let data = serde_json::from_str::<Value>(content.as_str());
|
||||||
|
match data {
|
||||||
|
Ok(data) => {
|
||||||
return data["port"].as_i64().unwrap();
|
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]
|
#[tauri::command]
|
||||||
@@ -80,9 +87,12 @@ pub fn get_autostart(app_handle: tauri::AppHandle) -> bool {
|
|||||||
path.set_extension("enc");
|
path.set_extension("enc");
|
||||||
|
|
||||||
let content = decrypt_file(path);
|
let content = decrypt_file(path);
|
||||||
let data: Value = serde_json::from_str(content.as_str()).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;}
|
||||||
|
}
|
||||||
|
|
||||||
return data["autostart"].as_bool().unwrap();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#[tauri::command]
|
#[tauri::command]
|
||||||
|
|||||||
+10
-2
@@ -1,8 +1,6 @@
|
|||||||
// Prevents additional console window on Windows in release, DO NOT REMOVE!!
|
// Prevents additional console window on Windows in release, DO NOT REMOVE!!
|
||||||
#![cfg_attr(not(debug_assertions), windows_subsystem = "windows")]
|
#![cfg_attr(not(debug_assertions), windows_subsystem = "windows")]
|
||||||
|
|
||||||
use std::thread;
|
|
||||||
|
|
||||||
// Learn more about Tauri commands at https://tauri.app/v1/guides/features/command
|
// 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/multithread_tcp_listener.rs"] mod multi_tcp_listener;
|
||||||
#[path= "api/handler_commands.rs"] mod handler_commands;
|
#[path= "api/handler_commands.rs"] mod handler_commands;
|
||||||
@@ -10,11 +8,14 @@ use std::thread;
|
|||||||
#[path= "api/aes_enc_dec.rs"] mod aes_enc_dec;
|
#[path= "api/aes_enc_dec.rs"] mod aes_enc_dec;
|
||||||
|
|
||||||
#[path="tasks_functions.rs"] mod tasks;
|
#[path="tasks_functions.rs"] mod tasks;
|
||||||
|
#[path="subtasks_functions.rs"] mod subtasks;
|
||||||
#[path="config.rs"] mod config;
|
#[path="config.rs"] mod config;
|
||||||
#[path="enc_dec_file.rs"] mod enc_dec_file;
|
#[path="enc_dec_file.rs"] mod enc_dec_file;
|
||||||
|
|
||||||
fn main() {
|
fn main() {
|
||||||
|
unsafe {
|
||||||
std::env::set_var("WEBKIT_DISABLE_DMABUF_RENDERER", "1");
|
std::env::set_var("WEBKIT_DISABLE_DMABUF_RENDERER", "1");
|
||||||
|
}
|
||||||
tauri::Builder::default()
|
tauri::Builder::default()
|
||||||
.setup(|app| {
|
.setup(|app| {
|
||||||
let app_handle = app.handle();
|
let app_handle = app.handle();
|
||||||
@@ -28,12 +29,19 @@ fn main() {
|
|||||||
})
|
})
|
||||||
.invoke_handler(tauri::generate_handler![
|
.invoke_handler(tauri::generate_handler![
|
||||||
tasks::check_or_create_tasks_file,
|
tasks::check_or_create_tasks_file,
|
||||||
|
// Tasks
|
||||||
tasks::get_tasks,
|
tasks::get_tasks,
|
||||||
tasks::search_tasks,
|
tasks::search_tasks,
|
||||||
tasks::add_task,
|
tasks::add_task,
|
||||||
tasks::edit_task,
|
tasks::edit_task,
|
||||||
tasks::delete_task,
|
tasks::delete_task,
|
||||||
tasks::set_task_field,
|
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::start_tcp_server,
|
||||||
multi_tcp_listener::get_status,
|
multi_tcp_listener::get_status,
|
||||||
multi_tcp_listener::send_command_to_server,
|
multi_tcp_listener::send_command_to_server,
|
||||||
|
|||||||
@@ -0,0 +1,103 @@
|
|||||||
|
use std::path::PathBuf;
|
||||||
|
use serde_json::{json, Value};
|
||||||
|
use crate::enc_dec_file::{decrypt_file, encrypt_n_save_file};
|
||||||
|
|
||||||
|
#[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());
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -6,7 +6,7 @@ use rust_fuzzy_search::fuzzy_search_best_n;
|
|||||||
|
|
||||||
use crate::enc_dec_file::{encrypt_n_save_file, decrypt_file};
|
use crate::enc_dec_file::{encrypt_n_save_file, decrypt_file};
|
||||||
|
|
||||||
static mut PATH_CONFIG: Option<&'static str> = None;
|
pub static mut PATH_CONFIG: Option<&'static str> = None;
|
||||||
|
|
||||||
#[tauri::command]
|
#[tauri::command]
|
||||||
pub fn check_or_create_tasks_file(app_handle: tauri::AppHandle) {
|
pub fn check_or_create_tasks_file(app_handle: tauri::AppHandle) {
|
||||||
@@ -59,11 +59,24 @@ pub fn check_or_create_tasks_file(app_handle: tauri::AppHandle) {
|
|||||||
pub fn get_tasks() -> Value {
|
pub fn get_tasks() -> Value {
|
||||||
unsafe {
|
unsafe {
|
||||||
let content = decrypt_file(PathBuf::from(PATH_CONFIG.unwrap()));
|
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();
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -121,6 +134,7 @@ pub fn add_task(date: String, time: String, name: String, description: String, p
|
|||||||
"name": name,
|
"name": name,
|
||||||
"description": description,
|
"description": description,
|
||||||
"priority": priority,
|
"priority": priority,
|
||||||
|
"subtasks": {},
|
||||||
"completed": false
|
"completed": false
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|||||||
@@ -7,7 +7,7 @@
|
|||||||
},
|
},
|
||||||
"package": {
|
"package": {
|
||||||
"productName": "to-do-app",
|
"productName": "to-do-app",
|
||||||
"version": "1.6.0"
|
"version": "1.7.0"
|
||||||
},
|
},
|
||||||
"tauri": {
|
"tauri": {
|
||||||
"allowlist": {
|
"allowlist": {
|
||||||
|
|||||||
+2
-2
@@ -111,7 +111,7 @@ function select_input(event){
|
|||||||
<template>
|
<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">
|
<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">
|
||||||
<div @click="$emit('close')" class="fixed left-0 top-0.5 text-lg dark:text-white cursor-pointer" title="Назад">
|
<div @click="$emit('close')" class="fixed left-0 top-0.5 text-lg dark:text-white cursor-pointer" title="Назад">
|
||||||
<Icon icon="material-symbols:arrow-back" width="32" height="32"/>
|
<Icon icon="material-symbols:arrow-back" width="28" height="28"/>
|
||||||
</div>
|
</div>
|
||||||
<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">
|
<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"/>
|
<Icon class="" icon="mdi:window-minimize" width="20" height="20"/>
|
||||||
@@ -176,7 +176,7 @@ function select_input(event){
|
|||||||
</div>
|
</div>
|
||||||
<div v-else>
|
<div v-else>
|
||||||
<button class="absolute top-10 left-2" @click="wiki_open = false">
|
<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"/>
|
<Icon icon="material-symbols:arrow-back" width="34" height="34" class="text-zinc-600 dark:text-zinc-200"/>
|
||||||
</button>
|
</button>
|
||||||
<PageHandler/>
|
<PageHandler/>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
+62
-18
@@ -11,6 +11,7 @@ import {useDark, useToggle} from "@vueuse/core";
|
|||||||
import PriorityModal from "./components/modals/PriorityModal.vue";
|
import PriorityModal from "./components/modals/PriorityModal.vue";
|
||||||
import {listen} from "@tauri-apps/api/event";
|
import {listen} from "@tauri-apps/api/event";
|
||||||
import ApiApp from "./ApiApp.vue";
|
import ApiApp from "./ApiApp.vue";
|
||||||
|
import SubtaskViewModal from "./components/modals/subtasks/ViewModal.vue";
|
||||||
|
|
||||||
const isDark = useDark();
|
const isDark = useDark();
|
||||||
const toggleDark = useToggle(isDark);
|
const toggleDark = useToggle(isDark);
|
||||||
@@ -24,6 +25,7 @@ let api_status = ref(false);
|
|||||||
let create_modal = ref(false);
|
let create_modal = ref(false);
|
||||||
let edit_modal = ref(false);
|
let edit_modal = ref(false);
|
||||||
let priority_modal = ref(false);
|
let priority_modal = ref(false);
|
||||||
|
let subtask_modal = ref(false);
|
||||||
|
|
||||||
let search_text = ref("");
|
let search_text = ref("");
|
||||||
|
|
||||||
@@ -36,6 +38,10 @@ let to_change_id = ref("");
|
|||||||
let to_change_name = ref("");
|
let to_change_name = ref("");
|
||||||
let to_change_priority = ref("");
|
let to_change_priority = ref("");
|
||||||
|
|
||||||
|
let id_task_for_subtasks = ref("");
|
||||||
|
|
||||||
|
|
||||||
|
onBeforeMount(async () => {
|
||||||
async function get_status() {
|
async function get_status() {
|
||||||
await invoke('get_status')
|
await invoke('get_status')
|
||||||
.then((res) => {
|
.then((res) => {
|
||||||
@@ -43,7 +49,6 @@ async function get_status() {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
onBeforeMount(async () => {
|
|
||||||
await invoke('check_or_create_tasks_file')
|
await invoke('check_or_create_tasks_file')
|
||||||
.then(async () => {
|
.then(async () => {
|
||||||
await get_tasks();
|
await get_tasks();
|
||||||
@@ -56,15 +61,27 @@ onBeforeMount(async () => {
|
|||||||
})
|
})
|
||||||
});
|
});
|
||||||
|
|
||||||
|
// Tasks
|
||||||
function compare_priority(a, b) {
|
function reorder_tasks(){
|
||||||
|
tasks.value = tasks.value.sort(function (a, b) {
|
||||||
if ( parseInt(a.priority) < parseInt(b.priority) ){
|
if ( parseInt(a.priority) < parseInt(b.priority) ){
|
||||||
return -1;
|
|
||||||
}
|
|
||||||
if ( parseInt(a.priority) > parseInt(b.priority) ){
|
|
||||||
return 1;
|
return 1;
|
||||||
}
|
}
|
||||||
|
if ( parseInt(a.priority) > parseInt(b.priority) ){
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
return 0;
|
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(){
|
async function get_tasks(){
|
||||||
@@ -73,20 +90,22 @@ async function get_tasks(){
|
|||||||
for (let value in res){
|
for (let value in res){
|
||||||
tmp_tasks.push(res[value]);
|
tmp_tasks.push(res[value]);
|
||||||
}
|
}
|
||||||
tasks.value = tmp_tasks.sort(compare_priority).reverse();
|
tasks.value = tmp_tasks;
|
||||||
|
reorder_tasks();
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
async function search_tasks(event){
|
async function search_tasks(event){
|
||||||
let tmp_tasks = [];
|
let tmp_tasks = [];
|
||||||
if (event.target.value) {
|
if (event.target.value.length > 1) {
|
||||||
await invoke('search_tasks', {
|
await invoke('search_tasks', {
|
||||||
value: event.target.value
|
value: event.target.value
|
||||||
}).then((res) => {
|
}).then((res) => {
|
||||||
for (let value in res){
|
for (let value in res){
|
||||||
tmp_tasks.push(res[value]);
|
tmp_tasks.push(res[value]);
|
||||||
}
|
}
|
||||||
tasks.value = tmp_tasks.sort(compare_priority).reverse();
|
tasks.value = tmp_tasks;
|
||||||
|
reorder_tasks();
|
||||||
});
|
});
|
||||||
} else{
|
} else{
|
||||||
await get_tasks();
|
await get_tasks();
|
||||||
@@ -113,6 +132,7 @@ async function set_task_field(id_task, completed){
|
|||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
reorder_tasks();
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -142,7 +162,18 @@ function change_priority(id_task, name, priority){
|
|||||||
priority_modal.value = true;
|
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;
|
||||||
|
}
|
||||||
|
|
||||||
|
// API
|
||||||
function change_api(){
|
function change_api(){
|
||||||
api.value = !api.value;
|
api.value = !api.value;
|
||||||
}
|
}
|
||||||
@@ -211,7 +242,7 @@ function change_api(){
|
|||||||
<div id="list" class="relative mx-[14vw] lg:mx-[22vw] mt-5">
|
<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">
|
<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">
|
<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">
|
<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' ]"><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>
|
<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">
|
<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 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>
|
||||||
@@ -220,25 +251,38 @@ function change_api(){
|
|||||||
</div>
|
</div>
|
||||||
<div class="shrink-0 group-hover/task:opacity-100 transition-all">
|
<div class="shrink-0 group-hover/task:opacity-100 transition-all">
|
||||||
<div class="inline-flex items-center gap-x-1">
|
<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)">
|
<div class="mr-4 text-zinc-400 cursor-pointer" @click="subtask_modal = !subtask_modal; id_task_for_subtasks=task.id">
|
||||||
<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"/>
|
<p v-if="Object.keys(task.subtasks).length === 0"
|
||||||
<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"/>
|
class="hidden group-hover/task:flex font-bold text-xl line-through">0/0</p>
|
||||||
<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"/>
|
<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"><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>
|
||||||
|
<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)">
|
||||||
|
<Icon v-if="parseInt(task.priority) === 0" class="opacity-60 dark:text-white text-gray-500 mr-1" 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 mr-1" 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 mr-1" 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) >= 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"/>
|
<Icon v-if="parseInt(task.priority) >= 9" class="opacity-100 text-red-500" icon="heroicons-solid:exclamation" width="32" height="32"/>
|
||||||
</div>
|
</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>
|
||||||
</div>
|
</div>
|
||||||
</li>
|
</li>
|
||||||
</ul>
|
</ul>
|
||||||
</div>
|
</div>
|
||||||
<div v-if="create_modal || edit_modal || priority_modal" class="bg-black w-[100vw] h-full fixed top-0 opacity-70 z-40"/>
|
<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"/>
|
<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"/>
|
<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"/>
|
<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"/>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div v-else>
|
<div v-else>
|
||||||
|
|||||||
@@ -25,7 +25,7 @@ async function send_edited_task(){
|
|||||||
</script>
|
</script>
|
||||||
|
|
||||||
<template>
|
<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 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">
|
<div class="relative p-4 max-h-full">
|
||||||
<!-- Modal content -->
|
<!-- Modal content -->
|
||||||
<form class="relative bg-white rounded-lg shadow dark:bg-[rgb(50,50,50)]" @submit.prevent="send_edited_task">
|
<form class="relative bg-white rounded-lg shadow dark:bg-[rgb(50,50,50)]" @submit.prevent="send_edited_task">
|
||||||
|
|||||||
@@ -21,7 +21,7 @@ async function send_edited_task(){
|
|||||||
</script>
|
</script>
|
||||||
|
|
||||||
<template>
|
<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 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">
|
<div class="relative p-4 max-h-full">
|
||||||
<!-- Modal content -->
|
<!-- Modal content -->
|
||||||
<form class="relative bg-white rounded-lg shadow dark:bg-[rgb(50,50,50)]" @submit.prevent="send_edited_task">
|
<form class="relative bg-white rounded-lg shadow dark:bg-[rgb(50,50,50)]" @submit.prevent="send_edited_task">
|
||||||
|
|||||||
@@ -0,0 +1,85 @@
|
|||||||
|
<script setup>
|
||||||
|
import {ref} from "vue";
|
||||||
|
import {invoke} from "@tauri-apps/api";
|
||||||
|
|
||||||
|
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";
|
||||||
|
|
||||||
|
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,138 @@
|
|||||||
|
<script setup>
|
||||||
|
import {onBeforeMount, ref} from "vue";
|
||||||
|
import {invoke} from "@tauri-apps/api";
|
||||||
|
import {Icon} from "@iconify/vue";
|
||||||
|
import CreateSubModal from "./CreateSubModal.vue";
|
||||||
|
import EditSubModal from "./EditSubModal.vue";
|
||||||
|
|
||||||
|
const props = defineProps({
|
||||||
|
task_id: String,
|
||||||
|
})
|
||||||
|
|
||||||
|
let task_id = ref(props.task_id);
|
||||||
|
|
||||||
|
let subtasks = ref({});
|
||||||
|
|
||||||
|
let create_modal = ref(false);
|
||||||
|
let edit_modal = ref(false);
|
||||||
|
|
||||||
|
let to_edit_id = ref("");
|
||||||
|
let to_edit_name = ref("");
|
||||||
|
let to_edit_description = ref("");
|
||||||
|
|
||||||
|
onBeforeMount(async () => {
|
||||||
|
await init();
|
||||||
|
});
|
||||||
|
|
||||||
|
async function init(){
|
||||||
|
create_modal.value = false;
|
||||||
|
await get_subtasks();
|
||||||
|
}
|
||||||
|
|
||||||
|
async function get_subtasks(){
|
||||||
|
await invoke('get_subtasks', {
|
||||||
|
idTask: task_id.value
|
||||||
|
}).then((res) => {
|
||||||
|
subtasks.value = res;
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
async function set_subtask_field(id_task, completed){
|
||||||
|
let field = "completed"
|
||||||
|
completed = completed.toString();
|
||||||
|
if (completed === "false"){
|
||||||
|
completed = "true"
|
||||||
|
} else{
|
||||||
|
completed = "false"
|
||||||
|
}
|
||||||
|
await invoke('set_subtask_field', {
|
||||||
|
idTask: task_id.value.toString(),
|
||||||
|
idSubtask: id_task.toString(),
|
||||||
|
field: field,
|
||||||
|
value: completed
|
||||||
|
}).then(async () => {
|
||||||
|
for (let index in subtasks.value){
|
||||||
|
if (subtasks.value[index].id === id_task){
|
||||||
|
subtasks.value[index][field] = completed;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
async function delete_subtask(id_subtask){
|
||||||
|
await invoke('delete_subtask', {
|
||||||
|
idTask: task_id.value.toString(),
|
||||||
|
idSubtask: id_subtask.toString()
|
||||||
|
}).then(async () => {
|
||||||
|
await get_subtasks();
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
function edit_subtask(id_task, name, description){
|
||||||
|
window.scrollTo({top:0});
|
||||||
|
to_edit_id.value = id_task;
|
||||||
|
to_edit_name.value = name;
|
||||||
|
to_edit_description.value = description;
|
||||||
|
edit_modal.value = true;
|
||||||
|
}
|
||||||
|
</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-[78vw] 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" class="flex justify-between gap-x-6 py-5 group/task">
|
||||||
|
<div @click="set_subtask_field(subtask.id, subtask.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="[ subtask.completed === 'true' ? 'border-green-500 group-hover:border-green-400' : 'border-gray-300 group-hover:border-gray-500' ]"><Icon class="m-0.5" :class="[ subtask.completed === 'true' ? 'text-green-500 group-hover:text-green-400' : 'text-gray-50 group-hover:text-gray-500 dark:text-[rgb(50,50,50)]' ]" 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="[ subtask.completed === 'true' ? 'line-through text-gray-400' : 'text-gray-900 dark:text-gray-300' ]" >{{subtask.name}}</p>
|
||||||
|
<p class="truncate text-xs leading-5 text-gray-500 dark:text-gray-400">{{subtask.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="hidden group-hover/task:flex gap-1">
|
||||||
|
<button @click="edit_subtask(subtask.id, subtask.name, subtask.description)" 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_subtask(subtask.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>
|
||||||
|
<p class="mt-0.5 text-xs text-gray-400 mx-2 text-center">{{subtask.date}}<br>{{subtask.time}}</p>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</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"/>
|
||||||
|
<EditSubModal v-if="edit_modal" :task_id="task_id" :subtask_id="to_edit_id" :subtask_name="to_edit_name" :subtask_description="to_edit_description" @close="edit_modal = !edit_modal"/>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<style scoped>
|
||||||
|
|
||||||
|
</style>
|
||||||
Reference in New Issue
Block a user