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<% } %>">
|
<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" href="icons/favicon.svg" type="image/svg+xml">
|
||||||
<link rel="icon" type="image/png" sizes="96x96" href="icons/favicon-96x96.png">
|
<link rel="icon" type="image/ico" href="icons/favicon.ico">
|
||||||
<link rel="icon" type="image/png" sizes="32x32" href="icons/favicon-32x32.png">
|
<link rel="apple-touch-icon" href="icons/apple-touch-icon.png">
|
||||||
<link rel="icon" type="image/png" sizes="16x16" href="icons/favicon-16x16.png">
|
|
||||||
<link rel="icon" type="image/ico" href="favicon.ico">
|
|
||||||
</head>
|
</head>
|
||||||
<body>
|
<body>
|
||||||
<!-- quasar:entry-point -->
|
<!-- 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 { defineBoot } from '#q-app/wrappers'
|
||||||
import axios, { type AxiosError } from 'axios'
|
import axios, { type AxiosError, type AxiosRequestConfig } from 'axios'
|
||||||
import { Notify } from 'quasar'
|
import { Notify } from 'quasar'
|
||||||
|
|
||||||
|
declare module 'axios' {
|
||||||
|
export interface AxiosRequestConfig {
|
||||||
|
suppressNotify?: boolean
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
class ServerError extends Error {
|
class ServerError extends Error {
|
||||||
constructor(
|
constructor(
|
||||||
public code: string,
|
public code: string,
|
||||||
@@ -31,12 +37,15 @@ api.interceptors.response.use(
|
|||||||
errorData.message
|
errorData.message
|
||||||
)
|
)
|
||||||
|
|
||||||
Notify.create({
|
if (!error.config?.suppressNotify) {
|
||||||
type: 'negative',
|
Notify.create({
|
||||||
message: errorData.code + ': ' + errorData.message,
|
type: 'negative',
|
||||||
icon: 'mdi-alert-outline',
|
message: errorData.code + ': ' + errorData.message,
|
||||||
position: 'bottom'
|
icon: 'mdi-alert-outline',
|
||||||
})
|
position: 'bottom'
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
return Promise.reject(serverError)
|
return Promise.reject(serverError)
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
<template>
|
<template>
|
||||||
<div class="flex row items-center">
|
<div class="flex row items-center no-wrap">
|
||||||
<svg
|
<svg
|
||||||
class="iconcolor q-mr-sm"
|
class="iconcolor q-mr-xs"
|
||||||
viewBox="0 0 8.4666662 8.4666662"
|
viewBox="0 0 8.4666662 8.4666662"
|
||||||
width="32"
|
width="32"
|
||||||
height="32"
|
height="32"
|
||||||
@@ -68,12 +68,17 @@
|
|||||||
</g>
|
</g>
|
||||||
</svg>
|
</svg>
|
||||||
|
|
||||||
<span class="text-h4 q-pa-0" style="color: var(--logo-color-bg-white);">
|
<span
|
||||||
projects
|
class="text-h4 text-brand"
|
||||||
|
style="
|
||||||
|
color: var(--logo-color-bg-white);
|
||||||
|
margin-right: 2px;"
|
||||||
|
>
|
||||||
|
tg
|
||||||
</span>
|
</span>
|
||||||
|
|
||||||
<span class="text-h4 text-brand text-bold q-pa-0">
|
<span class="text-h4 text-brand2 text-bold q-pa-0">
|
||||||
Node
|
Crew
|
||||||
</span>
|
</span>
|
||||||
|
|
||||||
</div>
|
</div>
|
||||||
@@ -99,16 +104,16 @@
|
|||||||
}
|
}
|
||||||
|
|
||||||
.iconcolor {
|
.iconcolor {
|
||||||
--icon-color: var(--logo-color-bg-white);
|
--icon-color: #27A7E7;
|
||||||
}
|
}
|
||||||
|
|
||||||
@keyframes blink {
|
@keyframes blink {
|
||||||
100%,
|
100%,
|
||||||
0% {
|
0% {
|
||||||
fill: $light-green-14;
|
fill: #fa9e7a;
|
||||||
}
|
}
|
||||||
60% {
|
60% {
|
||||||
fill: $green-14;
|
fill: #F36D3A;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -126,7 +126,6 @@
|
|||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
import { ref, computed } from 'vue'
|
import { ref, computed } from 'vue'
|
||||||
import type { AxiosError } from 'axios'
|
import type { AxiosError } from 'axios'
|
||||||
import { useQuasar } from 'quasar'
|
|
||||||
import { useI18n } from "vue-i18n"
|
import { useI18n } from "vue-i18n"
|
||||||
import { QInput } from 'quasar'
|
import { QInput } from 'quasar'
|
||||||
import { useAuthStore, type AuthFlowType } from 'stores/auth'
|
import { useAuthStore, type AuthFlowType } from 'stores/auth'
|
||||||
@@ -141,7 +140,6 @@
|
|||||||
: 'changeMethod'
|
: 'changeMethod'
|
||||||
})
|
})
|
||||||
|
|
||||||
const $q = useQuasar()
|
|
||||||
const { t } = useI18n()
|
const { t } = useI18n()
|
||||||
const authStore = useAuthStore()
|
const authStore = useAuthStore()
|
||||||
|
|
||||||
@@ -178,34 +176,17 @@
|
|||||||
})
|
})
|
||||||
|
|
||||||
const stepActions: Record<Step, () => Promise<void>> = {
|
const stepActions: Record<Step, () => Promise<void>> = {
|
||||||
1: async () => {
|
1: async () => {
|
||||||
await authStore.initRegistration(flowType.value, login.value)
|
await authStore.initRegistration(flowType.value, login.value)
|
||||||
},
|
},
|
||||||
2: async () => {
|
2: async () => {
|
||||||
await authStore.confirmCode(flowType.value, login.value, code.value)
|
await authStore.confirmCode(flowType.value, login.value, code.value)
|
||||||
},
|
},
|
||||||
3: async () => {
|
3: async () => {
|
||||||
await authStore.setPassword(flowType.value, login.value, code.value, password.value)
|
await authStore.setPassword(flowType.value, login.value, code.value, password.value)
|
||||||
if (flowType.value === 'register' || flowType.value === 'changeMethod') {
|
if (flowType.value === 'register' || flowType.value === 'changeMethod') {
|
||||||
await authStore.loginWithCredentials(login.value, password.value)
|
await authStore.loginWithCredentials(login.value, password.value)
|
||||||
}
|
}
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
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 = ''
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -218,7 +199,7 @@
|
|||||||
showSuccessOverlay.value = true
|
showSuccessOverlay.value = true
|
||||||
}
|
}
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
handleError(error as AxiosError)
|
console.error(error as AxiosError)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -7,8 +7,8 @@
|
|||||||
<q-btn
|
<q-btn
|
||||||
rounded color="primary"
|
rounded color="primary"
|
||||||
class="w100 q-mt-md q-mb-xs"
|
class="w100 q-mt-md q-mb-xs"
|
||||||
:disable="!(isFormValid && (isDirty(initialCompany, modelValue)))"
|
:disable="!isFormValid"
|
||||||
@click = "emit('update')"
|
@click = "onSubmit"
|
||||||
>
|
>
|
||||||
{{ $t(btnText) }}
|
{{ $t(btnText) }}
|
||||||
</q-btn>
|
</q-btn>
|
||||||
@@ -57,10 +57,10 @@
|
|||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
import {onMounted, computed, ref } from 'vue'
|
import { computed } from 'vue'
|
||||||
import { useI18n } from 'vue-i18n'
|
import { useI18n } from 'vue-i18n'
|
||||||
import { isDirty } from 'helpers/helpers'
|
|
||||||
import type { CompanyParams } from 'types/Company'
|
import type { CompanyParams } from 'types/Company'
|
||||||
|
import { convertEmptyStringsToNull } from 'helpers/helpers'
|
||||||
|
|
||||||
const { t }= useI18n()
|
const { t }= useI18n()
|
||||||
|
|
||||||
@@ -108,12 +108,10 @@
|
|||||||
return Object.values(validations).every(Boolean)
|
return Object.values(validations).every(Boolean)
|
||||||
})
|
})
|
||||||
|
|
||||||
const initialCompany = ref({} as CompanyParams)
|
function onSubmit() {
|
||||||
|
const cleanedData = convertEmptyStringsToNull(modelValue.value)
|
||||||
onMounted(() => {
|
emit('update', cleanedData)
|
||||||
console.log(111, modelValue.value)
|
}
|
||||||
initialCompany.value = { ...modelValue.value }
|
|
||||||
})
|
|
||||||
|
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
|
|||||||
@@ -7,7 +7,7 @@
|
|||||||
<q-btn
|
<q-btn
|
||||||
rounded color="primary"
|
rounded color="primary"
|
||||||
class="w100 q-mt-md q-mb-xs"
|
class="w100 q-mt-md q-mb-xs"
|
||||||
:disable="!(isFormValid && (isDirty(initialProject, modelValue)))"
|
:disable="!isFormValid"
|
||||||
@click = "emit('update')"
|
@click = "emit('update')"
|
||||||
>
|
>
|
||||||
{{ $t(btnText) }}
|
{{ $t(btnText) }}
|
||||||
@@ -64,7 +64,6 @@
|
|||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
import { onMounted, computed, ref } from 'vue'
|
import { onMounted, computed, ref } from 'vue'
|
||||||
import { useI18n } from 'vue-i18n'
|
import { useI18n } from 'vue-i18n'
|
||||||
import { isDirty } from 'helpers/helpers'
|
|
||||||
import type { ProjectParams } from 'types/Project'
|
import type { ProjectParams } from 'types/Project'
|
||||||
|
|
||||||
const { t } = useI18n()
|
const { t } = useI18n()
|
||||||
|
|||||||
@@ -7,8 +7,7 @@
|
|||||||
<q-btn
|
<q-btn
|
||||||
rounded color="primary"
|
rounded color="primary"
|
||||||
class="w100 q-mt-md q-mb-xs"
|
class="w100 q-mt-md q-mb-xs"
|
||||||
:disable="!(isDirty(initialUser, modelValue))"
|
@click = "onSubmit"
|
||||||
@click = "emit('update')"
|
|
||||||
>
|
>
|
||||||
{{ $t(btnText) }}
|
{{ $t(btnText) }}
|
||||||
</q-btn>
|
</q-btn>
|
||||||
@@ -108,12 +107,13 @@
|
|||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
import { onMounted, computed, ref } from 'vue'
|
import { computed } from 'vue'
|
||||||
import { useCompaniesStore } from 'stores/companies'
|
import { useCompaniesStore } from 'stores/companies'
|
||||||
import { useI18n } from 'vue-i18n'
|
import { useI18n } from 'vue-i18n'
|
||||||
import { isDirty } from 'helpers/helpers'
|
import { convertEmptyStringsToNull } from 'src/helpers/helpers'
|
||||||
import type { User } from 'types/Users'
|
import type { User } from 'types/Users'
|
||||||
|
|
||||||
|
|
||||||
const { t } = useI18n()
|
const { t } = useI18n()
|
||||||
|
|
||||||
const modelValue = defineModel<User>({
|
const modelValue = defineModel<User>({
|
||||||
@@ -127,11 +127,10 @@
|
|||||||
|
|
||||||
const emit = defineEmits(['update'])
|
const emit = defineEmits(['update'])
|
||||||
|
|
||||||
const initialUser = ref({} as User)
|
function onSubmit() {
|
||||||
|
const cleanedData = convertEmptyStringsToNull(modelValue.value)
|
||||||
onMounted(() => {
|
emit('update', cleanedData)
|
||||||
initialUser.value = { ...modelValue.value }
|
}
|
||||||
})
|
|
||||||
|
|
||||||
const companiesStore = useCompaniesStore()
|
const companiesStore = useCompaniesStore()
|
||||||
const companies = computed(() => companiesStore.companies)
|
const companies = computed(() => companiesStore.companies)
|
||||||
|
|||||||
@@ -1,10 +1,18 @@
|
|||||||
// app global css in SCSS form
|
// app global css in SCSS form
|
||||||
.text-brand {
|
.text-brand {
|
||||||
color: $green-14 !important;
|
color: #27A7E7 !important;
|
||||||
}
|
}
|
||||||
|
|
||||||
.bg-brand {
|
.bg-brand {
|
||||||
background: $green-14 !important;
|
background: #27A7E7 !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
.text-brand2 {
|
||||||
|
color: #F36D3A !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
.bg-brand2 {
|
||||||
|
background: #F36D3A !important;
|
||||||
}
|
}
|
||||||
|
|
||||||
$base-width: 100;
|
$base-width: 100;
|
||||||
@@ -65,3 +73,12 @@ body {
|
|||||||
border-bottom: 1px solid grey;
|
border-bottom: 1px solid grey;
|
||||||
margin: auto;
|
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.
|
// to match your app's branding.
|
||||||
// Tip: Use the "Theme Builder" on Quasar's documentation website.
|
// Tip: Use the "Theme Builder" on Quasar's documentation website.
|
||||||
|
|
||||||
$primary : #1976D2;
|
$primary : #27A7E7;
|
||||||
$secondary : #26A69A;
|
$secondary : #26A69A;
|
||||||
$accent : #9C27B0;
|
$accent : #9C27B0;
|
||||||
|
|
||||||
@@ -26,4 +26,16 @@ $warning : #F2C037;
|
|||||||
|
|
||||||
$lightgrey : #DCDCDC;
|
$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
|
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 {
|
export {
|
||||||
isDirty,
|
isDirty,
|
||||||
parseIntString
|
parseIntString,
|
||||||
|
convertEmptyStringsToNull
|
||||||
}
|
}
|
||||||
@@ -192,12 +192,10 @@
|
|||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
import { ref, computed } from 'vue'
|
import { ref, computed } from 'vue'
|
||||||
import type { AxiosError } from 'axios'
|
import type { AxiosError } from 'axios'
|
||||||
import { useQuasar } from 'quasar'
|
|
||||||
import { useI18n } from "vue-i18n"
|
import { useI18n } from "vue-i18n"
|
||||||
import { QInput } from 'quasar'
|
import { QInput } from 'quasar'
|
||||||
import { useAuthStore } from 'stores/auth'
|
import { useAuthStore } from 'stores/auth'
|
||||||
|
|
||||||
const $q = useQuasar()
|
|
||||||
const { t } = useI18n()
|
const { t } = useI18n()
|
||||||
const authStore = useAuthStore()
|
const authStore = useAuthStore()
|
||||||
|
|
||||||
@@ -231,39 +229,22 @@
|
|||||||
})
|
})
|
||||||
|
|
||||||
const stepActions: Record<Step, () => Promise<void>> = {
|
const stepActions: Record<Step, () => Promise<void>> = {
|
||||||
1: async () => {
|
1: async () => {
|
||||||
await authStore.getCodeCurrentEmail()
|
await authStore.getCodeCurrentEmail()
|
||||||
},
|
},
|
||||||
2: async () => {
|
2: async () => {
|
||||||
await authStore.confirmCurrentEmailCode(code.value)
|
await authStore.confirmCurrentEmailCode(code.value)
|
||||||
console.log(code.value)
|
console.log(code.value)
|
||||||
},
|
},
|
||||||
3: async () => {
|
3: async () => {
|
||||||
await authStore.getCodeNewEmail(code.value, newLogin.value)
|
await authStore.getCodeNewEmail(code.value, newLogin.value)
|
||||||
},
|
},
|
||||||
4: async () => {
|
4: async () => {
|
||||||
await authStore.confirmNewEmailCode(code.value, newCode.value, newLogin.value,)
|
await authStore.confirmNewEmailCode(code.value, newCode.value, newLogin.value,)
|
||||||
},
|
},
|
||||||
5: async () => {
|
5: async () => {
|
||||||
await authStore.setNewEmailPassword(code.value, newCode.value, newLogin.value, password.value)
|
await authStore.setNewEmailPassword(code.value, newCode.value, newLogin.value, password.value)
|
||||||
await authStore.loginWithCredentials(newLogin.value, password.value)
|
await authStore.loginWithCredentials(newLogin.value, password.value)
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
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 = ''
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -276,7 +257,7 @@
|
|||||||
showSuccessOverlay.value = true
|
showSuccessOverlay.value = true
|
||||||
}
|
}
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
handleError(error as AxiosError)
|
console.error(error as AxiosError)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
<template>
|
<template>
|
||||||
<pn-page-card>
|
<pn-page-card>
|
||||||
<template #title>
|
<template #title>
|
||||||
{{$t('login__register')}}
|
{{$t('login__register_title')}}
|
||||||
</template>
|
</template>
|
||||||
<pn-scroll-list>
|
<pn-scroll-list>
|
||||||
<account-helper :type :email/>
|
<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>
|
</template>
|
||||||
|
|
||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
import { ref } from 'vue'
|
import { reactive } from 'vue'
|
||||||
import { useRouter } from 'vue-router'
|
import { useRouter } from 'vue-router'
|
||||||
import companyBlock from 'components/companyBlock.vue'
|
import companyBlock from 'components/companyBlock.vue'
|
||||||
import { useCompaniesStore } from 'stores/companies'
|
import { useCompaniesStore } from 'stores/companies'
|
||||||
@@ -17,18 +17,10 @@
|
|||||||
const router = useRouter()
|
const router = useRouter()
|
||||||
const companiesStore = useCompaniesStore()
|
const companiesStore = useCompaniesStore()
|
||||||
|
|
||||||
const newCompany = ref(<CompanyParams>{
|
const newCompany = reactive<CompanyParams>({} as CompanyParams)
|
||||||
name: '',
|
|
||||||
logo: '',
|
|
||||||
description: '',
|
|
||||||
site: '',
|
|
||||||
address: '',
|
|
||||||
phone: '',
|
|
||||||
email: ''
|
|
||||||
})
|
|
||||||
|
|
||||||
async function addCompany () {
|
async function addCompany(companyData: CompanyParams) {
|
||||||
await companiesStore.add(newCompany.value)
|
await companiesStore.add(companyData)
|
||||||
router.go(-1)
|
router.go(-1)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -1,12 +1,12 @@
|
|||||||
<template>
|
<template>
|
||||||
<company-block
|
<company-block
|
||||||
v-if="companyMod"
|
v-if="company"
|
||||||
v-model="companyMod"
|
v-model="company"
|
||||||
title="company_edit__title_card"
|
title="company_edit__title_card"
|
||||||
btnText="company_edit__btn"
|
btnText="company_edit__btn"
|
||||||
@update="updateCompany"
|
@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="q-mb-md flex w100 justify-center">
|
||||||
<div class="flex items-center text-amber-10">
|
<div class="flex items-center text-amber-10">
|
||||||
<q-icon name="star" class="q-pr-xs"/>
|
<q-icon name="star" class="q-pr-xs"/>
|
||||||
@@ -32,24 +32,27 @@
|
|||||||
const route = useRoute()
|
const route = useRoute()
|
||||||
const companiesStore = useCompaniesStore()
|
const companiesStore = useCompaniesStore()
|
||||||
|
|
||||||
const companyMod = ref<CompanyParams | null>(null)
|
const company = ref<CompanyParams | null>(null)
|
||||||
const companyId = computed(() => parseIntString(route.params.companyId))
|
const companyId = computed(() => parseIntString(route.params.companyId))
|
||||||
|
|
||||||
if (companiesStore.isInit) {
|
function initCompany() {
|
||||||
companyMod.value = companyId.value
|
if (companiesStore.isInit && companyId.value) {
|
||||||
? { ...companiesStore.companyById(companyId.value) } as CompanyParams
|
const foundCompany = companiesStore.companyById(companyId.value)
|
||||||
: null
|
if (foundCompany) {
|
||||||
|
company.value = foundCompany as CompanyParams
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (companiesStore.isInit) initCompany()
|
||||||
|
|
||||||
watch(() => companiesStore.isInit, (isInit) => {
|
watch(() => companiesStore.isInit, (isInit) => {
|
||||||
if (isInit && companyId.value && !companyMod.value) {
|
if (isInit) initCompany()
|
||||||
companyMod.value = { ...companiesStore.companyById(companyId.value) as CompanyParams }
|
|
||||||
}
|
|
||||||
})
|
})
|
||||||
|
|
||||||
async function updateCompany () {
|
async function updateCompany(companyData: CompanyParams) {
|
||||||
if (companyId.value && companyMod.value) {
|
if (companyId.value) {
|
||||||
await companiesStore.update(companyId.value, companyMod.value)
|
await companiesStore.update(companyId.value, companyData)
|
||||||
router.go(-1)
|
router.go(-1)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,18 +1,23 @@
|
|||||||
<template>
|
<template>
|
||||||
<q-page class="flex column items-center justify-between">
|
<q-page
|
||||||
<div :style="{ height: `${blockHeight}px` }" />
|
class="q-ma-none w100"
|
||||||
|
>
|
||||||
|
<q-scroll-area
|
||||||
|
style="height: 100vh"
|
||||||
|
>
|
||||||
|
<div class="flex items-center justify-center" style="height: 100vh">
|
||||||
<q-card
|
<q-card
|
||||||
id="login_block"
|
id="login_block"
|
||||||
flat
|
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
|
<base-logo
|
||||||
class="col-grow q-pa-md"
|
class="col-grow q-pa-lg"
|
||||||
:style="{ alignItems: 'flex-end' }"
|
:style="{ alignItems: 'flex-end' }"
|
||||||
/>
|
/>
|
||||||
|
|
||||||
<div class="q-ma-md flex column input-login">
|
<div class="flex column w100">
|
||||||
<q-input
|
<q-input
|
||||||
v-model="login"
|
v-model="login"
|
||||||
autofocus
|
autofocus
|
||||||
@@ -67,29 +72,30 @@
|
|||||||
</q-btn>
|
</q-btn>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<q-btn
|
<q-btn
|
||||||
@click="sendAuth()"
|
@click="sendAuth()"
|
||||||
color="primary"
|
color="primary"
|
||||||
:disabled="!acceptTermsOfUse || !isEmailValid || !isPasswordValid"
|
class="w100 q-my-md"
|
||||||
>
|
:disabled="!isEmailValid || !isPasswordValid"
|
||||||
{{$t('login__sign_in')}}
|
>
|
||||||
</q-btn>
|
{{$t('login__sign_in')}}
|
||||||
<div class="q-pt-lg">
|
</q-btn>
|
||||||
|
|
||||||
<q-btn
|
<q-btn
|
||||||
flat
|
flat
|
||||||
sm
|
sm
|
||||||
no-caps
|
no-caps
|
||||||
|
class="q-my-md"
|
||||||
color="primary"
|
color="primary"
|
||||||
@click="createAccount"
|
@click="createAccount"
|
||||||
>
|
>
|
||||||
{{$t('login__register')}}
|
{{$t('login__register')}}
|
||||||
</q-btn>
|
</q-btn>
|
||||||
</div>
|
|
||||||
|
|
||||||
<div
|
<div
|
||||||
v-if="isTelegramApp"
|
v-if="isTelegramApp"
|
||||||
id="alt_login"
|
id="alt_login"
|
||||||
class="w80 q-flex column items-center q-pt-md"
|
class="flex w100 column items-center "
|
||||||
>
|
>
|
||||||
<div
|
<div
|
||||||
class="orline w100 text-grey"
|
class="orline w100 text-grey"
|
||||||
@@ -101,7 +107,7 @@
|
|||||||
sm
|
sm
|
||||||
no-caps
|
no-caps
|
||||||
color="primary"
|
color="primary"
|
||||||
:disabled="!acceptTermsOfUse"
|
class="q-my-md"
|
||||||
@click="handleTelegramLogin"
|
@click="handleTelegramLogin"
|
||||||
>
|
>
|
||||||
<div class="flex items-center text-blue">
|
<div class="flex items-center text-blue">
|
||||||
@@ -122,45 +128,19 @@
|
|||||||
</q-btn>
|
</q-btn>
|
||||||
</div>
|
</div>
|
||||||
</q-card>
|
</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>
|
</div>
|
||||||
|
</q-scroll-area>
|
||||||
</q-page>
|
</q-page>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
import { ref, computed, inject, onUnmounted } from 'vue'
|
import { ref, computed, inject, onUnmounted } from 'vue'
|
||||||
import { useRouter } from 'vue-router'
|
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 { useI18n } from "vue-i18n"
|
||||||
import { useAuthStore } from 'stores/auth'
|
import { useAuthStore } from 'stores/auth'
|
||||||
import type { WebApp } from '@twa-dev/types'
|
import type { WebApp } from '@twa-dev/types'
|
||||||
import { QInput } from 'quasar'
|
import { QInput } from 'quasar'
|
||||||
import { useNotify, type ServerError } from 'composables/useNotify'
|
|
||||||
const { notifyError } = useNotify()
|
|
||||||
|
|
||||||
type ValidationRule = (val: string) => boolean | string
|
type ValidationRule = (val: string) => boolean | string
|
||||||
|
|
||||||
@@ -174,10 +154,6 @@
|
|||||||
const login = ref<string>('')
|
const login = ref<string>('')
|
||||||
const password = ref<string>('')
|
const password = ref<string>('')
|
||||||
const isPwd = ref<boolean>(true)
|
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 emailInput = ref<InstanceType<typeof QInput>>()
|
||||||
const passwordInput = ref<InstanceType<typeof QInput>>()
|
const passwordInput = ref<InstanceType<typeof QInput>>()
|
||||||
@@ -223,7 +199,7 @@
|
|||||||
async function sendAuth() {
|
async function sendAuth() {
|
||||||
try { void await authStore.loginWithCredentials(login.value, password.value) }
|
try { void await authStore.loginWithCredentials(login.value, password.value) }
|
||||||
catch (error) {
|
catch (error) {
|
||||||
notifyError(error as ServerError)
|
console.error(error)
|
||||||
}
|
}
|
||||||
await router.push({ name: 'projects' })
|
await router.push({ name: 'projects' })
|
||||||
}
|
}
|
||||||
@@ -251,12 +227,6 @@
|
|||||||
await router.push({ name: 'projects' })
|
await router.push({ name: 'projects' })
|
||||||
}
|
}
|
||||||
|
|
||||||
function syncHeights() {
|
|
||||||
if (bottomBlock.value) {
|
|
||||||
blockHeight.value = bottomBlock.value.offsetHeight
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
onUnmounted(() => {
|
onUnmounted(() => {
|
||||||
Object.values(validateTimerId.value).forEach(timer => timer && clearTimeout(timer))
|
Object.values(validateTimerId.value).forEach(timer => timer && clearTimeout(timer))
|
||||||
})
|
})
|
||||||
@@ -264,16 +234,6 @@
|
|||||||
</script>
|
</script>
|
||||||
|
|
||||||
<style scoped>
|
<style scoped>
|
||||||
|
|
||||||
.maxh15 {
|
|
||||||
max-height: calc(100Vh *0.15);
|
|
||||||
}
|
|
||||||
|
|
||||||
.input-login {
|
|
||||||
width: calc(100% * 0.8);
|
|
||||||
max-width: 300px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.login-card {
|
.login-card {
|
||||||
opacity: 0.9 !important;
|
opacity: 0.9 !important;
|
||||||
border-radius: var(--top-raduis);
|
border-radius: var(--top-raduis);
|
||||||
|
|||||||
@@ -10,13 +10,12 @@
|
|||||||
|
|
||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
import { ref, computed, watch } from 'vue'
|
import { ref, computed, watch } from 'vue'
|
||||||
import { useRouter, useRoute } from 'vue-router'
|
import { useRouter } from 'vue-router'
|
||||||
import projectBlock from 'components/projectBlock.vue'
|
import projectBlock from 'components/projectBlock.vue'
|
||||||
import { useProjectsStore } from 'stores/projects'
|
import { useProjectsStore } from 'stores/projects'
|
||||||
import type { ProjectParams } from 'types/Project'
|
import type { ProjectParams } from 'types/Project'
|
||||||
|
|
||||||
const router = useRouter()
|
const router = useRouter()
|
||||||
const route = useRoute()
|
|
||||||
const projectsStore = useProjectsStore()
|
const projectsStore = useProjectsStore()
|
||||||
|
|
||||||
const projectId = computed(() => projectsStore.currentProjectId)
|
const projectId = computed(() => projectsStore.currentProjectId)
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
<template>
|
<template>
|
||||||
<user-block
|
<user-block
|
||||||
v-if="userMod"
|
v-if="user"
|
||||||
v-model="userMod"
|
v-model="user"
|
||||||
title="user_edit__title_card"
|
title="user_edit__title_card"
|
||||||
btnText="user_edit__btn"
|
btnText="user_edit__btn"
|
||||||
@update="updateUser"
|
@update="updateUser"
|
||||||
@@ -20,24 +20,25 @@
|
|||||||
const route = useRoute()
|
const route = useRoute()
|
||||||
const usersStore = useUsersStore()
|
const usersStore = useUsersStore()
|
||||||
|
|
||||||
const userMod = ref<User | null>(null)
|
const user = ref<User | null>(null)
|
||||||
const userId = computed(() => parseIntString(route.params.userId))
|
const userId = computed(() => parseIntString(route.params.userId))
|
||||||
|
|
||||||
if (usersStore.isInit) {
|
function initUser() {
|
||||||
userMod.value = userId.value
|
if (usersStore.isInit && userId.value) {
|
||||||
? { ...usersStore.userById(userId.value) } as User
|
const foundUser = usersStore.userById(userId.value)
|
||||||
: null
|
if (foundUser) {
|
||||||
|
user.value = { ...foundUser } as User
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
watch(() => usersStore.isInit, (isInit) => {
|
if (usersStore.isInit) initUser()
|
||||||
if (isInit && userId.value && !userMod.value) {
|
|
||||||
userMod.value = { ...usersStore.userById(userId.value) as User }
|
|
||||||
}
|
|
||||||
})
|
|
||||||
|
|
||||||
async function updateUser () {
|
watch(() => usersStore.isInit, initUser)
|
||||||
if (userId.value && userMod.value) {
|
|
||||||
await usersStore.update(userId.value, userMod.value)
|
async function updateUser (userData: User) {
|
||||||
|
if (userId.value) {
|
||||||
|
await usersStore.update(userId.value, userData)
|
||||||
router.go(-1)
|
router.go(-1)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -40,7 +40,7 @@ export const useAuthStore = defineStore('auth', () => {
|
|||||||
|
|
||||||
const initialize = async () => {
|
const initialize = async () => {
|
||||||
try {
|
try {
|
||||||
const { data } = await api.get('/customer/profile')
|
const { data } = await api.get('/customer/profile', { suppressNotify: true })
|
||||||
customer.value = data.data
|
customer.value = data.data
|
||||||
const socket = new WebSocket("wss://946gp81j-9000.euw.devtunnels.ms/api/admin")
|
const socket = new WebSocket("wss://946gp81j-9000.euw.devtunnels.ms/api/admin")
|
||||||
console.log(socket)
|
console.log(socket)
|
||||||
|
|||||||
@@ -1,18 +1,17 @@
|
|||||||
interface CompanyParams {
|
interface CompanyParams {
|
||||||
name: string
|
name: string
|
||||||
description: string
|
description: string | null
|
||||||
address: string
|
address: string | null
|
||||||
site: string
|
site: string | null
|
||||||
phone: string
|
phone: string | null
|
||||||
email: string
|
email: string | null
|
||||||
logo: string
|
logo: string | null
|
||||||
[key: string]: string | number
|
[key: string]: string | number | null
|
||||||
}
|
}
|
||||||
|
|
||||||
interface Company extends CompanyParams {
|
interface Company extends CompanyParams {
|
||||||
id: number
|
id: number
|
||||||
project_id: number
|
project_id: number
|
||||||
[key: string]: string | number
|
|
||||||
}
|
}
|
||||||
|
|
||||||
interface CompanyMask {
|
interface CompanyMask {
|
||||||
|
|||||||
@@ -1,11 +1,12 @@
|
|||||||
interface UserParams {
|
interface UserParams {
|
||||||
fullname: string
|
fullname: string | null
|
||||||
department: string
|
department: string | null
|
||||||
role: string
|
role: string | null
|
||||||
phone: string
|
phone: string | null
|
||||||
email: string
|
email: string | null
|
||||||
is_blocked: boolean
|
is_blocked: boolean
|
||||||
company_id: number | null
|
company_id: number | null
|
||||||
|
[key: string]: string | boolean | number | null
|
||||||
}
|
}
|
||||||
|
|
||||||
interface User extends UserParams {
|
interface User extends UserParams {
|
||||||
@@ -17,7 +18,6 @@ interface User extends UserParams {
|
|||||||
username: string | null
|
username: string | null
|
||||||
photo: string | null
|
photo: string | null
|
||||||
is_leave: boolean
|
is_leave: boolean
|
||||||
[key: string]: unknown
|
|
||||||
}
|
}
|
||||||
|
|
||||||
export type {
|
export type {
|
||||||
|
|||||||