Browse Source

chore(gui-v2): style updates

pull/3280/head
braks 2 years ago
parent
commit
5f48cfd86d
  1. 2
      packages/nc-gui-v2/layouts/base.vue
  2. 10
      packages/nc-gui-v2/pages/index/index.vue
  3. 17
      packages/nc-gui-v2/pages/index/user/index/index.vue
  4. 101
      packages/nc-gui-v2/pages/project/index/[id].vue
  5. 378
      packages/nc-gui-v2/pages/project/index/create-external.vue
  6. 101
      packages/nc-gui-v2/pages/project/index/create.vue
  7. 2
      packages/nc-gui-v2/pages/signin.vue

2
packages/nc-gui-v2/layouts/base.vue

@ -40,7 +40,7 @@ hooks.hook('page:finish', () => {
class="flex !bg-primary items-center text-white pl-4 pr-5 shadow-lg"
>
<div
v-if="route.name === 'index' || route.name === 'project-index-create' || route.name === 'project-index-create-external'"
v-if="!route.params.projectType"
class="transition-all duration-200 p-2 cursor-pointer transform hover:scale-105 nc-noco-brand-icon"
@click="navigateTo('/')"
>

10
packages/nc-gui-v2/pages/index/index.vue

@ -68,7 +68,7 @@ onMounted(() => {
<template>
<NuxtLayout>
<div class="flex flex-col md:flex-row flex-wrap gap-6 py-6 px-12">
<div class="flex flex-col lg:flex-row flex-wrap gap-6 py-6 px-12">
<div class="hidden xl:(block)">
<GeneralSponsors />
</div>
@ -191,10 +191,12 @@ onMounted(() => {
</a-card>
</div>
<div class="flex gap-6 md:block">
<GeneralSocialCard />
<div class="flex flex-1 justify-between gap-6 lg:block">
<div>
<GeneralSocialCard />
</div>
<div class="block mt-0 md:(!mt-6) xl:hidden">
<div class="block mt-0 lg:(!mt-6) xl:hidden">
<GeneralSponsors />
</div>
</div>

17
packages/nc-gui-v2/pages/index/user/index/index.vue

@ -1,11 +1,6 @@
<script lang="ts" setup>
import { useI18n } from 'vue-i18n'
import { message } from 'ant-design-vue'
import { navigateTo } from '#app'
import { extractSdkResponseErrorMsg } from '~/utils'
import { reactive, ref, useApi, useGlobal } from '#imports'
import MaterialSymbolsWarning from '~icons/material-symbols/warning'
import MdiKeyChange from '~icons/mdi/key-change'
import { extractSdkResponseErrorMsg, navigateTo, reactive, ref, useApi, useGlobal, useI18n } from '#imports'
const { api } = useApi()
@ -81,7 +76,10 @@ const resetError = () => {
<Transition name="layout">
<div v-if="error" class="self-center mb-4 bg-red-500 text-white rounded-lg w-3/4 p-1">
<div class="flex items-center gap-2 justify-center"><MaterialSymbolsWarning /> {{ error }}</div>
<div class="flex items-center gap-2 justify-center">
<MaterialSymbolsWarning />
{{ error }}
</div>
</div>
</Transition>
@ -117,7 +115,10 @@ const resetError = () => {
<div class="flex flex-wrap gap-4 items-center mt-4 md:justify-between w-full">
<button class="submit" type="submit">
<span class="flex items-center gap-2"><MdiKeyChange /> {{ $t('activity.changePwd') }}</span>
<span class="flex items-center gap-2">
<MdiKeyChange />
{{ $t('activity.changePwd') }}
</span>
</button>
</div>
</div>

101
packages/nc-gui-v2/pages/project/index/[id].vue

@ -1,15 +1,21 @@
<script lang="ts" setup>
import { onMounted } from '@vue/runtime-core'
import type { Form } from 'ant-design-vue'
import type { ProjectType } from 'nocodb-sdk'
import { ref } from 'vue'
import { message } from 'ant-design-vue'
import { navigateTo, useRoute } from '#app'
import { extractSdkResponseErrorMsg, projectTitleValidator } from '~/utils'
import MaterialSymbolsRocketLaunchOutline from '~icons/material-symbols/rocket-launch-outline'
import { nextTick, reactive, useSidebar } from '#imports'
import {
extractSdkResponseErrorMsg,
navigateTo,
nextTick,
onMounted,
projectTitleValidator,
reactive,
ref,
useApi,
useRoute,
useSidebar,
} from '#imports'
const { api } = useApi()
const { api, isLoading } = useApi()
useSidebar({ hasSidebar: false })
@ -23,6 +29,8 @@ const nameValidationRules = [
projectTitleValidator,
]
const form = ref<typeof Form>()
const formState = reactive({
title: '',
})
@ -46,8 +54,6 @@ const renameProject = async () => {
}
}
const form = ref<typeof Form>()
// select and focus title field on load
onMounted(async () => {
await getProject()
@ -56,7 +62,9 @@ onMounted(async () => {
// todo: replace setTimeout and follow better approach
setTimeout(() => {
const input = form.value?.$el?.querySelector('input[type=text]')
input.setSelectionRange(0, formState.title.length)
input.focus()
}, 500)
})
@ -64,20 +72,63 @@ onMounted(async () => {
</script>
<template>
<a-card class="w-[500px] !mx-auto !mt-100px shadow-md">
<h3 class="text-3xl text-center font-semibold mb-2">{{ $t('activity.editProject') }}</h3>
<a-form ref="form" :model="formState" name="basic" layout="vertical" autocomplete="off" @finish="renameProject">
<a-form-item :label="$t('labels.projName')" name="title" :rules="nameValidationRules" class="my-10 mx-10">
<a-input v-model:value="formState.title" name="title" class="nc-metadb-project-name" />
</a-form-item>
<a-form-item style="text-align: center" class="mt-2">
<a-button type="primary" html-type="submit" class="mx-auto flex justify-self-center">
<MaterialSymbolsRocketLaunchOutline class="mr-1" />
<span> {{ $t('general.edit') }} </span></a-button
>
</a-form-item>
</a-form>
</a-card>
<div
class="overflow-auto md:bg-primary bg-opacity-5 pb-4 update-project h-full min-h-[600px] flex flex-col justify-center items-end"
>
<div
class="bg-white mt-65px relative flex flex-col justify-center gap-2 w-full max-w-[500px] !mx-auto p-8 md:(rounded-lg border-1 border-gray-200 shadow-xl)"
>
<general-noco-icon class="color-transition hover:(ring ring-accent)" :class="[isLoading ? 'animated-bg-gradient' : '']" />
<h1 class="prose-2xl font-bold self-center my-4">{{ $t('activity.editProject') }}</h1>
<a-form ref="form" :model="formState" name="basic" layout="vertical" no-style autocomplete="off" @finish="renameProject">
<a-form-item :label="$t('labels.projName')" name="title" :rules="nameValidationRules">
<a-input v-model:value="formState.title" name="title" class="nc-metadb-project-name" />
</a-form-item>
<div class="text-center">
<button type="submit" class="submit">
<span class="flex items-center gap-2">
<MaterialSymbolsRocketLaunchOutline />
{{ $t('general.edit') }}
</span>
</button>
</div>
</a-form>
</div>
</div>
</template>
<style lang="scss">
.update-project {
.ant-input-affix-wrapper,
.ant-input {
@apply !appearance-none my-1 border-1 border-solid border-primary/50 rounded;
}
.password {
input {
@apply !border-none;
}
}
.submit {
@apply z-1 relative color-transition rounded p-3 text-white shadow-sm;
&::after {
@apply rounded absolute top-0 left-0 right-0 bottom-0 transition-all duration-150 ease-in-out bg-primary;
content: '';
z-index: -1;
}
&:hover::after {
@apply transform scale-110 ring ring-accent;
}
&:active::after {
@apply ring ring-accent;
}
}
}
</style>

378
packages/nc-gui-v2/pages/project/index/create-external.vue

@ -1,28 +1,36 @@
<script lang="ts" setup>
import { onMounted } from '@vue/runtime-core'
import { Form, Modal, message } from 'ant-design-vue'
import { useI18n } from 'vue-i18n'
import { computed, ref, useSidebar, watch } from '#imports'
import { navigateTo, useNuxtApp } from '#app'
import { ClientType } from '~/lib'
import type { ProjectCreateForm } from '~/utils'
import {
clientTypes,
computed,
extractSdkResponseErrorMsg,
fieldRequiredValidator,
generateUniqueName,
getDefaultConnectionConfig,
getTestDatabaseName,
navigateTo,
nextTick,
onMounted,
projectTitleValidator,
readFile,
ref,
sslUsage,
} from '~/utils'
useApi,
useI18n,
useNuxtApp,
useSidebar,
watch,
} from '#imports'
import { ClientType } from '~/lib'
import type { ProjectCreateForm } from '~/utils'
const useForm = Form.useForm
const loading = ref(false)
const testSuccess = ref(false)
const { $api, $e } = useNuxtApp()
const { api, isLoading } = useApi()
const { $e } = useNuxtApp()
useSidebar({ hasSidebar: false })
@ -123,11 +131,13 @@ const createProject = async () => {
focusInvalidInput()
return
}
loading.value = true
try {
const connection = getConnectionConfig()
const config = { ...formState.dataSource, connection }
const result = await $api.project.create({
const result = await api.project.create({
title: formState.title,
bases: [
{
@ -139,12 +149,13 @@ const createProject = async () => {
],
external: true,
})
$e('a:project:create:extdb')
await navigateTo(`/nc/${result.id}`)
} catch (e: any) {
message.error(await extractSdkResponseErrorMsg(e))
}
loading.value = false
}
const testConnection = async () => {
@ -154,19 +165,23 @@ const testConnection = async () => {
focusInvalidInput()
return
}
$e('a:project:create:extdb:test-connection', [])
try {
if (formState.dataSource.client === ClientType.SQLITE) {
testSuccess.value = true
} else {
const connection: any = getConnectionConfig()
connection.database = getTestDatabaseName(formState.dataSource)
const connection = getConnectionConfig()
connection.database = getTestDatabaseName(formState.dataSource)!
const testConnectionConfig = {
...formState.dataSource,
connection,
}
const result = await $api.utils.testConnection(testConnectionConfig)
const result = await api.utils.testConnection(testConnectionConfig)
if (result.code === 0) {
testSuccess.value = true
@ -183,11 +198,13 @@ const testConnection = async () => {
})
} else {
testSuccess.value = false
message.error(`${t('msg.error.dbConnectionFailed')} ${result.message}`)
}
}
} catch (e: any) {
testSuccess.value = false
message.error(await extractSdkResponseErrorMsg(e))
}
}
@ -213,171 +230,192 @@ onMounted(() => {
</script>
<template>
<a-card class="max-w-[600px] !mx-auto !mt-100px !mb-5 !shadow-md">
<GeneralNocoIcon />
<h3 class="text-3xl text-center font-semibold mt-8 mb-4">{{ $t('activity.createProject') }}</h3>
<a-form
ref="form"
:model="formState"
name="external-project-create-form"
layout="horizontal"
:label-col="{ span: 8 }"
:wrapper-col="{ span: 18 }"
class="!pr-5"
<div
class="overflow-auto md:bg-primary bg-opacity-5 pb-4 create-external h-full min-h-[600px] flex flex-col justify-center items-end"
>
<div
class="bg-white mt-275px relative flex flex-col justify-center gap-2 w-full max-w-[500px] !mx-auto p-8 md:(rounded-lg border-1 border-gray-200 shadow-xl)"
>
<a-form-item :label="$t('placeholder.projName')" v-bind="validateInfos.title">
<a-input v-model:value="formState.title" size="small" class="nc-extdb-proj-name" />
</a-form-item>
<a-form-item :label="$t('labels.dbType')" v-bind="validateInfos['dataSource.client']">
<a-select v-model:value="formState.dataSource.client" size="small" class="nc-extdb-db-type" @change="onClientChange">
<a-select-option v-for="client in clientTypes" :key="client.value" :value="client.value"
>{{ client.text }}
</a-select-option>
</a-select>
</a-form-item>
<!-- SQLite File -->
<a-form-item
v-if="formState.dataSource.client === ClientType.SQLITE"
:label="$t('labels.sqliteFile')"
v-bind="validateInfos['dataSource.connection.connection.filename']"
>
<a-input v-model:value="formState.dataSource.connection.connection.filename" size="small" />
</a-form-item>
<general-noco-icon class="color-transition hover:(ring ring-accent)" :class="[isLoading ? 'animated-bg-gradient' : '']" />
<template v-else>
<!-- Host Address -->
<a-form-item :label="$t('labels.hostAddress')" v-bind="validateInfos['dataSource.connection.host']">
<a-input v-model:value="formState.dataSource.connection.host" size="small" class="nc-extdb-host-address" />
</a-form-item>
<!-- Port Number -->
<a-form-item :label="$t('labels.port')" v-bind="validateInfos['dataSource.connection.port']">
<a-input-number v-model:value="formState.dataSource.connection.port" class="!w-full nc-extdb-host-port" size="small" />
</a-form-item>
<h1 class="prose-2xl font-bold self-center my-4">{{ $t('activity.createProject') }}</h1>
<!-- Username -->
<a-form-item :label="$t('labels.username')" v-bind="validateInfos['dataSource.connection.user']">
<a-input v-model:value="formState.dataSource.connection.user" size="small" class="nc-extdb-host-user" />
<a-form
ref="form"
:model="formState"
name="external-project-create-form"
layout="horizontal"
no-style
:label-col="{ span: 10 }"
>
<a-form-item :label="$t('placeholder.projName')" v-bind="validateInfos.title">
<a-input v-model:value="formState.title" size="small" class="nc-extdb-proj-name" />
</a-form-item>
<!-- Password -->
<a-form-item :label="$t('labels.password')">
<a-input-password
v-model:value="formState.dataSource.connection.password"
size="small"
class="nc-extdb-host-password"
/>
<a-form-item :label="$t('labels.dbType')" v-bind="validateInfos['dataSource.client']">
<a-select v-model:value="formState.dataSource.client" size="small" class="nc-extdb-db-type" @change="onClientChange">
<a-select-option v-for="client in clientTypes" :key="client.value" :value="client.value"
>{{ client.text }}
</a-select-option>
</a-select>
</a-form-item>
<!-- Database -->
<a-form-item :label="$t('labels.database')" v-bind="validateInfos['dataSource.connection.database']">
<!-- Database : create if not exists -->
<a-input
v-model:value="formState.dataSource.connection.database"
:placeholder="$t('labels.dbCreateIfNotExists')"
size="small"
class="nc-extdb-host-database"
/>
</a-form-item>
<!-- Schema name -->
<!-- SQLite File -->
<a-form-item
v-if="[ClientType.MSSQL, ClientType.PG].includes(formState.dataSource.client) && formState.dataSource.searchPath"
:label="$t('labels.schemaName')"
v-bind="validateInfos['dataSource.searchPath.0']"
v-if="formState.dataSource.client === ClientType.SQLITE"
:label="$t('labels.sqliteFile')"
v-bind="validateInfos['dataSource.connection.connection.filename']"
>
<a-input v-model:value="formState.dataSource.searchPath[0]" size="small" />
<a-input v-model:value="formState.dataSource.connection.connection.filename" size="small" />
</a-form-item>
<a-collapse ghost expand-icon-position="right" class="mt-6">
<a-collapse-panel key="1" :header="$t('title.advancedParameters')">
<!-- todo: add in i18n -->
<a-form-item label="SSL mode">
<a-select v-model:value="formState.sslUse" size="small" @change="onClientChange">
<a-select-option v-for="opt in sslUsage" :key="opt" :value="opt">{{ opt }}</a-select-option>
</a-select>
</a-form-item>
<a-form-item label="SSL keys">
<div class="flex gap-2">
<a-tooltip placement="top">
<!-- Select .cert file -->
<template #title>
<span>{{ $t('tooltip.clientCert') }}</span>
</template>
<a-button :disabled="!sslFilesRequired" size="small" class="shadow" @click="certFileInput.click()">
{{ $t('labels.clientCert') }}
</a-button>
</a-tooltip>
<a-tooltip placement="top">
<!-- Select .key file -->
<template #title>
<span>{{ $t('tooltip.clientKey') }}</span>
</template>
<a-button :disabled="!sslFilesRequired" size="small" class="shadow" @click="keyFileInput.click()">
{{ $t('labels.clientKey') }}
</a-button>
</a-tooltip>
<a-tooltip placement="top">
<!-- Select CA file -->
<template #title>
<span>{{ $t('tooltip.clientCA') }}</span>
</template>
<a-button :disabled="!sslFilesRequired" size="small" class="shadow" @click="caFileInput.click()">
{{ $t('labels.serverCA') }}
</a-button>
</a-tooltip>
<template v-else>
<!-- Host Address -->
<a-form-item :label="$t('labels.hostAddress')" v-bind="validateInfos['dataSource.connection.host']">
<a-input v-model:value="formState.dataSource.connection.host" size="small" class="nc-extdb-host-address" />
</a-form-item>
<!-- Port Number -->
<a-form-item :label="$t('labels.port')" v-bind="validateInfos['dataSource.connection.port']">
<a-input-number
v-model:value="formState.dataSource.connection.port"
class="!w-full nc-extdb-host-port"
size="small"
/>
</a-form-item>
<!-- Username -->
<a-form-item :label="$t('labels.username')" v-bind="validateInfos['dataSource.connection.user']">
<a-input v-model:value="formState.dataSource.connection.user" size="small" class="nc-extdb-host-user" />
</a-form-item>
<!-- Password -->
<a-form-item :label="$t('labels.password')">
<a-input-password
v-model:value="formState.dataSource.connection.password"
size="small"
class="nc-extdb-host-password"
/>
</a-form-item>
<!-- Database -->
<a-form-item :label="$t('labels.database')" v-bind="validateInfos['dataSource.connection.database']">
<!-- Database : create if not exists -->
<a-input
v-model:value="formState.dataSource.connection.database"
:placeholder="$t('labels.dbCreateIfNotExists')"
size="small"
class="nc-extdb-host-database"
/>
</a-form-item>
<!-- Schema name -->
<a-form-item
v-if="[ClientType.MSSQL, ClientType.PG].includes(formState.dataSource.client) && formState.dataSource.searchPath"
:label="$t('labels.schemaName')"
v-bind="validateInfos['dataSource.searchPath.0']"
>
<a-input v-model:value="formState.dataSource.searchPath[0]" size="small" />
</a-form-item>
<a-collapse ghost expand-icon-position="right" class="!mt-6">
<a-collapse-panel key="1" :header="$t('title.advancedParameters')">
<!-- todo: add in i18n -->
<a-form-item label="SSL mode">
<a-select v-model:value="formState.sslUse" size="small" @change="onClientChange">
<a-select-option v-for="opt in sslUsage" :key="opt" :value="opt">{{ opt }}</a-select-option>
</a-select>
</a-form-item>
<a-form-item label="SSL keys">
<div class="flex gap-2">
<a-tooltip placement="top">
<!-- Select .cert file -->
<template #title>
<span>{{ $t('tooltip.clientCert') }}</span>
</template>
<a-button :disabled="!sslFilesRequired" size="small" class="shadow" @click="certFileInput.click()">
{{ $t('labels.clientCert') }}
</a-button>
</a-tooltip>
<a-tooltip placement="top">
<!-- Select .key file -->
<template #title>
<span>{{ $t('tooltip.clientKey') }}</span>
</template>
<a-button :disabled="!sslFilesRequired" size="small" class="shadow" @click="keyFileInput.click()">
{{ $t('labels.clientKey') }}
</a-button>
</a-tooltip>
<a-tooltip placement="top">
<!-- Select CA file -->
<template #title>
<span>{{ $t('tooltip.clientCA') }}</span>
</template>
<a-button :disabled="!sslFilesRequired" size="small" class="shadow" @click="caFileInput.click()">
{{ $t('labels.serverCA') }}
</a-button>
</a-tooltip>
</div>
</a-form-item>
<input ref="caFileInput" type="file" class="!hidden" @change="onFileSelect('ca', caFileInput)" />
<input ref="certFileInput" type="file" class="!hidden" @change="onFileSelect('cert', certFileInput)" />
<input ref="keyFileInput" type="file" class="!hidden" @change="onFileSelect('key', keyFileInput)" />
<a-form-item :label="$t('labels.inflection.tableName')">
<a-select v-model:value="formState.inflection.inflectionTable" size="small" @change="onClientChange">
<a-select-option v-for="type in inflectionTypes" :key="type" :value="type">{{ type }}</a-select-option>
</a-select>
</a-form-item>
<a-form-item :label="$t('labels.inflection.columnName')">
<a-select v-model:value="formState.inflection.inflectionColumn" size="small" @change="onClientChange">
<a-select-option v-for="type in inflectionTypes" :key="type" :value="type">{{ type }}</a-select-option>
</a-select>
</a-form-item>
<div class="flex justify-end">
<a-button class="!shadow-md" @click="configEditDlg = true">
<!-- Edit connection JSON -->
{{ $t('activity.editConnJson') }}
</a-button>
</div>
</a-form-item>
<input ref="caFileInput" type="file" class="!hidden" @change="onFileSelect('ca', caFileInput)" />
<input ref="certFileInput" type="file" class="!hidden" @change="onFileSelect('cert', certFileInput)" />
<input ref="keyFileInput" type="file" class="!hidden" @change="onFileSelect('key', keyFileInput)" />
<a-form-item :label="$t('labels.inflection.tableName')">
<a-select v-model:value="formState.inflection.inflectionTable" size="small" @change="onClientChange">
<a-select-option v-for="type in inflectionTypes" :key="type" :value="type">{{ type }}</a-select-option>
</a-select>
</a-form-item>
<a-form-item :label="$t('labels.inflection.columnName')">
<a-select v-model:value="formState.inflection.inflectionColumn" size="small" @change="onClientChange">
<a-select-option v-for="type in inflectionTypes" :key="type" :value="type">{{ type }}</a-select-option>
</a-select>
</a-form-item>
<div class="flex justify-end">
<a-button size="small" class="!shadow-md" @click="configEditDlg = true">
<!-- Edit connection JSON -->
{{ $t('activity.editConnJson') }}
</a-button>
</div>
</a-collapse-panel>
</a-collapse>
</template>
<a-form-item class="flex justify-center mt-5">
<div class="flex justify-center gap-2">
<a-button type="primary" ghost class="nc-extdb-btn-test-connection" @click="testConnection">
{{ $t('activity.testDbConn') }}
</a-button>
<a-button type="primary" :disabled="!testSuccess" class="nc-extdb-btn-submit !shadow" @click="createProject">
Submit
</a-button>
</div>
</a-form-item>
</a-form>
<v-dialog v-model="configEditDlg">
<a-card>
<MonacoEditor v-if="configEditDlg" v-model="formState" class="h-[400px] w-[600px]" />
</a-card>
</v-dialog>
</a-card>
</a-collapse-panel>
</a-collapse>
</template>
<a-form-item class="flex justify-center !mt-5">
<div class="flex justify-center gap-2">
<a-button type="primary" ghost class="nc-extdb-btn-test-connection" @click="testConnection">
{{ $t('activity.testDbConn') }}
</a-button>
<a-button type="primary" :disabled="!testSuccess" class="nc-extdb-btn-submit !shadow" @click="createProject">
Submit
</a-button>
</div>
</a-form-item>
</a-form>
<!-- todo: needs replacement
<v-dialog v-model="configEditDlg">
<a-card>
<MonacoEditor v-if="configEditDlg" v-model="formState" class="h-[400px] w-[600px]" />
</a-card>
</v-dialog>
-->
</div>
</div>
</template>
<style scoped>
<style lang="scss" scoped>
:deep(.ant-collapse-header) {
@apply !pr-10 !-mt-4 text-right justify-end;
}
@ -401,4 +439,12 @@ onMounted(() => {
:deep(.ant-card-head-title) {
@apply !text-3xl;
}
.create-external {
:deep(.ant-input-affix-wrapper),
:deep(.ant-input),
:deep(.ant-select) {
@apply !appearance-none my-1 border-1 border-solid border-primary/50 rounded;
}
}
</style>

101
packages/nc-gui-v2/pages/project/index/create.vue

@ -1,12 +1,18 @@
<script lang="ts" setup>
import { onMounted } from '@vue/runtime-core'
import type { Form } from 'ant-design-vue'
import { message } from 'ant-design-vue'
import { nextTick, reactive, ref, useApi, useSidebar } from '#imports'
import { navigateTo, useNuxtApp } from '#app'
import { extractSdkResponseErrorMsg } from '~/utils/errorUtils'
import { projectTitleValidator } from '~/utils/validation'
import MaterialSymbolsRocketLaunchOutline from '~icons/material-symbols/rocket-launch-outline'
import {
extractSdkResponseErrorMsg,
navigateTo,
nextTick,
onMounted,
projectTitleValidator,
reactive,
ref,
useApi,
useNuxtApp,
useSidebar,
} from '#imports'
const { $e } = useNuxtApp()
@ -22,6 +28,8 @@ const nameValidationRules = [
projectTitleValidator,
]
const form = ref<typeof Form>()
const formState = reactive({
title: '',
})
@ -39,15 +47,15 @@ const createProject = async () => {
}
}
const form = ref<typeof Form>()
// select and focus title field on load
onMounted(async () => {
await nextTick(() => {
// todo: replace setTimeout and follow better approach
setTimeout(() => {
const input = form.value?.$el?.querySelector('input[type=text]')
input.setSelectionRange(0, formState.title.length)
input.focus()
}, 500)
})
@ -55,24 +63,61 @@ onMounted(async () => {
</script>
<template>
<a-card :loading="isLoading" class="w-[500px] !mx-auto !mt-100px shadow-md">
<GeneralNocoIcon />
<h3 class="text-3xl text-center font-semibold mt-8 mb-2">{{ $t('activity.createProject') }}</h3>
<a-form ref="form" :model="formState" name="basic" layout="vertical" autocomplete="off" @finish="createProject">
<a-form-item :label="$t('labels.projName')" name="title" :rules="nameValidationRules" class="my-10 mx-10">
<a-input v-model:value="formState.title" name="title" class="nc-metadb-project-name" />
</a-form-item>
<a-form-item style="text-align: center" class="mt-2">
<a-button type="primary" html-type="submit">
<div class="flex items-center">
<MaterialSymbolsRocketLaunchOutline class="mr-1" />
{{ $t('general.create') }}
</div>
</a-button>
</a-form-item>
</a-form>
</a-card>
<div class="overflow-auto md:bg-primary bg-opacity-5 pb-4 create h-full min-h-[600px] flex flex-col justify-center items-end">
<div
class="bg-white mt-65px relative flex flex-col justify-center gap-2 w-full max-w-[500px] !mx-auto p-8 md:(rounded-lg border-1 border-gray-200 shadow-xl)"
>
<general-noco-icon class="color-transition hover:(ring ring-accent)" :class="[isLoading ? 'animated-bg-gradient' : '']" />
<h1 class="prose-2xl font-bold self-center my-4">{{ $t('activity.createProject') }}</h1>
<a-form ref="form" :model="formState" name="basic" layout="vertical" no-style autocomplete="off" @finish="createProject">
<a-form-item :label="$t('labels.projName')" name="title" :rules="nameValidationRules" class="my-10 mx-10">
<a-input v-model:value="formState.title" name="title" class="nc-metadb-project-name" />
</a-form-item>
<div class="text-center">
<button class="submit" type="submit">
<span class="flex items-center gap-2">
<MaterialSymbolsRocketLaunchOutline />
{{ $t('general.create') }}
</span>
</button>
</div>
</a-form>
</div>
</div>
</template>
<style lang="scss">
.create {
.ant-input-affix-wrapper,
.ant-input {
@apply !appearance-none my-1 border-1 border-solid border-primary/50 rounded;
}
.password {
input {
@apply !border-none;
}
}
.submit {
@apply z-1 relative color-transition rounded p-3 text-white shadow-sm;
&::after {
@apply rounded absolute top-0 left-0 right-0 bottom-0 transition-all duration-150 ease-in-out bg-primary;
content: '';
z-index: -1;
}
&:hover::after {
@apply transform scale-110 ring ring-accent;
}
&:active::after {
@apply ring ring-accent;
}
}
}
</style>

2
packages/nc-gui-v2/pages/signin.vue

@ -80,7 +80,7 @@ function resetError() {
<template>
<NuxtLayout>
<div class="md:bg-primary bg-opacity-5 signin h-full min-h-[600px] flex flex-col justify-center items-center nc-form-signup">
<div class="md:bg-primary bg-opacity-5 signin h-full min-h-[600px] flex flex-col justify-center items-center nc-form-signin">
<div
class="bg-white mt-[60px] relative flex flex-col justify-center gap-2 w-full max-w-[500px] mx-auto p-8 md:(rounded-lg border-1 border-gray-200 shadow-xl)"
>

Loading…
Cancel
Save