Browse Source

Merge pull request #2721 from nocodb/feat/password-reset

pull/2728/head
Braks 2 years ago committed by GitHub
parent
commit
2c4a5bb364
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
  1. 4
      packages/nc-gui-v2/app.vue
  2. 44
      packages/nc-gui-v2/assets/style-v2.css
  3. 60
      packages/nc-gui-v2/assets/style-v2.scss
  4. 2
      packages/nc-gui-v2/components/cell/Duration.vue
  5. 9
      packages/nc-gui-v2/components/cell/Url.vue
  6. 14
      packages/nc-gui-v2/components/general/NocoIcon.vue
  7. 2
      packages/nc-gui-v2/components/general/Share.vue
  8. 2
      packages/nc-gui-v2/components/general/Social.vue
  9. 7
      packages/nc-gui-v2/nuxt.config.ts
  10. 19
      packages/nc-gui-v2/package-lock.json
  11. 1
      packages/nc-gui-v2/package.json
  12. 128
      packages/nc-gui-v2/pages/forgot-password.vue
  13. 4
      packages/nc-gui-v2/pages/projects/index.vue
  14. 2
      packages/nc-gui-v2/pages/projects/index/list.vue
  15. 25
      packages/nc-gui-v2/pages/signin.vue
  16. 11
      packages/nc-gui-v2/pages/signup.vue
  17. 4
      packages/nc-gui-v2/windi.config.ts

4
packages/nc-gui-v2/app.vue

