Compare commits
2 Commits
04ea1f83c6
...
83d2666f22
| Author | SHA1 | Date | |
|---|---|---|---|
| 83d2666f22 | |||
| 3b628cbfb3 |
5
.vscode/settings.json
vendored
5
.vscode/settings.json
vendored
@@ -12,5 +12,8 @@
|
||||
"typescript",
|
||||
"vue"
|
||||
],
|
||||
"typescript.tsdk": "node_modules/typescript/lib"
|
||||
"typescript.tsdk": "node_modules/typescript/lib",
|
||||
"[json]": {
|
||||
"editor.defaultFormatter": "vscode.json-language-features"
|
||||
}
|
||||
}
|
||||
@@ -1,234 +0,0 @@
|
||||
<template>
|
||||
<pn-page-card>
|
||||
<template #title>
|
||||
{{ $t('software__title') }}
|
||||
</template>
|
||||
|
||||
<pn-scroll-list>
|
||||
<template #card-body-header>
|
||||
<div class="q-pa-md">
|
||||
{{ $t('software__description') }}
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<q-list separator>
|
||||
<q-expansion-item
|
||||
v-for="item in software"
|
||||
group="somegroup"
|
||||
>
|
||||
<template #header>
|
||||
<q-item-section avatar>
|
||||
<q-avatar square size="sm" v-if="item.logo">
|
||||
<img :src="'3software/logo/' + item.logo">
|
||||
</q-avatar>
|
||||
</q-item-section>
|
||||
|
||||
<q-item-section>
|
||||
<div class="flex items-baseline">
|
||||
<span class="text-h6">
|
||||
{{ item.name }}
|
||||
</span>
|
||||
<span v-if = "item.ver" class="text-caption q-pl-xs">
|
||||
{{ 'v.' + item.ver }}
|
||||
</span>
|
||||
</div>
|
||||
</q-item-section>
|
||||
</template>
|
||||
<div class="w100 flex column q-px-md q-gutter-y-md q-py-sm">
|
||||
<div class="flex row no-wrap items-center">
|
||||
<q-icon name="mdi-scale-balance" size="sm" class="q-pr-lg" color="grey"/>
|
||||
<div
|
||||
@click="downloadFile('3software/license/' + item.license_file)"
|
||||
class="flex w100 column q-pl-sm cursor-pointer"
|
||||
>
|
||||
<span> {{ item.license }} </span>
|
||||
<span
|
||||
class="text-caption"
|
||||
style="white-space: pre-line;"
|
||||
>
|
||||
{{ item.license_copyright }}
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="flex row no-wrap items-center" v-if="item.web">
|
||||
<q-icon name="mdi-web" size="sm" class="q-pr-lg" color="grey"/>
|
||||
<span
|
||||
class="q-pl-sm cursor-pointer"
|
||||
@click="tg.openLink(item.web_url)"
|
||||
>
|
||||
{{ item.web }}
|
||||
</span>
|
||||
</div>
|
||||
|
||||
<div class="flex row no-wrap items-center" v-if="item.git">
|
||||
<q-icon name="mdi-github" size="sm" class="q-pr-lg" color="grey"/>
|
||||
<span
|
||||
class="q-pl-sm cursor-pointer"
|
||||
@click="tg.openLink(item.git_url)"
|
||||
>
|
||||
{{ item.git }}
|
||||
</span>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
</q-expansion-item>
|
||||
</q-list>
|
||||
</pn-scroll-list>
|
||||
</pn-page-card>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import { inject } from 'vue'
|
||||
import type { WebApp } from '@twa-dev/types'
|
||||
|
||||
const tg = inject('tg') as WebApp
|
||||
|
||||
const software = [
|
||||
{
|
||||
id: 1,
|
||||
name: 'Vue',
|
||||
ver: '3.4',
|
||||
logo: 'vue.webp',
|
||||
web: 'vuejs.org',
|
||||
web_url: 'https://vuejs.org/',
|
||||
git: 'vuejs/core',
|
||||
git_url: 'https://github.com/vuejs/core',
|
||||
license: 'MIT License',
|
||||
license_copyright: 'Copyright (c) 2018-present, Yuxi (Evan) You and Vue contributors',
|
||||
license_file: 'vue/LICENSE.txt'
|
||||
},
|
||||
{
|
||||
id: 2,
|
||||
name: 'Quasar',
|
||||
ver: '2.x',
|
||||
logo: 'quasar.png',
|
||||
web: 'quasar.dev',
|
||||
web_url: 'https://quasar.dev/',
|
||||
git: 'quasarframework/quasar',
|
||||
git_url: 'https://github.com/quasarframework/quasar',
|
||||
license: 'MIT License',
|
||||
license_copyright: 'Copyright (c) 2015-present Razvan Stoenescu',
|
||||
license_file: 'quasar/LICENSE.txt'
|
||||
},
|
||||
{
|
||||
id: 3,
|
||||
name: 'Pinia',
|
||||
ver: '2.x',
|
||||
logo: 'pinia.svg',
|
||||
web: 'pinia.vuejs.org',
|
||||
web_url: 'https://pinia.vuejs.org/',
|
||||
git: 'vuejs/pinia',
|
||||
git_url: 'https://github.com/vuejs/pinia',
|
||||
license: 'MIT License',
|
||||
license_copyright: 'Copyright (c) 2019-present Eduardo San Martin Morote',
|
||||
license_file: 'pinia/LICENSE.txt'
|
||||
},
|
||||
{
|
||||
id: 4,
|
||||
name: 'Vue Router',
|
||||
ver: '4.x',
|
||||
logo: 'vue.webp',
|
||||
web: 'router.vuejs.org',
|
||||
web_url: 'https://router.vuejs.org/',
|
||||
git: 'vuejs/router',
|
||||
git_url: 'https://github.com/vuejs/router',
|
||||
license: 'MIT License',
|
||||
license_copyright: 'Copyright (c) 2019-present Eduardo San Martin Morote',
|
||||
license_file: 'vue-router/LICENSE.txt'
|
||||
},
|
||||
{
|
||||
id: 5,
|
||||
name: 'Vue i18n',
|
||||
ver: '9.x',
|
||||
logo: 'vue-i18n.svg',
|
||||
web: 'vue-i18n.intlify.dev',
|
||||
web_url: 'https://vue-i18n.intlify.dev/',
|
||||
git: 'intlify/vue-i18n',
|
||||
git_url: 'https://github.com/intlify/vue-i18n',
|
||||
license: 'MIT License',
|
||||
license_copyright: 'Copyright (c) 2016-present kazuya kawaguchi and contributors',
|
||||
license_file: 'vue-i18n/LICENSE.txt'
|
||||
},
|
||||
{
|
||||
id: 6,
|
||||
name: 'Axios',
|
||||
ver: '1.x',
|
||||
logo: 'axios.svg',
|
||||
web: 'axios-http.com',
|
||||
web_url: 'https://axios-http.com/',
|
||||
git: 'axios/axios',
|
||||
git_url: 'https://github.com/axios/axios',
|
||||
license: 'MIT License',
|
||||
license_copyright: 'Copyright (c) 2014-present Matt Zabriskie & Collaborators',
|
||||
license_file: 'axios/LICENSE.txt'
|
||||
},
|
||||
{
|
||||
id: 7,
|
||||
name: 'better-sqlite3',
|
||||
ver: '11.x',
|
||||
logo: '',
|
||||
web: '',
|
||||
web_url: '',
|
||||
git: 'WiseLibs/better-sqlite3',
|
||||
git_url: 'https://github.com/WiseLibs/better-sqlite3',
|
||||
license: 'MIT License',
|
||||
license_copyright: 'Copyright (c) 2017 Joshua Wise',
|
||||
license_file: 'better-sqlite3/LICENSE.txt'
|
||||
},
|
||||
{
|
||||
id: 8,
|
||||
name: 'Express',
|
||||
ver: '4.x',
|
||||
logo: 'express.svg',
|
||||
web: 'expressjs.com',
|
||||
web_url: 'https://expressjs.com/',
|
||||
git: 'expressjs/express',
|
||||
git_url: 'https://github.com/expressjs/express',
|
||||
license: 'MIT License',
|
||||
license_copyright: `Copyright (c) 2009-2014 TJ Holowaychuk <tj@vision-media.ca>
|
||||
Copyright (c) 2013-2014 Roman Shtylman <shtylman+expressjs@gmail.com>
|
||||
Copyright (c) 2014-2015 Douglas Christopher Wilson <doug@somethingdoug.com>`,
|
||||
license_file: 'express/LICENSE.txt'
|
||||
}
|
||||
]
|
||||
|
||||
|
||||
const downloadFile = async (url: string) => {
|
||||
try {
|
||||
const fullUrl = '/admin/' + url;
|
||||
const fileUrl = new URL(fullUrl, window.location.origin).href;
|
||||
|
||||
const response = await fetch(fileUrl)
|
||||
|
||||
if (!response.ok) throw new Error(`HTTP error: ${response.status}`)
|
||||
|
||||
const blob = await response.blob()
|
||||
const downloadUrl = URL.createObjectURL(blob)
|
||||
const link = document.createElement('a')
|
||||
|
||||
const extractFileName = (url: string): string => {
|
||||
return url.substring(url.lastIndexOf('/') + 1)
|
||||
}
|
||||
|
||||
link.href = downloadUrl
|
||||
link.download = extractFileName(url)
|
||||
link.style.display = 'none'
|
||||
|
||||
document.body.appendChild(link)
|
||||
link.click()
|
||||
|
||||
setTimeout(() => {
|
||||
document.body.removeChild(link)
|
||||
URL.revokeObjectURL(downloadUrl)
|
||||
}, 100)
|
||||
|
||||
} catch (error) {
|
||||
console.error('Download error:', error)
|
||||
}
|
||||
}
|
||||
|
||||
</script>
|
||||
|
||||
<style scope>
|
||||
</style>
|
||||
BIN
i18n-2.xlsm
BIN
i18n-2.xlsm
Binary file not shown.
@@ -17,6 +17,7 @@
|
||||
<!--
|
||||
<meta name="viewport" content="user-scalable=no, initial-scale=1, maximum-scale=1, minimum-scale=1, width=device-width<% if (ctx.mode.cordova || ctx.mode.capacitor) { %>, viewport-fit=cover<% } %>">
|
||||
-->
|
||||
<link rel="manifest" href="/site.webmanifest">
|
||||
<link rel="icon" href="icons/favicon.svg" type="image/svg+xml">
|
||||
<link rel="icon" type="image/ico" href="icons/favicon.ico">
|
||||
<link rel="apple-touch-icon" href="icons/apple-touch-icon.png">
|
||||
|
||||
16
package-lock.json
generated
16
package-lock.json
generated
@@ -13,6 +13,7 @@
|
||||
"@quasar/extras": "^1.17.0",
|
||||
"@quasar/vite-plugin": "^1.10.0",
|
||||
"axios": "^1.2.1",
|
||||
"browser-image-compression": "^2.0.2",
|
||||
"pinia": "^2.0.11",
|
||||
"quasar": "^2.18.2",
|
||||
"vue": "^3.4.18",
|
||||
@@ -3478,6 +3479,15 @@
|
||||
"node": ">=8"
|
||||
}
|
||||
},
|
||||
"node_modules/browser-image-compression": {
|
||||
"version": "2.0.2",
|
||||
"resolved": "https://registry.npmjs.org/browser-image-compression/-/browser-image-compression-2.0.2.tgz",
|
||||
"integrity": "sha512-pBLlQyUf6yB8SmmngrcOw3EoS4RpQ1BcylI3T9Yqn7+4nrQTXJD4sJDe5ODnJdrvNMaio5OicFo75rDyJD2Ucw==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"uzip": "0.20201231.0"
|
||||
}
|
||||
},
|
||||
"node_modules/browserslist": {
|
||||
"version": "4.24.4",
|
||||
"resolved": "https://registry.npmjs.org/browserslist/-/browserslist-4.24.4.tgz",
|
||||
@@ -11023,6 +11033,12 @@
|
||||
"node": ">= 0.4.0"
|
||||
}
|
||||
},
|
||||
"node_modules/uzip": {
|
||||
"version": "0.20201231.0",
|
||||
"resolved": "https://registry.npmjs.org/uzip/-/uzip-0.20201231.0.tgz",
|
||||
"integrity": "sha512-OZeJfZP+R0z9D6TmBgLq2LHzSSptGMGDGigGiEe0pr8UBe/7fdflgHlHBNDASTXB5jnFuxHpNaJywSg8YFeGng==",
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/varint": {
|
||||
"version": "6.0.0",
|
||||
"resolved": "https://registry.npmjs.org/varint/-/varint-6.0.0.tgz",
|
||||
|
||||
@@ -18,6 +18,7 @@
|
||||
"@quasar/extras": "^1.17.0",
|
||||
"@quasar/vite-plugin": "^1.10.0",
|
||||
"axios": "^1.2.1",
|
||||
"browser-image-compression": "^2.0.2",
|
||||
"pinia": "^2.0.11",
|
||||
"quasar": "^2.18.2",
|
||||
"vue": "^3.4.18",
|
||||
|
||||
@@ -1,7 +0,0 @@
|
||||
# Copyright (c) 2014-present Matt Zabriskie & Collaborators
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||
@@ -1,21 +0,0 @@
|
||||
The MIT License (MIT)
|
||||
|
||||
Copyright (c) 2017 Joshua Wise
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
in the Software without restriction, including without limitation the rights
|
||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
copies of the Software, and to permit persons to whom the Software is
|
||||
furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in all
|
||||
copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
SOFTWARE.
|
||||
@@ -1,24 +0,0 @@
|
||||
(The MIT License)
|
||||
|
||||
Copyright (c) 2009-2014 TJ Holowaychuk <tj@vision-media.ca>
|
||||
Copyright (c) 2013-2014 Roman Shtylman <shtylman+expressjs@gmail.com>
|
||||
Copyright (c) 2014-2015 Douglas Christopher Wilson <doug@somethingdoug.com>
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining
|
||||
a copy of this software and associated documentation files (the
|
||||
'Software'), to deal in the Software without restriction, including
|
||||
without limitation the rights to use, copy, modify, merge, publish,
|
||||
distribute, sublicense, and/or sell copies of the Software, and to
|
||||
permit persons to whom the Software is furnished to do so, subject to
|
||||
the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be
|
||||
included in all copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED 'AS IS', WITHOUT WARRANTY OF ANY KIND,
|
||||
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
||||
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
|
||||
IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
|
||||
CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
|
||||
TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
|
||||
SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||
@@ -1,21 +0,0 @@
|
||||
The MIT License (MIT)
|
||||
|
||||
Copyright (c) 2019-present Eduardo San Martin Morote
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
in the Software without restriction, including without limitation the rights
|
||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
copies of the Software, and to permit persons to whom the Software is
|
||||
furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in all
|
||||
copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
SOFTWARE.
|
||||
@@ -1,21 +0,0 @@
|
||||
The MIT License (MIT)
|
||||
|
||||
Copyright (c) 2015-present Razvan Stoenescu
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
in the Software without restriction, including without limitation the rights
|
||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
copies of the Software, and to permit persons to whom the Software is
|
||||
furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in
|
||||
all copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
THE SOFTWARE.
|
||||
@@ -1,20 +0,0 @@
|
||||
The MIT License (MIT)
|
||||
|
||||
Copyright (c) 2016-present kazuya kawaguchi and contributors
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy of
|
||||
this software and associated documentation files (the "Software"), to deal in
|
||||
the Software without restriction, including without limitation the rights to
|
||||
use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
|
||||
the Software, and to permit persons to whom the Software is furnished to do so,
|
||||
subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in all
|
||||
copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
|
||||
FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
|
||||
COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
|
||||
IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
|
||||
CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||
@@ -1,21 +0,0 @@
|
||||
The MIT License (MIT)
|
||||
|
||||
Copyright (c) 2019-present Eduardo San Martin Morote
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
in the Software without restriction, including without limitation the rights
|
||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
copies of the Software, and to permit persons to whom the Software is
|
||||
furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in all
|
||||
copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
SOFTWARE.
|
||||
@@ -1,21 +0,0 @@
|
||||
The MIT License (MIT)
|
||||
|
||||
Copyright (c) 2018-present, Yuxi (Evan) You and Vue contributors
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
in the Software without restriction, including without limitation the rights
|
||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
copies of the Software, and to permit persons to whom the Software is
|
||||
furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in
|
||||
all copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
THE SOFTWARE.
|
||||
@@ -1,8 +0,0 @@
|
||||
<svg width="188" height="28" viewBox="0 0 188 28" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<path d="M93.295 3.65206L86.356 9.30495H90.8876V27.68L93.295 25.7411V3.65206Z" fill="#5A29E4"/>
|
||||
<path d="M95.295 24.0997L102.356 18.305H97.6975V0.350052L95.295 2.02275V24.0997Z" fill="#5A29E4"/>
|
||||
<path d="M182.695 6.95295C183.495 7.36895 184.071 7.72095 184.423 8.00895L186.919 3.25695C185.671 2.48895 184.167 1.80095 182.407 1.19295C180.679 0.584955 178.807 0.280952 176.791 0.280952C174.871 0.280952 173.095 0.600952 171.463 1.24095C169.863 1.88095 168.583 2.82495 167.623 4.07295C166.695 5.32095 166.231 6.87295 166.231 8.72895C166.231 10.809 166.887 12.409 168.199 13.529C169.543 14.617 171.591 15.513 174.343 16.217C176.551 16.793 178.327 17.321 179.671 17.801C181.047 18.249 181.735 19.001 181.735 20.057C181.735 21.625 180.263 22.409 177.319 22.409C175.847 22.409 174.455 22.233 173.143 21.881C171.831 21.529 170.679 21.097 169.687 20.585C168.727 20.073 168.039 19.609 167.623 19.193L165.031 24.233C166.695 25.289 168.599 26.121 170.743 26.729C172.887 27.337 175.047 27.641 177.223 27.641C179.111 27.641 180.871 27.385 182.503 26.873C184.135 26.329 185.447 25.465 186.439 24.281C187.463 23.065 187.975 21.4649 187.975 19.4809C187.975 17.8489 187.591 16.537 186.823 15.545C186.087 14.521 185.015 13.705 183.607 13.097C182.231 12.489 180.599 11.945 178.711 11.465C176.567 10.953 174.935 10.489 173.815 10.073C172.727 9.65695 172.183 8.95295 172.183 7.96095C172.183 6.26495 173.687 5.41695 176.695 5.41695C177.815 5.41695 178.903 5.57695 179.959 5.89695C181.015 6.18495 181.927 6.53695 182.695 6.95295Z" fill="#5A29E4"/>
|
||||
<path fill-rule="evenodd" clip-rule="evenodd" d="M132.182 27.497C130.07 27.497 128.15 27.129 126.422 26.393C124.694 25.625 123.205 24.601 121.957 23.321C120.709 22.009 119.749 20.537 119.077 18.905C118.406 17.273 118.069 15.593 118.069 13.865C118.069 12.105 118.421 10.409 119.125 8.77695C119.829 7.14495 120.822 5.70496 122.102 4.45695C123.382 3.17695 124.885 2.16895 126.613 1.43295C128.341 0.696953 130.229 0.328949 132.277 0.328949C134.389 0.328949 136.31 0.728952 138.038 1.52895C139.766 2.29695 141.238 3.33695 142.454 4.64895C143.702 5.92895 144.661 7.38495 145.333 9.01695C146.005 10.649 146.342 12.3129 146.342 14.0089C146.342 15.7689 145.99 17.465 145.286 19.097C144.582 20.697 143.589 22.137 142.309 23.417C141.061 24.665 139.574 25.657 137.846 26.393C136.118 27.129 134.23 27.497 132.182 27.497ZM123.925 13.913C123.925 15.353 124.262 16.729 124.934 18.041C125.605 19.321 126.549 20.361 127.765 21.161C129.013 21.961 130.501 22.361 132.229 22.361C133.989 22.361 135.477 21.945 136.693 21.113C137.91 20.249 138.837 19.177 139.477 17.8969C140.117 16.5849 140.438 15.241 140.438 13.865C140.438 12.425 140.102 11.0649 139.43 9.78495C138.758 8.50495 137.798 7.48095 136.549 6.71295C135.333 5.91295 133.878 5.51295 132.182 5.51295C130.422 5.51295 128.917 5.92895 127.669 6.76095C126.453 7.59295 125.525 8.64896 124.885 9.92896C124.245 11.209 123.925 12.537 123.925 13.913Z" fill="#5A29E4"/>
|
||||
<path fill-rule="evenodd" clip-rule="evenodd" d="M0 27.305L11.712 0.472954H16.464L28.128 27.305H21.984L19.296 21.017H8.88L6.192 27.305H0ZM14.112 7.52895L10.176 15.977H17.904L14.112 7.52895Z" fill="#5A29E4"/>
|
||||
<path d="M50.8211 0.472954L58.2131 9.97695L65.6051 0.472954H71.8931L61.2851 14.057L71.5571 27.305H65.2691L58.2131 18.185L51.2051 27.305H44.8211L55.1411 14.057L44.4851 0.472954H50.8211Z" fill="#5A29E4"/>
|
||||
</svg>
|
||||
|
Before Width: | Height: | Size: 3.4 KiB |
@@ -1,3 +0,0 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" class="express-logo" viewBox="0 0 1120.322 250" width="90" height="30">
|
||||
<path d="M347.47162 250V4.890464h13.29516v38.55596a50.415241 50.415241 0 0 0 4.33954-5.43506c11.10412-23.03785 34.52487-37.57744 60.09412-37.29026 30.31296-.90408 54.94623 10.31704 69.422 37.29026a119.86915 119.86915 0 0 1 2.89302 109.881816c-13.4866 30.22788-46.79895 45.25672-82.46189 39.73657a66.688515 66.688515 0 0 1-53.22317-35.12049v97.4801zm13.29516-158.403836 2.97812 28.781356c5.25425 32.75927 24.62263 52.11702 55.03132 55.75458a62.540425 62.540425 0 0 0 68.69874-39.73657c11.82737-28.18574 10.95521-60.094116-2.33995-87.620406a61.26409 61.26409 0 0 0-64.54001-35.66294 59.668671 59.668671 0 0 0-53.30827 44.07611 304.40595 304.40595 0 0 0-6.51995 34.39724zm420.10574 33.312346a71.687494 71.687494 0 0 1-70.06017 63.35941c-55.75458 2.80794-81.91945-34.21642-86.07817-76.94174a123.27271 123.27271 0 0 1 10.85948-67.890396 75.729222 75.729222 0 0 1 78.83497-42.26797 72.432023 72.432023 0 0 1 64.26348 55.12705 371.94535 371.94535 0 0 1 6.24341 40.73636H638.50796c-2.71221 38.736776 18.10269 69.879356 47.6073 77.388456 36.74782 9.04071 68.1563-6.88157 79.55823-41.82125 2.5314-8.96625 7.14748-10.23195 15.29475-7.68992zM638.4016 84.629504h130.97326c-.81898-41.26817-26.51586-71.26205-61.37045-71.60241-39.35367-.63816-67.89039 28.15383-69.60281 71.60241zm169.53986 41.183076h12.83781a51.478853 51.478853 0 0 0 30.22787 44.35265 79.026422 79.026422 0 0 0 68.61365-1.80814 30.844768 30.844768 0 0 0 18.10269-30.3236 27.973013 27.973013 0 0 0-18.82595-27.97301c-14.12477-5.25425-29.14298-8.14727-43.53366-12.763346a319.0838 319.0838 0 0 1-43.81021-16.01801c-23.18675-11.31684-24.62263-55.39295 1.62733-69.34755a92.427941 92.427941 0 0 1 88.34367-1.36142c16.95398 9.35979 26.32441 28.26019 23.53775 47.43712h-11.00839c0-.5318-.9998-.99979-.9998-1.54223-1.36142-35.09922-30.86604-46.07571-62.54043-42.99123-9.57251 1.06361-18.64513 3.95664-27.15403 8.23236a27.122123 27.122123 0 0 0-15.74147 27.15404 27.122123 27.122123 0 0 0 18.10269 25.5267c13.82697 5.07343 28.50482 8.32809 42.81041 12.306l34.56741 9.04071a40.842727 40.842727 0 0 1 28.05811 36.843536c2.76539 18.56004-6.70076 36.801-23.44203 45.25672-30.22787 17.10289-80.01558 12.58254-102.1919-9.04071-11.34875-11.41256-17.67724-26.9094-17.54961-42.99123zm306.10774-67.794666h-12.0401c0-1.62733-.6382-3.19084-.819-4.43526a39.353669 39.353669 0 0 0-32.0466-37.83271 79.026422 79.026422 0 0 0-50.7769 2.44631 30.844768 30.844768 0 0 0-22.35715 29.41953 28.398458 28.398458 0 0 0 21.71895 28.60054l55.0313 14.12478a153.05386 153.05386 0 0 1 17.5497 5.33934c17.5496 6.381666 29.462 22.676216 29.9938 41.300076a45.203539 45.203539 0 0 1-27.6539 42.96995 100.72412 100.72412 0 0 1-81.4621.81898 56.477833 56.477833 0 0 1-34.0356-54.85051h11.76356c4.42463 21.32544 19.07054 39.08777 39.16224 47.49031 20.0916 8.40254 43.0337 6.33913 61.3066-5.48824a32.333825 32.333825 0 0 0 17.3794-30.22787 27.973013 27.973013 0 0 0-19.1024-27.7922c-14.1248-5.25425-29.143-8.05155-43.5337-12.763356a320.67922 320.67922 0 0 1-44.0761-15.83719c-22.6337-11.13602-24.44184-54.8505 1.3614-68.79447a91.151606 91.151606 0 0 1 89.7902-.99979 47.330764 47.330764 0 0 1 22.7188 46.43733zM325.59311 183.84329a20.740447 20.740447 0 0 1-25.70752-9.7746l-46.79895-64.72083-6.78585-9.04071-54.30807 73.85727a19.889557 19.889557 0 0 1-24.44182 9.59378l69.96445-93.863816-65.0931-84.81247c9.6576-3.48865 20.42136.29781 25.79261 9.04071l48.50074 65.51854 48.78791-65.26328a19.464112 19.464112 0 0 1 24.261-9.05134l-25.2608 33.51443-34.21642 44.53347a9.040708 9.040708 0 0 0 0 13.48661l65.16755 86.982236zM622.66013 4.177844v12.76335a65.624902 65.624902 0 0 0-69.87935 67.79467v99.564786h-12.94417V4.975554h12.76336v36.74781c15.65637-26.80304 39.82165-36.74781 70.14525-37.47107ZM.021272 88.724414l5.700964-28.15383c15.656379-55.66949 79.473139-78.83497 123.379074-44.35265 25.70751 20.18737 32.1211 48.78792 30.86604 81.01538H15.135208C12.79526 154.79603 54.329335 189.55489 107.45679 171.81383c17.50706-6.38167 30.68522-20.93189 35.02476-39.01331 2.80794-9.04071 7.44529-10.59359 15.93292-7.9771a73.495636 73.495636 0 0 1-35.12049 53.68054 85.089014 85.089014 0 0 1-99.118064-12.66763c-12.93353-14.53959-20.740447-32.91881-22.261413-52.32975 0-3.19084-1.063613-6.16895-1.808142-9.0407Q0 96.414334 0 88.724414Zm15.294751-3.89282H146.28929c-.81898-41.72553-27.15403-71.32587-62.274525-71.60241-39.098402-.53181-67.071415 28.41973-68.794468 71.42159Z"></path>
|
||||
</svg>
|
||||
|
Before Width: | Height: | Size: 4.4 KiB |
File diff suppressed because one or more lines are too long
|
Before Width: | Height: | Size: 5.2 KiB |
Binary file not shown.
|
Before Width: | Height: | Size: 12 KiB |
@@ -1,123 +0,0 @@
|
||||
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
|
||||
<!-- Created with Inkscape (http://www.inkscape.org/) -->
|
||||
|
||||
<svg
|
||||
xmlns:dc="http://purl.org/dc/elements/1.1/"
|
||||
xmlns:cc="http://creativecommons.org/ns#"
|
||||
xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
|
||||
xmlns:svg="http://www.w3.org/2000/svg"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
|
||||
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
|
||||
width="256"
|
||||
height="224"
|
||||
viewBox="0 0 67.733332 59.266668"
|
||||
version="1.1"
|
||||
id="svg8"
|
||||
inkscape:version="0.92.2 5c3e80d, 2017-08-06"
|
||||
sodipodi:docname="vue-i18n.svg"
|
||||
inkscape:export-filename="/Users/kazupon/Desktop/vue-i18n.png"
|
||||
inkscape:export-xdpi="96"
|
||||
inkscape:export-ydpi="96">
|
||||
<defs
|
||||
id="defs2" />
|
||||
<sodipodi:namedview
|
||||
id="base"
|
||||
pagecolor="#ffffff"
|
||||
bordercolor="#666666"
|
||||
borderopacity="1.0"
|
||||
inkscape:pageopacity="0.0"
|
||||
inkscape:pageshadow="2"
|
||||
inkscape:zoom="2"
|
||||
inkscape:cx="65.171196"
|
||||
inkscape:cy="106.17152"
|
||||
inkscape:document-units="mm"
|
||||
inkscape:current-layer="layer1"
|
||||
showgrid="true"
|
||||
units="px"
|
||||
showguides="false"
|
||||
inkscape:lockguides="false"
|
||||
inkscape:window-width="1280"
|
||||
inkscape:window-height="751"
|
||||
inkscape:window-x="0"
|
||||
inkscape:window-y="1"
|
||||
inkscape:window-maximized="1">
|
||||
<inkscape:grid
|
||||
type="xygrid"
|
||||
id="grid3715" />
|
||||
</sodipodi:namedview>
|
||||
<metadata
|
||||
id="metadata5">
|
||||
<rdf:RDF>
|
||||
<cc:Work
|
||||
rdf:about="">
|
||||
<dc:format>image/svg+xml</dc:format>
|
||||
<dc:type
|
||||
rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
|
||||
<dc:title></dc:title>
|
||||
</cc:Work>
|
||||
</rdf:RDF>
|
||||
</metadata>
|
||||
<g
|
||||
inkscape:label="Layer 1"
|
||||
inkscape:groupmode="layer"
|
||||
id="layer1"
|
||||
transform="translate(0,-237.73331)">
|
||||
<rect
|
||||
style="fill:#42b983;fill-opacity:1;stroke-width:0.44801387"
|
||||
id="rect4524"
|
||||
width="67.73333"
|
||||
height="59.266666"
|
||||
x="0"
|
||||
y="237.73331"
|
||||
ry="6.8791666" />
|
||||
<rect
|
||||
style="fill:#34495e;fill-opacity:1;stroke-width:1.29214942"
|
||||
id="rect4528"
|
||||
width="55.033333"
|
||||
height="45.508335"
|
||||
x="6.3499999"
|
||||
y="244.61247"
|
||||
ry="6.8791666" />
|
||||
<path
|
||||
style="fill:#34495e;fill-opacity:1;stroke:none;stroke-width:5.39703941;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1;paint-order:stroke fill markers"
|
||||
d="m 33.866667,270.54165 v 26.45833 L 6.35,270.54165 Z"
|
||||
id="path4538"
|
||||
inkscape:connector-curvature="0" />
|
||||
<circle
|
||||
style="fill:none;fill-opacity:1;stroke:#ffffff;stroke-width:2.11716342;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1;paint-order:stroke fill markers"
|
||||
id="path4567"
|
||||
cx="34.008522"
|
||||
cy="-267.371"
|
||||
transform="scale(1,-1)"
|
||||
r="15.610168" />
|
||||
<ellipse
|
||||
style="fill:none;fill-opacity:1;stroke:#ffffff;stroke-width:2.067662;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1;paint-order:stroke fill markers"
|
||||
id="path4567-7"
|
||||
cx="33.929485"
|
||||
cy="-267.40591"
|
||||
transform="scale(1,-1)"
|
||||
rx="7.4328356"
|
||||
ry="15.634919" />
|
||||
<path
|
||||
style="fill:none;stroke:#ffffff;stroke-width:2.11666656;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
|
||||
d="m 33.866667,252.02081 v 30.42708 z"
|
||||
id="path4596"
|
||||
inkscape:connector-curvature="0" />
|
||||
<path
|
||||
style="fill:none;stroke:#ffffff;stroke-width:2.11666656;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
|
||||
d="M 48.947917,267.89581 H 18.520837 Z"
|
||||
id="path4596-2"
|
||||
inkscape:connector-curvature="0" />
|
||||
<path
|
||||
style="fill:none;stroke:#ffffff;stroke-width:2.02254486;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
|
||||
d="M 47.625,259.95831 H 19.843753 Z"
|
||||
id="path4596-2-8"
|
||||
inkscape:connector-curvature="0" />
|
||||
<path
|
||||
style="fill:none;stroke:#ffffff;stroke-width:2.02254486;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
|
||||
d="M 47.625,275.30414 H 19.843753 Z"
|
||||
id="path4596-2-8-4"
|
||||
inkscape:connector-curvature="0" />
|
||||
</g>
|
||||
</svg>
|
||||
|
Before Width: | Height: | Size: 4.4 KiB |
Binary file not shown.
|
Before Width: | Height: | Size: 16 KiB |
@@ -58,7 +58,7 @@ export default defineConfig((ctx) => {
|
||||
alias: {
|
||||
'composables': path.resolve(__dirname, './src/composables'),
|
||||
'types': path.resolve(__dirname, './src/types'),
|
||||
'helpers': path.resolve(__dirname, './src/helpers')
|
||||
'utils': path.resolve(__dirname, './src/utils')
|
||||
},
|
||||
|
||||
vueRouterMode: 'history', // available values: 'hash', 'history'
|
||||
|
||||
44
src/assets/docs/Subscription_guide_en.md
Normal file
44
src/assets/docs/Subscription_guide_en.md
Normal file
@@ -0,0 +1,44 @@
|
||||
*This document is an English adaptation of the Subscription Plan Terms originally drafted in Russian. In the event of any disputes subject to resolution in courts of the Russian Federation, the Russian-language version shall prevail.*
|
||||
|
||||
# Subscription Plan Terms
|
||||
###### Version 1.02 dated 02.09.2025
|
||||
|
||||
The text of this document is an integral part of the Terms of Use.
|
||||
|
||||
## 1. General Principles
|
||||
|
||||
1.1. All Plans provide identical functionality, except for the limitations on the number of supported (active) Connected Chats.
|
||||
1.2. The maximum subscription term is 730 days (2 years).
|
||||
1.3. Upon termination of the subscription, access to the Application's functionality may be suspended. Data created during the period without an active subscription may not be displayed in the Application.
|
||||
1.4. A notification of the need to renew the subscription is sent to the Administrator via the bot or by email (depending on the authentication method) no later than 14 calendar days before its expiration.
|
||||
1.5. Renewal and changes of subscription plans are performed exclusively through the Admin Panel in the "Settings > Subscribe" section.
|
||||
1.6. The Developer has the right to unilaterally extend the subscription term without additional charge (within the framework of marketing promotions or for other reasons).
|
||||
|
||||
## 2. Changing Subscription Plans
|
||||
2.1. The Application Administrator has the right to change the subscription plan after 14 (fourteen) calendar days from the moment of the previous Plan change (unless the TEST Plan is used).
|
||||
2.2. The change of the subscription plan is performed automatically (except for the case specified in clause 2.5.5), after confirmation of the remuneration transfer is received.
|
||||
2.3. When changing the Plan, the cost of the unused period of the current subscription is credited towards the payment for the new plan in the form of additional days.
|
||||
2.4. Upgrade to a Plan with a Higher Number of Chats
|
||||
2.4.1. When switching from the TEST Plan, no additional days are accrued.
|
||||
2.4.2. The base daily cost is calculated using the formula:
|
||||
|
||||
Base Daily Cost = (Base Plan Cost / 30) * (100% - Period Discount)
|
||||
|
||||
2.4.3. The number of additional days is calculated using the formula, with the result rounded up to a whole number:
|
||||
|
||||
Number of Additional Days = (Base Daily Cost on the current Plan * Number of unused days) / Base Daily Cost on the new Plan
|
||||
|
||||
2.5. Downgrade to a Plan with a Lower Number of Chats
|
||||
2.5.1. A Plan change is only possible if the number of current (active) Connected Chats in the Application does not exceed the number supported by the new Plan.
|
||||
2.5.2. When calculating the daily cost of the current Plan, the period discount is not taken into account if more than 14 days remain until the subscription expiration.
|
||||
2.5.3. The base daily cost is determined according to the formula, taking into account clause 2.5.2:
|
||||
|
||||
Base Daily Cost = (Base Plan Cost / 30) * (100% - Period Discount)
|
||||
|
||||
2.5.4. The number of additional days is calculated using the formula, with the result rounded up to a whole number or zero:
|
||||
|
||||
Number of Additional Days = (Paid cost of the current Plan – (Base Daily Cost on the current Plan * Number of days used)) / Base Daily Cost on the new Plan
|
||||
|
||||
2.5.5. Transition to the TEST Plan is performed only by contacting support, followed by a manual recalculation and refund.
|
||||
2.6. All calculations are performed using rounding. Claims for the under-accrual of up to 3 (Three) additional days are not accepted by the Developer.
|
||||
2.7. The Developer reserves the right to suspend the Administrator's account or cancel the results of the recalculation if signs of systematic abuse of the plan change mechanism are detected (e.g., frequent cyclic switching between Plans). In such cases, the Administrator's account may be blocked without a refund.
|
||||
40
src/assets/docs/Subscription_guide_ru.md
Normal file
40
src/assets/docs/Subscription_guide_ru.md
Normal file
@@ -0,0 +1,40 @@
|
||||
# Положение о Тарифных планах подписки
|
||||
###### Версия 1.02 от 02.09.2025
|
||||
Текст настоящего документа является неотъемлемой частью Пользовательского соглашения.
|
||||
|
||||
## 1. Общие принципы
|
||||
1.1. Все Тарифы обеспечивают идентичный функционал, за исключением ограничений на количество поддерживаемых (активных) Подключенных чатов.
|
||||
1.2. Максимальный срок действия подписки составляет 730 дней (2 года).
|
||||
1.3. При прекращении действия подписки доступ к функционалу Приложения может быть приостановлен. Данные, созданные в период отсутствия активной подписки, могут не отображаться в Приложении.
|
||||
1.4. Уведомление о необходимости продления подписки направляется Администратору через бота или по электронной почте (в зависимости от способа аутентификации) не позднее чем за 14 календарных дней до истечения её срока действия.
|
||||
1.5. Продление и смена тарифных планов осуществляются исключительно через Панель администратора в разделе «Настройки > Подписка».
|
||||
1.6. Разработчик вправе в одностороннем порядке продлить действие подписки без взимания дополнительной платы (в рамках маркетинговых акций или по иным причинам).
|
||||
|
||||
## 2. Смена тарифных планов
|
||||
2.1. Администратор Приложения вправе сменить тарифный план по истечении 14 (четырнадцати) календарных дней с момента предыдущего изменения Тарифа (если не используется Тариф TEST).
|
||||
2.2. Смена тарифного плана производится в автоматическом режиме (за исключением случая, указанного в п. 2.5.5), после получения подтверждения перечисления вознаграждения.
|
||||
2.3. При смене Тарифа стоимость неиспользованного периода текущей подписки засчитывается в счёт оплаты нового тарифного плана в виде дополнительных дней.
|
||||
2.4. Переход на Тариф с большим количеством чатов
|
||||
2.4.1. При переходе с Тарифа TEST дополнительные дни не начисляются.
|
||||
2.4.2. Базовая стоимость дня рассчитывается по формуле:
|
||||
|
||||
**Базовая стоимость дня = (Базовая стоимость Тарифа / 30) * (100% - Скидка за период)**
|
||||
|
||||
2.4.3. Количество дополнительных дней рассчитывается по формуле, с округлением полученного результата вверх до целого значения:
|
||||
|
||||
**Количество дополнительных дней = (Базовая стоимость дня на текущем Тарифе * Количество неиспользованных дней) / Базовая стоимость дня на новом Тарифе**
|
||||
|
||||
2.5. Переход на Тариф с меньшим количеством чатов
|
||||
2.5.1. Смена Тарифа возможна, только если количество текущих (активных) Подключенных чатов в Приложении не превышает количества, которое поддерживает новый Тариф.
|
||||
2.5.2. При расчете стоимости дня текущего Тарифа скидка за период не учитывается, если до окончания срока действия подписки осталось более 14 дней.
|
||||
2.5.3. Базовая стоимость дня принимается согласно формуле, с учетом п. 2.5.2:
|
||||
|
||||
**Базовая стоимость дня = (Базовая стоимость Тарифа / 30) * (100% - Скидка за период)**
|
||||
|
||||
2.5.4. Количество дополнительных дней рассчитывается по формуле, с округлением полученного результата вверх до целого значения или нуля:
|
||||
|
||||
**Количество дополнительных дней = (Оплаченная стоимость текущего Тарифа – (Базовая стоимость дня на текущем Тарифе * Количество использованных дней)) / Базовая стоимость дня на новом Тарифе**
|
||||
|
||||
2.5.5. Переход на Тариф TEST осуществляется только через обращение в поддержку с последующим ручным перерасчетом и возвратом средств.
|
||||
2.6. Все расчеты производятся с применением округлений. Претензии по недоначислению до 3 (Трех) дополнительных дней не принимаются Разработчиком.
|
||||
2.7. Разработчик оставляет за собой право приостановить действие аккаунта Администратора или отменить результаты перерасчета при выявлении признаков систематического злоупотребления механизмом смены тарифных планов (например, частых циклических переключений между Тарифами). В таких случаях аккаунт Администратора может быть заблокирован без возврата средств.
|
||||
@@ -13,6 +13,7 @@ export default defineBoot(({ app }) => {
|
||||
if (window.Telegram?.WebApp) {
|
||||
const webApp = window.Telegram.WebApp
|
||||
webApp.ready()
|
||||
sessionStorage.setItem('isTelegram', webApp.initData ? 'true' : 'false')
|
||||
webApp.SettingsButton.isVisible = true
|
||||
app.config.globalProperties.$tg = webApp
|
||||
app.provide('tg', webApp)
|
||||
|
||||
25
src/components/TelegramStar.vue
Normal file
25
src/components/TelegramStar.vue
Normal file
@@ -0,0 +1,25 @@
|
||||
<template>
|
||||
<svg
|
||||
class="q-ma-none q-pa-none"
|
||||
:style="{
|
||||
fill: color ?? 'red',
|
||||
height: size ?? '42px',
|
||||
width: 'auto'
|
||||
}"
|
||||
width="14" height="15" viewBox="0 0 14 15" xmlns="http://www.w3.org/2000/svg"><path fill-rule="evenodd" clip-rule="evenodd" d="M6.63869 12.1902L3.50621 14.1092C3.18049 14.3087 2.75468 14.2064 2.55515 13.8807C2.45769 13.7216 2.42864 13.5299 2.47457 13.3491L2.95948 11.4405C3.13452 10.7515 3.60599 10.1756 4.24682 9.86791L7.6642 8.22716C7.82352 8.15067 7.89067 7.95951 7.81418 7.80019C7.75223 7.67116 7.61214 7.59896 7.47111 7.62338L3.66713 8.28194C2.89387 8.41581 2.1009 8.20228 1.49941 7.69823L0.297703 6.69116C0.00493565 6.44581 -0.0335059 6.00958 0.211842 5.71682C0.33117 5.57442 0.502766 5.48602 0.687982 5.47153L4.35956 5.18419C4.61895 5.16389 4.845 4.99974 4.94458 4.75937L6.36101 1.3402C6.5072 0.987302 6.91179 0.819734 7.26469 0.965925C7.43413 1.03612 7.56876 1.17075 7.63896 1.3402L9.05539 4.75937C9.15496 4.99974 9.38101 5.16389 9.6404 5.18419L13.3322 5.47311C13.713 5.50291 13.9975 5.83578 13.9677 6.2166C13.9534 6.39979 13.8667 6.56975 13.7269 6.68896L10.9114 9.08928C10.7131 9.25826 10.6267 9.52425 10.6876 9.77748L11.5532 13.3733C11.6426 13.7447 11.414 14.1182 11.0427 14.2076C10.8642 14.2506 10.676 14.2208 10.5195 14.1249L7.36128 12.1902C7.13956 12.0544 6.8604 12.0544 6.63869 12.1902Z"></path></svg>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
defineProps({
|
||||
color: String,
|
||||
size: String,
|
||||
})
|
||||
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
.telegram-star-wrapper :deep(.telegram-star svg) {
|
||||
fill: red;
|
||||
}
|
||||
|
||||
</style>
|
||||
@@ -60,7 +60,7 @@
|
||||
import { computed } from 'vue'
|
||||
import { useI18n } from 'vue-i18n'
|
||||
import type { CompanyParams } from 'types/Company'
|
||||
import { convertEmptyStringsToNull } from 'helpers/helpers'
|
||||
import { convertEmptyStringsToNull } from 'src/utils/helpers'
|
||||
|
||||
const { t }= useI18n()
|
||||
|
||||
|
||||
@@ -1,13 +1,13 @@
|
||||
<template>
|
||||
<pn-page-card>
|
||||
<template #title>
|
||||
{{ $t(type + '__title') }}
|
||||
{{ $t(title) }}
|
||||
</template>
|
||||
<pn-scroll-list>
|
||||
<markdown-viewver
|
||||
class="q-pa-md"
|
||||
:locale
|
||||
:documentName = "getDocumentName()"
|
||||
:documentName
|
||||
/>
|
||||
</pn-scroll-list>
|
||||
</pn-page-card>
|
||||
@@ -18,22 +18,15 @@
|
||||
import { useSettingsStore } from 'stores/settings'
|
||||
import MarkdownViewver from 'components/MarkdownViewver.vue'
|
||||
|
||||
const props = defineProps<{
|
||||
type: 'terms_of_use' | 'privacy' | 'consent'
|
||||
defineProps<{
|
||||
title: string
|
||||
documentName: string
|
||||
}>()
|
||||
|
||||
const settingsStore = useSettingsStore()
|
||||
const DEFAULT_LOCALE = 'ru'
|
||||
const locale = ref('ru')
|
||||
|
||||
const getDocumentName = () =>{
|
||||
switch(props.type) {
|
||||
case 'terms_of_use': return 'Terms_of_use'
|
||||
case 'privacy': return 'Privacy-Policy'
|
||||
case 'consent': return 'Consent_to_Personal_Data_Processing'
|
||||
}
|
||||
}
|
||||
|
||||
const parseLocale = (locale: string) => locale.split(/[-_]/)[0] || DEFAULT_LOCALE
|
||||
|
||||
onMounted(() => {
|
||||
|
||||
@@ -61,6 +61,7 @@
|
||||
<script setup lang="ts">
|
||||
import { ref, Ref, computed } from 'vue' // eslint-disable-line
|
||||
import { QFile } from 'quasar'
|
||||
import imageCompression from 'browser-image-compression'
|
||||
|
||||
const modelValue = defineModel<string>()
|
||||
|
||||
@@ -70,6 +71,13 @@
|
||||
avatar?: boolean
|
||||
}>()
|
||||
|
||||
const compressionOptions = {
|
||||
maxSizeMB: 0.5, // Максимальный размер ~500 КБ
|
||||
maxWidthOrHeight: 1200, // Максимальное разрешение
|
||||
useWebWorker: true, // Для производительности
|
||||
fileType: 'image/jpeg' // Формат вывода
|
||||
}
|
||||
|
||||
const imageFile = ref(null) // file-from selector
|
||||
const imgFileSelector= ref() as Ref<QFile> // input file DOM
|
||||
const size = ref<number>(props.size ? props.size : 100)
|
||||
@@ -85,10 +93,21 @@
|
||||
return String(size.value) + 'px'
|
||||
})
|
||||
|
||||
async function handleUpload () {
|
||||
async function handleUpload() {
|
||||
if (imageFile.value) {
|
||||
const img = await imgToBase64(imageFile.value)
|
||||
modelValue.value = typeof img === 'string' ? img : ''
|
||||
try {
|
||||
const compressedFile = await imageCompression(
|
||||
imageFile.value,
|
||||
compressionOptions
|
||||
);
|
||||
|
||||
const img = await imgToBase64(compressedFile);
|
||||
modelValue.value = typeof img === 'string' ? img : '';
|
||||
} catch (error) {
|
||||
console.error('Image error compression:', error);
|
||||
const img = await imgToBase64(imageFile.value);
|
||||
modelValue.value = typeof img === 'string' ? img : '';
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -119,7 +138,14 @@
|
||||
}
|
||||
|
||||
function checkImgType(files: File[]): File[] {
|
||||
return files.filter((file: File) => file.type === 'image/x-png' || file.type === 'image/jpeg' || file.type === 'image/webp' )
|
||||
const allowedTypes = [
|
||||
'image/jpeg',
|
||||
'image/png',
|
||||
'image/webp',
|
||||
'image/gif',
|
||||
'image/bmp'
|
||||
]
|
||||
return files.filter((file: File) => allowedTypes.includes(file.type))
|
||||
}
|
||||
</script>
|
||||
|
||||
|
||||
@@ -8,7 +8,7 @@
|
||||
rounded color="primary"
|
||||
class="w100 q-mt-md q-mb-xs fix-disabled-btn"
|
||||
:disable="!isFormValid"
|
||||
@click = "emit('update')"
|
||||
@click = "onSubmit"
|
||||
>
|
||||
{{ $t(btnText) }}
|
||||
</q-btn>
|
||||
@@ -45,15 +45,6 @@
|
||||
class="w100 q-pt-sm"
|
||||
:label="$t('project_block__project_description')"
|
||||
/>
|
||||
|
||||
<!-- <q-checkbox
|
||||
v-if="modelValue.logo"
|
||||
v-model="modelValue.is_logo_bg"
|
||||
class="w100"
|
||||
dense
|
||||
>
|
||||
{{ $t('project_block__image_use_as_background_chats') }}
|
||||
</q-checkbox> -->
|
||||
</div>
|
||||
</div>
|
||||
</pn-scroll-list>
|
||||
@@ -62,9 +53,10 @@
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import { onMounted, computed, ref } from 'vue'
|
||||
import { computed } from 'vue'
|
||||
import { useI18n } from 'vue-i18n'
|
||||
import type { ProjectParams } from 'types/Project'
|
||||
import { convertEmptyStringsToNull } from 'src/utils/helpers'
|
||||
|
||||
const { t } = useI18n()
|
||||
|
||||
@@ -95,11 +87,10 @@
|
||||
return Object.values(validations).every(Boolean)
|
||||
})
|
||||
|
||||
const initialProject = ref({} as ProjectParams)
|
||||
|
||||
onMounted(() => {
|
||||
initialProject.value = { ...modelValue.value }
|
||||
})
|
||||
function onSubmit() {
|
||||
const cleanedData = convertEmptyStringsToNull(modelValue.value)
|
||||
emit('update', cleanedData)
|
||||
}
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
|
||||
@@ -25,7 +25,7 @@
|
||||
/>
|
||||
<div
|
||||
v-if="userStatus"
|
||||
class="absolute-center text-h4 text-bold q-pa-sm"
|
||||
class="absolute-center text-h4 text-bold q-pa-sm text-center"
|
||||
:class ="'status-' + userStatus.status"
|
||||
>
|
||||
{{ $t(userStatus.text) }}
|
||||
@@ -132,8 +132,8 @@
|
||||
import { computed } from 'vue'
|
||||
import { useCompaniesStore } from 'stores/companies'
|
||||
import { useI18n } from 'vue-i18n'
|
||||
import { convertEmptyStringsToNull } from 'src/helpers/helpers'
|
||||
import type { User } from 'types/Users'
|
||||
import { convertEmptyStringsToNull } from 'src/utils/helpers'
|
||||
import type { User } from 'types/User'
|
||||
|
||||
|
||||
const { t } = useI18n()
|
||||
@@ -177,8 +177,9 @@
|
||||
)
|
||||
|
||||
const userStatus = computed(() => {
|
||||
if (modelValue.value.is_blocked) return { status: 'blocked', text: 'user_block__user_blocked'}
|
||||
if (modelValue.value.is_leave) return { status: 'leave', text: 'user_block__user_leave'}
|
||||
if (modelValue.value.is_blocked) return { status: 'blocked', text: 'user_block__user_blocked' }
|
||||
if (modelValue.value.is_leave) return { status: 'leave', text: 'user_block__user_leave' }
|
||||
if (!modelValue.value.is_terms_accepted) return { status: 'pending', text: 'user_block__user_pending' }
|
||||
return null
|
||||
})
|
||||
|
||||
@@ -190,12 +191,17 @@
|
||||
}
|
||||
|
||||
.status-blocked {
|
||||
border: 2px solid red;
|
||||
color: red;
|
||||
border: 2px solid var(--q-negative);
|
||||
color: var(--q-negative);
|
||||
}
|
||||
|
||||
.status-leave {
|
||||
border: 2px solid var(--q-primary);
|
||||
color: var(--q-primary);
|
||||
}
|
||||
|
||||
.status-pending{
|
||||
border: 2px solid var(--q-secondary);
|
||||
color: var(--q-secondary);
|
||||
}
|
||||
</style>
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import { useCompaniesStore } from 'stores/companies'
|
||||
import type { User } from 'types/Users'
|
||||
import type { User } from 'types/User'
|
||||
|
||||
export function useUserSection() {
|
||||
const companiesStore = useCompaniesStore()
|
||||
|
||||
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
@@ -234,7 +234,6 @@
|
||||
},
|
||||
2: async () => {
|
||||
await authStore.confirmCurrentEmailCode(code.value)
|
||||
console.log(code.value)
|
||||
},
|
||||
3: async () => {
|
||||
await authStore.getCodeNewEmail(code.value, newLogin.value)
|
||||
|
||||
@@ -105,7 +105,8 @@
|
||||
await router.push({ name: 'login' })
|
||||
}
|
||||
|
||||
function onConfirmStopUsing () {
|
||||
async function onConfirmStopUsing () {
|
||||
await authStore.termsRevoked()
|
||||
tg.close()
|
||||
}
|
||||
|
||||
|
||||
@@ -6,37 +6,35 @@
|
||||
<pn-scroll-list>
|
||||
<div class="flex column w100 q-pa-md q-gutter-y-md">
|
||||
<div>
|
||||
{{ $t('agreements__description') }}
|
||||
<div v-if="route.query.type === 'update'">{{ $t('agreements__description_update') }}</div>
|
||||
<div>{{ $t('agreements__description') }}</div>
|
||||
</div>
|
||||
<div class="flex items-center no-wrap">
|
||||
<div class="flex items-center no-wrap text-caption">
|
||||
<q-checkbox v-model="agreement" val="1" class="q-pr-sm"/>
|
||||
<span>
|
||||
{{ $t('agreements__checkbox_agreement_terms') + ' ' }}
|
||||
<span
|
||||
@click="router.push({ name: 'terms' })"
|
||||
class="cursor-pointer"
|
||||
style="text-decoration: underline;"
|
||||
class="cursor-pointer text-primary"
|
||||
>
|
||||
{{ $t('agreements__checkbox_agreement_terms_doc') }}
|
||||
</span>
|
||||
</span>
|
||||
</div>
|
||||
<div class="flex items-center no-wrap">
|
||||
<div class="flex items-center no-wrap text-caption">
|
||||
<q-checkbox v-model="agreement" val="2" class="q-pr-sm"/>
|
||||
<span>
|
||||
{{ $t('agreements__checkbox_agreement_consent') + ' ' }}
|
||||
<span
|
||||
@click="router.push({ name: 'consent' })"
|
||||
class="cursor-pointer"
|
||||
style="text-decoration: underline;"
|
||||
class="cursor-pointer text-primary"
|
||||
>
|
||||
{{ $t('agreements__checkbox_agreement_consent_doc') }}
|
||||
</span>
|
||||
{{ ' ' + $t('agreements__checkbox_agreement_privacy') + ' ' }}
|
||||
<span
|
||||
@click="router.push({ name: 'consent' })"
|
||||
class="cursor-pointer"
|
||||
style="text-decoration: underline;"
|
||||
@click="router.push({ name: 'privacy' })"
|
||||
class="cursor-pointer text-primary"
|
||||
>
|
||||
{{ $t('agreements__checkbox_agreement_privacy_doc') }}
|
||||
</span>
|
||||
@@ -78,7 +76,7 @@
|
||||
|
||||
<script setup lang="ts">
|
||||
import { ref, inject } from 'vue'
|
||||
import { useRouter } from 'vue-router'
|
||||
import { useRouter, useRoute } from 'vue-router'
|
||||
import { useAuthStore } from 'stores/auth'
|
||||
import type { WebApp } from '@twa-dev/types'
|
||||
const tg = inject('tg') as WebApp
|
||||
@@ -87,14 +85,34 @@
|
||||
tg.close()
|
||||
}
|
||||
|
||||
const route = useRoute()
|
||||
|
||||
const authStore = useAuthStore()
|
||||
const router = useRouter()
|
||||
|
||||
async function onSubmit () {
|
||||
|
||||
if (!route.query.method || !route.query.type) {
|
||||
await authStore.logout()
|
||||
await router.push({ name: 'login' })
|
||||
return
|
||||
}
|
||||
|
||||
if (route.query.method === 'telegram' && route.query.type === 'register') {
|
||||
await authStore.registerWithTelegram(tg.initData)
|
||||
await authStore.loginWithTelegram(tg.initData)
|
||||
await router.push({ name: 'projects' })
|
||||
}
|
||||
|
||||
if (route.query.method === 'email' && route.query.type === 'register') {
|
||||
await router.push({ name: 'create_account' })
|
||||
}
|
||||
|
||||
if (route.query.type === 'update') {
|
||||
await authStore.termsAccepted()
|
||||
await router.push({ name: 'projects' })
|
||||
}
|
||||
}
|
||||
|
||||
const agreement = ref([])
|
||||
|
||||
@@ -26,15 +26,13 @@
|
||||
rounded
|
||||
/>
|
||||
<div
|
||||
class="flex row items-start justify-center q-pt-md text-bold"
|
||||
align="center"
|
||||
class="flex row items-start justify-center q-pt-md text-bold text-center"
|
||||
>
|
||||
{{ chat.name }}
|
||||
</div>
|
||||
<div
|
||||
v-if="chat.description"
|
||||
class="flex row items-start justify-center text-caption"
|
||||
align="center"
|
||||
class="flex row items-start justify-center text-caption text-center"
|
||||
>
|
||||
{{ chat.description }}
|
||||
</div>
|
||||
@@ -58,13 +56,12 @@
|
||||
/>
|
||||
</q-item-section>
|
||||
<q-item-section>
|
||||
<q-item-label lines="1" class="text-bold" v-if="item.section1">
|
||||
<q-badge
|
||||
v-if="item.is_blocked"
|
||||
color="negative"
|
||||
>
|
||||
{{ $t('chat_page__user_blocked') }}
|
||||
<q-item-label lines="1" v-if="getUserStatus(item).status">
|
||||
<q-badge :color="getUserStatus(item).color">
|
||||
{{ $t(getUserStatus(item).text) }}
|
||||
</q-badge>
|
||||
</q-item-label>
|
||||
<q-item-label lines="1" class="text-bold" v-if="item.section1">
|
||||
{{item.section1}}
|
||||
</q-item-label>
|
||||
<q-item-label lines="2" caption v-if="item.section3">
|
||||
@@ -91,8 +88,9 @@
|
||||
import { useChatsStore } from 'stores/chats'
|
||||
import { useUsersStore } from 'stores/users'
|
||||
import { useCompaniesStore } from 'stores/companies'
|
||||
import { parseIntString } from 'helpers/helpers'
|
||||
import { parseIntString } from 'src/utils/helpers'
|
||||
import type { Chat } from 'types/Chat'
|
||||
import type { User } from 'types/User'
|
||||
import type { WebApp } from '@twa-dev/types'
|
||||
import { useUserSection } from 'composables/useUserSection'
|
||||
|
||||
@@ -135,8 +133,9 @@
|
||||
return arr.map(el => ({
|
||||
...el,
|
||||
...userSection(el),
|
||||
companyName: el.company_id && companiesStore.companyById(el.company_id)
|
||||
? companiesStore.companyById(el.company_id)?.name
|
||||
companyName:
|
||||
(el.company_id && companiesStore.companyById(el.company_id))
|
||||
? companiesStore.companyById(el.company_id)?.name ?? null
|
||||
: null
|
||||
}))
|
||||
})
|
||||
@@ -145,4 +144,19 @@
|
||||
await router.push({ name: 'user_info', params: { id: route.params.id, userId: id }})
|
||||
}
|
||||
|
||||
interface chatUser extends User {
|
||||
section1: string
|
||||
section2_1: string
|
||||
section2_2: string
|
||||
section3: string
|
||||
companyName: string | null
|
||||
}
|
||||
|
||||
function getUserStatus (item: chatUser) {
|
||||
if (item.is_blocked) return { status: 'blocked', text: 'user_block__user_blocked', color: 'negative' }
|
||||
if (item.is_leave) return { status: 'leave', text: 'user_block__user_leave', color: 'primary' }
|
||||
if (!item.is_terms_accepted) return { status: 'pending', text: 'user_block__user_pending', color: 'secondary' }
|
||||
return { status: null, text: '', color: '' }
|
||||
}
|
||||
|
||||
</script>
|
||||
|
||||
@@ -3,7 +3,7 @@
|
||||
v-model="newCompany"
|
||||
title="company_add__title_card"
|
||||
btnText="company_add__btn"
|
||||
@update = "addCompany"
|
||||
@update="addCompany"
|
||||
/>
|
||||
</template>
|
||||
|
||||
|
||||
@@ -30,7 +30,7 @@
|
||||
import { useRouter, useRoute } from 'vue-router'
|
||||
import companyBlock from 'components/companyBlock.vue'
|
||||
import { useCompaniesStore } from 'stores/companies'
|
||||
import { parseIntString } from 'helpers/helpers'
|
||||
import { parseIntString } from 'src/utils/helpers'
|
||||
import type { CompanyParams } from 'types/Company'
|
||||
|
||||
const router = useRouter()
|
||||
@@ -59,7 +59,7 @@
|
||||
await router.push({ name: 'your_company' })
|
||||
}
|
||||
|
||||
async function updateCompany(companyData: CompanyParams) {
|
||||
async function updateCompany (companyData: CompanyParams) {
|
||||
if (companyId.value) {
|
||||
await companiesStore.update(companyId.value, companyData)
|
||||
router.go(-1)
|
||||
|
||||
@@ -212,7 +212,7 @@
|
||||
|
||||
async function createAccount() {
|
||||
sessionStorage.setItem('pendingLogin', login.value)
|
||||
await router.push({ name: 'create_account' })
|
||||
await router.push({ name: 'agreements', query: { method: 'email', type: 'register' }})
|
||||
}
|
||||
|
||||
const isTelegramApp = computed(() => {
|
||||
@@ -221,10 +221,12 @@
|
||||
})
|
||||
|
||||
async function handleTelegramLogin () {
|
||||
// @ts-expect-ignore
|
||||
const initData = window.Telegram.WebApp.initData
|
||||
await authStore.loginWithTelegram(initData)
|
||||
try {
|
||||
await authStore.loginWithTelegram(tg.initData)
|
||||
await router.push({ name: 'projects' })
|
||||
} catch {
|
||||
await router.push({ name: 'agreements', query: { method: 'telegram', type: 'register' }})
|
||||
}
|
||||
}
|
||||
|
||||
onUnmounted(() => {
|
||||
|
||||
@@ -17,15 +17,10 @@
|
||||
const router = useRouter()
|
||||
const projectsStore = useProjectsStore()
|
||||
|
||||
const newProject = ref(<ProjectParams>{
|
||||
name: '',
|
||||
logo: '',
|
||||
description: '',
|
||||
is_logo_bg: false
|
||||
})
|
||||
const newProject = ref<ProjectParams>({} as ProjectParams)
|
||||
|
||||
async function addProject () {
|
||||
const newDataProject = await projectsStore.add(newProject.value)
|
||||
async function addProject (projectData: ProjectParams) {
|
||||
const newDataProject = await projectsStore.add(projectData)
|
||||
await router.replace({ name: 'chats', params: { id: newDataProject.id }})
|
||||
}
|
||||
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
<template>
|
||||
<project-block
|
||||
v-if="projectMod"
|
||||
v-model="projectMod"
|
||||
v-if="project"
|
||||
v-model="project"
|
||||
title="project_edit__title_card"
|
||||
btnText="project_edit__btn"
|
||||
@update="updateProject"
|
||||
@@ -18,25 +18,27 @@
|
||||
const router = useRouter()
|
||||
const projectsStore = useProjectsStore()
|
||||
|
||||
const project = ref<ProjectParams | null>(null)
|
||||
const projectId = computed(() => projectsStore.currentProjectId)
|
||||
const projectMod = ref<ProjectParams | null>(null)
|
||||
|
||||
|
||||
if (projectsStore.isInit) {
|
||||
projectMod.value = projectId.value
|
||||
project.value = projectId.value
|
||||
? { ...projectsStore.projectById(projectId.value) } as ProjectParams
|
||||
: null
|
||||
}
|
||||
|
||||
watch(() => projectsStore.isInit, (isInit) => {
|
||||
if (isInit && projectId.value && !projectMod.value) {
|
||||
projectMod.value = { ...projectsStore.projectById(projectId.value) as ProjectParams }
|
||||
if (isInit && projectId.value && !project.value) {
|
||||
project.value = { ...projectsStore.projectById(projectId.value) as ProjectParams }
|
||||
}
|
||||
})
|
||||
|
||||
const updateProject = async () => {
|
||||
if (!projectId.value || !projectMod.value) return
|
||||
await projectsStore.update(projectId.value, projectMod.value)
|
||||
async function updateProject (projectData: ProjectParams) {
|
||||
if (projectId.value) {
|
||||
await projectsStore.update(projectId.value, projectData)
|
||||
router.go(-1)
|
||||
}
|
||||
}
|
||||
|
||||
</script>
|
||||
|
||||
@@ -1,9 +1,9 @@
|
||||
<template>
|
||||
<pn-page-card>
|
||||
<template #title>
|
||||
{{ $t('projects__projects') }}
|
||||
{{ $t('projects__title') }}
|
||||
<q-btn
|
||||
@click="goAccount()"
|
||||
@click="goAccount"
|
||||
flat rounded
|
||||
no-caps
|
||||
icon-right="mdi-chevron-right"
|
||||
@@ -18,10 +18,10 @@
|
||||
<pn-scroll-list>
|
||||
<template
|
||||
#card-body-header
|
||||
v-if="projects.length !== 0 || archiveProjects.length !== 0"
|
||||
v-if="projects.length!==0 || archiveProjects.length!==0"
|
||||
>
|
||||
<q-input
|
||||
v-if="projects.length !== 0"
|
||||
v-if="projects.length!== 0"
|
||||
v-model="searchProject"
|
||||
clearable
|
||||
clear-icon="close"
|
||||
@@ -35,7 +35,7 @@
|
||||
</q-input>
|
||||
</template>
|
||||
|
||||
<q-list separator v-if="projects.length !== 0">
|
||||
<q-list separator v-if="projects.length!==0">
|
||||
<template
|
||||
v-for = "item in activeProjects"
|
||||
:key="item.id"
|
||||
@@ -66,7 +66,7 @@
|
||||
caption lines="2"
|
||||
style="max-width: -webkit-fill-available; white-space: pre-line"
|
||||
>
|
||||
{{item.description}}
|
||||
{{ item.description }}
|
||||
</q-item-label>
|
||||
</q-item-section>
|
||||
<q-item-section side top>
|
||||
@@ -149,7 +149,7 @@
|
||||
caption lines="2"
|
||||
style="max-width: -webkit-fill-available; white-space: pre-line"
|
||||
>
|
||||
{{item.description}}
|
||||
{{ item.description }}
|
||||
</q-item-label>
|
||||
</q-item-section>
|
||||
</q-item>
|
||||
@@ -158,7 +158,7 @@
|
||||
</div>
|
||||
</div>
|
||||
<pn-onboard-btn
|
||||
v-if="projects.length === 0 && projectsInit"
|
||||
v-if="projects.length===0 && projectsInit"
|
||||
icon="mdi-briefcase-plus-outline"
|
||||
:message1="$t('projects__lets_start')"
|
||||
:message2="$t('projects__lets_start_description')"
|
||||
|
||||
38
src/pages/TelegramOnlyPage.vue
Normal file
38
src/pages/TelegramOnlyPage.vue
Normal file
@@ -0,0 +1,38 @@
|
||||
<template>
|
||||
<div
|
||||
class="q-pa-md flex items-center justify-center"
|
||||
style="height: 100vh"
|
||||
>
|
||||
<mesh-background/>
|
||||
<q-card
|
||||
class="q-py-md q-px-xl flex column q-gutter-y-lg justify-center items-center"
|
||||
style="opacity: 0.8"
|
||||
>
|
||||
<base-logo class="text-h6"/>
|
||||
<span class="text-h6 text-center">
|
||||
{{$t('only_telegram__continue')}}
|
||||
</span>
|
||||
<q-btn
|
||||
@click="openWebsite"
|
||||
color="primary"
|
||||
rounded
|
||||
class="q-mb-lg"
|
||||
>
|
||||
<div class="flex items-center">
|
||||
<q-icon name="telegram"/>
|
||||
Telegram
|
||||
</div>
|
||||
</q-btn>
|
||||
</q-card>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import baseLogo from 'components/BaseLogo.vue'
|
||||
import meshBackground from 'components/meshBackground.vue'
|
||||
import { BOT_NAME } from 'src/utils/constants'
|
||||
|
||||
function openWebsite () {
|
||||
window.open('https://t.me/'+ BOT_NAME)
|
||||
}
|
||||
</script>
|
||||
@@ -13,8 +13,8 @@
|
||||
import { useRouter, useRoute } from 'vue-router'
|
||||
import userBlock from 'components/userBlock.vue'
|
||||
import { useUsersStore } from 'stores/users'
|
||||
import { parseIntString } from 'helpers/helpers'
|
||||
import type { User } from 'types/Users'
|
||||
import { parseIntString } from 'src/utils/helpers'
|
||||
import type { User } from 'types/User'
|
||||
|
||||
const router = useRouter()
|
||||
const route = useRoute()
|
||||
|
||||
10
src/pages/account/ChangePlanRules.vue
Normal file
10
src/pages/account/ChangePlanRules.vue
Normal file
@@ -0,0 +1,10 @@
|
||||
<template>
|
||||
<doc-block title="subscription_guide__title" document-name="Subscription_guide"/>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import docBlock from 'components/docBlock.vue'
|
||||
</script>
|
||||
|
||||
<style>
|
||||
</style>
|
||||
@@ -1,5 +1,5 @@
|
||||
<template>
|
||||
<doc-block type="consent"/>
|
||||
<doc-block title="consent__title" document-name="Consent_to_Personal_Data_Processing"/>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
<template>
|
||||
<doc-block type="privacy"/>
|
||||
<doc-block title="privacy__title" document-name="Privacy-Policy"/>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
|
||||
@@ -1,72 +1,205 @@
|
||||
<!-- eslint-disable @typescript-eslint/ban-ts-comment -->
|
||||
<!-- eslint-disable @typescript-eslint/ban-ts-comment -->
|
||||
<!-- eslint-disable @typescript-eslint/ban-ts-comment -->
|
||||
<template>
|
||||
<pn-page-card>
|
||||
<template #title>
|
||||
{{$t('subscribe__title')}}
|
||||
</template>
|
||||
|
||||
<pn-scroll-list class="q-px-md">
|
||||
<div id="subscribe-current-balance" class="flex w100 justify-between items-center no-wrap text-h6">
|
||||
<span>
|
||||
{{ $t('subscribe__current_balance') }}
|
||||
<pn-scroll-list >
|
||||
<div class="q-px-md">
|
||||
<div
|
||||
id="subscribe-current-balance"
|
||||
class="flex w100 q-px-md q-py-sm row"
|
||||
style="border-radius: var(--top-raduis); border: 1px solid var(--q-primary);"
|
||||
>
|
||||
|
||||
<div class="flex w100 justify-between items-center no-wrap">
|
||||
<div class="flex no-wrap items-center text-h6 col-9">
|
||||
{{ $t('subscribe__current_plan') }}
|
||||
</div>
|
||||
|
||||
<div class="flex items-center column col-3">
|
||||
<span class="text-bold">
|
||||
{{ currentPlanData?.name }}
|
||||
</span>
|
||||
<span class="text-caption" style="line-height: 0.5em;">
|
||||
{{ $t('subscribe__plan_exp') }}
|
||||
{{ date.formatDate(currentPlanData.exp * 1000, 'DD.MM.YYYY') }}
|
||||
</span>
|
||||
|
||||
<div class="flex items-center">
|
||||
<q-icon name = "mdi-crown-circle-outline" color="orange" size="sm"/>
|
||||
<div class="text-bold q-pa-xs ">
|
||||
50
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div id="subscribe-need-tocken" :style = "{ borderLeft: 'solid 5px var(--q-info)' }" class="q-pl-sm">
|
||||
<q-icon name = "mdi-crown-circle-outline" color="orange" size="xs"/>{{ $t('subscribe__token_formula') }}
|
||||
<div class="text-caption">{{ $t('subscribe__token_formula_description') }}</div>
|
||||
</div>
|
||||
|
||||
<div id="qty_chats" class="flex column q-pt-lg w100">
|
||||
<div class="text-h6 flex items-center">
|
||||
<span>{{ $t('account__chats') }}</span>
|
||||
</div>
|
||||
<div class="flex row justify-between">
|
||||
<qty-chat-card
|
||||
v-for = "chat in chats"
|
||||
:key = chat.title
|
||||
:qty = chat.qty
|
||||
:bgColor = chat.color
|
||||
:title = chat.title
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="flex row w100 ow-wrap items-center text-caption q-pt-sm">
|
||||
<div class="col-9">{{ $t('subscribe__plan_active_chats') }}</div>
|
||||
<div class="col-3" align="center">
|
||||
<span class="text-brand2 text-bold q-pr-xs">{{ currentPlanData?.active_chats }}</span>
|
||||
<span class="text-grey">/{{ currentPlanData?.chatsQty }}</span>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
<div class="flex w100 justify-center text-grey q-pt-md q-pb-none">{{ $t('subscribe__plans')}}</div>
|
||||
<q-list separator>
|
||||
<q-item
|
||||
v-for="item in plans"
|
||||
:key="item.id"
|
||||
>
|
||||
<q-item-section avatar>
|
||||
<q-radio v-model="newPlan" :val="item" v-if="item.name!=='TEST'"/>
|
||||
</q-item-section>
|
||||
<q-item-section>
|
||||
<q-item-label class="text-bold">{{ item.name }}</q-item-label>
|
||||
<div class="flex no-wrap items-center text-caption">
|
||||
<span v-if="item.chatsQty" class="q-pr-xs">
|
||||
{{ $t('subscribe__chats_max') }}
|
||||
</span>
|
||||
<span v-if="item.chatsQty">
|
||||
{{ item.chatsQty }}
|
||||
</span>
|
||||
<q-icon v-else name="mdi-all-inclusive" size="sm"/>
|
||||
<span class="q-pl-xs">
|
||||
{{ $t('subscribe__chats')}}
|
||||
</span>
|
||||
</div>
|
||||
</q-item-section>
|
||||
<q-item-section side>
|
||||
<div v-if="item.price" class="flex column items-center">
|
||||
<div class="flex no-wrap items-center">
|
||||
<telegram-star color="gold" size="18px" class="q-mr-xs"/>
|
||||
<span class="text-h6">{{ formatNumber(item.price) }}</span>
|
||||
<span class="text-caption q-pl-xs">{{ $t('subscribe__per_month') }}</span>
|
||||
</div>
|
||||
</div>
|
||||
<div v-else class="text-bold">
|
||||
{{ $t('subscribe__free_tax') }}
|
||||
</div>
|
||||
</q-item-section>
|
||||
</q-item>
|
||||
</q-list>
|
||||
<div class="flex w100 justify-center text-caption text-grey q-pt-sm text-center">
|
||||
{{ $t('subscribe__plans_description') }}
|
||||
</div>
|
||||
|
||||
<q-btn-group spread flat class="w100 q-py-sm">
|
||||
<q-btn
|
||||
v-for="period in periods"
|
||||
:key="period.id"
|
||||
:color="selectPeriod === period.value ? 'primary' : 'white'"
|
||||
:text-color="selectPeriod === period.value ? 'white' : 'primary'"
|
||||
@click="selectPeriod = period.value"
|
||||
no-caps
|
||||
>
|
||||
<div class="column items-center w100 self-end">
|
||||
<q-badge v-show="period.sale" color="red">{{ period.sale }}% off</q-badge>
|
||||
<span>{{ $t(period.name) }}</span>
|
||||
<span class="text-caption">({{ $t(period.name_in_days) }})</span>
|
||||
</div>
|
||||
</q-btn>
|
||||
</q-btn-group>
|
||||
|
||||
<div class="text-caption column text-grey">
|
||||
<span>{{ $t('subscribe__plans_period_notes') + ' ' }}</span>
|
||||
<span
|
||||
@click="router.push({ name: 'change_plan_rules' })"
|
||||
class="text-info cursor-pointer"
|
||||
>
|
||||
{{ $t('subscribe__plans_period_notes_more_info') }}
|
||||
</span>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
</pn-scroll-list>
|
||||
</pn-page-card>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import { ref } from 'vue'
|
||||
// import { useRouter } from 'vue-router'
|
||||
// import { useAuthStore } from 'stores/auth'
|
||||
// import type { WebApp } from '@twa-dev/types'
|
||||
import qtyChatCard from 'components/account-page/qtyChatCard.vue'
|
||||
// import optionPayment from 'components/admin/account-page/optionPayment.vue'
|
||||
import { ref, computed, inject, onUnmounted, watch } from 'vue'
|
||||
import telegramStar from 'components/TelegramStar.vue'
|
||||
import { useRouter } from 'vue-router'
|
||||
import { date } from 'quasar'
|
||||
import { useI18n } from 'vue-i18n'
|
||||
import type { WebApp } from '@twa-dev/types'
|
||||
import { colors } from 'quasar';
|
||||
|
||||
// const router = useRouter()
|
||||
// const authStore = useAuthStore()
|
||||
const router = useRouter()
|
||||
|
||||
// const tg = inject('tg') as WebApp
|
||||
// const tgUser = tg.initDataUnsafe.user
|
||||
const tg = inject('tg') as WebApp
|
||||
tg.MainButton.show()
|
||||
|
||||
const chats = ref([
|
||||
{ title: 'account__chats_active', qty: 8, color: 'var(--q-primary)' },
|
||||
{ title: 'account__chats_unbound', qty: 2, color: 'grey' },
|
||||
{ title: 'account__chats_free', qty: 5, color: 'green' },
|
||||
{ title: 'account__chats_total', qty: 15, color: 'var(--q-info)' },
|
||||
])
|
||||
// @ts-expect-error: get hex text
|
||||
tg.MainButton.color = colors.getPaletteColor('primary')
|
||||
|
||||
/* const payment=ref([
|
||||
{ id: 1, qty: 50, stars: 200, discount: 0 },
|
||||
{ id: 2, qty: 120, stars: 400, discount: 20 },
|
||||
{ id: 3, qty: 220, stars: 500, discount: 30 }
|
||||
]) */
|
||||
const plans = [
|
||||
{ id: 1, name: 'TEST', val: 'test', price: null, chatsQty: 5 },
|
||||
{ id: 2, name: 'START', val: 'start', price: 1000, chatsQty: 15 },
|
||||
{ id: 3, name: 'PRO', val: 'pro', price: 5000, chatsQty: 40 },
|
||||
{ id: 4, name: 'VIP', val: 'vip', price: 12000, chatsQty: null }
|
||||
] as const
|
||||
|
||||
const periods = [
|
||||
{ id: 1, name: 'subscribe__1month', name_in_days: 'subscribe__30days', value: 30, sale: 0 },
|
||||
{ id: 2, name: 'subscribe__3months', name_in_days: 'subscribe__91days', value: 91, sale: 5 },
|
||||
{ id: 3, name: 'subscribe__1year', name_in_days: 'subscribe__365days', value: 365, sale: 15 }
|
||||
]
|
||||
|
||||
const selectPeriod = ref(periods[1]?.value)
|
||||
|
||||
const newPlan = ref(plans[1])
|
||||
|
||||
interface CurrentPlan {
|
||||
plan: string
|
||||
exp: number | null
|
||||
active_chats: number | null
|
||||
}
|
||||
|
||||
const currentPlan = ref<CurrentPlan>({ // temp, this get from api
|
||||
plan: plans[0].val,
|
||||
active_chats: 20,
|
||||
exp: Date.now() / 1000 + 500000
|
||||
})
|
||||
|
||||
interface CurrentPlanData extends CurrentPlan {
|
||||
price: number | null
|
||||
}
|
||||
|
||||
const currentPlanData = computed((): CurrentPlanData => ({
|
||||
active_chats: currentPlan.value.active_chats ?? null,
|
||||
exp: currentPlan.value.exp ?? null,
|
||||
...plans.find(el=> el?.val === currentPlan.value.plan)
|
||||
}))
|
||||
|
||||
const { t } = useI18n()
|
||||
const textBtn = computed(() => {
|
||||
const prorata = currentPlanData.value.price
|
||||
? Math.ceil(
|
||||
currentPlanData.value?.price / 30 *
|
||||
date.getDateDiff(new Date(currentPlan.value.exp * 1000), Date.now())
|
||||
)
|
||||
: 0
|
||||
const k = 1 - Number(periods.find(el => el.value === selectPeriod.value)?.sale) / 100
|
||||
const stars = formatNumber(
|
||||
Number(selectPeriod.value) *
|
||||
Number(newPlan.value?.price) *
|
||||
k - prorata
|
||||
)
|
||||
const newDateExp = date.addToDate(Date.now(), { months: Number(selectPeriod.value) })
|
||||
|
||||
return t('subscribe__pay') + ' ⭐' + stars +
|
||||
' (' + t('subscribe__plan_exp') + ' ' +
|
||||
date.formatDate(newDateExp, 'DD.MM.YYYY') + ')'
|
||||
})
|
||||
|
||||
|
||||
function formatNumber (number: string | number) {
|
||||
return number.toString().replace(/\B(?=(\d{3})+(?!\d))/g, " ")
|
||||
}
|
||||
|
||||
onUnmounted(() => tg.MainButton.hide())
|
||||
|
||||
watch(textBtn, () => tg.MainButton.setText(textBtn.value),
|
||||
{ immediate: true })
|
||||
|
||||
</script>
|
||||
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
<template>
|
||||
<doc-block type="terms_of_use"/>
|
||||
<doc-block title="terms_of_use__title" document-name="Terms_of_use"/>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
|
||||
@@ -56,9 +56,10 @@
|
||||
flat
|
||||
no-caps
|
||||
dense
|
||||
class="q-ml-xl"
|
||||
rounded
|
||||
class="q-ml-lg"
|
||||
>
|
||||
<span class="flex items-center no-wrap text-caption">
|
||||
<span class="flex items-center no-wrap q-pl-sm">
|
||||
{{ $t('header__to_projects') }}
|
||||
<q-icon name="mdi-chevron-right"/>
|
||||
</span>
|
||||
|
||||
@@ -42,6 +42,11 @@
|
||||
/>
|
||||
</q-item-section>
|
||||
<q-item-section>
|
||||
<q-item-label lines="1" v-if="!item.is_terms_accepted">
|
||||
<q-badge color="secondary">
|
||||
{{ $t('user_block__user_pending') }}
|
||||
</q-badge>
|
||||
</q-item-label>
|
||||
<q-item-label lines="1" class="text-bold" v-if="item.section1">
|
||||
{{item.section1}}
|
||||
</q-item-label>
|
||||
@@ -239,6 +244,7 @@
|
||||
import { useUsersStore } from 'stores/users'
|
||||
import { useCompaniesStore } from 'stores/companies'
|
||||
import { useUserSection } from 'composables/useUserSection'
|
||||
|
||||
defineOptions({ inheritAttrs: false })
|
||||
|
||||
const router = useRouter()
|
||||
@@ -261,7 +267,7 @@
|
||||
...el,
|
||||
...userSection(el),
|
||||
companyName: el.company_id && companiesStore.companyById(el.company_id)
|
||||
? companiesStore.companyById(el.company_id)?.name
|
||||
? companiesStore.companyById(el.company_id)?.name ?? null
|
||||
: null
|
||||
})))
|
||||
|
||||
|
||||
@@ -33,28 +33,28 @@ export default defineRouter(function (/* { store, ssrContext } */) {
|
||||
history: createHistory(process.env.VUE_ROUTER_BASE)
|
||||
})
|
||||
|
||||
const publicPath = ['404', 'terms', 'privacy', 'consent']
|
||||
|
||||
Router.beforeEach(async (to) => {
|
||||
if (to.name !== 'telegram_only' && sessionStorage.getItem('isTelegram') === 'false') {
|
||||
return { name: 'telegram_only', replace: true }
|
||||
}
|
||||
|
||||
if (to.name === 'telegram_only') return true
|
||||
|
||||
const authStore = useAuthStore()
|
||||
const projectsStore = useProjectsStore()
|
||||
|
||||
if (to.name === '404' || to.name === 'terms' || to.name === 'privacy' || to.name === 'consent') return true
|
||||
if (to.name === 'login')
|
||||
return authStore.isAuth ? { name: 'projects', replace: true } : true
|
||||
|
||||
if (to.name === 'login') return true
|
||||
if (typeof to.name === 'string' && publicPath.includes(to.name)) return true
|
||||
|
||||
if (to.name === 'agreements' && authStore.isAuth) return true
|
||||
if (to.name === 'agreements') return true
|
||||
|
||||
// if (!authStore.isTermsAccepted && authStore.isAuth) return { name: 'agreements' }
|
||||
if (to.name === 'create_account' && !authStore.isAuth) return true
|
||||
|
||||
if (to.meta.guestOnly && authStore.isAuth) {
|
||||
return { name: 'projects' }
|
||||
}
|
||||
|
||||
if (to.meta.requiresAuth && !authStore.isAuth) {
|
||||
return {
|
||||
name: 'login',
|
||||
replace: true
|
||||
}
|
||||
}
|
||||
if (!authStore.isAuth) return { name: 'login', replace: true }
|
||||
|
||||
if (to.params.id) {
|
||||
if (!projectsStore.isInit) await projectsStore.init()
|
||||
|
||||
@@ -14,32 +14,27 @@ const routes: RouteRecordRaw[] = [
|
||||
path: '/projects',
|
||||
component: () => import('pages/ProjectsPage.vue'),
|
||||
meta: {
|
||||
hideBackButton: true,
|
||||
requiresAuth: true
|
||||
hideBackButton: true
|
||||
}
|
||||
},
|
||||
{
|
||||
name: 'project_add',
|
||||
path: '/project/add',
|
||||
component: () => import('pages/ProjectAddPage.vue'),
|
||||
meta: { requiresAuth: true }
|
||||
component: () => import('pages/ProjectAddPage.vue')
|
||||
},
|
||||
{
|
||||
name: 'project_info',
|
||||
path: '/project/:id(\\d+)/info',
|
||||
component: () => import('pages/ProjectEditPage.vue'),
|
||||
meta: { requiresAuth: true }
|
||||
component: () => import('pages/ProjectEditPage.vue')
|
||||
},
|
||||
{
|
||||
name: 'company_mask',
|
||||
path: '/project/:id(\\d+)/company-mask',
|
||||
component: () => import('pages/CompanyMaskPage.vue'),
|
||||
meta: { requiresAuth: true }
|
||||
component: () => import('pages/CompanyMaskPage.vue')
|
||||
},
|
||||
{
|
||||
path: '/project/:id(\\d+)',
|
||||
component: () => import('pages/ProjectPage.vue'),
|
||||
meta: { requiresAuth: true },
|
||||
children: [
|
||||
{
|
||||
name: 'project',
|
||||
@@ -51,8 +46,7 @@ const routes: RouteRecordRaw[] = [
|
||||
path: 'chats',
|
||||
component: () => import('pages/project-page/ProjectPageChats.vue'),
|
||||
meta: {
|
||||
backRoute: 'projects',
|
||||
requiresAuth: true
|
||||
backRoute: 'projects'
|
||||
}
|
||||
},
|
||||
{
|
||||
@@ -60,8 +54,7 @@ const routes: RouteRecordRaw[] = [
|
||||
path: 'users',
|
||||
component: () => import('pages/project-page/ProjectPageUsers.vue'),
|
||||
meta: {
|
||||
backRoute: 'projects',
|
||||
requiresAuth: true
|
||||
backRoute: 'projects'
|
||||
}
|
||||
},
|
||||
{
|
||||
@@ -69,8 +62,7 @@ const routes: RouteRecordRaw[] = [
|
||||
path: 'companies',
|
||||
component: () => import('pages/project-page/ProjectPageCompanies.vue'),
|
||||
meta: {
|
||||
backRoute: 'projects',
|
||||
requiresAuth: true
|
||||
backRoute: 'projects'
|
||||
}
|
||||
}
|
||||
]
|
||||
@@ -78,67 +70,57 @@ const routes: RouteRecordRaw[] = [
|
||||
{
|
||||
name: 'chat_info',
|
||||
path: '/project/:id(\\d+)/chat/:chatId',
|
||||
component: () => import('pages/ChatPage.vue'),
|
||||
meta: { requiresAuth: true }
|
||||
component: () => import('pages/ChatPage.vue')
|
||||
},
|
||||
{
|
||||
name: 'add_company',
|
||||
path: '/project/:id(\\d+)/add-company',
|
||||
component: () => import('pages/CompanyAddPage.vue'),
|
||||
meta: { requiresAuth: true }
|
||||
component: () => import('pages/CompanyAddPage.vue')
|
||||
},
|
||||
{
|
||||
name: 'company_info',
|
||||
path: '/project/:id(\\d+)/company/:companyId',
|
||||
component: () => import('pages/CompanyEditPage.vue'),
|
||||
meta: { requiresAuth: true }
|
||||
component: () => import('pages/CompanyEditPage.vue')
|
||||
},
|
||||
{
|
||||
name: 'user_info',
|
||||
path: '/project/:id(\\d+)/user/:userId',
|
||||
component: () => import('pages/UserEditPage.vue'),
|
||||
meta: { requiresAuth: true }
|
||||
component: () => import('pages/UserEditPage.vue')
|
||||
},
|
||||
{
|
||||
name: 'account',
|
||||
path: '/account',
|
||||
component: () => import('pages/AccountPage.vue'),
|
||||
meta: { requiresAuth: true }
|
||||
component: () => import('pages/AccountPage.vue')
|
||||
},
|
||||
{
|
||||
name: 'create_account',
|
||||
path: '/create-account',
|
||||
component: () => import('pages/AccountCreatePage.vue'),
|
||||
meta: { guestOnly: true }
|
||||
component: () => import('pages/AccountCreatePage.vue')
|
||||
},
|
||||
{
|
||||
name: 'change_account_password',
|
||||
path: '/change-password',
|
||||
component: () => import('pages/AccountChangePasswordPage.vue'),
|
||||
meta: { requiresAuth: true }
|
||||
component: () => import('pages/AccountChangePasswordPage.vue')
|
||||
},
|
||||
{
|
||||
name: 'change_account_email',
|
||||
path: '/change-email',
|
||||
component: () => import('pages/AccountChangeEmailPage.vue'),
|
||||
meta: { requiresAuth: true }
|
||||
component: () => import('pages/AccountChangeEmailPage.vue')
|
||||
},
|
||||
{
|
||||
name: 'change_account_auth_method',
|
||||
path: '/change-auth-method',
|
||||
component: () => import('pages/AccountChangeAuthMethodPage.vue'),
|
||||
meta: { requiresAuth: true }
|
||||
component: () => import('pages/AccountChangeAuthMethodPage.vue')
|
||||
},
|
||||
{
|
||||
name: 'subscribe',
|
||||
path: '/subscribe',
|
||||
component: () => import('pages/account/SubscribePage.vue'),
|
||||
meta: { requiresAuth: true }
|
||||
component: () => import('pages/account/SubscribePage.vue')
|
||||
},
|
||||
{
|
||||
name: 'agreements',
|
||||
path: '/agreements',
|
||||
component: () => import('pages/account/AgreementsPage.vue')
|
||||
component: () => import('src/pages/AgreementsPage.vue')
|
||||
},
|
||||
{
|
||||
name: 'terms',
|
||||
@@ -155,6 +137,11 @@ const routes: RouteRecordRaw[] = [
|
||||
path: '/consent-pd',
|
||||
component: () => import('pages/account/ConsentPage.vue')
|
||||
},
|
||||
{
|
||||
name: 'change_plan_rules',
|
||||
path: '/change-plan-rules',
|
||||
component: () => import('pages/account/ChangePlanRules.vue')
|
||||
},
|
||||
{
|
||||
name: 'support',
|
||||
path: '/support',
|
||||
@@ -163,35 +150,36 @@ const routes: RouteRecordRaw[] = [
|
||||
{
|
||||
name: 'your_company',
|
||||
path: '/your-company',
|
||||
component: () => import('pages/CompanyYourPage.vue'),
|
||||
meta: { requiresAuth: true }
|
||||
component: () => import('pages/CompanyYourPage.vue')
|
||||
},
|
||||
{
|
||||
name: 'login',
|
||||
path: '/login',
|
||||
component: () => import('pages/LoginPage.vue'),
|
||||
meta: {
|
||||
hideBackButton: true,
|
||||
guestOnly: true
|
||||
hideBackButton: true
|
||||
}
|
||||
},
|
||||
{
|
||||
name: 'recovery_password',
|
||||
path: '/recovery-password',
|
||||
component: () => import('pages/AccountForgotPasswordPage.vue'),
|
||||
meta: { guestOnly: true }
|
||||
component: () => import('pages/AccountForgotPasswordPage.vue')
|
||||
},
|
||||
{
|
||||
name: 'settings',
|
||||
path: '/settings',
|
||||
component: () => import('pages/account/SettingsPage.vue'),
|
||||
meta: { requiresAuth: true }
|
||||
component: () => import('pages/account/SettingsPage.vue')
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
name: 'telegram_only',
|
||||
path: '/telegram-only',
|
||||
component: () => import('pages/TelegramOnlyPage.vue')
|
||||
},
|
||||
{
|
||||
path: '/:catchAll(.*)*',
|
||||
component: () => import('pages/ErrorNotFound.vue'),
|
||||
component: () => import('pages/ErrorNotFound.vue')
|
||||
}
|
||||
]
|
||||
|
||||
|
||||
@@ -33,7 +33,6 @@ export type AuthFlowType = keyof typeof ENDPOINT_MAP
|
||||
|
||||
export const useAuthStore = defineStore('auth', () => {
|
||||
const customer = ref<Customer | null>(null)
|
||||
const isTermsAccepted = ref(false)
|
||||
const wsEvent = ref<WSMessage | null>(null)
|
||||
const projectsStore = useProjectsStore()
|
||||
|
||||
@@ -74,10 +73,14 @@ export const useAuthStore = defineStore('auth', () => {
|
||||
}
|
||||
|
||||
const loginWithTelegram = async (initData: string) => {
|
||||
await api.post('/auth/telegram?'+ initData, {}, { withCredentials: true })
|
||||
await api.post('/auth/telegram?'+ initData, {}, { withCredentials: true, suppressNotify: true })
|
||||
await initialize()
|
||||
}
|
||||
|
||||
const registerWithTelegram = async (initData: string) => {
|
||||
await api.post('/auth/telegram/register?'+ initData, {}, { withCredentials: true })
|
||||
}
|
||||
|
||||
const logout = async () => {
|
||||
await api.get('/auth/logout', {})
|
||||
customer.value = null
|
||||
@@ -129,23 +132,21 @@ export const useAuthStore = defineStore('auth', () => {
|
||||
|
||||
// agreement
|
||||
async function termsAccepted () {
|
||||
const { data } = await api.post('/terms/accept')
|
||||
if (data.success) isTermsAccepted.value = true
|
||||
await api.post('/terms/accept')
|
||||
}
|
||||
|
||||
async function termsRevoked () {
|
||||
const { data } = await api.post('/terms/revoke')
|
||||
if (data.success) isTermsAccepted.value = false
|
||||
await api.post('/terms/revoke')
|
||||
}
|
||||
|
||||
return {
|
||||
customer,
|
||||
isTermsAccepted,
|
||||
isAuth,
|
||||
wsEvent,
|
||||
initialize,
|
||||
loginWithCredentials,
|
||||
loginWithTelegram,
|
||||
registerWithTelegram,
|
||||
logout,
|
||||
initRegistration,
|
||||
confirmCode,
|
||||
|
||||
@@ -39,8 +39,7 @@ export const useChatsStore = defineStore('chats', () => {
|
||||
}
|
||||
|
||||
async function getChatUsers (chatId: number) {
|
||||
const { data } = await api.get('/project/' + currentProjectId.value + '/chat/' + chatId)
|
||||
console.log(222, data)
|
||||
await api.get('/project/' + currentProjectId.value + '/chat/' + chatId)
|
||||
}
|
||||
|
||||
const getChats = computed(() => chats.value)
|
||||
|
||||
@@ -58,7 +58,6 @@ export const useCompaniesStore = defineStore('companies', () => {
|
||||
const { data } = await api.get('/project/' + currentProjectId.value + '/company/mapping')
|
||||
const companiesMaskAPI = data.data // arr [company_id, company_list[]]
|
||||
companiesMask.value = companiesMaskAPI
|
||||
console.log(11, companiesMaskAPI)
|
||||
}
|
||||
|
||||
function checkCompanyMasked (id: number) {
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
import { ref, computed, watch } from 'vue'
|
||||
import { defineStore } from 'pinia'
|
||||
import { api } from 'boot/axios'
|
||||
import type { User, UserParams } from 'types/Users'
|
||||
import type { User, UserParams } from 'types/User'
|
||||
import { useProjectsStore } from 'stores/projects'
|
||||
import { useAuthStore } from 'stores/auth'
|
||||
|
||||
@@ -27,7 +27,6 @@ export const useUsersStore = defineStore('users', () => {
|
||||
|
||||
async function update (userId: number, userData: UserParams) {
|
||||
const { data } = await api.put('/project/' + currentProjectId.value + '/user/' + userId, userData)
|
||||
console.log('update', data.data)
|
||||
const userAPI = data.data
|
||||
const idx = users.value.findIndex(item => item.id === userAPI.id)
|
||||
if (users.value[idx]) Object.assign(users.value[idx], userAPI)
|
||||
|
||||
@@ -1,8 +1,8 @@
|
||||
interface ProjectParams {
|
||||
name: string
|
||||
description: string
|
||||
logo: string
|
||||
is_logo_bg: boolean
|
||||
description: string | null
|
||||
logo: string | null
|
||||
is_logo_bg: boolean | null
|
||||
[key: string]: unknown
|
||||
}
|
||||
|
||||
|
||||
@@ -18,6 +18,7 @@ interface User extends UserParams {
|
||||
username: string | null
|
||||
photo: string | null
|
||||
is_leave: boolean
|
||||
is_terms_accepted: boolean
|
||||
}
|
||||
|
||||
export type {
|
||||
1
src/utils/constants.ts
Normal file
1
src/utils/constants.ts
Normal file
@@ -0,0 +1 @@
|
||||
export const BOT_NAME = 'ready_or_not_2025_bot'
|
||||
Reference in New Issue
Block a user