update empty string to null
BIN
i18n-2.xlsm
@@ -17,11 +17,9 @@
|
||||
<!--
|
||||
<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="icon" type="image/png" sizes="128x128" href="icons/favicon-128x128.png">
|
||||
<link rel="icon" type="image/png" sizes="96x96" href="icons/favicon-96x96.png">
|
||||
<link rel="icon" type="image/png" sizes="32x32" href="icons/favicon-32x32.png">
|
||||
<link rel="icon" type="image/png" sizes="16x16" href="icons/favicon-16x16.png">
|
||||
<link rel="icon" type="image/ico" href="favicon.ico">
|
||||
<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">
|
||||
</head>
|
||||
<body>
|
||||
<!-- quasar:entry-point -->
|
||||
|
||||
BIN
public/icons/android-chrome-192x192.png
Normal file
|
After Width: | Height: | Size: 24 KiB |
BIN
public/icons/android-chrome-512x512.png
Normal file
|
After Width: | Height: | Size: 146 KiB |
BIN
public/icons/apple-touch-icon.png
Normal file
|
After Width: | Height: | Size: 27 KiB |
BIN
public/icons/favicon-16x16.png
Normal file
|
After Width: | Height: | Size: 705 B |
BIN
public/icons/favicon-32x32.png
Normal file
|
After Width: | Height: | Size: 1.5 KiB |
BIN
public/icons/favicon.ico
Normal file
|
After Width: | Height: | Size: 15 KiB |
67
public/icons/favicon.svg
Normal file
@@ -0,0 +1,67 @@
|
||||
<svg
|
||||
|
||||
viewBox="0 0 8.4666662 8.4666662"
|
||||
width="32"
|
||||
height="32"
|
||||
version="1.1"
|
||||
id="svg1"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
xmlns:svg="http://www.w3.org/2000/svg"
|
||||
>
|
||||
<defs id="defs1" />
|
||||
<g id="layer1">
|
||||
<rect
|
||||
style="fill: #27A7E7 ;stroke-width:0.233149"
|
||||
id="rect5"
|
||||
width="6.9885192"
|
||||
height="0.35581663"
|
||||
x="3.114475"
|
||||
y="0.86827624"
|
||||
transform="matrix(0.77578367,0.63099897,-0.77578367,0.63099897,0,0)"
|
||||
/>
|
||||
<rect
|
||||
style="fill: #27A7E7 ;stroke-width:0.24961"
|
||||
id="rect5-7"
|
||||
width="7.4819207"
|
||||
height="0.3809379"
|
||||
x="-3.9267058"
|
||||
y="5.7988153"
|
||||
transform="matrix(-0.70756824,0.70664502,0.70756824,0.70664502,0,0)"
|
||||
/>
|
||||
<circle
|
||||
style="fill: #27A7E7 ;stroke-width:0.134869"
|
||||
id="path5-8"
|
||||
cx="1.5875"
|
||||
cy="6.8791666"
|
||||
r="1.0583333"
|
||||
/>
|
||||
<circle
|
||||
style="fill: #27A7E7 ;stroke-width:0.168586"
|
||||
id="path5-8-5"
|
||||
cx="7.1437502"
|
||||
cy="7.1437502"
|
||||
r="1.3229166"
|
||||
/>
|
||||
<circle
|
||||
style="fill: #27A7E7 ;stroke-width:0.118011"
|
||||
id="path5-8-5-1"
|
||||
cx="1.4552083"
|
||||
cy="2.5135417"
|
||||
r="0.92604166"
|
||||
/>
|
||||
<circle
|
||||
style="fill: #27A7E7 ;stroke-width:0.101152"
|
||||
id="path5-8-5-1-7"
|
||||
cx="7.1437502"
|
||||
cy="1.3229166"
|
||||
r="0.79374999"
|
||||
/>
|
||||
<circle
|
||||
style="fill: #F36D3A; stroke-width:0.23602"
|
||||
id="path5"
|
||||
cx="3.96875"
|
||||
cy="4.4979167"
|
||||
r="1.8520833"
|
||||
/>
|
||||
</g>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 1.7 KiB |
1
public/icons/site.webmanifest
Normal file
@@ -0,0 +1 @@
|
||||
{"name":"","short_name":"","icons":[{"src":"/android-chrome-192x192.png","sizes":"192x192","type":"image/png"},{"src":"/android-chrome-512x512.png","sizes":"512x512","type":"image/png"}],"theme_color":"#ffffff","background_color":"#ffffff","display":"standalone"}
|
||||
@@ -1,7 +1,13 @@
|
||||
import { defineBoot } from '#q-app/wrappers'
|
||||
import axios, { type AxiosError } from 'axios'
|
||||
import axios, { type AxiosError, type AxiosRequestConfig } from 'axios'
|
||||
import { Notify } from 'quasar'
|
||||
|
||||
declare module 'axios' {
|
||||
export interface AxiosRequestConfig {
|
||||
suppressNotify?: boolean
|
||||
}
|
||||
}
|
||||
|
||||
class ServerError extends Error {
|
||||
constructor(
|
||||
public code: string,
|
||||
@@ -31,12 +37,15 @@ api.interceptors.response.use(
|
||||
errorData.message
|
||||
)
|
||||
|
||||
if (!error.config?.suppressNotify) {
|
||||
Notify.create({
|
||||
type: 'negative',
|
||||
message: errorData.code + ': ' + errorData.message,
|
||||
icon: 'mdi-alert-outline',
|
||||
position: 'bottom'
|
||||
})
|
||||
}
|
||||
|
||||
return Promise.reject(serverError)
|
||||
}
|
||||
)
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
<template>
|
||||
<div class="flex row items-center">
|
||||
<div class="flex row items-center no-wrap">
|
||||
<svg
|
||||
class="iconcolor q-mr-sm"
|
||||
class="iconcolor q-mr-xs"
|
||||
viewBox="0 0 8.4666662 8.4666662"
|
||||
width="32"
|
||||
height="32"
|
||||
@@ -68,12 +68,17 @@
|
||||
</g>
|
||||
</svg>
|
||||
|
||||
<span class="text-h4 q-pa-0" style="color: var(--logo-color-bg-white);">
|
||||
projects
|
||||
<span
|
||||
class="text-h4 text-brand"
|
||||
style="
|
||||
color: var(--logo-color-bg-white);
|
||||
margin-right: 2px;"
|
||||
>
|
||||
tg
|
||||
</span>
|
||||
|
||||
<span class="text-h4 text-brand text-bold q-pa-0">
|
||||
Node
|
||||
<span class="text-h4 text-brand2 text-bold q-pa-0">
|
||||
Crew
|
||||
</span>
|
||||
|
||||
</div>
|
||||
@@ -99,16 +104,16 @@
|
||||
}
|
||||
|
||||
.iconcolor {
|
||||
--icon-color: var(--logo-color-bg-white);
|
||||
--icon-color: #27A7E7;
|
||||
}
|
||||
|
||||
@keyframes blink {
|
||||
100%,
|
||||
0% {
|
||||
fill: $light-green-14;
|
||||
fill: #fa9e7a;
|
||||
}
|
||||
60% {
|
||||
fill: $green-14;
|
||||
fill: #F36D3A;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -126,7 +126,6 @@
|
||||
<script setup lang="ts">
|
||||
import { ref, computed } from 'vue'
|
||||
import type { AxiosError } from 'axios'
|
||||
import { useQuasar } from 'quasar'
|
||||
import { useI18n } from "vue-i18n"
|
||||
import { QInput } from 'quasar'
|
||||
import { useAuthStore, type AuthFlowType } from 'stores/auth'
|
||||
@@ -141,7 +140,6 @@
|
||||
: 'changeMethod'
|
||||
})
|
||||
|
||||
const $q = useQuasar()
|
||||
const { t } = useI18n()
|
||||
const authStore = useAuthStore()
|
||||
|
||||
@@ -192,23 +190,6 @@
|
||||
}
|
||||
}
|
||||
|
||||
const handleError = (err: AxiosError) => {
|
||||
const error = err as AxiosError<{ error?: { message?: string } }>
|
||||
const message = error.response?.data?.error?.message || t('unknown_error')
|
||||
|
||||
$q.notify({
|
||||
message: `${t('error')}: ${message}`,
|
||||
type: 'negative',
|
||||
position: 'bottom',
|
||||
timeout: 2500
|
||||
})
|
||||
|
||||
if (step.value > 1) {
|
||||
code.value = ''
|
||||
password.value = ''
|
||||
}
|
||||
}
|
||||
|
||||
const handleSubmit = async () => {
|
||||
try {
|
||||
await stepActions[step.value]()
|
||||
@@ -218,7 +199,7 @@
|
||||
showSuccessOverlay.value = true
|
||||
}
|
||||
} catch (error) {
|
||||
handleError(error as AxiosError)
|
||||
console.error(error as AxiosError)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -7,8 +7,8 @@
|
||||
<q-btn
|
||||
rounded color="primary"
|
||||
class="w100 q-mt-md q-mb-xs"
|
||||
:disable="!(isFormValid && (isDirty(initialCompany, modelValue)))"
|
||||
@click = "emit('update')"
|
||||
:disable="!isFormValid"
|
||||
@click = "onSubmit"
|
||||
>
|
||||
{{ $t(btnText) }}
|
||||
</q-btn>
|
||||
@@ -57,10 +57,10 @@
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import {onMounted, computed, ref } from 'vue'
|
||||
import { computed } from 'vue'
|
||||
import { useI18n } from 'vue-i18n'
|
||||
import { isDirty } from 'helpers/helpers'
|
||||
import type { CompanyParams } from 'types/Company'
|
||||
import { convertEmptyStringsToNull } from 'helpers/helpers'
|
||||
|
||||
const { t }= useI18n()
|
||||
|
||||
@@ -108,12 +108,10 @@
|
||||
return Object.values(validations).every(Boolean)
|
||||
})
|
||||
|
||||
const initialCompany = ref({} as CompanyParams)
|
||||
|
||||
onMounted(() => {
|
||||
console.log(111, modelValue.value)
|
||||
initialCompany.value = { ...modelValue.value }
|
||||
})
|
||||
function onSubmit() {
|
||||
const cleanedData = convertEmptyStringsToNull(modelValue.value)
|
||||
emit('update', cleanedData)
|
||||
}
|
||||
|
||||
</script>
|
||||
|
||||
|
||||
@@ -7,7 +7,7 @@
|
||||
<q-btn
|
||||
rounded color="primary"
|
||||
class="w100 q-mt-md q-mb-xs"
|
||||
:disable="!(isFormValid && (isDirty(initialProject, modelValue)))"
|
||||
:disable="!isFormValid"
|
||||
@click = "emit('update')"
|
||||
>
|
||||
{{ $t(btnText) }}
|
||||
@@ -64,7 +64,6 @@
|
||||
<script setup lang="ts">
|
||||
import { onMounted, computed, ref } from 'vue'
|
||||
import { useI18n } from 'vue-i18n'
|
||||
import { isDirty } from 'helpers/helpers'
|
||||
import type { ProjectParams } from 'types/Project'
|
||||
|
||||
const { t } = useI18n()
|
||||
|
||||
@@ -7,8 +7,7 @@
|
||||
<q-btn
|
||||
rounded color="primary"
|
||||
class="w100 q-mt-md q-mb-xs"
|
||||
:disable="!(isDirty(initialUser, modelValue))"
|
||||
@click = "emit('update')"
|
||||
@click = "onSubmit"
|
||||
>
|
||||
{{ $t(btnText) }}
|
||||
</q-btn>
|
||||
@@ -108,12 +107,13 @@
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import { onMounted, computed, ref } from 'vue'
|
||||
import { computed } from 'vue'
|
||||
import { useCompaniesStore } from 'stores/companies'
|
||||
import { useI18n } from 'vue-i18n'
|
||||
import { isDirty } from 'helpers/helpers'
|
||||
import { convertEmptyStringsToNull } from 'src/helpers/helpers'
|
||||
import type { User } from 'types/Users'
|
||||
|
||||
|
||||
const { t } = useI18n()
|
||||
|
||||
const modelValue = defineModel<User>({
|
||||
@@ -127,11 +127,10 @@
|
||||
|
||||
const emit = defineEmits(['update'])
|
||||
|
||||
const initialUser = ref({} as User)
|
||||
|
||||
onMounted(() => {
|
||||
initialUser.value = { ...modelValue.value }
|
||||
})
|
||||
function onSubmit() {
|
||||
const cleanedData = convertEmptyStringsToNull(modelValue.value)
|
||||
emit('update', cleanedData)
|
||||
}
|
||||
|
||||
const companiesStore = useCompaniesStore()
|
||||
const companies = computed(() => companiesStore.companies)
|
||||
|
||||
@@ -1,10 +1,18 @@
|
||||
// app global css in SCSS form
|
||||
.text-brand {
|
||||
color: $green-14 !important;
|
||||
color: #27A7E7 !important;
|
||||
}
|
||||
|
||||
.bg-brand {
|
||||
background: $green-14 !important;
|
||||
background: #27A7E7 !important;
|
||||
}
|
||||
|
||||
.text-brand2 {
|
||||
color: #F36D3A !important;
|
||||
}
|
||||
|
||||
.bg-brand2 {
|
||||
background: #F36D3A !important;
|
||||
}
|
||||
|
||||
$base-width: 100;
|
||||
@@ -65,3 +73,12 @@ body {
|
||||
border-bottom: 1px solid grey;
|
||||
margin: auto;
|
||||
}
|
||||
|
||||
@font-face {
|
||||
font-family: 'myFont';
|
||||
src: url(./fonts/OpenSans-Regular.woff2);
|
||||
}
|
||||
|
||||
button[disabled] {
|
||||
background-color: $grey-5 !important;
|
||||
}
|
||||
|
||||
BIN
src/css/fonts/OpenSans-Regular.woff2
Normal file
@@ -12,7 +12,7 @@
|
||||
// to match your app's branding.
|
||||
// Tip: Use the "Theme Builder" on Quasar's documentation website.
|
||||
|
||||
$primary : #1976D2;
|
||||
$primary : #27A7E7;
|
||||
$secondary : #26A69A;
|
||||
$accent : #9C27B0;
|
||||
|
||||
@@ -26,4 +26,16 @@ $warning : #F2C037;
|
||||
|
||||
$lightgrey : #DCDCDC;
|
||||
|
||||
$body-font-size: var(--dynamic-font-size)
|
||||
$body-font-size: var(--dynamic-font-size);
|
||||
|
||||
$typography-font-family: 'myFont', Roboto !default;
|
||||
|
||||
body, html, #q-app {
|
||||
font-family: $typography-font-family;
|
||||
-webkit-font-smoothing: antialiased;
|
||||
-moz-osx-font-smoothing: grayscale;
|
||||
}
|
||||
|
||||
* {
|
||||
font-family: inherit;
|
||||
}
|
||||
@@ -92,7 +92,22 @@ function parseIntString (s: string | string[] | undefined) :number | null {
|
||||
return regex.test(s) ? Number(s) : null
|
||||
}
|
||||
|
||||
// Функция для преобразования пустых строк в null
|
||||
function convertEmptyStringsToNull<T extends Record<string, unknown>>(obj: T): T {
|
||||
const result = { ...obj } as Record<string, unknown>
|
||||
|
||||
Object.keys(result).forEach(key => {
|
||||
if (result[key] === '') {
|
||||
result[key] = null
|
||||
}
|
||||
})
|
||||
|
||||
return result as T
|
||||
}
|
||||
|
||||
|
||||
export {
|
||||
isDirty,
|
||||
parseIntString
|
||||
parseIntString,
|
||||
convertEmptyStringsToNull
|
||||
}
|
||||
@@ -192,12 +192,10 @@
|
||||
<script setup lang="ts">
|
||||
import { ref, computed } from 'vue'
|
||||
import type { AxiosError } from 'axios'
|
||||
import { useQuasar } from 'quasar'
|
||||
import { useI18n } from "vue-i18n"
|
||||
import { QInput } from 'quasar'
|
||||
import { useAuthStore } from 'stores/auth'
|
||||
|
||||
const $q = useQuasar()
|
||||
const { t } = useI18n()
|
||||
const authStore = useAuthStore()
|
||||
|
||||
@@ -250,23 +248,6 @@
|
||||
}
|
||||
}
|
||||
|
||||
const handleError = (err: AxiosError) => {
|
||||
const error = err as AxiosError<{ error?: { message?: string } }>
|
||||
const message = error.response?.data?.error?.message || t('unknown_error')
|
||||
|
||||
$q.notify({
|
||||
message: `${t('error')}: ${message}`,
|
||||
type: 'negative',
|
||||
position: 'bottom',
|
||||
timeout: 2500
|
||||
})
|
||||
|
||||
if (step.value > 1) {
|
||||
code.value = ''
|
||||
password.value = ''
|
||||
}
|
||||
}
|
||||
|
||||
const handleSubmit = async () => {
|
||||
try {
|
||||
await stepActions[step.value]()
|
||||
@@ -276,7 +257,7 @@
|
||||
showSuccessOverlay.value = true
|
||||
}
|
||||
} catch (error) {
|
||||
handleError(error as AxiosError)
|
||||
console.error(error as AxiosError)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
<template>
|
||||
<pn-page-card>
|
||||
<template #title>
|
||||
{{$t('login__register')}}
|
||||
{{$t('login__register_title')}}
|
||||
</template>
|
||||
<pn-scroll-list>
|
||||
<account-helper :type :email/>
|
||||
|
||||
91
src/pages/ChatPage.vue
Normal file
@@ -0,0 +1,91 @@
|
||||
<template>
|
||||
<pn-page-card>
|
||||
<template #title>
|
||||
<pn-account-block-name/>
|
||||
<q-btn
|
||||
@click="logout()"
|
||||
flat
|
||||
round
|
||||
icon="mdi-logout"
|
||||
/>
|
||||
</template>
|
||||
|
||||
<pn-scroll-list>
|
||||
<q-list separator>
|
||||
<q-item
|
||||
v-for="item in displayItems"
|
||||
:key="item.id"
|
||||
@click="goTo(item.pathName)"
|
||||
clickable
|
||||
v-ripple
|
||||
>
|
||||
<q-item-section avatar>
|
||||
<q-avatar
|
||||
:icon="item.icon"
|
||||
:color="item.iconColor ? item.iconColor: 'brand'"
|
||||
text-color="white"
|
||||
rounded
|
||||
font-size ="26px"
|
||||
/>
|
||||
</q-item-section>
|
||||
<q-item-section>
|
||||
<q-item-label>
|
||||
{{ $t(item.name) }}
|
||||
</q-item-label>
|
||||
<q-item-label class="text-caption" v-if="$te(item.description)">
|
||||
{{ $t(item.description) }}
|
||||
</q-item-label>
|
||||
</q-item-section>
|
||||
</q-item>
|
||||
</q-list>
|
||||
</pn-scroll-list>
|
||||
</pn-page-card>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import { computed } from 'vue'
|
||||
import { useRouter } from 'vue-router'
|
||||
import { useAuthStore } from 'stores/auth'
|
||||
|
||||
const router = useRouter()
|
||||
const authStore = useAuthStore()
|
||||
|
||||
interface ItemList {
|
||||
id: number
|
||||
name: string
|
||||
description?: string
|
||||
icon: string
|
||||
iconColor?: string
|
||||
pathName: string
|
||||
display?: boolean
|
||||
}
|
||||
|
||||
const items = computed(() => ([
|
||||
{ id: 1, name: 'account__subscribe', description: 'account__subscribe_description', icon: 'mdi-crown-circle-outline', iconColor: 'orange', pathName: 'subscribe' },
|
||||
{ id: 2, name: 'account__auth_change_method', description: 'account__auth_change_method_description', icon: 'mdi-account-sync-outline', iconColor: 'primary', pathName: 'change_account_auth_method', display: !authStore.customer?.email },
|
||||
{ id: 3, name: 'account__auth_change_password', description: 'account__auth_change_password_description', icon: 'mdi-account-key-outline', iconColor: 'primary', pathName: 'change_account_password', display: !!authStore.customer?.email },
|
||||
{ id: 4, name: 'account__auth_change_account', description: 'account__auth_change_account_description', icon: 'mdi-account-switch-outline', iconColor: 'primary', pathName: 'change_account_email', display: !!authStore.customer?.email },
|
||||
{ id: 5, name: 'account__company_data', icon: 'mdi-account-group-outline', description: 'account__company_data_description', pathName: 'your_company' },
|
||||
{ id: 6, name: 'account__settings', icon: 'mdi-cog-outline', description: 'account__settings_description', iconColor: 'info', pathName: 'settings' },
|
||||
{ id: 7, name: 'account__support', icon: 'mdi-lifebuoy', description: 'account__support_description', iconColor: 'info', pathName: 'support' },
|
||||
{ id: 9, name: 'account__terms_of_use', icon: 'mdi-book-open-variant-outline', description: '', iconColor: 'grey', pathName: 'terms' },
|
||||
{ id: 10, name: 'account__privacy', icon: 'mdi-lock-outline', description: '', iconColor: 'grey', pathName: 'privacy' }
|
||||
]))
|
||||
|
||||
const displayItems = computed(() => (
|
||||
items.value.filter((item: ItemList) => !('display' in item) || item.display === true)
|
||||
))
|
||||
|
||||
async function goTo (path: string) {
|
||||
await router.push({ name: path })
|
||||
}
|
||||
|
||||
async function logout () {
|
||||
await authStore.logout()
|
||||
await router.push({ name: 'login' })
|
||||
}
|
||||
|
||||
</script>
|
||||
|
||||
<style lang="scss">
|
||||
</style>
|
||||
@@ -8,7 +8,7 @@
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import { ref } from 'vue'
|
||||
import { reactive } from 'vue'
|
||||
import { useRouter } from 'vue-router'
|
||||
import companyBlock from 'components/companyBlock.vue'
|
||||
import { useCompaniesStore } from 'stores/companies'
|
||||
@@ -17,18 +17,10 @@
|
||||
const router = useRouter()
|
||||
const companiesStore = useCompaniesStore()
|
||||
|
||||
const newCompany = ref(<CompanyParams>{
|
||||
name: '',
|
||||
logo: '',
|
||||
description: '',
|
||||
site: '',
|
||||
address: '',
|
||||
phone: '',
|
||||
email: ''
|
||||
})
|
||||
const newCompany = reactive<CompanyParams>({} as CompanyParams)
|
||||
|
||||
async function addCompany () {
|
||||
await companiesStore.add(newCompany.value)
|
||||
async function addCompany(companyData: CompanyParams) {
|
||||
await companiesStore.add(companyData)
|
||||
router.go(-1)
|
||||
}
|
||||
|
||||
|
||||
@@ -1,12 +1,12 @@
|
||||
<template>
|
||||
<company-block
|
||||
v-if="companyMod"
|
||||
v-model="companyMod"
|
||||
v-if="company"
|
||||
v-model="company"
|
||||
title="company_edit__title_card"
|
||||
btnText="company_edit__btn"
|
||||
@update="updateCompany"
|
||||
>
|
||||
<template #myCompany v-if="companyMod.is_own">
|
||||
<template #myCompany v-if="company.is_own">
|
||||
<div class="q-mb-md flex w100 justify-center">
|
||||
<div class="flex items-center text-amber-10">
|
||||
<q-icon name="star" class="q-pr-xs"/>
|
||||
@@ -32,24 +32,27 @@
|
||||
const route = useRoute()
|
||||
const companiesStore = useCompaniesStore()
|
||||
|
||||
const companyMod = ref<CompanyParams | null>(null)
|
||||
const company = ref<CompanyParams | null>(null)
|
||||
const companyId = computed(() => parseIntString(route.params.companyId))
|
||||
|
||||
if (companiesStore.isInit) {
|
||||
companyMod.value = companyId.value
|
||||
? { ...companiesStore.companyById(companyId.value) } as CompanyParams
|
||||
: null
|
||||
function initCompany() {
|
||||
if (companiesStore.isInit && companyId.value) {
|
||||
const foundCompany = companiesStore.companyById(companyId.value)
|
||||
if (foundCompany) {
|
||||
company.value = foundCompany as CompanyParams
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (companiesStore.isInit) initCompany()
|
||||
|
||||
watch(() => companiesStore.isInit, (isInit) => {
|
||||
if (isInit && companyId.value && !companyMod.value) {
|
||||
companyMod.value = { ...companiesStore.companyById(companyId.value) as CompanyParams }
|
||||
}
|
||||
if (isInit) initCompany()
|
||||
})
|
||||
|
||||
async function updateCompany () {
|
||||
if (companyId.value && companyMod.value) {
|
||||
await companiesStore.update(companyId.value, companyMod.value)
|
||||
async function updateCompany(companyData: CompanyParams) {
|
||||
if (companyId.value) {
|
||||
await companiesStore.update(companyId.value, companyData)
|
||||
router.go(-1)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,18 +1,23 @@
|
||||
<template>
|
||||
<q-page class="flex column items-center justify-between">
|
||||
<div :style="{ height: `${blockHeight}px` }" />
|
||||
|
||||
<q-page
|
||||
class="q-ma-none w100"
|
||||
>
|
||||
<q-scroll-area
|
||||
style="height: 100vh"
|
||||
>
|
||||
<div class="flex items-center justify-center" style="height: 100vh">
|
||||
<q-card
|
||||
id="login_block"
|
||||
flat
|
||||
class="flex column items-center w80 justify-between q-py-md login-card "
|
||||
class="flex column no-wrap items-center justify-between q-px-xl q-py-md login-card"
|
||||
style="max-width: 400px;"
|
||||
>
|
||||
<login-logo
|
||||
class="col-grow q-pa-md"
|
||||
<base-logo
|
||||
class="col-grow q-pa-lg"
|
||||
:style="{ alignItems: 'flex-end' }"
|
||||
/>
|
||||
|
||||
<div class="q-ma-md flex column input-login">
|
||||
<div class="flex column w100">
|
||||
<q-input
|
||||
v-model="login"
|
||||
autofocus
|
||||
@@ -70,26 +75,27 @@
|
||||
<q-btn
|
||||
@click="sendAuth()"
|
||||
color="primary"
|
||||
:disabled="!acceptTermsOfUse || !isEmailValid || !isPasswordValid"
|
||||
class="w100 q-my-md"
|
||||
:disabled="!isEmailValid || !isPasswordValid"
|
||||
>
|
||||
{{$t('login__sign_in')}}
|
||||
</q-btn>
|
||||
<div class="q-pt-lg">
|
||||
|
||||
<q-btn
|
||||
flat
|
||||
sm
|
||||
no-caps
|
||||
class="q-my-md"
|
||||
color="primary"
|
||||
@click="createAccount"
|
||||
>
|
||||
{{$t('login__register')}}
|
||||
</q-btn>
|
||||
</div>
|
||||
|
||||
<div
|
||||
v-if="isTelegramApp"
|
||||
id="alt_login"
|
||||
class="w80 q-flex column items-center q-pt-md"
|
||||
class="flex w100 column items-center "
|
||||
>
|
||||
<div
|
||||
class="orline w100 text-grey"
|
||||
@@ -101,7 +107,7 @@
|
||||
sm
|
||||
no-caps
|
||||
color="primary"
|
||||
:disabled="!acceptTermsOfUse"
|
||||
class="q-my-md"
|
||||
@click="handleTelegramLogin"
|
||||
>
|
||||
<div class="flex items-center text-blue">
|
||||
@@ -122,45 +128,19 @@
|
||||
</q-btn>
|
||||
</div>
|
||||
</q-card>
|
||||
|
||||
<div
|
||||
id="term-of-use"
|
||||
class="q-pb-md text-white flex justify-center row text-caption"
|
||||
ref="bottomBlock"
|
||||
>
|
||||
<q-resize-observer @resize="syncHeights" />
|
||||
<q-checkbox
|
||||
v-model="acceptTermsOfUse"
|
||||
checked-icon="task_alt"
|
||||
unchecked-icon="highlight_off"
|
||||
:color="acceptTermsOfUse ? 'brand' : 'red'"
|
||||
dense
|
||||
keep-color
|
||||
size="sm"
|
||||
/>
|
||||
<span class="q-px-xs">
|
||||
{{ $t('login__accept_terms_of_use') + ' ' }}
|
||||
</span>
|
||||
<span
|
||||
@click="router.push('terms-of-use')"
|
||||
style="text-decoration: underline;"
|
||||
>
|
||||
{{ $t('login__terms_of_use') }}
|
||||
</span>
|
||||
</div>
|
||||
</q-scroll-area>
|
||||
</q-page>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import { ref, computed, inject, onUnmounted } from 'vue'
|
||||
import { useRouter } from 'vue-router'
|
||||
import loginLogo from 'components/login-page/loginLogo.vue'
|
||||
import baseLogo from 'components/BaseLogo.vue'
|
||||
import { useI18n } from "vue-i18n"
|
||||
import { useAuthStore } from 'stores/auth'
|
||||
import type { WebApp } from '@twa-dev/types'
|
||||
import { QInput } from 'quasar'
|
||||
import { useNotify, type ServerError } from 'composables/useNotify'
|
||||
const { notifyError } = useNotify()
|
||||
|
||||
type ValidationRule = (val: string) => boolean | string
|
||||
|
||||
@@ -174,10 +154,6 @@
|
||||
const login = ref<string>('')
|
||||
const password = ref<string>('')
|
||||
const isPwd = ref<boolean>(true)
|
||||
const acceptTermsOfUse = ref<boolean>(true)
|
||||
|
||||
const bottomBlock = ref<HTMLDivElement | null>(null)
|
||||
const blockHeight = ref<number>(0)
|
||||
|
||||
const emailInput = ref<InstanceType<typeof QInput>>()
|
||||
const passwordInput = ref<InstanceType<typeof QInput>>()
|
||||
@@ -223,7 +199,7 @@
|
||||
async function sendAuth() {
|
||||
try { void await authStore.loginWithCredentials(login.value, password.value) }
|
||||
catch (error) {
|
||||
notifyError(error as ServerError)
|
||||
console.error(error)
|
||||
}
|
||||
await router.push({ name: 'projects' })
|
||||
}
|
||||
@@ -251,12 +227,6 @@
|
||||
await router.push({ name: 'projects' })
|
||||
}
|
||||
|
||||
function syncHeights() {
|
||||
if (bottomBlock.value) {
|
||||
blockHeight.value = bottomBlock.value.offsetHeight
|
||||
}
|
||||
}
|
||||
|
||||
onUnmounted(() => {
|
||||
Object.values(validateTimerId.value).forEach(timer => timer && clearTimeout(timer))
|
||||
})
|
||||
@@ -264,16 +234,6 @@
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
|
||||
.maxh15 {
|
||||
max-height: calc(100Vh *0.15);
|
||||
}
|
||||
|
||||
.input-login {
|
||||
width: calc(100% * 0.8);
|
||||
max-width: 300px;
|
||||
}
|
||||
|
||||
.login-card {
|
||||
opacity: 0.9 !important;
|
||||
border-radius: var(--top-raduis);
|
||||
|
||||
@@ -10,13 +10,12 @@
|
||||
|
||||
<script setup lang="ts">
|
||||
import { ref, computed, watch } from 'vue'
|
||||
import { useRouter, useRoute } from 'vue-router'
|
||||
import { useRouter } from 'vue-router'
|
||||
import projectBlock from 'components/projectBlock.vue'
|
||||
import { useProjectsStore } from 'stores/projects'
|
||||
import type { ProjectParams } from 'types/Project'
|
||||
|
||||
const router = useRouter()
|
||||
const route = useRoute()
|
||||
const projectsStore = useProjectsStore()
|
||||
|
||||
const projectId = computed(() => projectsStore.currentProjectId)
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
<template>
|
||||
<user-block
|
||||
v-if="userMod"
|
||||
v-model="userMod"
|
||||
v-if="user"
|
||||
v-model="user"
|
||||
title="user_edit__title_card"
|
||||
btnText="user_edit__btn"
|
||||
@update="updateUser"
|
||||
@@ -20,24 +20,25 @@
|
||||
const route = useRoute()
|
||||
const usersStore = useUsersStore()
|
||||
|
||||
const userMod = ref<User | null>(null)
|
||||
const user = ref<User | null>(null)
|
||||
const userId = computed(() => parseIntString(route.params.userId))
|
||||
|
||||
if (usersStore.isInit) {
|
||||
userMod.value = userId.value
|
||||
? { ...usersStore.userById(userId.value) } as User
|
||||
: null
|
||||
function initUser() {
|
||||
if (usersStore.isInit && userId.value) {
|
||||
const foundUser = usersStore.userById(userId.value)
|
||||
if (foundUser) {
|
||||
user.value = { ...foundUser } as User
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
watch(() => usersStore.isInit, (isInit) => {
|
||||
if (isInit && userId.value && !userMod.value) {
|
||||
userMod.value = { ...usersStore.userById(userId.value) as User }
|
||||
}
|
||||
})
|
||||
if (usersStore.isInit) initUser()
|
||||
|
||||
async function updateUser () {
|
||||
if (userId.value && userMod.value) {
|
||||
await usersStore.update(userId.value, userMod.value)
|
||||
watch(() => usersStore.isInit, initUser)
|
||||
|
||||
async function updateUser (userData: User) {
|
||||
if (userId.value) {
|
||||
await usersStore.update(userId.value, userData)
|
||||
router.go(-1)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -40,7 +40,7 @@ export const useAuthStore = defineStore('auth', () => {
|
||||
|
||||
const initialize = async () => {
|
||||
try {
|
||||
const { data } = await api.get('/customer/profile')
|
||||
const { data } = await api.get('/customer/profile', { suppressNotify: true })
|
||||
customer.value = data.data
|
||||
const socket = new WebSocket("wss://946gp81j-9000.euw.devtunnels.ms/api/admin")
|
||||
console.log(socket)
|
||||
|
||||
@@ -1,18 +1,17 @@
|
||||
interface CompanyParams {
|
||||
name: string
|
||||
description: string
|
||||
address: string
|
||||
site: string
|
||||
phone: string
|
||||
email: string
|
||||
logo: string
|
||||
[key: string]: string | number
|
||||
description: string | null
|
||||
address: string | null
|
||||
site: string | null
|
||||
phone: string | null
|
||||
email: string | null
|
||||
logo: string | null
|
||||
[key: string]: string | number | null
|
||||
}
|
||||
|
||||
interface Company extends CompanyParams {
|
||||
id: number
|
||||
project_id: number
|
||||
[key: string]: string | number
|
||||
}
|
||||
|
||||
interface CompanyMask {
|
||||
|
||||
@@ -1,11 +1,12 @@
|
||||
interface UserParams {
|
||||
fullname: string
|
||||
department: string
|
||||
role: string
|
||||
phone: string
|
||||
email: string
|
||||
fullname: string | null
|
||||
department: string | null
|
||||
role: string | null
|
||||
phone: string | null
|
||||
email: string | null
|
||||
is_blocked: boolean
|
||||
company_id: number | null
|
||||
[key: string]: string | boolean | number | null
|
||||
}
|
||||
|
||||
interface User extends UserParams {
|
||||
@@ -17,7 +18,6 @@ interface User extends UserParams {
|
||||
username: string | null
|
||||
photo: string | null
|
||||
is_leave: boolean
|
||||
[key: string]: unknown
|
||||
}
|
||||
|
||||
export type {
|
||||
|
||||