@ -66,7 +66,7 @@ const signOut = () => {
class="group hover:(bg-gray-200) dark:(hover:bg-gray-600) flex items-center p-2 no-underline"
to="/user/settings"
>
<MdiAt class="mt-1 color-transition group-hover:text-success" />&nbsp;
<MdiAt class="mt-1 group-hover:text-success" />&nbsp;
<span class="prose">{{ email }}</span>
</nuxt-link>
@ -77,7 +77,7 @@ const signOut = () => {
class="group flex flex-row cursor-pointer hover:bg-gray-200 dark:(hover:bg-gray-600) flex items-center p-2"
@click="signOut"
>
<MdiLogout class="color-transition dark:text-white group-hover:(!text-red-500)" />&nbsp;
<MdiLogout class="dark:text-white group-hover:(!text-red-500)" />&nbsp;
<span class="prose font-semibold text-gray-500">{{ $t('general.signOut') }}</span>
</div>
</v-list>

44
packages/nc-gui-v2/assets/style-v2.css

@ -1,44 +0,0 @@
html,
body,
#__nuxt,
.v-application__wrap {
@apply m-0 h-full w-full bg-white dark:(bg-black text-white);
}
.v-main {
@apply w-full h-full;
overflow: hidden;
flex: unset !important;
}
.v-main .v-main__wrap {
@apply flex-0 w-full relative scrollbar-thin-primary;
overflow-x: hidden;
}
nav,
nav .v-list {
@apply dark:(bg-gray-900 text-white)
}
.v-divider {
@apply dark:bg-white
}
.page-enter-active,
.page-leave-active,
.layout-enter-active,
.layout-leave-active {
@apply transition-opacity duration-300 ease-in-out;
}
.page-enter,
.page-leave-active,
.layout-enter,
.layout-leave-active {
@apply opacity-0;
}
a {
@apply prose text-primary underline hover:opacity-75 dark:(text-secondary) hover:(opacity-75);
}

60
packages/nc-gui-v2/assets/style-v2.scss

@ -0,0 +1,60 @@
html,
body,
#__nuxt,
.v-application__wrap {
@apply color-transition m-0 h-full w-full bg-white dark:(bg-black text-white);
}
a, button {
@apply color-transition;
}
.v-main {
@apply w-full h-full;
overflow: hidden;
flex: unset !important;
}
.v-main .v-main__wrap {
@apply flex-0 w-full relative scrollbar-thin-primary;
overflow-x: hidden;
}
nav,
nav .v-list {
@apply color-transition dark:(bg-gray-900 text-white)
}
.v-divider {
@apply dark:bg-white
}
.page-enter-active,
.page-leave-active,
.layout-enter-active,
.layout-leave-active {
@apply transition-opacity duration-300 ease-in-out;
}
.page-enter,
.page-leave-active,
.layout-enter,
.layout-leave-active {
@apply opacity-0;
}
a {
@apply cursor-pointer prose text-primary underline hover:opacity-75 dark:(text-secondary) hover:(opacity-75);
}
.v-field__field {
@apply bg-white dark:(!bg-gray-900 text-white);
input {
@apply bg-white dark:(!bg-gray-700) !appearance-none my-1 border-1 border-solid border-primary/50 rounded;
}
}
.nc-icon {
@apply color-transition;
}

2
packages/nc-gui-v2/components/cell/Duration.vue

@ -1,5 +1,5 @@
<script setup lang="ts">
import { ref, computed, inject } from '#imports'
import { computed, inject, ref } from '#imports'
import { ColumnInj } from '~/components'
import { convertDurationToSeconds, convertMS2Duration, durationOptions } from '~/utils/durationHelper'

9
packages/nc-gui-v2/components/cell/Url.vue

@ -1,8 +1,10 @@
<script setup lang="ts">
import { ref, computed } from '#imports'
import { computed, ref } from '#imports'
import { ColumnInj } from '~/components'
import { isValidURL } from '~/utils/urlUtils'
const { modelValue: value } = defineProps<Props>()
const emit = defineEmits(['update:modelValue'])
const column = inject(ColumnInj)
const editEnabled = inject<boolean>('editEnabled')
@ -10,9 +12,6 @@ interface Props {
modelValue: string
}
const { modelValue: value } = defineProps<Props>()
const emit = defineEmits(['update:modelValue'])
const localState = computed({
get: () => value,
set: (val) => {
@ -35,7 +34,7 @@ onMounted(() => {
<input ref="root" v-model="localState" />
</span>
<span v-else>
<a class="caption py-2 text-primary underline hover:opacity-75" v-if="isValid" :href="value" target="_blank">{{ value }}</a>
<a v-if="isValid" class="caption py-2 text-primary underline hover:opacity-75" :href="value" target="_blank">{{ value }}</a>
<span v-else>{{ value }}</span>
</span>
</template>

14
packages/nc-gui-v2/components/general/NocoIcon.vue

@ -0,0 +1,14 @@
<script lang="ts" setup>
interface Props {
width?: number
height?: number
}
const { width = 90, height = 90 } = defineProps<Props>()
</script>
<template>
<div :style="{ left: `calc(50% - ${width / 2}px)`, top: `-${height * 0.6}px` }" class="absolute rounded-lg bg-primary">
<img :width="width" :height="height" alt="NocoDB" src="~/assets/img/icons/512x512-trans.png" />
</div>
</template>

2
packages/nc-gui-v2/components/general/Share.vue

@ -292,6 +292,6 @@ const openUrl = (url: string) => {
}
a {
@apply cursor-pointer text-3xl rounded-full p-2 bg-gray-100 shadow-md hover:(shadow-lg bg-gray-200) color-transition;
@apply cursor-pointer text-3xl rounded-full p-2 bg-gray-100 shadow-md hover:(shadow-lg bg-gray-200);
}
</style>

2
packages/nc-gui-v2/components/general/Social.vue

@ -46,7 +46,7 @@ const isZhLang = $computed(() => locale.value.startsWith('zh'))
<style scoped>
.icon {
@apply cursor-pointer text-3xl rounded-full p-2 bg-gray-100 shadow-md hover:(shadow-lg bg-gray-200) color-transition;
@apply cursor-pointer text-3xl rounded-full p-2 bg-gray-100 shadow-md hover:(shadow-lg bg-gray-200);
}
.discourse {

7
packages/nc-gui-v2/nuxt.config.ts

@ -2,8 +2,8 @@ import path from 'path'
import { defineNuxtConfig } from 'nuxt'
import vueI18n from '@intlify/vite-plugin-vue-i18n'
import Icons from 'unplugin-icons/vite'
import Components from 'unplugin-vue-components/vite';
import { AntDesignVueResolver } from 'unplugin-vue-components/resolvers';
import Components from 'unplugin-vue-components/vite'
import { AntDesignVueResolver } from 'unplugin-vue-components/resolvers'
// https://v3.nuxtjs.org/api/configuration/nuxt.config
export default defineNuxtConfig({
@ -19,7 +19,7 @@ export default defineNuxtConfig({
'~/assets/css/global.css',
'~/assets/style/style.css',
'~/assets/style.css',
'~/assets/style-v2.css',
'~/assets/style-v2.scss',
],
meta: {
@ -53,6 +53,7 @@ export default defineNuxtConfig({
Icons({
autoInstall: true,
compiler: 'vue3',
defaultClass: 'nc-icon',
}),
Components({
resolvers: [AntDesignVueResolver()],

19
packages/nc-gui-v2/package-lock.json generated

@ -18,6 +18,7 @@
},
"devDependencies": {
"@antfu/eslint-config": "^0.25.2",
"@iconify-json/clarity": "^1.1.4",
"@iconify-json/material-symbols": "^1.1.8",
"@iconify-json/mdi": "^1.1.25",
"@intlify/vite-plugin-vue-i18n": "^4.0.0",
@ -952,6 +953,15 @@
"dev": true,
"peer": true
},
"node_modules/@iconify-json/clarity": {
"version": "1.1.4",
"resolved": "https://registry.npmjs.org/@iconify-json/clarity/-/clarity-1.1.4.tgz",
"integrity": "sha512-WnSp4vqVBDqoTvJqUsp39Vo+39iHsPckpLcKmlA5yKxghbt7C3nd9Twy4+WGFuVYq/p2Jy2PFr6EWqciFJSbNw==",
"dev": true,
"dependencies": {
"@iconify/types": "*"
}
},
"node_modules/@iconify-json/material-symbols": {
"version": "1.1.8",
"resolved": "https://registry.npmjs.org/@iconify-json/material-symbols/-/material-symbols-1.1.8.tgz",
@ -14669,6 +14679,15 @@
"dev": true,
"peer": true
},
"@iconify-json/clarity": {
"version": "1.1.4",
"resolved": "https://registry.npmjs.org/@iconify-json/clarity/-/clarity-1.1.4.tgz",
"integrity": "sha512-WnSp4vqVBDqoTvJqUsp39Vo+39iHsPckpLcKmlA5yKxghbt7C3nd9Twy4+WGFuVYq/p2Jy2PFr6EWqciFJSbNw==",
"dev": true,
"requires": {
"@iconify/types": "*"
}
},
"@iconify-json/material-symbols": {
"version": "1.1.8",
"resolved": "https://registry.npmjs.org/@iconify-json/material-symbols/-/material-symbols-1.1.8.tgz",

1
packages/nc-gui-v2/package.json

@ -24,6 +24,7 @@
},
"devDependencies": {
"@antfu/eslint-config": "^0.25.2",
"@iconify-json/clarity": "^1.1.4",
"@iconify-json/material-symbols": "^1.1.8",
"@iconify-json/mdi": "^1.1.25",
"@intlify/vite-plugin-vue-i18n": "^4.0.0",

128
packages/nc-gui-v2/pages/forgot-password.vue

@ -0,0 +1,128 @@
<script setup lang="ts">
import { useI18n } from 'vue-i18n'
import { definePageMeta } from '#imports'
import { extractSdkResponseErrorMsg } from '~/utils/errorUtils'
import { useNuxtApp } from '#app'
import { isEmail } from '~/utils/validation'
import MdiLogin from '~icons/mdi/login'
import MaterialSymbolsWarning from '~icons/material-symbols/warning'
import ClaritySuccessLine from '~icons/clarity/success-line'
const { $api } = $(useNuxtApp())
const { t } = useI18n()
definePageMeta({
requiresAuth: false,
title: 'title.resetPassword',
})
let error = $ref<string | null>(null)
let success = $ref(false)
const valid = ref()
const formValidator = ref()
const form = reactive({
email: '',
})
const formRules = {
email: [
// E-mail is required
(v: string) => !!v || t('msg.error.signUpRules.emailReqd'),
// E-mail must be valid format
(v: string) => isEmail(v) || t('msg.error.signUpRules.emailInvalid'),
],
}
const resetPassword = async () => {
error = null
try {
await $api.auth.passwordForgot(form)
success = true
} catch (e: any) {
// todo: errors should not expose what was wrong (i.e. do not show "Password is wrong" messages)
error = await extractSdkResponseErrorMsg(e)
}
}
const resetError = () => {
if (error) {
error = null
}
}
</script>
<template>
<NuxtLayout>
<v-form
ref="formValidator"
v-model="valid"
class="h-full min-h-[600px] flex justify-center items-center"
@submit.prevent="resetPassword"
>
<div class="h-full w-full flex flex-col flex-wrap justify-center items-center">
<div
class="color-transition bg-white dark:(!bg-gray-900 !text-white) md: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 />
<div class="self-center flex flex-col justify-center items-center text-center gap-4">
<h1 class="prose-2xl font-bold my-4 w-full">{{ $t('title.resetPassword') }}</h1>
<template v-if="!success">
<p class="prose-sm">{{ $t('msg.info.passwordRecovery.message_1') }}</p>
<p class="prose-sm mb-4">{{ $t('msg.info.passwordRecovery.message_2') }}</p>
</template>
<template v-else>
<p class="prose-sm text-success flex items-center leading-8 gap-2">
{{ $t('msg.info.passwordRecovery.success') }} <ClaritySuccessLine />
</p>
<nuxt-link to="/signin">{{ $t('general.signIn') }}</nuxt-link>
</template>
</div>
<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>
</Transition>
<v-text-field
id="email"
v-model="form.email"
class="bg-white dark:!bg-gray-900"
:rules="formRules.email"
:label="$t('labels.email')"
:placeholder="$t('labels.email')"
:persistent-placeholder="true"
type="text"
@focus="resetError"
/>
<div class="self-center flex flex-wrap gap-4 items-center mt-4 md:mx-8 md:justify-between justify-center w-full">
<button
:disabled="!valid"
:class="[
!valid
? '!opacity-50 !cursor-default'
: 'text-white bg-primary hover:(text-primary !bg-primary/75) dark:(!bg-secondary/75 hover:!bg-secondary/50)',
]"
class="ml-1 border-1 border-solid border-gray-300 color-transition rounded-lg p-4 bg-gray-100/50"
type="submit"
>
<span class="flex items-center gap-2"><MdiLogin /> {{ $t('activity.sendEmail') }}</span>
</button>
<div class="text-end prose-sm">
{{ $t('msg.info.signUp.alreadyHaveAccount') }}
<nuxt-link to="/signin">{{ $t('general.signIn') }}</nuxt-link>
</div>
</div>
</div>
</div>
</v-form>
</NuxtLayout>
</template>

4
packages/nc-gui-v2/pages/projects/index.vue

@ -114,12 +114,12 @@ const activePage = $ref(navDrawerOptions[0].title)
<div class="self-end flex text-4xl mb-1">
<MaterialSymbolsGridView
:class="route.name === 'projects-index' ? 'text-primary dark:(!text-secondary/75)' : ''"
class="color-transition cursor-pointer p-2 hover:bg-gray-300/50 rounded-full"
class="cursor-pointer p-2 hover:bg-gray-300/50 rounded-full"
@click="navigateTo('/projects')"
/>
<MaterialSymbolsFormatListBulletedRounded
:class="route.name === 'projects-index-list' ? 'text-primary dark:(!text-secondary/75)' : ''"
class="color-transition cursor-pointer p-2 hover:bg-gray-300/50 rounded-full"
class="cursor-pointer p-2 hover:bg-gray-300/50 rounded-full"
@click="navigateTo('/projects/list')"
/>
</div>

2
packages/nc-gui-v2/pages/projects/index/list.vue

@ -28,7 +28,7 @@ const openProject = async (project: ProjectType) => {
<template v-for="project of projects" :key="project.id">
<div
class="cursor-pointer grid grid-cols-3 gap-2 prose-md hover:(bg-gray-100 shadow-sm dark:text-black) p-2 color-transition"
class="cursor-pointer grid grid-cols-3 gap-2 prose-md hover:(bg-gray-100 shadow-sm dark:text-black) p-2"
@click="openProject(project)"
>
<div class="font-semibold">{{ project.title || 'Untitled' }}</div>

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

@ -81,12 +81,7 @@ const resetError = () => {
<div
class="bg-white dark:(!bg-gray-900 !text-white) md: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)"
>
<div
style="left: -moz-calc(50% - 45px); left: -webkit-calc(50% - 45px); left: calc(50% - 45px)"
class="absolute top-12 md:top-[-10%] rounded-lg bg-primary"
>
<img width="90" height="90" src="~/assets/img/icons/512x512-trans.png" />
</div>
<general-noco-icon />
<h1 class="prose-2xl font-bold self-center my-4">{{ $t('general.signIn') }}</h1>
@ -132,9 +127,9 @@ const resetError = () => {
:class="[
!valid
? '!opacity-50 !cursor-default'
: 'shadow-md hover:(text-primary bg-primary/10 dark:text-white dark:!bg-primary/50)',
: 'text-white bg-primary hover:(text-primary !bg-primary/75) dark:(!bg-secondary/75 hover:!bg-secondary/50)',
]"
class="ml-1 border-1 border-solid border-gray-300 color-transition rounded-lg p-4 bg-gray-100/50"
class="ml-1 border-1 border-solid border-gray-300 rounded-lg p-4 bg-gray-100/50"
type="submit"
>
<span class="flex items-center gap-2"><MdiLogin /> {{ $t('general.signIn') }}</span>
@ -144,8 +139,8 @@ const resetError = () => {
<nuxt-link to="/signup">{{ $t('general.signUp') }}</nuxt-link>
</div>
<div class="prose-sm md:hidden">
<nuxt-link class="prose-sm text-primary underline hover:opacity-75" to="/forgot-password">
<div class="md:hidden">
<nuxt-link class="prose-sm" to="/forgot-password">
{{ $t('msg.info.signUp.forgotPassword') }}
</nuxt-link>
</div>
@ -155,13 +150,3 @@ const resetError = () => {
</v-form>
</NuxtLayout>
</template>
<style lang="scss">
.v-field__field {
@apply bg-white dark:(!bg-gray-900 text-white);
input {
@apply bg-white dark:(!bg-gray-700) !appearance-none my-1 border-1 border-solid border-primary/50 rounded;
}
}
</style>

11
packages/nc-gui-v2/pages/signup.vue

@ -71,12 +71,7 @@ const resetError = () => {
<div
class="bg-white dark:(!bg-gray-900 !text-white) md: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)"
>
<div
style="left: -moz-calc(50% - 45px); left: -webkit-calc(50% - 45px); left: calc(50% - 45px)"
class="absolute top-12 md:top-[-10%] rounded-lg bg-primary"
>
<img width="90" height="90" src="~/assets/img/icons/512x512-trans.png" />
</div>
<general-noco-icon />
<h1 class="prose-2xl font-bold self-center my-4">{{ $t('general.signUp') }}</h1>
@ -128,9 +123,9 @@ const resetError = () => {
:class="[
!valid
? '!opacity-50 !cursor-default'
: 'shadow-md hover:(text-primary bg-primary/10 dark:text-white dark:!bg-primary/50)',
: 'text-white bg-primary hover:(text-primary !bg-primary/75) dark:(!bg-secondary/75 hover:!bg-secondary/50)',
]"
class="ml-1 border-1 border-solid border-gray-300 color-transition rounded-lg p-4 bg-gray-100/50"
class="ml-1 border-1 border-solid border-gray-300 rounded-lg p-4 bg-gray-100/50"
type="submit"
>
<span class="flex items-center gap-2"><MaterialSymbolsRocketLaunchOutline /> {{ $t('general.signUp') }}</span>

4
packages/nc-gui-v2/windi.config.ts

@ -15,12 +15,10 @@ import colors, { themeColors } from './utils/colorsUtils'
export default defineConfig({
extract: {
include: ['**/*.{vue,html,jsx,tsx}'],
include: ['**/*.{vue,html,jsx,tsx,css}'],
exclude: ['node_modules', '.git'],
},
attributify: true,
darkMode: 'class',
plugins: [

Loading…
Cancel
Save