Browse Source

Merge branch 'develop' into feat/import-optimization

pull/3811/head
Wing-Kam Wong 2 years ago
parent
commit
1f2a9ed561
  1. 8
      README.md
  2. 24
      packages/nc-gui/assets/style.scss
  3. 23
      packages/nc-gui/components/dashboard/TreeView.vue
  4. 12
      packages/nc-gui/components/general/NocoIcon.vue
  5. 2
      packages/nc-gui/pages/[projectType]/[projectId]/index.vue
  6. 5
      packages/nc-gui/pages/forgot-password.vue
  7. 2
      packages/nc-gui/pages/index/index/[projectId].vue
  8. 2
      packages/nc-gui/pages/index/index/create-external.vue
  9. 2
      packages/nc-gui/pages/index/index/create.vue
  10. 5
      packages/nc-gui/pages/signin.vue
  11. 5
      packages/nc-gui/pages/signup/[[token]].vue
  12. 7
      scripts/cypress/integration/common/00_pre_configurations.js
  13. 4
      scripts/cypress/integration/common/1b_table_column_operations.js
  14. 15
      scripts/cypress/integration/common/3c_lookup_column.js
  15. 14
      scripts/cypress/integration/common/3d_rollup_column.js
  16. 8
      scripts/cypress/integration/common/3e_duration_column.js
  17. 27
      scripts/cypress/integration/common/3f_link_to_another_record.js
  18. 9
      scripts/cypress/integration/common/5a_user_role.js
  19. 4
      scripts/cypress/integration/common/9a_QuickTest.js
  20. 17
      scripts/cypress/support/commands.js
  21. 12
      scripts/cypress/support/page_objects/mainPage.js
  22. 22
      scripts/cypress/support/page_objects/navigation.js

8
README.md

