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" class="flex !bg-primary items-center text-white pl-4 pr-5 shadow-lg"
> >
<div <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" class="transition-all duration-200 p-2 cursor-pointer transform hover:scale-105 nc-noco-brand-icon"
@click="navigateTo('/')" @click="navigateTo('/')"
> >

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

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

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

@ -1,11 +1,6 @@
<script lang="ts" setup> <script lang="ts" setup>
import { useI18n } from 'vue-i18n'
import { message } from 'ant-design-vue' import { message } from 'ant-design-vue'
import { navigateTo } from '#app' import { extractSdkResponseErrorMsg, navigateTo, reactive, ref, useApi, useGlobal, useI18n } from '#imports'
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'
const { api } = useApi() const { api } = useApi()
@ -81,7 +76,10 @@ const resetError = () => {
<Transition name="layout"> <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 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> </div>
</Transition> </Transition>
@ -117,7 +115,10 @@ const resetError = () => {
<div class="flex flex-wrap gap-4 items-center mt-4 md:justify-between w-full"> <div class="flex flex-wrap gap-4 items-center mt-4 md:justify-between w-full">
<button class="submit" type="submit"> <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> </button>
</div> </div>
</div> </div>

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

@ -1,15 +1,21 @@
<script lang="ts" setup> <script lang="ts" setup>
import { onMounted } from '@vue/runtime-core'
import type { Form } from 'ant-design-vue' import type { Form } from 'ant-design-vue'
import type { ProjectType } from 'nocodb-sdk' import type { ProjectType } from 'nocodb-sdk'
import { ref } from 'vue'
import { message } from 'ant-design-vue' import { message } from 'ant-design-vue'
import { navigateTo, useRoute } from '#app' import {
import { extractSdkResponseErrorMsg, projectTitleValidator } from '~/utils' extractSdkResponseErrorMsg,
import MaterialSymbolsRocketLaunchOutline from '~icons/material-symbols/rocket-launch-outline' navigateTo,
import { nextTick, reactive, useSidebar } from '#imports' nextTick,
onMounted,
projectTitleValidator,
reactive,
ref,
useApi,
useRoute,
useSidebar,
} from '#imports'
const { api } = useApi() const { api, isLoading } = useApi()
useSidebar({ hasSidebar: false }) useSidebar({ hasSidebar: false })
@ -23,6 +29,8 @@ const nameValidationRules = [
projectTitleValidator, projectTitleValidator,
] ]
const form = ref<typeof Form>()
const formState = reactive({ const formState = reactive({
title: '', title: '',
}) })
@ -46,8 +54,6 @@ const renameProject = async () => {
} }
} }
const form = ref<typeof Form>()
// select and focus title field on load // select and focus title field on load
onMounted(async () => { onMounted(async () => {
await getProject() await getProject()
@ -56,7 +62,9 @@ onMounted(async () => {
// todo: replace setTimeout and follow better approach // todo: replace setTimeout and follow better approach
setTimeout(() => { setTimeout(() => {
const input = form.value?.$el?.querySelector('input[type=text]') const input = form.value?.$el?.querySelector('input[type=text]')
input.setSelectionRange(0, formState.title.length) input.setSelectionRange(0, formState.title.length)
input.focus() input.focus()
}, 500) }, 500)
}) })
@ -64,20 +72,63 @@ onMounted(async () => {
</script> </script>
<template> <template>
<a-card class="w-[500px] !mx-auto !mt-100px shadow-md"> <div
<h3 class="text-3xl text-center font-semibold mb-2">{{ $t('activity.editProject') }}</h3> 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"
>
<a-form ref="form" :model="formState" name="basic" layout="vertical" autocomplete="off" @finish="renameProject"> <div
<a-form-item :label="$t('labels.projName')" name="title" :rules="nameValidationRules" class="my-10 mx-10"> 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)"
<a-input v-model:value="formState.title" name="title" class="nc-metadb-project-name" /> >
</a-form-item> <general-noco-icon class="color-transition hover:(ring ring-accent)" :class="[isLoading ? 'animated-bg-gradient' : '']" />
<a-form-item style="text-align: center" class="mt-2"> <h1 class="prose-2xl font-bold self-center my-4">{{ $t('activity.editProject') }}</h1>
<a-button type="primary" html-type="submit" class="mx-auto flex justify-self-center">
<MaterialSymbolsRocketLaunchOutline class="mr-1" /> <a-form ref="form" :model="formState" name="basic" layout="vertical" no-style autocomplete="off" @finish="renameProject">
<span> {{ $t('general.edit') }} </span></a-button <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> </a-form-item>
</a-form>
</a-card> <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> </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> <script lang="ts" setup>
import { onMounted } from '@vue/runtime-core'
import { Form, Modal, message } from 'ant-design-vue' 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 { import {
clientTypes, clientTypes,
computed,
extractSdkResponseErrorMsg, extractSdkResponseErrorMsg,
fieldRequiredValidator, fieldRequiredValidator,
generateUniqueName, generateUniqueName,
getDefaultConnectionConfig, getDefaultConnectionConfig,
getTestDatabaseName, getTestDatabaseName,
navigateTo,
nextTick,
onMounted,
projectTitleValidator, projectTitleValidator,
readFile, readFile,
ref,
sslUsage, sslUsage,
} from '~/utils' useApi,
useI18n,
useNuxtApp,
useSidebar,
watch,
} from '#imports'
import { ClientType } from '~/lib'
import type { ProjectCreateForm } from '~/utils'
const useForm = Form.useForm const useForm = Form.useForm
const loading = ref(false)
const testSuccess = ref(false) const testSuccess = ref(false)
const { $api, $e } = useNuxtApp() const { api, isLoading } = useApi()
const { $e } = useNuxtApp()
useSidebar({ hasSidebar: false }) useSidebar({ hasSidebar: false })
@ -123,11 +131,13 @@ const createProject = async () => {
focusInvalidInput() focusInvalidInput()
return return
} }
loading.value = true
try { try {
const connection = getConnectionConfig() const connection = getConnectionConfig()
const config = { ...formState.dataSource, connection } const config = { ...formState.dataSource, connection }
const result = await $api.project.create({
const result = await api.project.create({
title: formState.title, title: formState.title,
bases: [ bases: [
{ {
@ -139,12 +149,13 @@ const createProject = async () => {
], ],
external: true, external: true,
}) })
$e('a:project:create:extdb') $e('a:project:create:extdb')
await navigateTo(`/nc/${result.id}`) await navigateTo(`/nc/${result.id}`)
} catch (e: any) { } catch (e: any) {
message.error(await extractSdkResponseErrorMsg(e)) message.error(await extractSdkResponseErrorMsg(e))
} }
loading.value = false
} }
const testConnection = async () => { const testConnection = async () => {
@ -154,19 +165,23 @@ const testConnection = async () => {
focusInvalidInput() focusInvalidInput()
return return
} }
$e('a:project:create:extdb:test-connection', []) $e('a:project:create:extdb:test-connection', [])
try { try {
if (formState.dataSource.client === ClientType.SQLITE) { if (formState.dataSource.client === ClientType.SQLITE) {
testSuccess.value = true testSuccess.value = true
} else { } else {
const connection: any = getConnectionConfig() const connection = getConnectionConfig()
connection.database = getTestDatabaseName(formState.dataSource)
connection.database = getTestDatabaseName(formState.dataSource)!
const testConnectionConfig = { const testConnectionConfig = {
...formState.dataSource, ...formState.dataSource,
connection, connection,
} }
const result = await $api.utils.testConnection(testConnectionConfig) const result = await api.utils.testConnection(testConnectionConfig)
if (result.code === 0) { if (result.code === 0) {
testSuccess.value = true testSuccess.value = true
@ -183,11 +198,13 @@ const testConnection = async () => {
}) })
} else { } else {
testSuccess.value = false testSuccess.value = false
message.error(`${t('msg.error.dbConnectionFailed')} ${result.message}`) message.error(`${t('msg.error.dbConnectionFailed')} ${result.message}`)
} }
} }
} catch (e: any) { } catch (e: any) {
testSuccess.value = false testSuccess.value = false
message.error(await extractSdkResponseErrorMsg(e)) message.error(await extractSdkResponseErrorMsg(e))
} }
} }
@ -213,171 +230,192 @@ onMounted(() => {
</script> </script>
<template> <template>
<a-card class="max-w-[600px] !mx-auto !mt-100px !mb-5 !shadow-md"> <div
<GeneralNocoIcon /> 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"
>
<h3 class="text-3xl text-center font-semibold mt-8 mb-4">{{ $t('activity.createProject') }}</h3> <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
ref="form"
:model="formState"
name="external-project-create-form"
layout="horizontal"
:label-col="{ span: 8 }"
:wrapper-col="{ span: 18 }"
class="!pr-5"
> >
<a-form-item :label="$t('placeholder.projName')" v-bind="validateInfos.title"> <general-noco-icon class="color-transition hover:(ring ring-accent)" :class="[isLoading ? 'animated-bg-gradient' : '']" />
<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>
<template v-else> <h1 class="prose-2xl font-bold self-center my-4">{{ $t('activity.createProject') }}</h1>
<!-- 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
<a-form-item :label="$t('labels.username')" v-bind="validateInfos['dataSource.connection.user']"> ref="form"
<a-input v-model:value="formState.dataSource.connection.user" size="small" class="nc-extdb-host-user" /> :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> </a-form-item>
<!-- Password --> <a-form-item :label="$t('labels.dbType')" v-bind="validateInfos['dataSource.client']">
<a-form-item :label="$t('labels.password')"> <a-select v-model:value="formState.dataSource.client" size="small" class="nc-extdb-db-type" @change="onClientChange">
<a-input-password <a-select-option v-for="client in clientTypes" :key="client.value" :value="client.value"
v-model:value="formState.dataSource.connection.password" >{{ client.text }}
size="small" </a-select-option>
class="nc-extdb-host-password" </a-select>
/>
</a-form-item> </a-form-item>
<!-- Database --> <!-- SQLite File -->
<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 <a-form-item
v-if="[ClientType.MSSQL, ClientType.PG].includes(formState.dataSource.client) && formState.dataSource.searchPath" v-if="formState.dataSource.client === ClientType.SQLITE"
:label="$t('labels.schemaName')" :label="$t('labels.sqliteFile')"
v-bind="validateInfos['dataSource.searchPath.0']" 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-form-item>
<a-collapse ghost expand-icon-position="right" class="mt-6"> <template v-else>
<a-collapse-panel key="1" :header="$t('title.advancedParameters')"> <!-- Host Address -->
<!-- todo: add in i18n --> <a-form-item :label="$t('labels.hostAddress')" v-bind="validateInfos['dataSource.connection.host']">
<a-form-item label="SSL mode"> <a-input v-model:value="formState.dataSource.connection.host" size="small" class="nc-extdb-host-address" />
<a-select v-model:value="formState.sslUse" size="small" @change="onClientChange"> </a-form-item>
<a-select-option v-for="opt in sslUsage" :key="opt" :value="opt">{{ opt }}</a-select-option>
</a-select> <!-- Port Number -->
</a-form-item> <a-form-item :label="$t('labels.port')" v-bind="validateInfos['dataSource.connection.port']">
<a-input-number
<a-form-item label="SSL keys"> v-model:value="formState.dataSource.connection.port"
<div class="flex gap-2"> class="!w-full nc-extdb-host-port"
<a-tooltip placement="top"> size="small"
<!-- Select .cert file --> />
<template #title> </a-form-item>
<span>{{ $t('tooltip.clientCert') }}</span>
</template> <!-- Username -->
<a-button :disabled="!sslFilesRequired" size="small" class="shadow" @click="certFileInput.click()"> <a-form-item :label="$t('labels.username')" v-bind="validateInfos['dataSource.connection.user']">
{{ $t('labels.clientCert') }} <a-input v-model:value="formState.dataSource.connection.user" size="small" class="nc-extdb-host-user" />
</a-button> </a-form-item>
</a-tooltip>
<a-tooltip placement="top"> <!-- Password -->
<!-- Select .key file --> <a-form-item :label="$t('labels.password')">
<template #title> <a-input-password
<span>{{ $t('tooltip.clientKey') }}</span> v-model:value="formState.dataSource.connection.password"
</template> size="small"
<a-button :disabled="!sslFilesRequired" size="small" class="shadow" @click="keyFileInput.click()"> class="nc-extdb-host-password"
{{ $t('labels.clientKey') }} />
</a-button> </a-form-item>
</a-tooltip>
<a-tooltip placement="top"> <!-- Database -->
<!-- Select CA file --> <a-form-item :label="$t('labels.database')" v-bind="validateInfos['dataSource.connection.database']">
<template #title> <!-- Database : create if not exists -->
<span>{{ $t('tooltip.clientCA') }}</span> <a-input
</template> v-model:value="formState.dataSource.connection.database"
<a-button :disabled="!sslFilesRequired" size="small" class="shadow" @click="caFileInput.click()"> :placeholder="$t('labels.dbCreateIfNotExists')"
{{ $t('labels.serverCA') }} size="small"
</a-button> class="nc-extdb-host-database"
</a-tooltip> />
</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> </div>
</a-form-item> </a-collapse-panel>
</a-collapse>
<input ref="caFileInput" type="file" class="!hidden" @change="onFileSelect('ca', caFileInput)" /> </template>
<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 class="flex justify-center !mt-5">
<div class="flex justify-center gap-2">
<a-form-item :label="$t('labels.inflection.tableName')"> <a-button type="primary" ghost class="nc-extdb-btn-test-connection" @click="testConnection">
<a-select v-model:value="formState.inflection.inflectionTable" size="small" @change="onClientChange"> {{ $t('activity.testDbConn') }}
<a-select-option v-for="type in inflectionTypes" :key="type" :value="type">{{ type }}</a-select-option> </a-button>
</a-select>
</a-form-item> <a-button type="primary" :disabled="!testSuccess" class="nc-extdb-btn-submit !shadow" @click="createProject">
<a-form-item :label="$t('labels.inflection.columnName')"> Submit
<a-select v-model:value="formState.inflection.inflectionColumn" size="small" @change="onClientChange"> </a-button>
<a-select-option v-for="type in inflectionTypes" :key="type" :value="type">{{ type }}</a-select-option> </div>
</a-select> </a-form-item>
</a-form-item> </a-form>
<div class="flex justify-end">
<a-button size="small" class="!shadow-md" @click="configEditDlg = true"> <!-- todo: needs replacement
<!-- Edit connection JSON --> <v-dialog v-model="configEditDlg">
{{ $t('activity.editConnJson') }} <a-card>
</a-button> <MonacoEditor v-if="configEditDlg" v-model="formState" class="h-[400px] w-[600px]" />
</div> </a-card>
</a-collapse-panel> </v-dialog>
</a-collapse> -->
</template> </div>
</div>
<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>
</template> </template>
<style scoped> <style lang="scss" scoped>
:deep(.ant-collapse-header) { :deep(.ant-collapse-header) {
@apply !pr-10 !-mt-4 text-right justify-end; @apply !pr-10 !-mt-4 text-right justify-end;
} }
@ -401,4 +439,12 @@ onMounted(() => {
:deep(.ant-card-head-title) { :deep(.ant-card-head-title) {
@apply !text-3xl; @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> </style>

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

@ -1,12 +1,18 @@
<script lang="ts" setup> <script lang="ts" setup>
import { onMounted } from '@vue/runtime-core'
import type { Form } from 'ant-design-vue' import type { Form } from 'ant-design-vue'
import { message } from 'ant-design-vue' import { message } from 'ant-design-vue'
import { nextTick, reactive, ref, useApi, useSidebar } from '#imports' import {
import { navigateTo, useNuxtApp } from '#app' extractSdkResponseErrorMsg,
import { extractSdkResponseErrorMsg } from '~/utils/errorUtils' navigateTo,
import { projectTitleValidator } from '~/utils/validation' nextTick,
import MaterialSymbolsRocketLaunchOutline from '~icons/material-symbols/rocket-launch-outline' onMounted,
projectTitleValidator,
reactive,
ref,
useApi,
useNuxtApp,
useSidebar,
} from '#imports'
const { $e } = useNuxtApp() const { $e } = useNuxtApp()
@ -22,6 +28,8 @@ const nameValidationRules = [
projectTitleValidator, projectTitleValidator,
] ]
const form = ref<typeof Form>()
const formState = reactive({ const formState = reactive({
title: '', title: '',
}) })
@ -39,15 +47,15 @@ const createProject = async () => {
} }
} }
const form = ref<typeof Form>()
// select and focus title field on load // select and focus title field on load
onMounted(async () => { onMounted(async () => {
await nextTick(() => { await nextTick(() => {
// todo: replace setTimeout and follow better approach // todo: replace setTimeout and follow better approach
setTimeout(() => { setTimeout(() => {
const input = form.value?.$el?.querySelector('input[type=text]') const input = form.value?.$el?.querySelector('input[type=text]')
input.setSelectionRange(0, formState.title.length) input.setSelectionRange(0, formState.title.length)
input.focus() input.focus()
}, 500) }, 500)
}) })
@ -55,24 +63,61 @@ onMounted(async () => {
</script> </script>
<template> <template>
<a-card :loading="isLoading" class="w-[500px] !mx-auto !mt-100px shadow-md"> <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">
<GeneralNocoIcon /> <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)"
<h3 class="text-3xl text-center font-semibold mt-8 mb-2">{{ $t('activity.createProject') }}</h3> >
<general-noco-icon class="color-transition hover:(ring ring-accent)" :class="[isLoading ? 'animated-bg-gradient' : '']" />
<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"> <h1 class="prose-2xl font-bold self-center my-4">{{ $t('activity.createProject') }}</h1>
<a-input v-model:value="formState.title" name="title" class="nc-metadb-project-name" />
</a-form-item> <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-form-item style="text-align: center" class="mt-2"> <a-input v-model:value="formState.title" name="title" class="nc-metadb-project-name" />
<a-button type="primary" html-type="submit"> </a-form-item>
<div class="flex items-center">
<MaterialSymbolsRocketLaunchOutline class="mr-1" /> <div class="text-center">
{{ $t('general.create') }} <button class="submit" type="submit">
</div> <span class="flex items-center gap-2">
</a-button> <MaterialSymbolsRocketLaunchOutline />
</a-form-item> {{ $t('general.create') }}
</a-form> </span>
</a-card> </button>
</div>
</a-form>
</div>
</div>
</template> </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> <template>
<NuxtLayout> <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 <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)" 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