diff --git a/i18n-2.xlsm b/i18n-2.xlsm index 2f47286..912d8ec 100644 Binary files a/i18n-2.xlsm and b/i18n-2.xlsm differ diff --git a/index.html b/index.html index e7797b8..d05b9b2 100644 --- a/index.html +++ b/index.html @@ -14,6 +14,7 @@ + diff --git a/package-lock.json b/package-lock.json index 0545aad..32966f1 100644 --- a/package-lock.json +++ b/package-lock.json @@ -26,7 +26,7 @@ "@twa-dev/types": "^8.0.2", "@types/node": "^20.17.30", "@types/telegram-web-app": "^7.10.1", - "@vue/devtools": "^7.7.2", + "@vue/devtools": "^7.7.6", "@vue/eslint-config-typescript": "^14.1.3", "autoprefixer": "^10.4.2", "eslint": "^9.14.0", @@ -2142,9 +2142,9 @@ "license": "MIT" }, "node_modules/@types/cors": { - "version": "2.8.17", - "resolved": "https://registry.npmjs.org/@types/cors/-/cors-2.8.17.tgz", - "integrity": "sha512-8CGDvrBj1zgo2qE+oS3pOCyYNqCPryMWY2bGfwA0dcfopWGgxs+78df0Rs3rc9THP4JkOhLsAa+15VdpAqkcUA==", + "version": "2.8.19", + "resolved": "https://registry.npmjs.org/@types/cors/-/cors-2.8.19.tgz", + "integrity": "sha512-mFNylyeyqN93lfe/9CSxOGREz8cpzAhH+E93xJ4xWQf62V8sQ/24reV2nyzUWM6H6Xji+GGHpkbLe7pVoUEskg==", "dev": true, "license": "MIT", "dependencies": { @@ -2642,14 +2642,14 @@ } }, "node_modules/@vue/devtools": { - "version": "7.7.2", - "resolved": "https://registry.npmjs.org/@vue/devtools/-/devtools-7.7.2.tgz", - "integrity": "sha512-YLGea5P5cX3av6ExnQ08cbk/BYSUyfp0frRPQQgEYVfC53QV8UVisYFVdB2eFCjsQ9b+z0LmEIGtXAmTMnKQNw==", + "version": "7.7.6", + "resolved": "https://registry.npmjs.org/@vue/devtools/-/devtools-7.7.6.tgz", + "integrity": "sha512-i/mADVyhxpvy6F2nFzN/3eY0OkGZDiDmIxGcAx/0BziDpHpi+1lWJRiJrt8yuKhpKe7Zfv9FM+UqXuZf6xHkHw==", "dev": true, "license": "MIT", "dependencies": { - "@vue/devtools-electron": "^7.7.2", - "@vue/devtools-kit": "^7.7.2" + "@vue/devtools-electron": "^7.7.6", + "@vue/devtools-kit": "^7.7.6" }, "bin": { "vue-devtools": "cli.mjs" @@ -2662,18 +2662,18 @@ "license": "MIT" }, "node_modules/@vue/devtools-core": { - "version": "7.7.2", - "resolved": "https://registry.npmjs.org/@vue/devtools-core/-/devtools-core-7.7.2.tgz", - "integrity": "sha512-lexREWj1lKi91Tblr38ntSsy6CvI8ba7u+jmwh2yruib/ltLUcsIzEjCnrkh1yYGGIKXbAuYV2tOG10fGDB9OQ==", + "version": "7.7.6", + "resolved": "https://registry.npmjs.org/@vue/devtools-core/-/devtools-core-7.7.6.tgz", + "integrity": "sha512-ghVX3zjKPtSHu94Xs03giRIeIWlb9M+gvDRVpIZ/cRIxKHdW6HE/sm1PT3rUYS3aV92CazirT93ne+7IOvGUWg==", "dev": true, "license": "MIT", "dependencies": { - "@vue/devtools-kit": "^7.7.2", - "@vue/devtools-shared": "^7.7.2", + "@vue/devtools-kit": "^7.7.6", + "@vue/devtools-shared": "^7.7.6", "mitt": "^3.0.1", - "nanoid": "^5.0.9", - "pathe": "^2.0.2", - "vite-hot-client": "^0.2.4" + "nanoid": "^5.1.0", + "pathe": "^2.0.3", + "vite-hot-client": "^2.0.4" }, "peerDependencies": { "vue": "^3.0.0" @@ -2706,43 +2706,43 @@ "license": "MIT" }, "node_modules/@vue/devtools-electron": { - "version": "7.7.2", - "resolved": "https://registry.npmjs.org/@vue/devtools-electron/-/devtools-electron-7.7.2.tgz", - "integrity": "sha512-WxCwLdqBdKDGHwEAU9BozOgPIOMwncM8lcze4fDs5HYRGaICclW9du1ARH5eewJl8wTvSGs2I0r/p5q60p6wAA==", + "version": "7.7.6", + "resolved": "https://registry.npmjs.org/@vue/devtools-electron/-/devtools-electron-7.7.6.tgz", + "integrity": "sha512-RAQr0hRiZbXE86OZi/gOWguTPEW2kUWk7ox1CO24e8H7P+85YdYByv/rYBccGukdrfIygbuJmhkiiYLmdsS3EQ==", "dev": true, "license": "MIT", "dependencies": { - "@vue/devtools-core": "^7.7.2", - "@vue/devtools-kit": "^7.7.2", - "@vue/devtools-shared": "^7.7.2", - "electron": "^32.2.6", - "execa": "^9.5.1", - "h3": "^1.13.0", + "@vue/devtools-core": "^7.7.6", + "@vue/devtools-kit": "^7.7.6", + "@vue/devtools-shared": "^7.7.6", + "electron": "^33.4.8", + "execa": "^9.5.2", + "h3": "^1.15.1", "ip": "^2.0.1", - "pathe": "^2.0.2", + "pathe": "^2.0.3", "socket.io": "^4.8.1", "socket.io-client": "^4.8.1" } }, "node_modules/@vue/devtools-electron/node_modules/execa": { - "version": "9.5.2", - "resolved": "https://registry.npmjs.org/execa/-/execa-9.5.2.tgz", - "integrity": "sha512-EHlpxMCpHWSAh1dgS6bVeoLAXGnJNdR93aabr4QCGbzOM73o5XmRfM/e5FUqsw3aagP8S8XEWUWFAxnRBnAF0Q==", + "version": "9.6.0", + "resolved": "https://registry.npmjs.org/execa/-/execa-9.6.0.tgz", + "integrity": "sha512-jpWzZ1ZhwUmeWRhS7Qv3mhpOhLfwI+uAX4e5fOcXqwMR7EcJ0pj2kV1CVzHVMX/LphnKWD3LObjZCoJ71lKpHw==", "dev": true, "license": "MIT", "dependencies": { "@sindresorhus/merge-streams": "^4.0.0", - "cross-spawn": "^7.0.3", + "cross-spawn": "^7.0.6", "figures": "^6.1.0", "get-stream": "^9.0.0", - "human-signals": "^8.0.0", + "human-signals": "^8.0.1", "is-plain-obj": "^4.1.0", "is-stream": "^4.0.1", "npm-run-path": "^6.0.0", - "pretty-ms": "^9.0.0", + "pretty-ms": "^9.2.0", "signal-exit": "^4.1.0", "strip-final-newline": "^4.0.0", - "yoctocolors": "^2.0.0" + "yoctocolors": "^2.1.1" }, "engines": { "node": "^18.19.0 || >=20.5.0" @@ -2855,25 +2855,25 @@ } }, "node_modules/@vue/devtools-kit": { - "version": "7.7.2", - "resolved": "https://registry.npmjs.org/@vue/devtools-kit/-/devtools-kit-7.7.2.tgz", - "integrity": "sha512-CY0I1JH3Z8PECbn6k3TqM1Bk9ASWxeMtTCvZr7vb+CHi+X/QwQm5F1/fPagraamKMAHVfuuCbdcnNg1A4CYVWQ==", + "version": "7.7.6", + "resolved": "https://registry.npmjs.org/@vue/devtools-kit/-/devtools-kit-7.7.6.tgz", + "integrity": "sha512-geu7ds7tem2Y7Wz+WgbnbZ6T5eadOvozHZ23Atk/8tksHMFOFylKi1xgGlQlVn0wlkEf4hu+vd5ctj1G4kFtwA==", "dev": true, "license": "MIT", "dependencies": { - "@vue/devtools-shared": "^7.7.2", - "birpc": "^0.2.19", + "@vue/devtools-shared": "^7.7.6", + "birpc": "^2.3.0", "hookable": "^5.5.3", "mitt": "^3.0.1", "perfect-debounce": "^1.0.0", "speakingurl": "^14.0.1", - "superjson": "^2.2.1" + "superjson": "^2.2.2" } }, "node_modules/@vue/devtools-shared": { - "version": "7.7.2", - "resolved": "https://registry.npmjs.org/@vue/devtools-shared/-/devtools-shared-7.7.2.tgz", - "integrity": "sha512-uBFxnp8gwW2vD6FrJB8JZLUzVb6PNRG0B0jBnHsOH8uKyva2qINY8PTF5Te4QlTbMDqU5K6qtJDr6cNsKWhbOA==", + "version": "7.7.6", + "resolved": "https://registry.npmjs.org/@vue/devtools-shared/-/devtools-shared-7.7.6.tgz", + "integrity": "sha512-yFEgJZ/WblEsojQQceuyK6FzpFDx4kqrz2ohInxNj5/DnhoX023upTv4OD6lNPLAA5LLkbwPVb10o/7b+Y4FVA==", "dev": true, "license": "MIT", "dependencies": { @@ -3488,9 +3488,9 @@ } }, "node_modules/birpc": { - "version": "0.2.19", - "resolved": "https://registry.npmjs.org/birpc/-/birpc-0.2.19.tgz", - "integrity": "sha512-5WeXXAvTmitV1RqJFppT5QtUiz2p1mRSYU000Jkft5ZUCLJIk4uQriYNO50HknxKwM6jd8utNc66K1qGIwwWBQ==", + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/birpc/-/birpc-2.3.0.tgz", + "integrity": "sha512-ijbtkn/F3Pvzb6jHypHRyve2QApOCZDR25D/VnkY2G/lBNcXCTsnsCxgY4k4PkVB7zfwzYbY3O9Lcqe3xufS5g==", "dev": true, "license": "MIT", "funding": { @@ -4485,9 +4485,9 @@ } }, "node_modules/crossws": { - "version": "0.3.4", - "resolved": "https://registry.npmjs.org/crossws/-/crossws-0.3.4.tgz", - "integrity": "sha512-uj0O1ETYX1Bh6uSgktfPvwDiPYGQ3aI4qVsaC/LWpkIzGj1nUYm5FK3K+t11oOlpN01lGbprFCH4wBlKdJjVgw==", + "version": "0.3.5", + "resolved": "https://registry.npmjs.org/crossws/-/crossws-0.3.5.tgz", + "integrity": "sha512-ojKiDvcmByhwa8YYqbQI/hg7MEU0NC03+pSdEq4ZUnZR9xXpwk7E43SMNGkn+JxJGPFtNvQ48+vV2p+P1ml5PA==", "dev": true, "license": "MIT", "dependencies": { @@ -4914,9 +4914,9 @@ "license": "MIT" }, "node_modules/electron": { - "version": "32.3.3", - "resolved": "https://registry.npmjs.org/electron/-/electron-32.3.3.tgz", - "integrity": "sha512-7FT8tDg+MueAw8dBn5LJqDvlM4cZkKJhXfgB3w7P5gvSoUQVAY6LIQcXJxgL+vw2rIRY/b9ak7ZBFbCMF2Bk4w==", + "version": "33.4.11", + "resolved": "https://registry.npmjs.org/electron/-/electron-33.4.11.tgz", + "integrity": "sha512-xmdAs5QWRkInC7TpXGNvzo/7exojubk+72jn1oJL7keNeIlw7xNglf8TGtJtkR4rWC5FJq0oXiIXPS9BcK2Irg==", "dev": true, "hasInstallScript": true, "license": "MIT", @@ -6636,20 +6636,20 @@ "license": "MIT" }, "node_modules/h3": { - "version": "1.15.1", - "resolved": "https://registry.npmjs.org/h3/-/h3-1.15.1.tgz", - "integrity": "sha512-+ORaOBttdUm1E2Uu/obAyCguiI7MbBvsLTndc3gyK3zU+SYLoZXlyCP9Xgy0gikkGufFLTZXCXD6+4BsufnmHA==", + "version": "1.15.3", + "resolved": "https://registry.npmjs.org/h3/-/h3-1.15.3.tgz", + "integrity": "sha512-z6GknHqyX0h9aQaTx22VZDf6QyZn+0Nh+Ym8O/u0SGSkyF5cuTJYKlc8MkzW3Nzf9LE1ivcpmYC3FUGpywhuUQ==", "dev": true, "license": "MIT", "dependencies": { "cookie-es": "^1.2.2", - "crossws": "^0.3.3", + "crossws": "^0.3.4", "defu": "^6.1.4", - "destr": "^2.0.3", + "destr": "^2.0.5", "iron-webcrypto": "^1.2.1", "node-mock-http": "^1.0.0", "radix3": "^1.1.2", - "ufo": "^1.5.4", + "ufo": "^1.6.1", "uncrypto": "^0.1.3" } }, @@ -11326,9 +11326,9 @@ } }, "node_modules/ufo": { - "version": "1.5.4", - "resolved": "https://registry.npmjs.org/ufo/-/ufo-1.5.4.tgz", - "integrity": "sha512-UsUk3byDzKd04EyoZ7U4DOlxQaD14JUKQl6/P7wiX4FNvUfm3XL246n9W5AmqwW5RSFJ27NAuM0iLscAOYUiGQ==", + "version": "1.6.1", + "resolved": "https://registry.npmjs.org/ufo/-/ufo-1.6.1.tgz", + "integrity": "sha512-9a4/uxlTWJ4+a5i0ooc1rU7C7YOw3wT+UGqdeNNHWnOF9qcMBgLRS+4IYUqbczewFx4mLEig6gawh7X6mFlEkA==", "dev": true, "license": "MIT" }, @@ -11619,9 +11619,9 @@ } }, "node_modules/vite-hot-client": { - "version": "0.2.4", - "resolved": "https://registry.npmjs.org/vite-hot-client/-/vite-hot-client-0.2.4.tgz", - "integrity": "sha512-a1nzURqO7DDmnXqabFOliz908FRmIppkBKsJthS8rbe8hBEXwEwe4C3Pp33Z1JoFCYfVL4kTOMLKk0ZZxREIeA==", + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/vite-hot-client/-/vite-hot-client-2.0.4.tgz", + "integrity": "sha512-W9LOGAyGMrbGArYJN4LBCdOC5+Zwh7dHvOHC0KmGKkJhsOzaKbpo/jEjpPKVHIW0/jBWj8RZG0NUxfgA8BxgAg==", "dev": true, "license": "MIT", "funding": { diff --git a/package.json b/package.json index 48a5004..687f01a 100644 --- a/package.json +++ b/package.json @@ -31,7 +31,7 @@ "@twa-dev/types": "^8.0.2", "@types/node": "^20.17.30", "@types/telegram-web-app": "^7.10.1", - "@vue/devtools": "^7.7.2", + "@vue/devtools": "^7.7.6", "@vue/eslint-config-typescript": "^14.1.3", "autoprefixer": "^10.4.2", "eslint": "^9.14.0", diff --git a/quasar.config.ts b/quasar.config.ts index 902d21b..8312784 100644 --- a/quasar.config.ts +++ b/quasar.config.ts @@ -50,7 +50,8 @@ export default defineConfig((ctx) => { alias: { 'composables': path.resolve(__dirname, './src/composables'), - 'types': path.resolve(__dirname, './src/types') + 'types': path.resolve(__dirname, './src/types'), + 'helpers': path.resolve(__dirname, './src/helpers') }, typescript: { diff --git a/src/App.vue b/src/App.vue index d2eeb01..5acbc42 100644 --- a/src/App.vue +++ b/src/App.vue @@ -3,13 +3,15 @@ - - - - \ No newline at end of file diff --git a/src/boot/axios.ts b/src/boot/axios.ts index 4c44fdc..aa0b788 100644 --- a/src/boot/axios.ts +++ b/src/boot/axios.ts @@ -1,6 +1,5 @@ import { defineBoot } from '#q-app/wrappers' import axios, { type AxiosInstance } from 'axios' -import { useAuthStore } from 'src/stores/auth' declare module 'vue' { interface ComponentCustomProperties { diff --git a/src/boot/global-components.ts b/src/boot/global-components.ts index 4488a41..ab598f4 100644 --- a/src/boot/global-components.ts +++ b/src/boot/global-components.ts @@ -3,9 +3,13 @@ import pnPageCard from 'components/pnPageCard.vue' import pnScrollList from 'components/pnScrollList.vue' import pnAutoAvatar from 'components/pnAutoAvatar.vue' import pnOverlay from 'components/pnOverlay.vue' -import pnDialogBody from 'components/pnDialogBody.vue' +import pnSmallDialog from 'components/pnSmallDialog.vue' import pnImageSelector from 'components/pnImageSelector.vue' import pnTaskPriorityIcon from 'components/pnTaskPriorityIcon.vue' +import pnChainAvatar from 'components/pnChainAvatar.vue' +import pnShadowScroll from 'components/pnShadowScroll.vue' +import pnFileUploader from 'components/pnFileUploader.vue' +import pnBottomSheetDialog from 'components/pnBottomSheetDialog.vue' const components = { pnPageCard, @@ -13,8 +17,12 @@ const components = { pnAutoAvatar, pnOverlay, pnImageSelector, - pnDialogBody, - pnTaskPriorityIcon + pnSmallDialog, + pnBottomSheetDialog, + pnTaskPriorityIcon, + pnChainAvatar, + pnShadowScroll, + pnFileUploader } export default boot(({ app }) => { diff --git a/src/boot/helpers.ts b/src/boot/helpers.ts deleted file mode 100644 index 6b1a2ea..0000000 --- a/src/boot/helpers.ts +++ /dev/null @@ -1,73 +0,0 @@ -function isDirty ( - obj1: Record | null | undefined, - obj2: Record | null | undefined -): boolean { - const actualObj1 = obj1 ?? {} - const actualObj2 = obj2 ?? {} - - const filteredObj1 = filterIgnored(actualObj1) - const filteredObj2 = filterIgnored(actualObj2) - - const allKeys = new Set([...Object.keys(filteredObj1), ...Object.keys(filteredObj2)]) - - for (const key of allKeys) { - const hasKey1 = Object.hasOwn(filteredObj1, key) - const hasKey2 = Object.hasOwn(filteredObj2, key) - - if (hasKey1 !== hasKey2) return false - - if (hasKey1 && hasKey2) { - const val1 = filteredObj1[key] - const val2 = filteredObj2[key] - - if (typeof val1 === 'string' && typeof val2 === 'string') { - if (val1.trim() !== val2.trim()) return false - } else if (val1 !== val2) { - return false - } - } - } - - return true -} - -function filterIgnored(obj: Record): Record { - const filtered: Record = {} - - for (const key in obj) { - const originalValue = obj[key] - - // Пропускаем значения, которые не string, number или boolean - if ( - typeof originalValue !== 'string' && - typeof originalValue !== 'number' && - typeof originalValue !== 'boolean' - ) { - continue - } - - let value = originalValue - - if (typeof value === 'string') { - value = value.trim() - if (value === '') continue - } - - if (value === 0 || value === false) continue - - filtered[key] = value - } - - return filtered -} - -function parseIntString (s: string | string[] | undefined) :number | null { - if (typeof s !== 'string') return null - const regex = /^[+-]?\d+$/ - return regex.test(s) ? Number(s) : null -} - -export { - isDirty, - parseIntString -} \ No newline at end of file diff --git a/src/boot/telegram-boot.ts b/src/boot/telegram-boot.ts index aa8c7df..445645c 100644 --- a/src/boot/telegram-boot.ts +++ b/src/boot/telegram-boot.ts @@ -1,6 +1,5 @@ import { defineBoot } from '#q-app/wrappers' import type { WebApp } from "@twa-dev/types" -import { useProjectsStore } from 'stores/projects' declare global { interface Window { @@ -16,10 +15,6 @@ export default defineBoot(({ app }) => { webApp.ready() webApp.SettingsButton.isVisible = true app.config.globalProperties.$tg = webApp - const projectStore = useProjectsStore() - if (Number(webApp.initDataUnsafe.start_param)) { - projectStore.setStartProjectId(Number(webApp.initDataUnsafe.start_param)) - } app.provide('tg', webApp) } }) diff --git a/src/components/companyInfoPersons.vue b/src/components/companyInfoPersons.vue index db051f9..ce1be05 100644 --- a/src/components/companyInfoPersons.vue +++ b/src/components/companyInfoPersons.vue @@ -13,10 +13,11 @@ @click="goPersonInfo()" > - - - - + diff --git a/src/components/meetingBlock.vue b/src/components/meetingBlock.vue index 4a2cdbd..7eb5b8a 100644 --- a/src/components/meetingBlock.vue +++ b/src/components/meetingBlock.vue @@ -1,253 +1,307 @@ + + + + + diff --git a/src/components/pnBottomSheetDialog.vue b/src/components/pnBottomSheetDialog.vue new file mode 100644 index 0000000..79d03d6 --- /dev/null +++ b/src/components/pnBottomSheetDialog.vue @@ -0,0 +1,97 @@ + + + + + diff --git a/src/components/pnChainAvatar.vue b/src/components/pnChainAvatar.vue new file mode 100644 index 0000000..6489c90 --- /dev/null +++ b/src/components/pnChainAvatar.vue @@ -0,0 +1,56 @@ + + + + + diff --git a/src/components/pnDialogBody.vue b/src/components/pnDialogBody.vue deleted file mode 100644 index da26e2c..0000000 --- a/src/components/pnDialogBody.vue +++ /dev/null @@ -1,37 +0,0 @@ - - - - - diff --git a/src/components/pnFileUploader.vue b/src/components/pnFileUploader.vue new file mode 100644 index 0000000..77cf454 --- /dev/null +++ b/src/components/pnFileUploader.vue @@ -0,0 +1,158 @@ + + + + + \ No newline at end of file diff --git a/src/components/pnPageCard.vue b/src/components/pnPageCard.vue index db8cf53..7f4b394 100644 --- a/src/components/pnPageCard.vue +++ b/src/components/pnPageCard.vue @@ -6,15 +6,13 @@ - +
+ +
diff --git a/src/components/pnScrollList.vue b/src/components/pnScrollList.vue index 0d88983..9b23999 100644 --- a/src/components/pnScrollList.vue +++ b/src/components/pnScrollList.vue @@ -5,62 +5,32 @@ >
- -
+
- + -
- - - -
- +
\ No newline at end of file + diff --git a/src/components/pnShadowScroll.vue b/src/components/pnShadowScroll.vue new file mode 100644 index 0000000..733f6fa --- /dev/null +++ b/src/components/pnShadowScroll.vue @@ -0,0 +1,107 @@ + + + + + diff --git a/src/components/pnSmallDialog.vue b/src/components/pnSmallDialog.vue new file mode 100644 index 0000000..3e04756 --- /dev/null +++ b/src/components/pnSmallDialog.vue @@ -0,0 +1,85 @@ + + + + + diff --git a/src/components/pnTaskPriorityIcon.vue b/src/components/pnTaskPriorityIcon.vue index 298b338..9c563c7 100644 --- a/src/components/pnTaskPriorityIcon.vue +++ b/src/components/pnTaskPriorityIcon.vue @@ -1,22 +1,45 @@ diff --git a/src/components/projectInfoBlock.vue b/src/components/projectInfoBlock.vue deleted file mode 100644 index 15cb87e..0000000 --- a/src/components/projectInfoBlock.vue +++ /dev/null @@ -1,74 +0,0 @@ - - - - - diff --git a/src/components/taskBlock.vue b/src/components/taskBlock.vue new file mode 100644 index 0000000..b72e06c --- /dev/null +++ b/src/components/taskBlock.vue @@ -0,0 +1,393 @@ + + + + + diff --git a/src/components/taskItem.vue b/src/components/taskItem.vue index 4ff1afc..d935393 100644 --- a/src/components/taskItem.vue +++ b/src/components/taskItem.vue @@ -1,50 +1,146 @@ diff --git a/src/pages/MeetingInfoPage.vue b/src/pages/MeetingInfoPage.vue index 6e637f9..b8748a5 100644 --- a/src/pages/MeetingInfoPage.vue +++ b/src/pages/MeetingInfoPage.vue @@ -1,195 +1,133 @@ diff --git a/src/pages/TaskAddPage.vue b/src/pages/TaskAddPage.vue index c308cbf..4096f8e 100644 --- a/src/pages/TaskAddPage.vue +++ b/src/pages/TaskAddPage.vue @@ -1,236 +1,39 @@ - - + \ No newline at end of file diff --git a/src/pages/TaskEditPage.vue b/src/pages/TaskEditPage.vue new file mode 100644 index 0000000..274c90c --- /dev/null +++ b/src/pages/TaskEditPage.vue @@ -0,0 +1,34 @@ + + + diff --git a/src/pages/TaskInfoPage.vue b/src/pages/TaskInfoPage.vue index 84eebc2..7756665 100644 --- a/src/pages/TaskInfoPage.vue +++ b/src/pages/TaskInfoPage.vue @@ -1,93 +1,327 @@ - - - - - - - - - {{ $t('settings__language') }} - - - - - - - - - - - {{ $t('settings__font_size') }} - - -
- - -
-
-
-
+ + + +
+ + + + + +
+ + +
diff --git a/src/pages/UserInfoPage.vue b/src/pages/UserInfoPage.vue index fe5884c..d623b00 100644 --- a/src/pages/UserInfoPage.vue +++ b/src/pages/UserInfoPage.vue @@ -2,37 +2,35 @@
- - - - - - +
{{ tname }}
@{{ user.username }}
-
@@ -44,6 +42,7 @@ v-show="item" :model-value="displayUser[key]" dense + readonly filled class="w100" :label = "$t('user_card__' + key)" @@ -60,21 +59,19 @@ diff --git a/src/pages/main/UsersPage.vue b/src/pages/main/UsersPage.vue index 536172d..15cc982 100644 --- a/src/pages/main/UsersPage.vue +++ b/src/pages/main/UsersPage.vue @@ -27,26 +27,26 @@ @click="goUserInfo(item.id)" > - - - - - - - - {{item.section1}} - - - {{item.section3}} - - -
- -
{{item.section2_1}}
-
{{'@' + item.section2_2}}
-
-
-
+ + + + + {{item.section1}} + + + {{item.section3}} + + +
+ +
{{item.section2_1}}
+
{{'@' + item.section2_2}}
+
+
+
diff --git a/src/router/index.ts b/src/router/index.ts index a346060..770fa93 100644 --- a/src/router/index.ts +++ b/src/router/index.ts @@ -24,26 +24,42 @@ export default defineRouter(function (/* { store, ssrContext } */) { }) Router.beforeEach(async (to) => { - console.log(to) - if (to.name === 'settings') return; - const projectsStore = useProjectsStore() + + if (to.name === 'settings') return + if (to.name === '404') return + const projectsStore = useProjectsStore() + console.log('router mount', projectsStore.startRouteInfo) + + if (projectsStore.startRouteInfo && to.path === '/') { + const { id, taskId, meetingId } = projectsStore.startRouteInfo + projectsStore.setStartRouteInfo(null) + + if (!projectsStore.isInit) await projectsStore.init() + const project = projectsStore.projectById(id) + + if (!project) return { name: '404' } + + return taskId + ? { name: 'task_info', params: { id, taskId } } + : meetingId + ? { name: 'meeting_info', params: { id, meetingId } } + : { name: 'files', params: { id } } + } + if (to.params.id) { const projectId = Number(to.params.id) - if (!projectsStore.isInit) await projectsStore.init() const project = projectsStore.projectById(projectId) - if (!project) return { name: 'page404' } - + if (!project) return { name: '404' } + if (projectsStore.currentProjectId !== projectId) { projectsStore.setCurrentProjectId(projectId) } } else { - if (!projectsStore.startProjectId) return { name: 'page404' } - projectsStore.setCurrentProjectId(projectsStore.startProjectId) - return { name: 'files', params: { id: projectsStore.startProjectId }} + return { name: '404' } } }) @@ -61,7 +77,7 @@ export default defineRouter(function (/* { store, ssrContext } */) { } Router.afterEach((to) => { - const BackButton = window.Telegram?.WebApp?.BackButton; + const BackButton = window.Telegram?.WebApp?.BackButton if (BackButton) { // Управление видимостью if (to.meta.hideBackButton) { diff --git a/src/router/routes.ts b/src/router/routes.ts index 39d66c8..a94d273 100644 --- a/src/router/routes.ts +++ b/src/router/routes.ts @@ -48,6 +48,11 @@ const routes: RouteRecordRaw[] = [ path: '/project/:id(\\d+)/task/add', component: () => import('pages/TaskAddPage.vue') }, + { + name: 'task_edit', + path: '/project/:id(\\d+)/task/:taskId(\\d+)/edit', + component: () => import('pages/TaskEditPage.vue'), + }, { name: 'task_info', path: '/project/:id(\\d+)/task/:taskId(\\d+)', @@ -60,7 +65,7 @@ const routes: RouteRecordRaw[] = [ }, { name: 'meeting_edit', - path: '/project/:id(\\d+)/meeting/:meetingId(\\d+)', + path: '/project/:id(\\d+)/meeting/:meetingId(\\d+)/edit', component: () => import('pages/MeetingEditPage.vue'), }, { @@ -81,6 +86,7 @@ const routes: RouteRecordRaw[] = [ ] }, { + name: '404', path: '/:catchAll(.*)*', component: () => import('pages/ErrorNotFound.vue'), } diff --git a/src/stores/auth.ts b/src/stores/auth.ts new file mode 100644 index 0000000..78ab3fd --- /dev/null +++ b/src/stores/auth.ts @@ -0,0 +1,21 @@ +import { defineStore } from 'pinia' +import { ref } from 'vue' +import { api } from 'boot/axios' +import type { WebApp } from '@twa-dev/types' + +export const useAuthStore = defineStore('auth', () => { + const isInit = ref(false) + const telegramUserData = ref() + + async function init (tg: WebApp) { + await api.post('/auth?' + tg?.initData) + telegramUserData.value = tg?.initDataUnsafe.user + isInit.value = true + } + + return { + isInit, + telegramUserData, + init + } +}) diff --git a/src/stores/chats.ts b/src/stores/chats.ts index a4ef668..0d135b7 100644 --- a/src/stores/chats.ts +++ b/src/stores/chats.ts @@ -13,8 +13,8 @@ export const useChatsStore = defineStore('chats', () => { const currentProjectId = computed(() => projectsStore.currentProjectId) async function init () { - const response = await api.get('/project/' + currentProjectId.value + '/chat') - const chatsAPI = response.data.data + const { data } = await api.get('/project/' + currentProjectId.value + '/chat') + const chatsAPI = data.data chats.value.push(...chatsAPI) isInit.value = true } @@ -24,6 +24,8 @@ export const useChatsStore = defineStore('chats', () => { isInit.value = false } + const getChats = computed(() => chats.value) + function chatById (id: number) { return chats.value.find(el =>el.id === id) } @@ -33,6 +35,7 @@ export const useChatsStore = defineStore('chats', () => { isInit, init, reset, + getChats, chatById } }) diff --git a/src/stores/files.ts b/src/stores/files.ts index 9eacac1..9f3b735 100644 --- a/src/stores/files.ts +++ b/src/stores/files.ts @@ -2,19 +2,19 @@ import { ref, computed } from 'vue' import { defineStore } from 'pinia' import { api } from 'boot/axios' import { useProjectsStore } from 'stores/projects' -import type { File } from 'types/File' +import type { FileLink } from 'types/FileLink' export const useFilesStore = defineStore('files', () => { - const files = ref([]) + const files = ref([]) const isInit = ref(false) const projectsStore = useProjectsStore() const currentProjectId = computed(() => projectsStore.currentProjectId) async function init () { - const response = await api.get('/project/' + currentProjectId.value + '/file') - const filesAPI = response.data.data + const { data } = await api.get('/project/' + currentProjectId.value + '/file') + const filesAPI = data.data files.value.push(...filesAPI) isInit.value = true } @@ -34,6 +34,7 @@ export const useFilesStore = defineStore('files', () => { return (await response).data.data } + const getFiles = computed(() => files.value) function fileById (id: number) { return files.value.find(el =>el.id === id) @@ -46,6 +47,7 @@ export const useFilesStore = defineStore('files', () => { reset, fileUrl, remove, + getFiles, fileById } }) diff --git a/src/stores/meetings.ts b/src/stores/meetings.ts index 9a280e9..7c20051 100644 --- a/src/stores/meetings.ts +++ b/src/stores/meetings.ts @@ -13,8 +13,8 @@ export const useMeetingsStore = defineStore('meetings', () => { const currentProjectId = computed(() => projectsStore.currentProjectId) async function init () { - const response = await api.get('/project/' + currentProjectId.value + '/meeting') - const meetingsAPI = response.data.data + const { data } = await api.get('/project/' + currentProjectId.value + '/meeting') + const meetingsAPI = data.data meetings.value.push(...meetingsAPI) isInit.value = true } @@ -24,44 +24,57 @@ export const useMeetingsStore = defineStore('meetings', () => { isInit.value = false } - async function add (meetingData: MeetingParams) { - const response = await api.post('/project/' + currentProjectId.value + '/meeting', meetingData) - const newMeetingAPI = response.data.data + async function add (meetingData: MeetingParams, newFiles: File[]) { + const { data } = await api.post('/project/' + currentProjectId.value + '/meeting', meetingData) + const newMeetingAPI = data.data meetings.value.push(newMeetingAPI) - return newMeetingAPI + const id = newMeetingAPI.id + await updateParticipants(id, meetingData.participants) + if (newFiles.length !== 0) await attachFiles(id, newFiles) + return newMeetingAPI //not include files and participants!! } async function update (meetingId: number, meetingData: MeetingParams) { - const response = await api.put('/project/' + currentProjectId.value + '/meeting/' + meetingId, meetingData) - const meetingAPI = response.data.data + const { data } = await api.put('/project/' + currentProjectId.value + '/meeting/' + meetingId, meetingData) + const meetingAPI = data.data const idx = meetings.value.findIndex(item => item.id === meetingAPI.id) if (meetings.value[idx]) Object.assign(meetings.value[idx], meetingAPI) } async function updateParticipants (meetingId: number, participants: number[]) { - const response = await api.put('/project/' + currentProjectId.value + '/meeting/' + meetingId + '/participant', participants) - const participantsAPI = response.data.data + const { data } = await api.put('/project/' + currentProjectId.value + '/meeting/' + meetingId + '/participant', participants) + const participantsAPI = data.data const idx = meetings.value.findIndex(item => item.id === meetingId) if (meetings.value[idx]) meetings.value[idx].participants = participantsAPI } - async function attachFiles (meetingId: number, files: number[]) { - const response = await api.put('/project/' + currentProjectId.value + '/meeting/' + meetingId + '/attach', files) - const filesAPI = response.data.data + async function attachFiles (meetingId: number, files: File[]) { + const formData = new FormData() + files.forEach(file => formData.append('files[]', file)) + const { data } = await api.post( + '/project/' + currentProjectId.value + '/meeting/' + meetingId + '/attach', + formData, + { + headers: { + 'Content-Type': 'multipart/form-data' + } + } + ) + const filesAPI = data.data const idx = meetings.value.findIndex(item => item.id === meetingId) if (meetings.value[idx]) meetings.value[idx].files = filesAPI } async function setCancelStatus (meetingId: number, status: boolean) { - const response = await api.put('/project/' + currentProjectId.value + '/meeting/' + meetingId, { is_cancel: status }) - const meetingAPI = response.data.data + const { data } = await api.put('/project/' + currentProjectId.value + '/meeting/' + meetingId, { is_cancel: status }) + const meetingAPI = data.data const idx = meetings.value.findIndex(item => item.id === meetingAPI.id) if (meetings.value[idx]) Object.assign(meetings.value[idx], meetingAPI) } async function remove (meetingId: number) { - const response = await api.delete('/project/' + currentProjectId.value + '/meeting/' + meetingId) - const meetingAPIid = response.data.data.id + const { data } = await api.delete('/project/' + currentProjectId.value + '/meeting/' + meetingId) + const meetingAPIid = data.data.id const idx = meetings.value.findIndex(item => item.id === meetingAPIid) meetings.value.splice(idx, 1) } @@ -70,6 +83,9 @@ export const useMeetingsStore = defineStore('meetings', () => { return meetings.value.find(el => el.id === id) } + // getters + const getMeetings = computed(() => meetings.value) + return { meetings, isInit, @@ -81,6 +97,7 @@ export const useMeetingsStore = defineStore('meetings', () => { attachFiles, setCancelStatus, remove, + getMeetings, meetingById } }) diff --git a/src/stores/projects.ts b/src/stores/projects.ts index 6af0459..5ec6881 100644 --- a/src/stores/projects.ts +++ b/src/stores/projects.ts @@ -1,7 +1,9 @@ -import { ref, watch } from 'vue' +import { ref, watch, computed } from 'vue' import { defineStore } from 'pinia' import { api } from 'boot/axios' +import { useAuthStore } from 'stores/auth' + import { useFilesStore } from 'stores/files' import { useTasksStore } from 'stores/tasks' import { useMeetingsStore } from 'stores/meetings' @@ -13,7 +15,6 @@ import type { Project } from 'types/Project' export const useProjectsStore = defineStore('projects', () => { const projects = ref([]) const currentProjectId = ref(null) - const startProjectId = ref(null) const isInit = ref(false) const filesStore = useFilesStore() @@ -23,8 +24,8 @@ export const useProjectsStore = defineStore('projects', () => { const chatsStore = useChatsStore() async function init () { - const response = await api.get('/project') - const projectsAPI = response.data.data + const { data } = await api.get('/project') + const projectsAPI = data.data projects.value.push(...projectsAPI) isInit.value = true } @@ -43,18 +44,21 @@ export const useProjectsStore = defineStore('projects', () => { currentProjectId.value = id } - function setStartProjectId (id: number | null) { - startProjectId.value = id - } + const authStore = useAuthStore() + async function initStores () { resetStores() - if (!filesStore.isInit) await filesStore.init() - if (!tasksStore.isInit) await tasksStore.init() - if (!meetingsStore.isInit) await meetingsStore.init() - if (!usersStore.isInit) await usersStore.init() - if (!chatsStore.isInit) await chatsStore.init() - + + await Promise.all([ + filesStore.init(), + tasksStore.init(), + meetingsStore.init(), + usersStore.init(), + chatsStore.init() + ]) + + usersStore.setMyId(authStore.telegramUserData.id) } function resetStores () { @@ -65,6 +69,14 @@ export const useProjectsStore = defineStore('projects', () => { chatsStore.reset() } + const getProjects = computed(() => projects.value) + + const startRouteInfo = ref<{ id: number; taskId?: number; meetingId?: number } | null>(null) + + function setStartRouteInfo (info: { id: number; taskId?: number; meetingId?: number } | null) { + startRouteInfo.value = info + } + watch (currentProjectId, async (newId) => { if (newId) await initStores(); else resetStores() }, { flush: 'sync' }) @@ -75,11 +87,12 @@ export const useProjectsStore = defineStore('projects', () => { isInit, projects, currentProjectId, - startProjectId, projectById, setCurrentProjectId, - setStartProjectId, initStores, - resetStores + resetStores, + getProjects, + startRouteInfo, + setStartRouteInfo } }) diff --git a/src/stores/settings.ts b/src/stores/settings.ts index 9453984..5619c08 100644 --- a/src/stores/settings.ts +++ b/src/stores/settings.ts @@ -54,8 +54,6 @@ export const useSettingsStore = defineStore('settings', () => { console.error('Quasar Error load locale:', quasarLang, e) } } - - const detectLocale = (): string => { const localeMap = { @@ -113,10 +111,10 @@ export const useSettingsStore = defineStore('settings', () => { const init = async () => { try { - const response = await api.get('/settings') + const { data } = await api.get('/settings') settings.value = { - fontSize: response.data.data.settings.fontSize || defaultSettings.fontSize, - locale: response.data.data.settings.locale || detectLocale() + fontSize: data.data.settings.fontSize || defaultSettings.fontSize, + locale: data.data.settings.locale || detectLocale() } } catch { settings.value.locale = detectLocale() diff --git a/src/stores/tasks.ts b/src/stores/tasks.ts index ba0efb6..0ac89b5 100644 --- a/src/stores/tasks.ts +++ b/src/stores/tasks.ts @@ -45,13 +45,37 @@ export const useTasksStore = defineStore('tasks', () => { if (tasks.value[idx]) tasks.value[idx].participants = observersAPI } - async function attachFiles (taskId: number, files: number[]) { - const response = await api.put('/project/' + currentProjectId.value + '/task/' + taskId + '/attach', files) + async function attachFiles (taskId: number, files: File[]) { + const formData = new FormData() + files.forEach(file => formData.append('files[]', file)) + const response = await api.post( + '/project/' + currentProjectId.value + '/task/' + taskId + '/attach', + formData, + { + headers: { + 'Content-Type': 'multipart/form-data' + } + } + ) const filesAPI = response.data.data const idx = tasks.value.findIndex(item => item.id === taskId) if (tasks.value[idx]) tasks.value[idx].files = filesAPI } + async function setCancelStatus (taskId: number) { + const response = await api.put('/project/' + currentProjectId.value + '/task/' + taskId, { status: 6 }) + const taskAPI = response.data.data + const idx = tasks.value.findIndex(item => item.id === taskAPI.id) + if (tasks.value[idx]) Object.assign(tasks.value[idx], taskAPI) + } + + async function setRestoreStatus (taskId: number) { + const response = await api.put('/project/' + currentProjectId.value + '/task/' + taskId, { status: 0 }) + const taskAPI = response.data.data + const idx = tasks.value.findIndex(item => item.id === taskAPI.id) + if (tasks.value[idx]) Object.assign(tasks.value[idx], taskAPI) + } + async function remove (taskId: number) { const response = await api.delete('/project/' + currentProjectId.value + '/task/' + taskId) const taskAPIid = response.data.data.id @@ -59,6 +83,8 @@ export const useTasksStore = defineStore('tasks', () => { tasks.value.splice(idx, 1) } + const getTasks = computed(() => tasks.value) + function taskById (id :number) { return tasks.value.find(el => el.id === id) } @@ -72,7 +98,10 @@ export const useTasksStore = defineStore('tasks', () => { update, updateObservers, attachFiles, + setCancelStatus, + setRestoreStatus, remove, + getTasks, taskById } }) diff --git a/src/stores/users.ts b/src/stores/users.ts index 8529bcb..f112cb4 100644 --- a/src/stores/users.ts +++ b/src/stores/users.ts @@ -6,15 +6,21 @@ import type { User } from 'types/User' export const useUsersStore = defineStore('users', () => { + interface myId { + telegram_id: number + id: number + } + const users = ref([]) const isInit = ref(false) + const myId = ref({ telegram_id: -1, id: -1 }) const projectsStore = useProjectsStore() const currentProjectId = computed(() => projectsStore.currentProjectId) async function init () { - const response = await api.get('/project/' + currentProjectId.value + '/user') - const usersAPI = response.data.data + const { data } = await api.get('/project/' + currentProjectId.value + '/user') + const usersAPI = data.data users.value.push(...usersAPI) isInit.value = true } @@ -34,6 +40,8 @@ export const useUsersStore = defineStore('users', () => { return users.value.find(el =>el.id === id) } + const getUsers = computed(() => users.value) + function userNameById (id: number) { const user = userById(id) return user?.fullname @@ -42,13 +50,22 @@ export const useUsersStore = defineStore('users', () => { || '---' } + function setMyId (telegram_id: number | undefined) { + if (!telegram_id) return + const me = users.value.find(el => el.telegram_id === telegram_id) + if (me) myId.value = { telegram_id: telegram_id, id: me.id } + } + return { users, isInit, + myId, init, reset, reload, userById, - userNameById + getUsers, + userNameById, + setMyId } }) diff --git a/src/types/File.ts b/src/types/FileLink.ts similarity index 86% rename from src/types/File.ts rename to src/types/FileLink.ts index 7dcf80e..5495ea4 100644 --- a/src/types/File.ts +++ b/src/types/FileLink.ts @@ -1,10 +1,11 @@ -interface File { +interface FileLink { id: number project_id: number origin_chat_id: number origin_message_id: number chat_id: number message_id: number + telegram_chat_id: number file_id: number filename: string mime: string @@ -19,5 +20,5 @@ interface File { } export type { - File + FileLink } diff --git a/src/types/Meeting.ts b/src/types/Meeting.ts index 7f1f8f8..e9ece19 100644 --- a/src/types/Meeting.ts +++ b/src/types/Meeting.ts @@ -3,16 +3,18 @@ interface MeetingParams { description: string place: string meet_date: number - chat_attach: number | null + chat_id: number | null participants: number[] files: number[] is_cancel: boolean + [key: string]: unknown } interface Meeting extends MeetingParams { id: number project_id: number created_by: number + is_editable: boolean [key: string]: unknown } diff --git a/src/types/Task.ts b/src/types/Task.ts index 72a52ae..1c9df80 100644 --- a/src/types/Task.ts +++ b/src/types/Task.ts @@ -1,12 +1,16 @@ interface TaskParams { name: string description: string - assigned_to: number + assigned_to: number | null priority: 0 | 1 | 2 | 3 - status: 1 | 5 + status: 1 | 5 | 6 time_spent?: number - create_date: number plan_date: number + observers: number[] + files: number[] + chat_id: number | null + close_files: number[] + close_comment: string } interface Task extends TaskParams { @@ -14,8 +18,7 @@ interface Task extends TaskParams { project_id: number created_by: number closed_by: number | null - observers: number[] - files: number[] + create_date: number close_date: number [key: string]: unknown }