Browse Source

Merge pull request #7286 from nocodb/nc-fix/ui-ux-fixes

Nc fix/UI ux fixes
pull/7293/head
Raju Udava 11 months ago committed by GitHub
parent
commit
1faba733af
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
  1. 2
      packages/nc-gui/components/dlg/share-and-collaborate/Collaborate.vue
  2. 19
      packages/nc-gui/components/project/AccessSettings.vue
  3. 17
      packages/nc-gui/components/workspace/CollaboratorsList.vue
  4. 5
      packages/nc-gui/pages/forgot-password.vue
  5. 4
      packages/nc-gui/pages/signin.vue
  6. 3
      packages/nc-gui/pages/signup/[[token]].vue
  7. 15
      packages/nocodb-sdk/src/lib/dateTimeHelper.ts
  8. 12
      tests/playwright/pages/Dashboard/common/Cell/SelectOptionCell.ts
  9. 12
      tests/playwright/pages/Dashboard/common/Cell/UserOptionCell.ts

2
packages/nc-gui/components/dlg/share-and-collaborate/Collaborate.vue

@ -13,7 +13,7 @@ const validators = computed(() => {
{ {
validator: (rule: any, value: string, callback: (errMsg?: string) => void) => { validator: (rule: any, value: string, callback: (errMsg?: string) => void) => {
if (!value || value.length === 0) { if (!value || value.length === 0) {
callback(t('msg.error.signUpRules.emailReqd')) callback(t('msg.error.signUpRules.emailRequired'))
return return
} }
const invalidEmails = (value || '').split(/\s*,\s*/).filter((e: string) => !validateEmail(e)) const invalidEmails = (value || '').split(/\s*,\s*/).filter((e: string) => !validateEmail(e))

19
packages/nc-gui/components/project/AccessSettings.vue

@ -6,6 +6,7 @@ import {
WorkspaceRolesToProjectRoles, WorkspaceRolesToProjectRoles,
extractRolesObj, extractRolesObj,
timeAgo, timeAgo,
parseStringDateTime,
} from 'nocodb-sdk' } from 'nocodb-sdk'
import type { WorkspaceUserRoles } from 'nocodb-sdk' import type { WorkspaceUserRoles } from 'nocodb-sdk'
import { isEeUI, storeToRefs } from '#imports' import { isEeUI, storeToRefs } from '#imports'
@ -145,8 +146,8 @@ const filteredCollaborators = computed(() =>
<div class="flex flex-col rounded-lg overflow-hidden border-1 max-w-350 max-h-[calc(100%-8rem)]"> <div class="flex flex-col rounded-lg overflow-hidden border-1 max-w-350 max-h-[calc(100%-8rem)]">
<div class="flex flex-row bg-gray-50 min-h-12 items-center border-b-1"> <div class="flex flex-row bg-gray-50 min-h-12 items-center border-b-1">
<div class="text-gray-700 users-email-grid">{{ $t('objects.users') }}</div> <div class="text-gray-700 users-email-grid">{{ $t('objects.users') }}</div>
<div class="text-gray-700 date-joined-grid">{{ $t('title.dateJoined') }}</div>
<div class="text-gray-700 user-access-grid">{{ $t('general.access') }}</div> <div class="text-gray-700 user-access-grid">{{ $t('general.access') }}</div>
<div class="text-gray-700 date-joined-grid">{{ $t('title.dateJoined') }}</div>
</div> </div>
<div class="flex flex-col nc-scrollbar-md"> <div class="flex flex-col nc-scrollbar-md">
@ -161,7 +162,6 @@ const filteredCollaborators = computed(() =>
{{ collab.email }} {{ collab.email }}
</span> </span>
</div> </div>
<div class="date-joined-grid">{{ timeAgo(collab.created_at) }}</div>
<div class="user-access-grid"> <div class="user-access-grid">
<template v-if="accessibleRoles.includes(collab.roles)"> <template v-if="accessibleRoles.includes(collab.roles)">
<RolesSelector <RolesSelector
@ -180,6 +180,16 @@ const filteredCollaborators = computed(() =>
<RolesBadge :role="collab.roles" /> <RolesBadge :role="collab.roles" />
</template> </template>
</div> </div>
<div class="date-joined-grid">
<NcTooltip class="max-w-full">
<template #title>
{{ parseStringDateTime(collab.created_at) }}
</template>
<span>
{{ timeAgo(collab.created_at) }}
</span>
</NcTooltip>
</div>
</div> </div>
</div> </div>
</div> </div>
@ -206,12 +216,11 @@ const filteredCollaborators = computed(() =>
} }
.date-joined-grid { .date-joined-grid {
@apply flex items-start; @apply w-1/4 flex items-start;
width: calc(50% - 10rem);
} }
.user-access-grid { .user-access-grid {
@apply w-40; @apply w-1/4 flex justify-start;
} }
.user-row { .user-row {

17
packages/nc-gui/components/workspace/CollaboratorsList.vue

@ -1,5 +1,5 @@
<script lang="ts" setup> <script lang="ts" setup>
import { OrderedWorkspaceRoles, WorkspaceUserRoles, timeAgo } from 'nocodb-sdk' import { OrderedWorkspaceRoles, WorkspaceUserRoles, parseStringDateTime, timeAgo } from 'nocodb-sdk'
import { storeToRefs, useWorkspace } from '#imports' import { storeToRefs, useWorkspace } from '#imports'
const { workspaceRoles, loadRoles } = useRoles() const { workspaceRoles, loadRoles } = useRoles()
@ -59,9 +59,9 @@ onMounted(async () => {
<div v-else class="nc-collaborators-list mt-6 h-full"> <div v-else class="nc-collaborators-list mt-6 h-full">
<div class="flex flex-col rounded-lg overflow-hidden border-1 max-w-350 max-h-[calc(100%-8rem)]"> <div class="flex flex-col rounded-lg overflow-hidden border-1 max-w-350 max-h-[calc(100%-8rem)]">
<div class="flex flex-row bg-gray-50 min-h-12 items-center"> <div class="flex flex-row bg-gray-50 min-h-12 items-center">
<div class="text-gray-700 users-email-grid w-3/8 ml-10">{{ $t('objects.users') }}</div> <div class="text-gray-700 users-email-grid w-3/8 ml-10 mr-3">{{ $t('objects.users') }}</div>
<div class="text-gray-700 date-joined-grid w-2/8 mr-3 pl-1">{{ $t('title.dateJoined') }}</div>
<div class="text-gray-700 user-access-grid w-2/8 mr-3">{{ $t('general.access') }}</div> <div class="text-gray-700 user-access-grid w-2/8 mr-3">{{ $t('general.access') }}</div>
<div class="text-gray-700 date-joined-grid w-2/8 mr-3">{{ $t('title.dateJoined') }}</div>
<div class="text-gray-700 user-access-grid w-1/8">Actions</div> <div class="text-gray-700 user-access-grid w-1/8">Actions</div>
</div> </div>
@ -77,7 +77,6 @@ onMounted(async () => {
{{ collab.email }} {{ collab.email }}
</span> </span>
</div> </div>
<div class="date-joined-grid w-2/8">{{ timeAgo(collab.created_at) }}</div>
<div class="user-access-grid w-2/8"> <div class="user-access-grid w-2/8">
<template v-if="accessibleRoles.includes(collab.roles)"> <template v-if="accessibleRoles.includes(collab.roles)">
<div class="w-[30px]"> <div class="w-[30px]">
@ -94,6 +93,16 @@ onMounted(async () => {
<RolesBadge :role="collab.roles" class="cursor-default" /> <RolesBadge :role="collab.roles" class="cursor-default" />
</template> </template>
</div> </div>
<div class="date-joined-grid w-2/8 flex justify-start">
<NcTooltip class="max-w-full">
<template #title>
{{ parseStringDateTime(collab.created_at) }}
</template>
<span>
{{ timeAgo(collab.created_at) }}
</span>
</NcTooltip>
</div>
<div class="w-1/8 pl-6"> <div class="w-1/8 pl-6">
<NcDropdown v-if="collab.roles !== WorkspaceUserRoles.OWNER" :trigger="['click']"> <NcDropdown v-if="collab.roles !== WorkspaceUserRoles.OWNER" :trigger="['click']">
<MdiDotsVertical <MdiDotsVertical

5
packages/nc-gui/pages/forgot-password.vue

@ -23,12 +23,13 @@ const form = reactive({
const formRules = { const formRules = {
email: [ email: [
// E-mail is required // E-mail is required
{ required: true, message: t('msg.error.signUpRules.emailReqd') }, { required: true, message: t('msg.error.signUpRules.emailRequired') },
// E-mail must be valid format // E-mail must be valid format
{ {
validator: (_: unknown, v: string) => { validator: (_: unknown, v: string) => {
return new Promise((resolve, reject) => { return new Promise((resolve, reject) => {
if (validateEmail(v)) return resolve() if (!v?.length || validateEmail(v)) return resolve()
reject(new Error(t('msg.error.signUpRules.emailInvalid'))) reject(new Error(t('msg.error.signUpRules.emailInvalid')))
}) })
}, },

4
packages/nc-gui/pages/signin.vue

@ -38,12 +38,12 @@ const form = reactive({
const formRules: Record<string, RuleObject[]> = { const formRules: Record<string, RuleObject[]> = {
email: [ email: [
// E-mail is required // E-mail is required
{ required: true, message: t('msg.error.signUpRules.emailReqd') }, { required: true, message: t('msg.error.signUpRules.emailRequired') },
// E-mail must be valid format // E-mail must be valid format
{ {
validator: (_: unknown, v: string) => { validator: (_: unknown, v: string) => {
return new Promise((resolve, reject) => { return new Promise((resolve, reject) => {
if (validateEmail(v)) return resolve() if (!v?.length || validateEmail(v)) return resolve()
reject(new Error(t('msg.error.signUpRules.emailInvalid'))) reject(new Error(t('msg.error.signUpRules.emailInvalid')))
}) })

3
packages/nc-gui/pages/signup/[[token]].vue

@ -44,12 +44,13 @@ const form = reactive({
const formRules = { const formRules = {
email: [ email: [
// E-mail is required // E-mail is required
{ required: true, message: t('msg.error.signUpRules.emailReqd') }, { required: true, message: t('msg.error.signUpRules.emailRequired') },
// E-mail must be valid format // E-mail must be valid format
{ {
validator: (_: unknown, v: string) => { validator: (_: unknown, v: string) => {
return new Promise((resolve, reject) => { return new Promise((resolve, reject) => {
if (!v?.length || validateEmail(v)) return resolve() if (!v?.length || validateEmail(v)) return resolve()
reject(new Error(t('msg.error.signUpRules.emailInvalid'))) reject(new Error(t('msg.error.signUpRules.emailInvalid')))
}) })
}, },

15
packages/nocodb-sdk/src/lib/dateTimeHelper.ts

@ -77,6 +77,21 @@ export function parseStringDate(v: string, dateFormat: string) {
return v; return v;
} }
export function parseStringDateTime(
v: string,
dateTimeFormat: string = `${dateFormats[0]} ${timeFormats[0]}`
) {
const dayjsObj = dayjs(v).local();
if (dayjsObj.isValid()) {
v = dayjsObj.format(dateTimeFormat);
} else {
v = dayjs(v, dateTimeFormat).local().format(dateTimeFormat);
}
return v;
}
export function convertToTargetFormat( export function convertToTargetFormat(
v: string, v: string,
oldDataFormat, oldDataFormat,

12
tests/playwright/pages/Dashboard/common/Cell/SelectOptionCell.ts

@ -39,9 +39,15 @@ export class SelectOptionCellPageObject extends BasePage {
await selectCell.click(); await selectCell.click();
if (index === -1) if (index === -1) {
await this.rootPage.getByTestId(`select-option-${columnHeader}-undefined`).getByText(option).click(); const selectOption = this.rootPage.getByTestId(`select-option-${columnHeader}-undefined`).getByText(option);
else await this.rootPage.getByTestId(`select-option-${columnHeader}-${index}`).getByText(option).click(); await selectOption.waitFor({ state: 'visible' });
await selectOption.click();
} else {
const selectOption = this.rootPage.getByTestId(`select-option-${columnHeader}-${index}`).getByText(option);
await selectOption.waitFor({ state: 'visible' });
await selectOption.click();
}
if (multiSelect) await this.get({ index, columnHeader }).click(); if (multiSelect) await this.get({ index, columnHeader }).click();

12
tests/playwright/pages/Dashboard/common/Cell/UserOptionCell.ts

@ -37,9 +37,15 @@ export class UserOptionCellPageObject extends BasePage {
await selectCell.click(); await selectCell.click();
if (index === -1) if (index === -1) {
await this.rootPage.getByTestId(`select-option-${columnHeader}-undefined`).getByText(option).click(); const selectOption = this.rootPage.getByTestId(`select-option-${columnHeader}-undefined`).getByText(option);
else await this.rootPage.getByTestId(`select-option-${columnHeader}-${index}`).getByText(option).click(); await selectOption.waitFor({ state: 'visible' });
await selectOption.click();
} else {
const selectOption = this.rootPage.getByTestId(`select-option-${columnHeader}-${index}`).getByText(option);
await selectOption.waitFor({ state: 'visible' });
await selectOption.click();
}
if (multiSelect) await this.get({ index, columnHeader }).click(); if (multiSelect) await this.get({ index, columnHeader }).click();

Loading…
Cancel
Save