@ -1,6 +1,6 @@
<h1 align="center" style="border-bottom: none">
<b>
<a href="https://www.nocodb.com">NocoDB</a><br>
<a href="https://www.nocodb.com"><img src="/packages/nc-gui/public/icon.png" width="80" /><br>NocoDB</a><br>
</b>
The Open Source Airtable Alternative <br>
</h1>
@ -263,6 +263,7 @@ Access Dashboard using : [http://localhost:8080/dashboard](http://localhost:8080
- [Contributing](#contributing)
- [Why are we building this?](#why-are-we-building-this)
- [Our Mission](#our-mission)
- [License](#license)
- [Contributors](#contributors)
# Features
@ -323,6 +324,11 @@ Most internet businesses equip themselves with either spreadsheet or a database
# Our Mission
Our mission is to provide the most powerful no-code interface for databases which is open source to every single internet business in the world. This would not only democratise access to a powerful computing tool but also bring forth a billion+ people who will have radical tinkering-and-building abilities on internet.
# License
<p>
This project is licensed under <a href="./LICENSE">AGPLv3</a>.</p>
<br>
# Contributors
[//]: contributor-faces

24
packages/nc-gui/assets/style.scss

@ -100,7 +100,7 @@ a {
}
.animated-bg-gradient {
background: linear-gradient(122deg, #6f3381, #81c7d4, #fedfe1, #9ee59e);
background: linear-gradient(122deg, #6f3381, #ec4899, #fedfe1, #4351e8);
background-size: 800% 800%;
-webkit-animation: gradient 4s ease infinite;
@ -168,6 +168,28 @@ a {
transform: translate(-100%, 0);
}
.slide-right-enter-active,
.slide-right-leave-active {
transition: all 400ms ease;
}
.slide-right-enter-from,
.slide-right-leave-to {
transform: translate(100%, 0%);
opacity: 0;
}
.slide-left-enter-active,
.slide-left-leave-active {
transition: all 400ms ease;
}
.slide-left-enter-from,
.slide-left-leave-to {
transform: translate(-100%, 0%);
opacity: 0;
}
.glow-enter-active,
.glow-leave-active {
@apply transition-all duration-300 ease-in-out;

23
packages/nc-gui/components/dashboard/TreeView.vue

@ -14,6 +14,7 @@ import {
useProject,
useTable,
useTabs,
useToggle,
useUIPermission,
watchEffect,
} from '#imports'
@ -33,6 +34,8 @@ const { deleteTable } = useTable()
const { isUIAllowed } = useUIPermission()
const [searchActive, toggleSearchActive] = useToggle()
const isLocked = inject('TreeViewIsLockedInj')
let key = $ref(0)
@ -126,8 +129,6 @@ const contextMenuTarget = reactive<{ type?: 'table' | 'main'; value?: any }>({})
const setMenuContext = (type: 'table' | 'main', value?: any) => {
contextMenuTarget.type = type
contextMenuTarget.value = value
// $e('c:table:create:navdraw:right-click')
}
const reloadTables = async () => {
@ -215,12 +216,26 @@ function openTableCreateDialog() {
<div class="nc-treeview-container flex flex-col">
<a-dropdown :trigger="['contextmenu']" overlay-class-name="nc-dropdown-tree-view-context-menu">
<div class="pt-2 pl-2 pb-2 flex-1 overflow-y-auto flex flex-col scrollbar-thin-dull" :class="{ 'mb-[20px]': isSharedBase }">
<div class="py-1 px-3 flex w-full items-center gap-1 cursor-pointer" @contextmenu="setMenuContext('main')">
<span class="flex-1 text-bold uppercase nc-project-tree text-gray-500 font-weight-bold">
<div class="min-h-[36px] py-1 px-3 flex w-full items-center gap-1 cursor-pointer" @contextmenu="setMenuContext('main')">
<Transition name="slide-left" mode="out-in">
<a-input
v-if="searchActive"
v-model:value="filterQuery"
class="flex-1 rounded"
:placeholder="$t('placeholder.searchProjectTree')"
/>
<span v-else class="flex-1 text-bold uppercase nc-project-tree text-gray-500 font-weight-bold">
{{ $t('objects.tables') }}
<template v-if="tables?.length"> ({{ tables.length }}) </template>
</span>
</Transition>
<Transition name="layout" mode="out-in">
<MdiClose v-if="searchActive" class="text-lg mx-1 mt-0.5" @click="toggleSearchActive(false)" />
<IcRoundSearch v-else class="text-lg text-primary mx-1 mt-0.5" @click="toggleSearchActive(true)" />
</Transition>
</div>
<div class="flex-1">

12
packages/nc-gui/components/general/NocoIcon.vue

@ -2,13 +2,21 @@
interface Props {
width?: number
height?: number
animate?: boolean
}
const { width = 90, height = 90 } = defineProps<Props>()
const { width = 90, height = 90, animate = false } = defineProps<Props>()
</script>
<template>
<div :style="{ left: `calc(50% - ${width / 2}px)`, top: `-${height / 2}px` }" class="absolute rounded-lg">
<div :style="{ left: `calc(50% - ${width / 2}px)`, top: `-${height / 2}px` }" class="absolute rounded-lg pt-1 pl-1 -ml-1">
<div class="relative">
<img :width="width" :height="height" alt="NocoDB" src="~/assets/img/icons/512x512.png" />
<template v-if="animate">
<div class="animated-bg-gradient opacity-100 rounded-full z-0 absolute bottom-1.45 right-1.45 h-4.5 w-4.5" />
<div class="animate-ping bg-primary bg-opacity-50 rounded-full z-0 absolute bottom-0.9 right-1 h-5.5 w-5.5" />
</template>
</div>
</div>
</template>

2
packages/nc-gui/pages/[projectType]/[projectId]/index.vue

@ -186,7 +186,7 @@ onBeforeUnmount(reset)
>
<div
style="height: var(--header-height)"
:class="isOpen ? 'pl-6' : ''"
:class="isOpen ? 'pl-4' : ''"
class="flex items-center !bg-primary text-white px-1 gap-2"
>
<div

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

@ -55,10 +55,7 @@ function resetError() {
<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)"
>
<LazyGeneralNocoIcon
class="color-transition hover:(ring ring-accent)"
:class="[isLoading ? 'animated-bg-gradient' : '']"
/>
<LazyGeneralNocoIcon class="color-transition hover:(ring ring-accent)" :animate="isLoading" />
<div class="self-center flex flex-col justify-center items-center text-center gap-2">
<h1 class="prose-2xl font-bold my-4 w-full">{{ $t('title.resetPassword') }}</h1>

2
packages/nc-gui/pages/index/index/[projectId].vue

@ -68,7 +68,7 @@ onMounted(async () => {
<div
class="update-project bg-white relative flex-auto flex flex-col justify-center gap-2 p-8 md:(rounded-lg border-1 border-gray-200 shadow-xl)"
>
<LazyGeneralNocoIcon class="color-transition hover:(ring ring-accent)" :class="[isLoading ? 'animated-bg-gradient' : '']" />
<LazyGeneralNocoIcon class="color-transition hover:(ring ring-accent)" :animate="isLoading" />
<div
class="color-transition transform group absolute top-5 left-5 text-4xl rounded-full bg-white cursor-pointer"

2
packages/nc-gui/pages/index/index/create-external.vue

@ -337,7 +337,7 @@ onMounted(async () => {
<div
class="create-external bg-white relative flex flex-col justify-center gap-2 w-full p-8 md:(rounded-lg border-1 border-gray-200 shadow-xl)"
>
<LazyGeneralNocoIcon class="color-transition hover:(ring ring-accent)" :class="[isLoading ? 'animated-bg-gradient' : '']" />
<LazyGeneralNocoIcon class="color-transition hover:(ring ring-accent)" :animate="isLoading" />
<div
class="color-transition transform group absolute top-5 left-5 text-4xl rounded-full bg-white cursor-pointer"

2
packages/nc-gui/pages/index/index/create.vue

@ -66,7 +66,7 @@ onMounted(async () => {
<div
class="create bg-white relative flex flex-col justify-center gap-2 w-full p-8 md:(rounded-lg border-1 border-gray-200 shadow-xl)"
>
<LazyGeneralNocoIcon class="color-transition hover:(ring ring-accent)" :class="[isLoading ? 'animated-bg-gradient' : '']" />
<LazyGeneralNocoIcon class="color-transition hover:(ring ring-accent)" :animate="isLoading" />
<div
class="color-transition transform group absolute top-5 left-5 text-4xl rounded-full bg-white cursor-pointer"

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

@ -67,10 +67,7 @@ function resetError() {
<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)"
>
<LazyGeneralNocoIcon
class="color-transition hover:(ring ring-accent)"
:class="[isLoading ? 'animated-bg-gradient' : '']"
/>
<LazyGeneralNocoIcon class="color-transition hover:(ring ring-accent)" :animate="isLoading" />
<h1 class="prose-2xl font-bold self-center my-4">{{ $t('general.signIn') }}</h1>

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

@ -85,10 +85,7 @@ function resetError() {
<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)"
>
<LazyGeneralNocoIcon
class="color-transition hover:(ring ring-accent)"
:class="[isLoading ? 'animated-bg-gradient' : '']"
/>
<LazyGeneralNocoIcon class="color-transition hover:(ring ring-accent)" :animate="isLoading" />
<h1 class="prose-2xl font-bold self-center my-4">
{{ $t('general.signUp') }}

7
scripts/cypress/integration/common/00_pre_configurations.js

@ -243,12 +243,7 @@ export const genTest = (apiType, dbType) => {
// wait for tab to be rendered completely
cy.wait(2000);
cy.getSettled("button.ant-tabs-tab-remove")
.should("be.visible")
.click();
cy.get("button.ant-tabs-tab-remove").should("not.exist");
cy.wait(2000);
cy.closeTableTab();
// first instance of updating local storage information
cy.saveLocalStorage();

4
scripts/cypress/integration/common/1b_table_column_operations.js

@ -75,9 +75,7 @@ export const genTest = (apiType, dbType) => {
.find(".nc-column-edit")
.click();
// fix me! wait till the modal rendering (input highlight) is completed
// focus shifts back to the input field to select text after the dropdown is rendered
cy.wait(500);
cy.inputHighlightRenderWait();
// change column type and verify
// cy.get(".nc-column-type-input").last().click();

15
scripts/cypress/integration/common/3c_lookup_column.js

@ -11,18 +11,7 @@ export const genTest = (apiType, dbType) => {
cy.get("label").contains(label).parents(".ant-row").click();
};
// Run once before test- create project (rest/graphql)
//
// before(() => {
// cy.fileHook();
// mainPage.tabReset();
// // open a table to work on views
// //
//
// // // close team & auth tab
// // cy.get('button.ant-tabs-tab-remove').should('exist').click();
//
// cy.openTableTab("City", 25);
// });
beforeEach(() => {
@ -60,9 +49,7 @@ export const genTest = (apiType, dbType) => {
.contains("Lookup")
.click();
// fix me! wait till the modal rendering (input highlight) is completed
// focus shifts back to the input field to select text after the dropdown is rendered
cy.wait(500);
cy.inputHighlightRenderWait();
// Configure Child table & column names
fetchParentFromLabel("Child table");

14
scripts/cypress/integration/common/3d_rollup_column.js

@ -11,17 +11,7 @@ export const genTest = (apiType, dbType) => {
cy.get("label").contains(label).parents(".ant-row").click();
};
// Run once before test- create project (rest/graphql)
//
// before(() => {
// cy.fileHook();
// mainPage.tabReset();
//
// // // close team & auth tab
// // cy.get('button.ant-tabs-tab-remove').should('exist').click();
// // open a table to work on views
// //
// cy.openTableTab("Country", 25);
// });
beforeEach(() => {
@ -64,9 +54,7 @@ export const genTest = (apiType, dbType) => {
.contains("Rollup")
.click();
// fix me! wait till the modal rendering (input highlight) is completed
// focus shifts back to the input field to select text after the dropdown is rendered
cy.wait(500);
cy.inputHighlightRenderWait();
// Configure Child table & column names
fetchParentFromLabel("Child table");

8
scripts/cypress/integration/common/3e_duration_column.js

@ -57,9 +57,7 @@ export const genTest = (apiType, dbType) => {
.contains("Duration")
.click();
// fix me! wait till the modal rendering (input highlight) is completed
// focus shifts back to the input field to select text after the dropdown is rendered
cy.wait(500);
cy.inputHighlightRenderWait();
// Configure Duration format
fetchParentFromLabel("Duration Format");
@ -101,9 +99,7 @@ export const genTest = (apiType, dbType) => {
.clear()
.type(newName);
// fix me! wait till the modal rendering (input highlight) is completed
// focus shifts back to the input field to select text after the dropdown is rendered
cy.wait(500);
cy.inputHighlightRenderWait();
// Configure Duration format
fetchParentFromLabel("Duration Format");

27
scripts/cypress/integration/common/3f_link_to_another_record.js

@ -6,7 +6,7 @@ export const genTest = (apiType, dbType) => {
if (!isTestSuiteActive(apiType, dbType)) return;
// tbd: this needs a proper fix
let waitTime = 2000;
let waitTime = 0;
let clear;
describe(`${apiType.toUpperCase()} api - Link to another record`, () => {
@ -185,7 +185,6 @@ export const genTest = (apiType, dbType) => {
// Click on `Add new row` button
cy.get(".nc-add-new-row-btn:visible").should("exist");
cy.get(".nc-add-new-row-btn").click();
cy.wait(waitTime);
// Title
cy.get(".nc-expand-col-Title")
@ -205,32 +204,26 @@ export const genTest = (apiType, dbType) => {
.find(".nc-action-icon")
.should("exist")
.click({ force: true });
cy.wait(waitTime);
cy.wait("@waitForCardLoad");
cy.getActiveModal(".nc-modal-link-record")
.find(".ant-card")
.should("exist")
.eq(0)
.click();
cy.wait(waitTime);
// MM
cy.get(".nc-expand-col-Sheet1.List").find(".ant-btn-primary").click();
cy.wait(waitTime);
cy.wait("@waitForCardLoad");
cy.getActiveModal(".nc-modal-link-record")
.find(".ant-card")
.should("exist")
.eq(0)
.click();
cy.wait(waitTime);
// HM
cy.get(".nc-expand-col-Link2-1hm").find(".ant-btn-primary").click();
cy.wait(waitTime);
cy.wait("@waitForCardLoad");
cy.getActiveModal().find(".ant-card").should("exist").eq(0).click();
cy.wait(waitTime);
// Save row
cy.getActiveDrawer(".nc-drawer-expanded-form")
@ -256,13 +249,11 @@ export const genTest = (apiType, dbType) => {
.getCell("Sheet1", 2)
.find(".nc-action-icon")
.click({ force: true });
cy.wait(waitTime);
cy.getActiveModal(".nc-modal-link-record")
.find(".ant-card")
.should("exist")
.eq(1)
.click();
cy.wait(waitTime);
// MM
mainPage
@ -270,13 +261,11 @@ export const genTest = (apiType, dbType) => {
.find(".nc-action-icon")
.last()
.click({ force: true });
cy.wait(waitTime);
cy.getActiveModal(".nc-modal-link-record")
.find(".ant-card")
.should("exist")
.eq(1)
.click();
cy.wait(waitTime);
// HM
mainPage
@ -284,13 +273,11 @@ export const genTest = (apiType, dbType) => {
.find(".nc-action-icon")
.last()
.click({ force: true });
cy.wait(waitTime);
cy.getActiveModal(".nc-modal-link-record")
.find(".ant-card")
.should("exist")
.eq(1)
.click();
cy.wait(waitTime);
});
// Existing row, expand record
@ -300,49 +287,37 @@ export const genTest = (apiType, dbType) => {
addRow(3, "2c");
cy.wait(waitTime);
cy.get(".nc-row-expand").eq(2).click({ force: true });
cy.wait(waitTime);
// wait for page render to complete
cy.get('button:contains("Save row"):visible').should("exist");
// BT
cy.wait(waitTime);
cy.get(".nc-expand-col-Sheet1")
.find(".nc-action-icon")
.should("exist")
.click({ force: true });
cy.wait(waitTime);
// cy.wait("@waitForCardLoad");
cy.getActiveModal(".nc-modal-link-record")
.find(".ant-card")
.should("exist")
.eq(2)
.click();
cy.wait(waitTime);
// MM
cy.get(".nc-expand-col-Sheet1.List").find(".ant-btn-primary").click();
cy.wait(waitTime);
// cy.wait("@waitForCardLoad");
cy.getActiveModal(".nc-modal-link-record")
.find(".ant-card")
.should("exist")
.eq(2)
.click();
cy.wait(waitTime);
// HM
cy.get(".nc-expand-col-Link2-1hm").find(".ant-btn-primary").click();
cy.wait(waitTime);
// cy.wait("@waitForCardLoad");
cy.getActiveModal(".nc-modal-link-record")
.find(".ant-card")
.should("exist")
.eq(2)
.click();
cy.wait(waitTime);
cy.getActiveDrawer(".nc-drawer-expanded-form")
.find("button")

9
scripts/cypress/integration/common/5a_user_role.js

@ -145,14 +145,7 @@ export const genTest = (apiType, dbType) => {
}
if (roleType === "creator") {
// kludge: wait for page load to finish
// close team & auth tab
// cy.wait(500);
// cy.get("button.ant-tabs-tab-remove").should("exist").click();
cy.getSettled("button.ant-tabs-tab-remove")
.should("be.visible")
.click();
cy.wait(2000);
cy.closeTableTab();
}
cy.saveLocalStorage();

4
scripts/cypress/integration/common/9a_QuickTest.js

@ -113,9 +113,7 @@ export const genTest = (apiType, dbType, testMode) => {
// kludge: wait for page load to finish
cy.wait(2000);
// close team & auth tab
cy.get("button.ant-tabs-tab-remove").should("exist").click();
cy.wait(2000);
cy.closeTableTab();
} else {
cy.restoreLocalStorage();
}

17
scripts/cypress/support/commands.js

@ -189,6 +189,9 @@ Cypress.Commands.add("openTableTab", (tn, rc) => {
Cypress.Commands.add("closeTableTab", (tn) => {
cy.task("log", `[closeTableTab] ${tn}`);
if (tn) {
// request to close specific tab
cy.get(".ant-tabs-tab-btn")
.contains(tn)
.should("exist")
@ -197,9 +200,13 @@ Cypress.Commands.add("closeTableTab", (tn) => {
.parent()
.find("button")
.click();
} else {
// lone tab active; close it
cy.getSettled("button.ant-tabs-tab-remove").should("be.visible").click();
cy.get("button.ant-tabs-tab-remove").should("not.exist");
}
// subsequent tab open commands will fail if tab is not closed completely
cy.wait(2000)
cy.wait(2000);
});
Cypress.Commands.add("openOrCreateGqlProject", (_args) => {
@ -403,6 +410,12 @@ Cypress.Commands.add("toastWait", (msg) => {
);
});
Cypress.Commands.add("inputHighlightRenderWait", (selector) => {
// fix me! wait till the modal rendering (input highlight) is completed
// focus shifts back to the input field to select text after the dropdown is rendered
cy.wait(500);
});
// vn: view name
// rc: expected row count. validate row count if rc!=0
Cypress.Commands.add("openViewsTab", (vn, rc) => {

12
scripts/cypress/support/page_objects/mainPage.js

@ -615,17 +615,7 @@ export class _mainPage {
});
}
tabReset() {
// temporary disable (kludge)
// mainPage.toolBarTopLeft(mainPage.HOME).click({ force: true });
// cy.get(".project-row").should("exist").click({ force: true });
// projectsPage.waitHomePageLoad();
// option-2
// cy.openTableTab("Country", 0);
// cy.get(".mdi-close").click({ multiple: true });
// cy.get("button.ant-tabs-tab-remove").click({ multiple: true });
// cy.get('.ant-tabs-tab-remove').should('not.exist')
}
tabReset() {}
toggleRightSidebar() {
cy.get(".nc-toggle-right-navbar").should("exist").click();

22
scripts/cypress/support/page_objects/navigation.js

@ -70,16 +70,6 @@ export class _loginPage {
loginAndOpenProject(apiType, dbType) {
loginPage.signIn(roles.owner.credentials);
projectsPage.openConfiguredProject(apiType, dbType);
// if (dbType === "mysql") {
// projectsPage.openProject(staticProjects.externalREST.basic.name);
// } else if (dbType === "xcdb") {
// projectsPage.openProject(staticProjects.sampleREST.basic.name);
// } else if (dbType === "postgres") {
// projectsPage.openProject(staticProjects.pgExternalREST.basic.name);
// }
//
// // close team & auth tab
// cy.get('button.ant-tabs-tab-remove').should('exist').click();
}
}
@ -108,9 +98,7 @@ export class _projectsPage {
cy.wait("@waitForPageLoad");
// close team & auth tab
cy.getSettled("button.ant-tabs-tab-remove").should("be.visible").click();
cy.get("button.ant-tabs-tab-remove").should("not.exist");
cy.wait(2000);
cy.closeTableTab();
}
// Open existing project
@ -151,9 +139,7 @@ export class _projectsPage {
cy.get(".nc-metadb-project-name").should("exist");
cy.contains("button", "Create").should("exist");
// fix me! wait till the modal rendering (input highlight) is completed
// focus shifts back to the input field to select text after the dropdown is rendered
cy.wait(1000);
cy.inputHighlightRenderWait();
// feed project name
cy.get(".nc-metadb-project-name", { timeout: 20000 })
@ -183,9 +169,7 @@ export class _projectsPage {
cy.get(".nc-extdb-proj-name").should("exist");
cy.get(".nc-extdb-btn-test-connection").should("exist");
// fix me! wait till the modal rendering (input highlight) is completed
// focus shifts back to the input field to select text after the dropdown is rendered
cy.wait(1000);
cy.inputHighlightRenderWait();
cy.get(".nc-extdb-proj-name").clear().type(projectName);

Loading…
Cancel
Save